Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 15 Nov 2013 19:18:45 +0000 (14:18 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 15 Nov 2013 19:18:45 +0000 (14:18 -0500)
2402 files changed:
CREDITS
Documentation/ABI/stable/sysfs-bus-usb
Documentation/ABI/testing/sysfs-class-net-batman-adv
Documentation/ABI/testing/sysfs-class-net-mesh
Documentation/ABI/testing/sysfs-devices-power
Documentation/ABI/testing/sysfs-power
Documentation/DocBook/80211.tmpl
Documentation/acpi/dsdt-override.txt
Documentation/arm64/tagged-pointers.txt
Documentation/block/00-INDEX
Documentation/block/cmdline-partition.txt
Documentation/connector/ucon.c
Documentation/devicetree/bindings/memory.txt [deleted file]
Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt [deleted file]
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/tmio_mmc.txt
Documentation/devicetree/bindings/net/cpsw-phy-sel.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
Documentation/devicetree/bindings/pci/designware-pcie.txt
Documentation/devicetree/bindings/serial/qca,ar9330-uart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/tty/serial/qca,ar9330-uart.txt [deleted file]
Documentation/kernel-parameters.txt
Documentation/networking/batman-adv.txt
Documentation/networking/bonding.txt
Documentation/networking/can.txt
Documentation/networking/dccp.txt
Documentation/networking/e100.txt
Documentation/networking/ieee802154.txt
Documentation/networking/ip-sysctl.txt
Documentation/networking/l2tp.txt
Documentation/networking/netdev-FAQ.txt
Documentation/networking/netdevices.txt
Documentation/networking/netlink_mmap.txt
Documentation/networking/operstates.txt
Documentation/networking/rxrpc.txt
Documentation/networking/stmmac.txt
Documentation/networking/vortex.txt
Documentation/networking/x25-iface.txt
Documentation/ptp/testptp.c
Documentation/sound/alsa/HD-Audio-Models.txt
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/include/uapi/asm/socket.h
arch/arc/include/asm/spinlock.h
arch/arc/include/asm/uaccess.h
arch/arc/kernel/ptrace.c
arch/arc/kernel/signal.c
arch/arc/kernel/time.c
arch/arc/kernel/unaligned.c
arch/arc/mm/fault.c
arch/arm/Kconfig
arch/arm/Makefile
arch/arm/boot/Makefile
arch/arm/boot/dts/Makefile
arch/arm/boot/dts/am335x-bone-common.dtsi [new file with mode: 0644]
arch/arm/boot/dts/am335x-bone.dts
arch/arm/boot/dts/am335x-boneblack.dts [new file with mode: 0644]
arch/arm/boot/dts/am33xx.dtsi
arch/arm/boot/dts/armada-370-netgear-rn102.dts
arch/arm/boot/dts/armada-xp.dtsi
arch/arm/boot/dts/at91sam9x5.dtsi
arch/arm/boot/dts/atlas6.dtsi
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/boot/dts/imx27.dtsi
arch/arm/boot/dts/imx51.dtsi
arch/arm/boot/dts/imx6q-pinfunc.h
arch/arm/boot/dts/integratorcp.dts
arch/arm/boot/dts/kirkwood.dtsi
arch/arm/boot/dts/omap3-beagle-xm.dts
arch/arm/boot/dts/omap3-igep.dtsi
arch/arm/boot/dts/omap3.dtsi
arch/arm/boot/dts/omap4-panda-common.dtsi
arch/arm/boot/dts/omap4-sdp.dts
arch/arm/boot/dts/omap5.dtsi
arch/arm/boot/dts/prima2.dtsi
arch/arm/boot/dts/r8a73a4.dtsi
arch/arm/boot/dts/r8a7778.dtsi
arch/arm/boot/dts/r8a7779.dtsi
arch/arm/boot/dts/r8a7790.dtsi
arch/arm/boot/dts/sh73a0.dtsi
arch/arm/boot/install.sh
arch/arm/common/edma.c
arch/arm/common/mcpm_entry.c
arch/arm/common/sharpsl_param.c
arch/arm/configs/multi_v7_defconfig
arch/arm/crypto/aes-armv4.S
arch/arm/include/asm/Kbuild
arch/arm/include/asm/jump_label.h
arch/arm/include/asm/mcpm.h
arch/arm/include/asm/syscall.h
arch/arm/include/asm/uaccess.h
arch/arm/kernel/entry-common.S
arch/arm/kernel/entry-header.S
arch/arm/kernel/head.S
arch/arm/kvm/reset.c
arch/arm/mach-at91/at91rm9200_time.c
arch/arm/mach-at91/at91sam926x_time.c
arch/arm/mach-at91/at91sam9g45_reset.S
arch/arm/mach-at91/at91x40_time.c
arch/arm/mach-davinci/board-dm365-evm.c
arch/arm/mach-davinci/include/mach/serial.h
arch/arm/mach-imx/clk-fixup-mux.c
arch/arm/mach-imx/clk-imx27.c
arch/arm/mach-imx/clk-imx51-imx53.c
arch/arm/mach-imx/mach-imx6q.c
arch/arm/mach-imx/system.c
arch/arm/mach-integrator/pci_v3.h
arch/arm/mach-mvebu/coherency.c
arch/arm/mach-mvebu/pmsu.c
arch/arm/mach-mvebu/system-controller.c
arch/arm/mach-omap2/board-generic.c
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/mach-omap2/cclock44xx_data.c
arch/arm/mach-omap2/cpuidle44xx.c
arch/arm/mach-omap2/gpmc-onenand.c
arch/arm/mach-omap2/gpmc.c
arch/arm/mach-omap2/mux.h
arch/arm/mach-omap2/mux34xx.c
arch/arm/mach-omap2/omap-smp.c
arch/arm/mach-omap2/omap_device.c
arch/arm/mach-omap2/timer.c
arch/arm/mach-sa1100/collie.c
arch/arm/mach-shmobile/board-armadillo800eva.c
arch/arm/mach-shmobile/board-lager.c
arch/arm/mach-shmobile/clock-r8a73a4.c
arch/arm/mach-shmobile/clock-sh73a0.c
arch/arm/mach-u300/Kconfig
arch/arm/mach-ux500/cache-l2x0.c
arch/arm/mach-vexpress/tc2_pm.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/init.c
arch/arm/net/bpf_jit_32.c
arch/arm64/Kconfig.debug
arch/arm64/configs/defconfig
arch/arm64/include/asm/hwcap.h
arch/arm64/include/asm/uaccess.h
arch/arm64/kernel/fpsimd.c
arch/arm64/kernel/process.c
arch/arm64/kernel/setup.c
arch/arm64/mm/fault.c
arch/arm64/mm/tlb.S
arch/avr32/include/asm/Kbuild
arch/avr32/include/asm/cputime.h [deleted file]
arch/avr32/include/asm/delay.h [deleted file]
arch/avr32/include/asm/device.h [deleted file]
arch/avr32/include/asm/div64.h [deleted file]
arch/avr32/include/asm/emergency-restart.h [deleted file]
arch/avr32/include/asm/futex.h [deleted file]
arch/avr32/include/asm/irq_regs.h [deleted file]
arch/avr32/include/asm/local.h [deleted file]
arch/avr32/include/asm/local64.h [deleted file]
arch/avr32/include/asm/percpu.h [deleted file]
arch/avr32/include/asm/scatterlist.h [deleted file]
arch/avr32/include/asm/sections.h [deleted file]
arch/avr32/include/asm/topology.h [deleted file]
arch/avr32/include/asm/xor.h [deleted file]
arch/avr32/include/uapi/asm/socket.h
arch/avr32/kernel/process.c
arch/avr32/kernel/time.c
arch/cris/include/uapi/asm/socket.h
arch/frv/include/uapi/asm/socket.h
arch/h8300/include/uapi/asm/socket.h
arch/ia64/include/uapi/asm/socket.h
arch/m32r/include/uapi/asm/socket.h
arch/mips/alchemy/board-mtx1.c
arch/mips/include/asm/cpu-features.h
arch/mips/include/asm/jump_label.h
arch/mips/include/uapi/asm/socket.h
arch/mips/kernel/octeon_switch.S
arch/mips/kernel/perf_event_mipsxx.c
arch/mips/kernel/r2300_switch.S
arch/mips/kernel/r4k_switch.S
arch/mips/mm/c-r4k.c
arch/mips/mm/dma-default.c
arch/mips/mti-malta/malta-int.c
arch/mips/ralink/timer.c
arch/mn10300/include/uapi/asm/socket.h
arch/openrisc/include/asm/prom.h
arch/parisc/configs/712_defconfig
arch/parisc/configs/a500_defconfig
arch/parisc/configs/b180_defconfig
arch/parisc/configs/c3000_defconfig
arch/parisc/configs/c8000_defconfig
arch/parisc/configs/default_defconfig
arch/parisc/include/asm/traps.h
arch/parisc/include/uapi/asm/socket.h
arch/parisc/kernel/head.S
arch/parisc/kernel/smp.c
arch/parisc/kernel/traps.c
arch/parisc/lib/memcpy.c
arch/parisc/mm/fault.c
arch/powerpc/boot/Makefile
arch/powerpc/boot/epapr-wrapper.c [new file with mode: 0644]
arch/powerpc/boot/epapr.c
arch/powerpc/boot/of.c
arch/powerpc/boot/wrapper
arch/powerpc/include/asm/irq.h
arch/powerpc/include/asm/jump_label.h
arch/powerpc/include/asm/processor.h
arch/powerpc/include/uapi/asm/socket.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/iommu.c
arch/powerpc/kernel/irq.c
arch/powerpc/kernel/misc_32.S
arch/powerpc/kernel/misc_64.S
arch/powerpc/kernel/process.c
arch/powerpc/kernel/prom_init.c
arch/powerpc/kernel/sysfs.c
arch/powerpc/kernel/tm.S
arch/powerpc/kernel/vio.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/e500_mmu_host.c
arch/powerpc/lib/checksum_64.S
arch/powerpc/lib/sstep.c
arch/powerpc/mm/init_64.c
arch/powerpc/mm/mem.c
arch/powerpc/net/bpf_jit_comp.c
arch/powerpc/perf/power8-pmu.c
arch/powerpc/platforms/pseries/smp.c
arch/s390/Kconfig
arch/s390/include/asm/jump_label.h
arch/s390/include/asm/mutex.h
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/processor.h
arch/s390/include/asm/spinlock.h
arch/s390/include/asm/timex.h
arch/s390/include/uapi/asm/socket.h
arch/s390/kernel/compat_signal.c
arch/s390/kernel/crash_dump.c
arch/s390/kernel/debug.c
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/s390/kernel/kprobes.c
arch/s390/kvm/interrupt.c
arch/s390/lib/delay.c
arch/s390/net/bpf_jit_comp.c
arch/score/Kconfig
arch/score/Makefile
arch/score/include/asm/checksum.h
arch/score/include/asm/io.h
arch/score/include/asm/pgalloc.h
arch/score/kernel/entry.S
arch/score/kernel/process.c
arch/sparc/Kconfig
arch/sparc/include/asm/floppy_64.h
arch/sparc/include/asm/jump_label.h
arch/sparc/include/uapi/asm/socket.h
arch/sparc/kernel/Makefile
arch/sparc/kernel/ds.c
arch/sparc/kernel/ldc.c
arch/sparc/net/bpf_jit_comp.c
arch/tile/include/asm/atomic.h
arch/tile/include/asm/atomic_32.h
arch/tile/include/asm/cmpxchg.h
arch/tile/include/asm/percpu.h
arch/tile/kernel/hardwall.c
arch/tile/kernel/intvec_32.S
arch/tile/kernel/intvec_64.S
arch/tile/kernel/stack.c
arch/tile/lib/atomic_32.c
arch/um/kernel/exitcode.c
arch/x86/Kconfig
arch/x86/include/asm/cpufeature.h
arch/x86/include/asm/jump_label.h
arch/x86/include/asm/mutex_64.h
arch/x86/include/asm/percpu.h
arch/x86/include/asm/xen/page.h
arch/x86/kernel/apic/x2apic_uv_x.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_uncore.c
arch/x86/kernel/jump_label.c
arch/x86/kernel/kvm.c
arch/x86/kernel/microcode_amd.c
arch/x86/kernel/nmi.c
arch/x86/kernel/reboot.c
arch/x86/kernel/sysfb_simplefb.c
arch/x86/kvm/vmx.c
arch/x86/net/bpf_jit_comp.c
arch/x86/pci/mmconfig-shared.c
arch/x86/platform/efi/efi.c
arch/x86/xen/p2m.c
arch/x86/xen/smp.c
arch/x86/xen/spinlock.c
arch/xtensa/include/uapi/asm/socket.h
arch/xtensa/kernel/entry.S
arch/xtensa/kernel/signal.c
arch/xtensa/platforms/iss/network.c
block/Kconfig
block/Makefile
block/blk-cgroup.c
block/blk-core.c
block/blk-exec.c
block/cfq-iosched.c
block/deadline-iosched.c
block/elevator.c
block/genhd.c
block/partitions/Kconfig
block/partitions/cmdline.c
block/partitions/efi.c
drivers/acpi/Kconfig
drivers/acpi/acpi_ipmi.c
drivers/acpi/device_pm.c
drivers/acpi/power.c
drivers/acpi/scan.c
drivers/ata/ahci.c
drivers/ata/ahci_platform.c
drivers/ata/libahci.c
drivers/ata/libata-acpi.c
drivers/ata/libata-eh.c
drivers/ata/libata-scsi.c
drivers/ata/libata.h
drivers/ata/pata_isapnp.c
drivers/ata/sata_promise.c
drivers/atm/firestream.h
drivers/base/core.c
drivers/base/memory.c
drivers/bcma/host_pci.c
drivers/block/cciss.c
drivers/block/cpqarray.c
drivers/bluetooth/Makefile
drivers/bluetooth/ath3k.c
drivers/bluetooth/bfusb.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bpa10x.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmrvl_sdio.h
drivers/bluetooth/btsdio.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btusb.c
drivers/bluetooth/btwilink.c
drivers/bluetooth/dtl1_cs.c
drivers/bluetooth/hci_bcsp.c
drivers/bluetooth/hci_h4.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_ll.c
drivers/bluetooth/hci_vhci.c
drivers/bus/mvebu-mbus.c
drivers/char/random.c
drivers/char/tpm/xen-tpmfront.c
drivers/clk/clk-nomadik.c
drivers/clk/mvebu/armada-370.c
drivers/clk/socfpga/clk.c
drivers/clk/versatile/clk-icst.c
drivers/clocksource/Kconfig
drivers/clocksource/clksrc-of.c
drivers/clocksource/em_sti.c
drivers/clocksource/exynos_mct.c
drivers/connector/cn_proc.c
drivers/connector/connector.c
drivers/cpufreq/acpi-cpufreq.c
drivers/cpufreq/cpufreq-cpu0.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/exynos5440-cpufreq.c
drivers/cpufreq/imx6q-cpufreq.c
drivers/cpufreq/intel_pstate.c
drivers/cpufreq/s3c64xx-cpufreq.c
drivers/cpufreq/spear-cpufreq.c
drivers/dma/Kconfig
drivers/dma/edma.c
drivers/dma/imx-dma.c
drivers/dma/sh/rcar-hpbdma.c
drivers/gpio/gpio-lynxpoint.c
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-rcar.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/drm_context.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos_drm_buf.c
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
drivers/gpu/drm/gma500/gtt.c
drivers/gpu/drm/i2c/tda998x_drv.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gpu_error.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/msm/adreno/adreno_gpu.c
drivers/gpu/drm/msm/mdp4/mdp4_kms.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_drv.h
drivers/gpu/drm/msm/msm_gem.c
drivers/gpu/drm/msm/msm_gem.h
drivers/gpu/drm/msm/msm_gem_submit.c
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/nouveau/core/subdev/mc/base.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/btc_dpm.c
drivers/gpu/drm/radeon/btc_dpm.h
drivers/gpu/drm/radeon/ci_dpm.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/dce6_afmt.c
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreen_hdmi.c
drivers/gpu/drm/radeon/evergreend.h
drivers/gpu/drm/radeon/kv_dpm.c
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/ni_dpm.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_dpm.c
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_atombios.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_ring.c
drivers/gpu/drm/radeon/radeon_test.c
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/radeon/trinity_dpm.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/hid/Kconfig
drivers/hid/hid-core.c
drivers/hid/hid-holtek-mouse.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-roccat-koneplus.c
drivers/hid/hid-roccat-kovaplus.c
drivers/hid/hid-roccat-pyra.c
drivers/hid/hid-wiimote-core.c
drivers/hid/hid-wiimote-modules.c
drivers/hid/hid-wiimote.h
drivers/hid/hidraw.c
drivers/hid/uhid.c
drivers/hid/usbhid/hid-quirks.c
drivers/hv/connection.c
drivers/hv/hv_kvp.c
drivers/hv/hv_snapshot.c
drivers/hv/hv_util.c
drivers/hwmon/applesmc.c
drivers/i2c/busses/i2c-designware-core.c
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-ismt.c
drivers/i2c/busses/i2c-mv64xxx.c
drivers/i2c/busses/i2c-mxs.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-stu300.c
drivers/i2c/i2c-core.c
drivers/i2c/muxes/i2c-arb-gpio-challenge.c
drivers/i2c/muxes/i2c-mux-gpio.c
drivers/i2c/muxes/i2c-mux-pinctrl.c
drivers/iio/accel/bma180.c
drivers/iio/adc/at91_adc.c
drivers/iio/amplifiers/ad8366.c
drivers/iio/buffer_cb.c
drivers/iio/dac/mcp4725.c
drivers/iio/frequency/adf4350.c
drivers/iio/iio_core.h
drivers/iio/industrialio-buffer.c
drivers/iio/industrialio-core.c
drivers/iio/industrialio-event.c
drivers/iio/magnetometer/st_magn_core.c
drivers/iio/temperature/tmp006.c
drivers/infiniband/Kconfig
drivers/infiniband/core/cma.c
drivers/infiniband/core/uverbs.h
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/hw/amso1100/c2_ae.c
drivers/infiniband/hw/mlx4/main.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mr.c
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/hw/mlx5/srq.c
drivers/infiniband/hw/mthca/mthca_eq.c
drivers/infiniband/hw/ocrdma/ocrdma_hw.c
drivers/infiniband/hw/ocrdma/ocrdma_main.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/srpt/ib_srpt.c
drivers/input/input.c
drivers/input/keyboard/pxa27x_keypad.c
drivers/input/misc/cm109.c
drivers/input/mouse/alps.c
drivers/input/serio/i8042.c
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/iommu/Kconfig
drivers/iommu/arm-smmu.c
drivers/isdn/hardware/eicon/divasmain.c
drivers/isdn/hardware/eicon/um_idi.c
drivers/isdn/icn/icn.c
drivers/isdn/sc/init.c
drivers/mailbox/mailbox-omap2.c
drivers/md/bcache/bcache.h
drivers/md/bcache/bset.c
drivers/md/bcache/btree.c
drivers/md/bcache/journal.c
drivers/md/bcache/request.c
drivers/md/bcache/sysfs.c
drivers/md/bcache/util.c
drivers/md/bcache/util.h
drivers/md/bcache/writeback.c
drivers/md/dm-io.c
drivers/md/dm-mpath.c
drivers/md/dm-snap-persistent.c
drivers/md/dm-snap.c
drivers/md/dm-stats.c
drivers/md/dm-thin.c
drivers/md/dm.c
drivers/md/dm.h
drivers/md/md.c
drivers/md/raid1.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/media/dvb-frontends/tda10071.c
drivers/media/i2c/ad9389b.c
drivers/media/i2c/adv7511.c
drivers/media/i2c/adv7842.c
drivers/media/i2c/ths8200.c
drivers/media/pci/saa7134/saa7134-video.c
drivers/media/platform/s5p-jpeg/jpeg-core.c
drivers/media/platform/sh_vou.c
drivers/media/platform/soc_camera/mx3_camera.c
drivers/media/tuners/e4000.c
drivers/media/usb/stkwebcam/stk-webcam.c
drivers/media/usb/uvc/uvc_driver.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/media/v4l2-core/videobuf2-dma-contig.c
drivers/misc/mei/amthif.c
drivers/misc/mei/bus.c
drivers/misc/mei/client.h
drivers/misc/mei/hbm.c
drivers/misc/mei/init.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h
drivers/mmc/host/sh_mobile_sdhi.c
drivers/mtd/devices/m25p80.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/net/bonding/Makefile
drivers/net/bonding/bond_3ad.c
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_alb.h
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_netlink.c [new file with mode: 0644]
drivers/net/bonding/bond_options.c [new file with mode: 0644]
drivers/net/bonding/bond_procfs.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bonding.h
drivers/net/can/at91_can.c
drivers/net/can/bfin_can.c
drivers/net/can/c_can/c_can.c
drivers/net/can/c_can/c_can_pci.c
drivers/net/can/c_can/c_can_platform.c
drivers/net/can/cc770/cc770_platform.c
drivers/net/can/dev.c
drivers/net/can/flexcan.c
drivers/net/can/janz-ican3.c
drivers/net/can/mcp251x.c
drivers/net/can/mscan/mscan.h
drivers/net/can/pch_can.c
drivers/net/can/sja1000/ems_pci.c
drivers/net/can/sja1000/kvaser_pci.c
drivers/net/can/sja1000/peak_pci.c
drivers/net/can/sja1000/plx_pci.c
drivers/net/can/sja1000/sja1000_platform.c
drivers/net/can/slcan.c
drivers/net/can/softing/softing.h
drivers/net/can/softing/softing_main.c
drivers/net/can/ti_hecc.c
drivers/net/can/usb/kvaser_usb.c
drivers/net/can/usb/peak_usb/pcan_usb_core.c
drivers/net/ethernet/3com/Kconfig
drivers/net/ethernet/3com/typhoon.c
drivers/net/ethernet/8390/8390.h
drivers/net/ethernet/8390/ax88796.c
drivers/net/ethernet/8390/ne2k-pci.c
drivers/net/ethernet/adaptec/starfire.c
drivers/net/ethernet/adi/bfin_mac.h
drivers/net/ethernet/amd/7990.h
drivers/net/ethernet/amd/amd8111e.c
drivers/net/ethernet/amd/atarilance.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/declance.c
drivers/net/ethernet/amd/lance.c
drivers/net/ethernet/amd/pcnet32.c
drivers/net/ethernet/apple/bmac.c
drivers/net/ethernet/arc/emac_main.c
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/atheros/atl1c/atl1c.h
drivers/net/ethernet/atheros/atl1c/atl1c_hw.c
drivers/net/ethernet/atheros/atl1e/atl1e.h
drivers/net/ethernet/atheros/atl1e/atl1e_main.c
drivers/net/ethernet/atheros/atlx/atl2.h
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bnx2.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
drivers/net/ethernet/broadcom/cnic.c
drivers/net/ethernet/broadcom/cnic_if.h
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h
drivers/net/ethernet/brocade/bna/bnad.c
drivers/net/ethernet/brocade/bna/bnad.h
drivers/net/ethernet/calxeda/xgmac.c
drivers/net/ethernet/chelsio/cxgb/common.h
drivers/net/ethernet/chelsio/cxgb/cxgb2.c
drivers/net/ethernet/chelsio/cxgb/pm3393.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
drivers/net/ethernet/chelsio/cxgb3/regs.h
drivers/net/ethernet/chelsio/cxgb3/sge.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/davicom/dm9000.c
drivers/net/ethernet/dec/tulip/de2104x.c
drivers/net/ethernet/dec/tulip/de4x5.c
drivers/net/ethernet/dec/tulip/dmfe.c
drivers/net/ethernet/dec/tulip/tulip_core.c
drivers/net/ethernet/dec/tulip/uli526x.c
drivers/net/ethernet/dec/tulip/winbond-840.c
drivers/net/ethernet/dec/tulip/xircom_cb.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/dlink/sundance.c
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_cmds.h
drivers/net/ethernet/emulex/benet/be_ethtool.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/fealnx.c
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar.h
drivers/net/ethernet/freescale/gianfar_ptp.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/fujitsu/Kconfig
drivers/net/ethernet/hp/hp100.c
drivers/net/ethernet/i825xx/82596.c
drivers/net/ethernet/i825xx/lib82596.c
drivers/net/ethernet/ibm/emac/core.c
drivers/net/ethernet/ibm/emac/debug.h
drivers/net/ethernet/ibm/emac/mal.c
drivers/net/ethernet/ibm/emac/rgmii.h
drivers/net/ethernet/ibm/emac/tah.h
drivers/net/ethernet/ibm/emac/zmii.h
drivers/net/ethernet/ibm/ibmveth.c
drivers/net/ethernet/icplus/ipg.c
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000/e1000.h
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/igb/e1000_82575.h
drivers/net/ethernet/intel/igb/e1000_hw.h
drivers/net/ethernet/intel/igb/e1000_i210.h
drivers/net/ethernet/intel/igb/e1000_mac.h
drivers/net/ethernet/intel/igb/e1000_phy.c
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igbvf/igbvf.h
drivers/net/ethernet/intel/igbvf/netdev.c
drivers/net/ethernet/intel/igbvf/vf.c
drivers/net/ethernet/intel/ixgb/ixgb.h
drivers/net/ethernet/intel/ixgb/ixgb_hw.h
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
drivers/net/ethernet/intel/ixgbevf/ethtool.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/intel/ixgbevf/vf.c
drivers/net/ethernet/jme.c
drivers/net/ethernet/jme.h
drivers/net/ethernet/korina.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvmdio.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/skge.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/cq.c
drivers/net/ethernet/mellanox/mlx4/en_cq.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_main.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_port.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/en_selftest.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/eq.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx4/icm.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mcg.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx4/mr.c
drivers/net/ethernet/mellanox/mlx4/pd.c
drivers/net/ethernet/mellanox/mlx4/port.c
drivers/net/ethernet/mellanox/mlx4/qp.c
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/mellanox/mlx4/srq.c
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/micrel/ks8851_mll.c
drivers/net/ethernet/micrel/ksz884x.c
drivers/net/ethernet/moxa/moxart_ether.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/natsemi/natsemi.c
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/neterion/vxge/vxge-main.c
drivers/net/ethernet/octeon/octeon_mgmt.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
drivers/net/ethernet/packetengines/hamachi.c
drivers/net/ethernet/packetengines/yellowfin.c
drivers/net/ethernet/pasemi/pasemi_mac.c
drivers/net/ethernet/qlogic/netxen/netxen_nic.h
drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h
drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qla3xxx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
drivers/net/ethernet/qlogic/qlge/qlge.h
drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
drivers/net/ethernet/qlogic/qlge/qlge_main.c
drivers/net/ethernet/qlogic/qlge/qlge_mpi.c
drivers/net/ethernet/rdc/r6040.c
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/realtek/8139too.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/sfc/ef10_regs.h
drivers/net/ethernet/sfc/efx.h
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/io.h
drivers/net/ethernet/sfc/mcdi.c
drivers/net/ethernet/sfc/mcdi.h
drivers/net/ethernet/sfc/mcdi_pcol.h
drivers/net/ethernet/sfc/mdio_10g.h
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/nic.c
drivers/net/ethernet/sfc/nic.h
drivers/net/ethernet/sfc/phy.h
drivers/net/ethernet/sfc/rx.c
drivers/net/ethernet/sfc/selftest.h
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/sgi/meth.c
drivers/net/ethernet/sis/sis190.c
drivers/net/ethernet/smsc/Kconfig
drivers/net/ethernet/smsc/epic100.c
drivers/net/ethernet/smsc/smc911x.c
drivers/net/ethernet/smsc/smc911x.h
drivers/net/ethernet/smsc/smc9194.c
drivers/net/ethernet/smsc/smc91c92_cs.c
drivers/net/ethernet/smsc/smc91x.c
drivers/net/ethernet/smsc/smc91x.h
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/smsc/smsc9420.c
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
drivers/net/ethernet/stmicro/stmmac/mmc.h
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
drivers/net/ethernet/sun/cassini.c
drivers/net/ethernet/sun/niu.c
drivers/net/ethernet/sun/sungem.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/sun/sunqe.c
drivers/net/ethernet/tehuti/tehuti.c
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/Makefile
drivers/net/ethernet/ti/cpsw-phy-sel.c [new file with mode: 0644]
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw.h
drivers/net/ethernet/ti/cpts.h
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/tile/tilegx.c
drivers/net/ethernet/toshiba/ps3_gelic_net.h
drivers/net/ethernet/toshiba/ps3_gelic_wireless.h
drivers/net/ethernet/toshiba/spider_net.c
drivers/net/ethernet/toshiba/spider_net.h
drivers/net/ethernet/toshiba/tc35815.c
drivers/net/ethernet/via/via-rhine.c
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xilinx/xilinx_emaclite.c
drivers/net/fddi/skfp/fplustm.c
drivers/net/fddi/skfp/h/smc.h
drivers/net/fddi/skfp/skfddi.c
drivers/net/hamradio/baycom_ser_fdx.c
drivers/net/hamradio/baycom_ser_hdx.c
drivers/net/hamradio/scc.c
drivers/net/hamradio/yam.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/irda/bfin_sir.c
drivers/net/irda/donauboe.c
drivers/net/irda/sh_irda.c
drivers/net/irda/sh_sir.c
drivers/net/irda/sir-dev.h
drivers/net/macvlan.c
drivers/net/netconsole.c
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/at803x.c
drivers/net/phy/marvell.c
drivers/net/phy/mdio-moxart.c [new file with mode: 0644]
drivers/net/phy/micrel.c
drivers/net/plip/plip.c
drivers/net/slip/slip.c
drivers/net/tun.c
drivers/net/usb/Kconfig
drivers/net/usb/Makefile
drivers/net/usb/ax88179_178a.c
drivers/net/usb/catc.c
drivers/net/usb/cdc-phonet.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/dm9601.c
drivers/net/usb/huawei_cdc_ncm.c [new file with mode: 0644]
drivers/net/usb/qmi_wwan.c
drivers/net/usb/usbnet.c
drivers/net/veth.c
drivers/net/virtio_net.c
drivers/net/vmxnet3/vmxnet3_int.h
drivers/net/vxlan.c
drivers/net/wan/farsync.c
drivers/net/wan/hostess_sv11.c
drivers/net/wan/sbni.c
drivers/net/wan/sealevel.c
drivers/net/wan/wanxl.c
drivers/net/wan/x25_asy.h
drivers/net/wan/z85230.h
drivers/net/wimax/i2400m/i2400m-usb.h
drivers/net/wimax/i2400m/i2400m.h
drivers/net/wireless/adm8211.c
drivers/net/wireless/airo.c
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ar5523/ar5523.c
drivers/net/wireless/ath/ath10k/bmi.c
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/debug.h
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htc.h
drivers/net/wireless/ath/ath10k/htt.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.h
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/rx_desc.h
drivers/net/wireless/ath/ath10k/trace.h
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath10k/txrx.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath5k/ahb.c
drivers/net/wireless/ath/ath6kl/common.h
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/htc.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ahb.c
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/antenna.c
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9002_calib.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_mci.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9003_rtt.c
drivers/net/wireless/ath/ath9k/ar9485_initvals.h
drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/calib.h
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/dfs.h
drivers/net/wireless/ath/ath9k/dfs_debug.c
drivers/net/wireless/ath/ath9k/dfs_debug.h
drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pri_detector.c [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pri_detector.h [deleted file]
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/eeprom_9287.c
drivers/net/wireless/ath/ath9k/eeprom_def.c
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/htc_drv_debug.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/wmi.h
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/dfs_pattern_detector.c [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pattern_detector.h [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pri_detector.c [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pri_detector.h [new file with mode: 0644]
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wcn36xx/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/Makefile [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/debug.c [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/debug.h [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/dxe.c [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/dxe.h [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/hal.h [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/main.c [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/pmc.c [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/pmc.h [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/smd.c [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/smd.h [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/txrx.c [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/txrx.h [new file with mode: 0644]
drivers/net/wireless/ath/wcn36xx/wcn36xx.h [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/atmel.c
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/b43legacy/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
drivers/net/wireless/brcm80211/brcmsmac/antsel.h
drivers/net/wireless/brcm80211/brcmsmac/channel.h
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmsmac/main.h
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h
drivers/net/wireless/brcm80211/brcmsmac/pmu.h
drivers/net/wireless/brcm80211/brcmsmac/pub.h
drivers/net/wireless/brcm80211/brcmsmac/rate.h
drivers/net/wireless/brcm80211/brcmsmac/stf.h
drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/brcm80211/include/brcmu_d11.h
drivers/net/wireless/brcm80211/include/brcmu_utils.h
drivers/net/wireless/cw1200/cw1200_spi.c
drivers/net/wireless/hostap/hostap_info.c
drivers/net/wireless/ipw2x00/ipw2200.c
drivers/net/wireless/ipw2x00/libipw.h
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/3945.h
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/4965.h
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/rs.h
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/constants.h
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/testmode.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/debugfs.c
drivers/net/wireless/libertas/firmware.c
drivers/net/wireless/libertas/if_cs.c
drivers/net/wireless/libertas/if_sdio.c
drivers/net/wireless/libertas/if_spi.c
drivers/net/wireless/libertas/if_usb.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ie.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwifiex/wmm.h
drivers/net/wireless/mwl8k.c
drivers/net/wireless/orinoco/orinoco.h
drivers/net/wireless/orinoco/orinoco_nortel.c
drivers/net/wireless/orinoco/orinoco_pci.c
drivers/net/wireless/orinoco/orinoco_plx.c
drivers/net/wireless/orinoco/orinoco_tmd.c
drivers/net/wireless/p54/p54pci.c
drivers/net/wireless/p54/p54spi.c
drivers/net/wireless/prism54/isl_ioctl.c
drivers/net/wireless/prism54/islpci_dev.c
drivers/net/wireless/prism54/oid_mgt.c
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/Makefile
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800mmio.c [new file with mode: 0644]
drivers/net/wireless/rt2x00/rt2800mmio.h [new file with mode: 0644]
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800pci.h
drivers/net/wireless/rt2x00/rt2800soc.c [new file with mode: 0644]
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00crypto.c
drivers/net/wireless/rt2x00/rt2x00debug.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00lib.h
drivers/net/wireless/rt2x00/rt2x00link.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00pci.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00usb.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/base.h
drivers/net/wireless/rtlwifi/cam.h
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/efuse.c
drivers/net/wireless/rtlwifi/efuse.h
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
drivers/net/wireless/rtlwifi/rtl8188ee/phy.c
drivers/net/wireless/rtlwifi/rtl8188ee/phy.h
drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
drivers/net/wireless/rtlwifi/rtl8188ee/trx.c
drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c
drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h
drivers/net/wireless/rtlwifi/rtl8192ce/def.h
drivers/net/wireless/rtlwifi/rtl8192ce/phy.h
drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
drivers/net/wireless/rtlwifi/rtl8192ce/rf.h
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
drivers/net/wireless/rtlwifi/rtl8192cu/mac.c
drivers/net/wireless/rtlwifi/rtl8192cu/rf.h
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192cu/trx.c
drivers/net/wireless/rtlwifi/rtl8192de/dm.c
drivers/net/wireless/rtlwifi/rtl8192de/hw.c
drivers/net/wireless/rtlwifi/rtl8192de/hw.h
drivers/net/wireless/rtlwifi/rtl8192de/phy.c
drivers/net/wireless/rtlwifi/rtl8192de/phy.h
drivers/net/wireless/rtlwifi/rtl8192de/rf.h
drivers/net/wireless/rtlwifi/rtl8192de/sw.c
drivers/net/wireless/rtlwifi/rtl8192de/trx.c
drivers/net/wireless/rtlwifi/rtl8192se/reg.h
drivers/net/wireless/rtlwifi/rtl8192se/rf.c
drivers/net/wireless/rtlwifi/rtl8192se/trx.c
drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
drivers/net/wireless/rtlwifi/rtl8723ae/phy.h
drivers/net/wireless/rtlwifi/rtl8723ae/rf.h
drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
drivers/net/wireless/rtlwifi/usb.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wl1251/wl1251.h
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/reg.h
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/testmode.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/net/xen-netback/common.h
drivers/net/xen-netback/interface.c
drivers/net/xen-netback/netback.c
drivers/net/xen-netback/xenbus.c
drivers/net/xen-netfront.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/mei_phy.c
drivers/nfc/microread/i2c.c
drivers/nfc/microread/mei.c
drivers/nfc/microread/microread.c
drivers/nfc/nfcsim.c
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/i2c.c
drivers/nfc/pn544/pn544.c
drivers/nfc/port100.c [new file with mode: 0644]
drivers/of/Kconfig
drivers/of/Makefile
drivers/of/base.c
drivers/of/fdt.c
drivers/of/of_reserved_mem.c [deleted file]
drivers/of/platform.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/pci-acpi.c
drivers/pci/pci.c
drivers/pinctrl/pinconf.c
drivers/pinctrl/pinctrl-exynos.c
drivers/pinctrl/pinctrl-palmas.c
drivers/pinctrl/pinctrl-tegra114.c
drivers/platform/x86/Kconfig
drivers/platform/x86/sony-laptop.c
drivers/regulator/da9063-regulator.c
drivers/regulator/palmas-regulator.c
drivers/regulator/ti-abb-regulator.c
drivers/regulator/wm831x-ldo.c
drivers/regulator/wm8350-regulator.c
drivers/s390/block/dasd_eckd.c
drivers/s390/char/sclp.c
drivers/s390/char/sclp_cmd.c
drivers/s390/char/tty3270.c
drivers/s390/char/vmlogrdr.c
drivers/s390/cio/cio.c
drivers/s390/cio/qdio_main.c
drivers/s390/net/qeth_core_main.c
drivers/scsi/BusLogic.c
drivers/scsi/aacraid/linit.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/sd.c
drivers/scsi/sg.c
drivers/spi/spi-atmel.c
drivers/spi/spi-clps711x.c
drivers/spi/spi-fsl-dspi.c
drivers/spi/spi-mpc512x-psc.c
drivers/spi/spi-pxa2xx.c
drivers/spi/spi-s3c64xx.c
drivers/spi/spi-sh-hspi.c
drivers/staging/bcm/Bcmchar.c
drivers/staging/comedi/Kconfig
drivers/staging/comedi/drivers/ni_65xx.c
drivers/staging/dgap/dgap_driver.c
drivers/staging/dgnc/dgnc_driver.c
drivers/staging/iio/Kconfig
drivers/staging/iio/light/isl29018.c
drivers/staging/iio/magnetometer/hmc5843.c
drivers/staging/iio/meter/ade7854-spi.c
drivers/staging/imx-drm/imx-drm-core.c
drivers/staging/line6/toneport.c
drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c
drivers/staging/lustre/lustre/Kconfig
drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
drivers/staging/lustre/lustre/libcfs/workitem.c
drivers/staging/lustre/lustre/obdecho/echo_client.c
drivers/staging/lustre/lustre/ptlrpc/pinger.c
drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
drivers/staging/lustre/lustre/ptlrpc/service.c
drivers/staging/media/msi3101/Kconfig
drivers/staging/media/msi3101/sdr-msi3101.c
drivers/staging/octeon-usb/cvmx-usb.c
drivers/staging/ozwpan/ozcdev.c
drivers/staging/rtl8188eu/core/rtw_ieee80211.c
drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
drivers/staging/rtl8188eu/core/rtw_mp.c
drivers/staging/rtl8188eu/core/rtw_wlan_util.c
drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
drivers/staging/rtl8188eu/include/odm.h
drivers/staging/rtl8188eu/include/rtl8188e_hal.h
drivers/staging/rtl8188eu/include/rtw_mlme_ext.h
drivers/staging/rtl8188eu/os_dep/ioctl_linux.c
drivers/staging/rtl8188eu/os_dep/usb_intf.c
drivers/staging/rtl8192u/r819xU_cmdpkt.c
drivers/staging/sb105x/sb_pci_mp.c
drivers/staging/vt6656/card.c
drivers/staging/vt6656/iwctl.c
drivers/staging/vt6656/main_usb.c
drivers/staging/vt6656/rxtx.c
drivers/staging/wlags49_h2/wl_priv.c
drivers/staging/xillybus/xillybus_core.c
drivers/staging/zram/zram_drv.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_nego.c
drivers/target/iscsi/iscsi_target_util.c
drivers/target/target_core_pscsi.c
drivers/target/target_core_sbc.c
drivers/target/target_core_transport.c
drivers/target/target_core_xcopy.c
drivers/thermal/samsung/exynos_thermal_common.c
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/samsung/exynos_tmu.h
drivers/thermal/samsung/exynos_tmu_data.c
drivers/thermal/samsung/exynos_tmu_data.h
drivers/thermal/thermal_hwmon.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/x86_pkg_temp_thermal.c
drivers/tty/hvc/hvc_xen.c
drivers/tty/n_tty.c
drivers/tty/serial/atmel_serial.c
drivers/tty/serial/imx.c
drivers/tty/serial/pch_uart.c
drivers/tty/serial/serial-tegra.c
drivers/tty/serial/vt8500_serial.c
drivers/tty/tty_ioctl.c
drivers/uio/uio.c
drivers/usb/chipidea/Kconfig
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/ci_hdrc_pci.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/host.c
drivers/usb/chipidea/udc.c
drivers/usb/core/devio.c
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/cdc2.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/f_ecm.c
drivers/usb/gadget/f_eem.c
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/f_mass_storage.c
drivers/usb/gadget/fotg210-udc.c
drivers/usb/gadget/fusb300_udc.c
drivers/usb/gadget/multi.c
drivers/usb/gadget/mv_u3d_core.c
drivers/usb/gadget/pxa25x_udc.c
drivers/usb/gadget/s3c-hsotg.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-grlib.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-octeon.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-pmcmsp.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sead3.c
drivers/usb/host/ehci-sh.c
drivers/usb/host/ehci-tilegx.c
drivers/usb/host/ehci-w90x900.c
drivers/usb/host/ehci-xilinx-of.c
drivers/usb/host/fsl-mph-dr-of.c
drivers/usb/host/imx21-hcd.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-q.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/uhci-pci.c
drivers/usb/host/uhci-q.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/Kconfig
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_virthub.c
drivers/usb/phy/phy-gpio-vbus-usb.c
drivers/usb/phy/phy-omap-usb3.c
drivers/usb/serial/Kconfig
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio_ids.h
drivers/usb/serial/option.c
drivers/usb/serial/pl2303.c
drivers/usb/serial/ti_usb_3410_5052.c
drivers/usb/storage/scsiglue.c
drivers/usb/storage/unusual_devs.h
drivers/vfio/vfio_iommu_type1.c
drivers/vhost/scsi.c
drivers/vhost/vhost.c
drivers/video/au1100fb.c
drivers/video/au1200fb.c
drivers/video/mmp/hw/mmp_ctrl.c
drivers/video/mxsfb.c
drivers/video/neofb.c
drivers/video/of_display_timing.c
drivers/video/omap2/displays-new/Kconfig
drivers/video/omap2/displays-new/connector-analog-tv.c
drivers/video/omap2/displays-new/connector-dvi.c
drivers/video/omap2/displays-new/connector-hdmi.c
drivers/video/omap2/dss/dispc.c
drivers/video/s3fb.c
drivers/w1/w1.c
drivers/watchdog/hpwdt.c
drivers/watchdog/kempld_wdt.c
drivers/watchdog/sunxi_wdt.c
drivers/watchdog/ts72xx_wdt.c
drivers/xen/balloon.c
fs/afs/dir.c
fs/aio.c
fs/binfmt_elf.c
fs/bio-integrity.c
fs/bio.c
fs/btrfs/async-thread.c
fs/btrfs/async-thread.h
fs/btrfs/btrfs_inode.h
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/dev-replace.c
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/free-space-cache.h
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/relocation.c
fs/btrfs/root-tree.c
fs/btrfs/scrub.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/tree-log.c
fs/btrfs/volumes.c
fs/buffer.c
fs/cachefiles/namei.c
fs/cachefiles/xattr.c
fs/cifs/cifsfs.c
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifssmb.c
fs/cifs/file.c
fs/cifs/fscache.c
fs/cifs/fscache.h
fs/cifs/inode.c
fs/cifs/netmisc.c
fs/cifs/readdir.c
fs/cifs/sess.c
fs/cifs/smb2pdu.c
fs/cifs/smbfsctl.h
fs/cifs/transport.c
fs/dcache.c
fs/ecryptfs/crypto.c
fs/ecryptfs/keystore.c
fs/eventpoll.c
fs/ext3/namei.c
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/xattr.c
fs/file_table.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/jfs/jfs_inode.c
fs/namei.c
fs/nfs/dir.c
fs/nfs/nfs4file.c
fs/nfs/nfs4filelayoutdev.c
fs/nfs/nfs4proc.c
fs/nilfs2/page.c
fs/nilfs2/segment.c
fs/ocfs2/dcache.c
fs/ocfs2/super.c
fs/proc/inode.c
fs/proc/task_mmu.c
fs/reiserfs/journal.c
fs/select.c
fs/seq_file.c
fs/statfs.c
fs/super.c
fs/sysv/super.c
fs/udf/ialloc.c
fs/udf/super.c
fs/udf/udf_sb.h
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_da_btree.c
fs/xfs/xfs_dir2_block.c
fs/xfs/xfs_dir2_format.h
fs/xfs/xfs_dir2_readdir.c
fs/xfs/xfs_dir2_sf.c
fs/xfs/xfs_dquot.c
fs/xfs/xfs_fs.h
fs/xfs/xfs_icache.c
fs/xfs/xfs_log_recover.c
include/acpi/acpi_bus.h
include/asm-generic/hugetlb.h
include/asm-generic/vtime.h
include/drm/drmP.h
include/dt-bindings/pinctrl/omap.h
include/linux/balloon_compaction.h
include/linux/blkdev.h
include/linux/compiler-gcc4.h
include/linux/crc32.h
include/linux/device-mapper.h
include/linux/etherdevice.h
include/linux/fcdevice.h
include/linux/fddidevice.h
include/linux/filter.h
include/linux/hippidevice.h
include/linux/hyperv.h
include/linux/ieee80211.h
include/linux/if_macvlan.h
include/linux/if_vlan.h
include/linux/inetdevice.h
include/linux/intel-iommu.h
include/linux/ipc_namespace.h
include/linux/ipv6.h
include/linux/jump_label.h
include/linux/jump_label_ratelimit.h
include/linux/kernel.h
include/linux/memcontrol.h
include/linux/miscdevice.h
include/linux/mlx4/cmd.h
include/linux/mlx4/device.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mutex.h
include/linux/net.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/netfilter.h
include/linux/netfilter/ipset/ip_set.h
include/linux/netfilter/ipset/ip_set_comment.h [new file with mode: 0644]
include/linux/netfilter/ipset/ip_set_timeout.h
include/linux/netfilter/nf_conntrack_common.h
include/linux/netfilter/nf_conntrack_h323.h
include/linux/netfilter/nf_conntrack_proto_gre.h
include/linux/netfilter/nf_conntrack_sip.h
include/linux/netfilter/nfnetlink.h
include/linux/netfilter/nfnetlink_acct.h
include/linux/netfilter/x_tables.h
include/linux/netfilter_bridge.h
include/linux/netfilter_ipv4.h
include/linux/netfilter_ipv6.h
include/linux/netpoll.h
include/linux/nfs_xdr.h
include/linux/of_irq.h
include/linux/of_reserved_mem.h [deleted file]
include/linux/percpu.h
include/linux/perf_event.h
include/linux/random.h
include/linux/regulator/driver.h
include/linux/rtnetlink.h
include/linux/sched.h
include/linux/skbuff.h
include/linux/smp.h
include/linux/ssb/ssb_driver_gige.h
include/linux/tc_act/tc_defact.h [deleted file]
include/linux/timex.h
include/linux/usb/cdc_ncm.h
include/linux/usb/usb_phy_gen_xceiv.h
include/linux/usb/usbnet.h
include/linux/usb_usual.h
include/linux/vgaarb.h
include/linux/yam.h
include/net/addrconf.h
include/net/bluetooth/a2mp.h [deleted file]
include/net/bluetooth/amp.h [deleted file]
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/rfcomm.h
include/net/bluetooth/sco.h
include/net/bluetooth/smp.h [deleted file]
include/net/caif/caif_hsi.h
include/net/cfg80211.h
include/net/checksum.h
include/net/cipso_ipv4.h
include/net/codel.h
include/net/compat.h
include/net/dcbevent.h
include/net/dn.h
include/net/dn_dev.h
include/net/dn_fib.h
include/net/dn_neigh.h
include/net/dn_nsp.h
include/net/dn_route.h
include/net/dst.h
include/net/esp.h
include/net/fib_rules.h
include/net/flow.h
include/net/flow_keys.h
include/net/garp.h
include/net/gen_stats.h
include/net/genetlink.h
include/net/gre.h
include/net/icmp.h
include/net/if_inet6.h
include/net/inet6_connection_sock.h
include/net/inet6_hashtables.h
include/net/inet_common.h
include/net/inet_connection_sock.h
include/net/inet_frag.h
include/net/inet_hashtables.h
include/net/inet_sock.h
include/net/inet_timewait_sock.h
include/net/inetpeer.h
include/net/ip.h
include/net/ip6_checksum.h
include/net/ip6_fib.h
include/net/ip6_route.h
include/net/ip_fib.h
include/net/ip_tunnels.h
include/net/ip_vs.h
include/net/ipv6.h
include/net/ipx.h
include/net/irda/ircomm_tty.h
include/net/irda/irda.h
include/net/irda/irda_device.h
include/net/irda/irlap_event.h
include/net/irda/irlap_frame.h
include/net/iw_handler.h
include/net/lapb.h
include/net/llc.h
include/net/llc_c_ac.h
include/net/llc_c_ev.h
include/net/llc_conn.h
include/net/llc_if.h
include/net/llc_pdu.h
include/net/llc_s_ac.h
include/net/llc_s_ev.h
include/net/llc_sap.h
include/net/mac80211.h
include/net/mac802154.h
include/net/mrp.h
include/net/ndisc.h
include/net/net_namespace.h
include/net/netevent.h
include/net/netfilter/ipv4/nf_conntrack_ipv4.h
include/net/netfilter/ipv4/nf_defrag_ipv4.h
include/net/netfilter/ipv6/nf_defrag_ipv6.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_conntrack_acct.h
include/net/netfilter/nf_conntrack_core.h
include/net/netfilter/nf_conntrack_ecache.h
include/net/netfilter/nf_conntrack_extend.h
include/net/netfilter/nf_conntrack_helper.h
include/net/netfilter/nf_conntrack_l3proto.h
include/net/netfilter/nf_conntrack_l4proto.h
include/net/netfilter/nf_conntrack_seqadj.h
include/net/netfilter/nf_conntrack_synproxy.h
include/net/netfilter/nf_conntrack_timeout.h
include/net/netfilter/nf_conntrack_timestamp.h
include/net/netfilter/nf_nat.h
include/net/netfilter/nf_nat_core.h
include/net/netfilter/nf_nat_helper.h
include/net/netfilter/nf_nat_l3proto.h
include/net/netfilter/nf_nat_l4proto.h
include/net/netfilter/nf_queue.h
include/net/netfilter/nf_tables.h [new file with mode: 0644]
include/net/netfilter/nf_tables_core.h [new file with mode: 0644]
include/net/netfilter/nf_tables_ipv4.h [new file with mode: 0644]
include/net/netfilter/nf_tables_ipv6.h [new file with mode: 0644]
include/net/netfilter/xt_rateest.h
include/net/netlink.h
include/net/netns/ipv4.h
include/net/netns/nftables.h [new file with mode: 0644]
include/net/netrom.h
include/net/nfc/digital.h [new file with mode: 0644]
include/net/nfc/hci.h
include/net/nfc/nci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/p8022.h
include/net/ping.h
include/net/protocol.h
include/net/psnap.h
include/net/raw.h
include/net/rawv6.h
include/net/request_sock.h
include/net/rose.h
include/net/route.h
include/net/rtnetlink.h
include/net/sch_generic.h
include/net/scm.h
include/net/sctp/checksum.h
include/net/sctp/sctp.h
include/net/secure_seq.h
include/net/sock.h
include/net/stp.h
include/net/tcp.h
include/net/tcp_memcontrol.h
include/net/udp.h
include/net/udplite.h
include/net/vxlan.h
include/net/wext.h
include/net/wimax.h
include/net/x25.h
include/net/xfrm.h
include/sound/rcar_snd.h
include/trace/events/block.h
include/trace/events/btrfs.h
include/trace/events/target.h
include/uapi/asm-generic/socket.h
include/uapi/drm/drm_mode.h
include/uapi/drm/radeon_drm.h
include/uapi/linux/can/bcm.h
include/uapi/linux/can/error.h
include/uapi/linux/can/gw.h
include/uapi/linux/can/netlink.h
include/uapi/linux/can/raw.h
include/uapi/linux/hsr_netlink.h [new file with mode: 0644]
include/uapi/linux/if.h
include/uapi/linux/if_bonding.h
include/uapi/linux/if_ether.h
include/uapi/linux/if_link.h
include/uapi/linux/in.h
include/uapi/linux/ip_vs.h
include/uapi/linux/netfilter/Kbuild
include/uapi/linux/netfilter/ipset/ip_set.h
include/uapi/linux/netfilter/nf_conntrack_common.h
include/uapi/linux/netfilter/nf_tables.h [new file with mode: 0644]
include/uapi/linux/netfilter/nf_tables_compat.h [new file with mode: 0644]
include/uapi/linux/netfilter/nfnetlink.h
include/uapi/linux/netfilter/nfnetlink_cttimeout.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/linux/openvswitch.h
include/uapi/linux/perf_event.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/tc_act/Kbuild
include/uapi/linux/tc_act/tc_defact.h [new file with mode: 0644]
include/uapi/rdma/ib_user_verbs.h
include/xen/interface/io/netif.h
init/main.c
ipc/ipc_sysctl.c
ipc/msg.c
ipc/sem.c
ipc/shm.c
ipc/util.c
ipc/util.h
kernel/audit.c
kernel/cgroup.c
kernel/context_tracking.c
kernel/events/core.c
kernel/events/ring_buffer.c
kernel/jump_label.c
kernel/kmod.c
kernel/mutex.c
kernel/params.c
kernel/pid.c
kernel/power/hibernate.c
kernel/power/snapshot.c
kernel/power/user.c
kernel/reboot.c
kernel/sched/fair.c
kernel/softirq.c
kernel/time/clockevents.c
kernel/watchdog.c
lib/Kconfig.debug
lib/crc32.c
lib/hexdump.c
lib/kobject.c
lib/lockref.c
lib/percpu-refcount.c
lib/scatterlist.c
mm/Kconfig
mm/bounce.c
mm/compaction.c
mm/filemap.c
mm/huge_memory.c
mm/hugetlb.c
mm/hwpoison-inject.c
mm/list_lru.c
mm/madvise.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/migrate.c
mm/mlock.c
mm/mprotect.c
mm/mremap.c
mm/oom_kill.c
mm/page-writeback.c
mm/page_alloc.c
mm/pagewalk.c
mm/slab_common.c
mm/swapfile.c
mm/vmscan.c
mm/zswap.c
net/802/mrp.c
net/8021q/vlan.c
net/8021q/vlan.h
net/8021q/vlan_dev.c
net/8021q/vlan_netlink.c
net/Kconfig
net/Makefile
net/ax25/af_ax25.c
net/batman-adv/Makefile
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/bridge_loop_avoidance.h
net/batman-adv/debugfs.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/distributed-arp-table.h
net/batman-adv/fragmentation.c [new file with mode: 0644]
net/batman-adv/fragmentation.h [new file with mode: 0644]
net/batman-adv/gateway_client.c
net/batman-adv/gateway_client.h
net/batman-adv/gateway_common.c
net/batman-adv/gateway_common.h
net/batman-adv/hard-interface.c
net/batman-adv/hard-interface.h
net/batman-adv/icmp_socket.c
net/batman-adv/icmp_socket.h
net/batman-adv/main.c
net/batman-adv/main.h
net/batman-adv/network-coding.c
net/batman-adv/network-coding.h
net/batman-adv/originator.c
net/batman-adv/originator.h
net/batman-adv/packet.h
net/batman-adv/routing.c
net/batman-adv/routing.h
net/batman-adv/send.c
net/batman-adv/send.h
net/batman-adv/soft-interface.c
net/batman-adv/soft-interface.h
net/batman-adv/sysfs.c
net/batman-adv/sysfs.h
net/batman-adv/translation-table.c
net/batman-adv/translation-table.h
net/batman-adv/types.h
net/batman-adv/unicast.c [deleted file]
net/batman-adv/unicast.h [deleted file]
net/batman-adv/vis.c [deleted file]
net/batman-adv/vis.h [deleted file]
net/bluetooth/Makefile
net/bluetooth/a2mp.c
net/bluetooth/a2mp.h [new file with mode: 0644]
net/bluetooth/af_bluetooth.c
net/bluetooth/amp.c
net/bluetooth/amp.h [new file with mode: 0644]
net/bluetooth/bnep/core.c
net/bluetooth/cmtp/core.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/hci_sysfs.c
net/bluetooth/hidp/core.c
net/bluetooth/hidp/hidp.h
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/bluetooth/smp.h [new file with mode: 0644]
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_input.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netfilter.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_private_stp.h
net/bridge/br_stp_if.c
net/bridge/br_vlan.c
net/bridge/netfilter/Kconfig
net/bridge/netfilter/Makefile
net/bridge/netfilter/ebt_among.c
net/bridge/netfilter/ebt_ulog.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/bridge/netfilter/nf_tables_bridge.c [new file with mode: 0644]
net/caif/cfpkt_skbuff.c
net/can/af_can.h
net/ceph/auth_none.h
net/ceph/auth_x.h
net/ceph/crypto.h
net/compat.c
net/core/datagram.c
net/core/dev.c
net/core/dev_addr_lists.c
net/core/ethtool.c
net/core/fib_rules.c
net/core/filter.c
net/core/flow_dissector.c
net/core/iovec.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/netpoll.c
net/core/netprio_cgroup.c
net/core/rtnetlink.c
net/core/secure_seq.c
net/core/skbuff.c
net/core/sock.c
net/core/utils.c
net/dccp/ackvec.h
net/dccp/ccid.h
net/dccp/ccids/lib/loss_interval.h
net/dccp/ccids/lib/packet_history.h
net/dccp/ccids/lib/tfrc.h
net/dccp/dccp.h
net/dccp/feat.h
net/dccp/ipv4.c
net/dccp/ipv6.c
net/dccp/ipv6.h
net/dccp/minisocks.c
net/dccp/output.c
net/dccp/proto.c
net/decnet/netfilter/dn_rtmsg.c
net/ethernet/eth.c
net/hsr/Kconfig [new file with mode: 0644]
net/hsr/Makefile [new file with mode: 0644]
net/hsr/hsr_device.c [new file with mode: 0644]
net/hsr/hsr_device.h [new file with mode: 0644]
net/hsr/hsr_framereg.c [new file with mode: 0644]
net/hsr/hsr_framereg.h [new file with mode: 0644]
net/hsr/hsr_main.c [new file with mode: 0644]
net/hsr/hsr_main.h [new file with mode: 0644]
net/hsr/hsr_netlink.c [new file with mode: 0644]
net/hsr/hsr_netlink.h [new file with mode: 0644]
net/ieee802154/6lowpan.c
net/ipv4/af_inet.c
net/ipv4/esp4.c
net/ipv4/fib_frontend.c
net/ipv4/fib_lookup.h
net/ipv4/fib_semantics.c
net/ipv4/fib_trie.c
net/ipv4/gre_demux.c
net/ipv4/gre_offload.c
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_diag.c
net/ipv4/inet_fragment.c
net/ipv4/inet_hashtables.c
net/ipv4/inet_timewait_sock.c
net/ipv4/ip_fragment.c
net/ipv4/ip_output.c
net/ipv4/ip_sockglue.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ip_vti.c
net/ipv4/ipip.c
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/Makefile
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/arptable_filter.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_CLUSTERIP.c
net/ipv4/netfilter/ipt_SYNPROXY.c
net/ipv4/netfilter/ipt_ULOG.c
net/ipv4/netfilter/iptable_filter.c
net/ipv4/netfilter/iptable_mangle.c
net/ipv4/netfilter/iptable_nat.c
net/ipv4/netfilter/iptable_raw.c
net/ipv4/netfilter/iptable_security.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv4/netfilter/nf_tables_arp.c [new file with mode: 0644]
net/ipv4/netfilter/nf_tables_ipv4.c [new file with mode: 0644]
net/ipv4/netfilter/nft_chain_nat_ipv4.c [new file with mode: 0644]
net/ipv4/netfilter/nft_chain_route_ipv4.c [new file with mode: 0644]
net/ipv4/netfilter/nft_reject_ipv4.c [new file with mode: 0644]
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_bic.c
net/ipv4/tcp_cong.c
net/ipv4/tcp_cubic.c
net/ipv4/tcp_fastopen.c
net/ipv4/tcp_highspeed.c
net/ipv4/tcp_htcp.c
net/ipv4/tcp_hybla.c
net/ipv4/tcp_illinois.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_lp.c
net/ipv4/tcp_memcontrol.c
net/ipv4/tcp_metrics.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_offload.c
net/ipv4/tcp_output.c
net/ipv4/tcp_probe.c
net/ipv4/tcp_scalable.c
net/ipv4/tcp_timer.c
net/ipv4/tcp_vegas.c
net/ipv4/tcp_vegas.h
net/ipv4/tcp_veno.c
net/ipv4/tcp_yeah.c
net/ipv4/udp.c
net/ipv4/udp_impl.h
net/ipv4/udp_offload.c
net/ipv4/xfrm4_mode_tunnel.c
net/ipv4/xfrm4_policy.c
net/ipv6/Kconfig
net/ipv6/Makefile
net/ipv6/addrconf.c
net/ipv6/af_inet6.c
net/ipv6/ah6.c
net/ipv6/datagram.c
net/ipv6/esp6.c
net/ipv6/inet6_connection_sock.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_flowlabel.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6_tunnel.c
net/ipv6/ip6_vti.c [new file with mode: 0644]
net/ipv6/ipcomp6.c
net/ipv6/ipv6_sockglue.c
net/ipv6/mcast.c
net/ipv6/netfilter/Kconfig
net/ipv6/netfilter/Makefile
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6t_REJECT.c
net/ipv6/netfilter/ip6t_SYNPROXY.c
net/ipv6/netfilter/ip6table_filter.c
net/ipv6/netfilter/ip6table_mangle.c
net/ipv6/netfilter/ip6table_nat.c
net/ipv6/netfilter/ip6table_raw.c
net/ipv6/netfilter/ip6table_security.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
net/ipv6/netfilter/nf_tables_ipv6.c [new file with mode: 0644]
net/ipv6/netfilter/nft_chain_nat_ipv6.c [new file with mode: 0644]
net/ipv6/netfilter/nft_chain_route_ipv6.c [new file with mode: 0644]
net/ipv6/ping.c
net/ipv6/raw.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/syncookies.c
net/ipv6/tcp_ipv6.c
net/ipv6/tcpv6_offload.c
net/ipv6/udp.c
net/ipv6/udp_impl.h
net/ipv6/udp_offload.c
net/ipv6/xfrm6_mode_tunnel.c
net/ipv6/xfrm6_policy.c
net/irda/irnet/irnet.h
net/key/af_key.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_debugfs.c
net/l2tp/l2tp_ip6.c
net/l2tp/l2tp_netlink.c
net/l2tp/l2tp_ppp.c
net/lapb/lapb_timer.c
net/mac80211/Kconfig
net/mac80211/aes_ccm.c
net/mac80211/aes_ccm.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debug.h
net/mac80211/debugfs.c
net/mac80211/debugfs_netdev.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mesh_ps.c
net/mac80211/mlme.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_pid_debugfs.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/spectmgmt.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wpa.c
net/mac802154/ieee802154_dev.c
net/mac802154/wpan.c
net/mpls/mpls_gso.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/core.c
net/netfilter/ipset/Kconfig
net/netfilter/ipset/Makefile
net/netfilter/ipset/ip_set_bitmap_gen.h
net/netfilter/ipset/ip_set_bitmap_ip.c
net/netfilter/ipset/ip_set_bitmap_ipmac.c
net/netfilter/ipset/ip_set_bitmap_port.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipset/ip_set_getport.c
net/netfilter/ipset/ip_set_hash_gen.h
net/netfilter/ipset/ip_set_hash_ip.c
net/netfilter/ipset/ip_set_hash_ipport.c
net/netfilter/ipset/ip_set_hash_ipportip.c
net/netfilter/ipset/ip_set_hash_ipportnet.c
net/netfilter/ipset/ip_set_hash_net.c
net/netfilter/ipset/ip_set_hash_netiface.c
net/netfilter/ipset/ip_set_hash_netnet.c [new file with mode: 0644]
net/netfilter/ipset/ip_set_hash_netport.c
net/netfilter/ipset/ip_set_hash_netportnet.c [new file with mode: 0644]
net/netfilter/ipset/ip_set_list_set.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_est.c
net/netfilter/ipvs/ip_vs_lblc.c
net/netfilter/ipvs/ip_vs_lblcr.c
net/netfilter/ipvs/ip_vs_nq.c
net/netfilter/ipvs/ip_vs_pe_sip.c
net/netfilter/ipvs/ip_vs_proto_sctp.c
net/netfilter/ipvs/ip_vs_sed.c
net/netfilter/ipvs/ip_vs_sh.c
net/netfilter/ipvs/ip_vs_wlc.c
net/netfilter/nf_conntrack_acct.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_h323_main.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_sip.c
net/netfilter/nf_internals.h
net/netfilter/nf_nat_core.c
net/netfilter/nf_nat_sip.c
net/netfilter/nf_synproxy_core.c
net/netfilter/nf_tables_api.c [new file with mode: 0644]
net/netfilter/nf_tables_core.c [new file with mode: 0644]
net/netfilter/nfnetlink.c
net/netfilter/nfnetlink_cttimeout.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue_core.c
net/netfilter/nft_bitwise.c [new file with mode: 0644]
net/netfilter/nft_byteorder.c [new file with mode: 0644]
net/netfilter/nft_cmp.c [new file with mode: 0644]
net/netfilter/nft_compat.c [new file with mode: 0644]
net/netfilter/nft_counter.c [new file with mode: 0644]
net/netfilter/nft_ct.c [new file with mode: 0644]
net/netfilter/nft_expr_template.c [new file with mode: 0644]
net/netfilter/nft_exthdr.c [new file with mode: 0644]
net/netfilter/nft_hash.c [new file with mode: 0644]
net/netfilter/nft_immediate.c [new file with mode: 0644]
net/netfilter/nft_limit.c [new file with mode: 0644]
net/netfilter/nft_log.c [new file with mode: 0644]
net/netfilter/nft_lookup.c [new file with mode: 0644]
net/netfilter/nft_meta.c [new file with mode: 0644]
net/netfilter/nft_meta_target.c [new file with mode: 0644]
net/netfilter/nft_nat.c [new file with mode: 0644]
net/netfilter/nft_payload.c [new file with mode: 0644]
net/netfilter/nft_rbtree.c [new file with mode: 0644]
net/netfilter/x_tables.c
net/netfilter/xt_NFQUEUE.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_TPROXY.c
net/netfilter/xt_connbytes.c
net/netfilter/xt_set.c
net/netfilter/xt_socket.c
net/netlabel/netlabel_kapi.c
net/nfc/Kconfig
net/nfc/Makefile
net/nfc/core.c
net/nfc/digital.h [new file with mode: 0644]
net/nfc/digital_core.c [new file with mode: 0644]
net/nfc/digital_dep.c [new file with mode: 0644]
net/nfc/digital_technology.c [new file with mode: 0644]
net/nfc/nci/spi.c
net/nfc/netlink.c
net/nfc/rawsock.c
net/openvswitch/Makefile
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/dp_notify.c
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/flow_netlink.c [new file with mode: 0644]
net/openvswitch/flow_netlink.h [new file with mode: 0644]
net/openvswitch/flow_table.c [new file with mode: 0644]
net/openvswitch/flow_table.h [new file with mode: 0644]
net/openvswitch/vport-gre.c
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport-netdev.c
net/openvswitch/vport-netdev.h
net/openvswitch/vport-vxlan.c
net/rds/connection.c
net/rds/rds.h
net/rfkill/Kconfig
net/rfkill/rfkill-gpio.c
net/rxrpc/ar-internal.h
net/sched/Kconfig
net/sched/Makefile
net/sched/act_police.c
net/sched/cls_basic.c
net/sched/cls_bpf.c [new file with mode: 0644]
net/sched/cls_cgroup.c
net/sched/em_ipset.c
net/sched/em_meta.c
net/sched/sch_api.c
net/sched/sch_fq.c
net/sched/sch_generic.c
net/sched/sch_htb.c
net/sched/sch_netem.c
net/sched/sch_tbf.c
net/sctp/associola.c
net/sctp/auth.c
net/sctp/chunk.c
net/sctp/ipv6.c
net/sctp/output.c
net/sctp/sm_make_chunk.c
net/sctp/sm_sideeffect.c
net/sctp/socket.c
net/socket.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/svcsock.c
net/sysctl_net.c
net/tipc/bcast.c
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/core.h
net/tipc/eth_media.c
net/tipc/ib_media.c
net/tipc/link.c
net/tipc/link.h
net/tipc/msg.c
net/tipc/msg.h
net/tipc/node.c
net/tipc/node.h
net/tipc/port.c
net/tipc/port.h
net/tipc/socket.c
net/unix/af_unix.c
net/unix/diag.c
net/wimax/wimax-internal.h
net/wireless/chan.c
net/wireless/core.h
net/wireless/debugfs.c
net/wireless/genregdb.awk
net/wireless/ibss.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/sysfs.h
net/wireless/util.c
net/x25/Kconfig
net/xfrm/xfrm_algo.c
net/xfrm/xfrm_hash.h
net/xfrm/xfrm_ipcomp.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_replay.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c
scripts/checkpatch.pl
scripts/kallsyms.c
scripts/link-vmlinux.sh
security/apparmor/apparmorfs.c
security/apparmor/crypto.c
security/apparmor/include/policy.h
security/apparmor/policy.c
security/lsm_audit.c
security/selinux/avc.c
security/selinux/hooks.c
security/selinux/include/avc.h
sound/core/compress_offload.c
sound/core/pcm.c
sound/pci/ac97/ac97_codec.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_generic.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/rme9652/hdsp.c
sound/soc/blackfin/bf6xx-i2s.c
sound/soc/codecs/88pm860x-codec.c
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/max98095.c
sound/soc/codecs/pcm1681.c
sound/soc/codecs/pcm1792a.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/wm_hubs.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-mc13783.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-ssi.c
sound/soc/fsl/imx-ssi.h
sound/soc/omap/Kconfig
sound/soc/sh/rcar/rsnd.h
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/usb/usx2y/us122l.c
sound/usb/usx2y/usbusx2yaudio.c
sound/usb/usx2y/usx2yhwdeppcm.c
tools/lib/lk/debugfs.c
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Makefile
tools/perf/arch/x86/util/tsc.c
tools/perf/builtin-inject.c
tools/perf/builtin-kmem.c
tools/perf/builtin-kvm.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/config/Makefile
tools/perf/config/feature-tests.mak
tools/perf/tests/code-reading.c
tools/perf/tests/keep-tracking.c
tools/perf/tests/mmap-basic.c
tools/perf/tests/open-syscall-tp-fields.c
tools/perf/tests/perf-record.c
tools/perf/tests/perf-time-to-tsc.c
tools/perf/tests/sw-clock.c
tools/perf/tests/task-exit.c
tools/perf/ui/stdio/hist.c
tools/perf/util/annotate.c
tools/perf/util/callchain.h
tools/perf/util/dwarf-aux.c
tools/perf/util/dwarf-aux.h
tools/perf/util/event.c
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/header.c
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/machine.c
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h
tools/perf/util/python.c
tools/perf/util/scripting-engines/trace-event-perl.c
tools/perf/util/scripting-engines/trace-event-python.c
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/symbol-elf.c
tools/perf/util/trace-event-parse.c
tools/testing/selftests/timers/posix_timers.c
virt/kvm/kvm_main.c

diff --git a/CREDITS b/CREDITS
index 9416a9a..0640e16 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -2808,8 +2808,7 @@ S: Ottawa, Ontario
 S: Canada K2P 0X8
 
 N: Mikael Pettersson
-E: mikpe@it.uu.se
-W: http://user.it.uu.se/~mikpe/linux/
+E: mikpelinux@gmail.com
 D: Miscellaneous fixes
 
 N: Reed H. Petty
index 2be603c..a6b6857 100644 (file)
@@ -37,8 +37,8 @@ Description:
                that the USB device has been connected to the machine.  This
                file is read-only.
 Users:
-               PowerTOP <power@bughost.org>
-               http://www.lesswatts.org/projects/powertop/
+               PowerTOP <powertop@lists.01.org>
+               https://01.org/powertop/
 
 What:          /sys/bus/usb/device/.../power/active_duration
 Date:          January 2008
@@ -57,8 +57,8 @@ Description:
                will give an integer percentage.  Note that this does not
                account for counter wrap.
 Users:
-               PowerTOP <power@bughost.org>
-               http://www.lesswatts.org/projects/powertop/
+               PowerTOP <powertop@lists.01.org>
+               https://01.org/powertop/
 
 What:          /sys/bus/usb/devices/<busnum>-<port[.port]>...:<config num>-<interface num>/supports_autosuspend
 Date:          January 2008
index bdc0070..7f34a95 100644 (file)
@@ -1,13 +1,13 @@
 
 What:           /sys/class/net/<iface>/batman-adv/iface_status
 Date:           May 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Indicates the status of <iface> as it is seen by batman.
 
 What:           /sys/class/net/<iface>/batman-adv/mesh_iface
 Date:           May 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 The /sys/class/net/<iface>/batman-adv/mesh_iface file
                 displays the batman mesh interface this <iface>
index bdcd8b4..0baa657 100644 (file)
@@ -1,22 +1,23 @@
 
 What:           /sys/class/net/<mesh_iface>/mesh/aggregated_ogms
 Date:           May 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Indicates whether the batman protocol messages of the
                 mesh <mesh_iface> shall be aggregated or not.
 
-What:           /sys/class/net/<mesh_iface>/mesh/ap_isolation
+What:           /sys/class/net/<mesh_iface>/mesh/<vlan_subdir>/ap_isolation
 Date:           May 2011
-Contact:        Antonio Quartulli <ordex@autistici.org>
+Contact:        Antonio Quartulli <antonio@meshcoding.com>
 Description:
                 Indicates whether the data traffic going from a
                 wireless client to another wireless client will be
-                silently dropped.
+                silently dropped. <vlan_subdir> is empty when referring
+               to the untagged lan.
 
 What:           /sys/class/net/<mesh_iface>/mesh/bonding
 Date:           June 2010
-Contact:        Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
+Contact:        Simon Wunderlich <sw@simonwunderlich.de>
 Description:
                 Indicates whether the data traffic going through the
                 mesh will be sent using multiple interfaces at the
@@ -24,7 +25,7 @@ Description:
 
 What:           /sys/class/net/<mesh_iface>/mesh/bridge_loop_avoidance
 Date:           November 2011
-Contact:        Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
+Contact:        Simon Wunderlich <sw@simonwunderlich.de>
 Description:
                 Indicates whether the bridge loop avoidance feature
                 is enabled. This feature detects and avoids loops
@@ -41,21 +42,21 @@ Description:
 
 What:           /sys/class/net/<mesh_iface>/mesh/gw_bandwidth
 Date:           October 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Defines the bandwidth which is propagated by this
                 node if gw_mode was set to 'server'.
 
 What:           /sys/class/net/<mesh_iface>/mesh/gw_mode
 Date:           October 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Defines the state of the gateway features. Can be
                 either 'off', 'client' or 'server'.
 
 What:           /sys/class/net/<mesh_iface>/mesh/gw_sel_class
 Date:           October 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Defines the selection criteria this node will use
                 to choose a gateway if gw_mode was set to 'client'.
@@ -77,25 +78,14 @@ Description:
 
 What:           /sys/class/net/<mesh_iface>/mesh/orig_interval
 Date:           May 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Defines the interval in milliseconds in which batman
                 sends its protocol messages.
 
 What:           /sys/class/net/<mesh_iface>/mesh/routing_algo
 Date:           Dec 2011
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
+Contact:        Marek Lindner <mareklindner@neomailbox.ch>
 Description:
                 Defines the routing procotol this mesh instance
                 uses to find the optimal paths through the mesh.
-
-What:           /sys/class/net/<mesh_iface>/mesh/vis_mode
-Date:           May 2010
-Contact:        Marek Lindner <lindner_marek@yahoo.de>
-Description:
-                Each batman node only maintains information about its
-                own local neighborhood, therefore generating graphs
-                showing the topology of the entire mesh is not easily
-                feasible without having a central instance to collect
-                the local topologies from all nodes. This file allows
-                to activate the collecting (server) mode.
index 9d43e76..efe449b 100644 (file)
@@ -1,6 +1,6 @@
 What:          /sys/devices/.../power/
 Date:          January 2009
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../power directory contains attributes
                allowing the user space to check and modify some power
@@ -8,7 +8,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup
 Date:          January 2009
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../power/wakeup attribute allows the user
                space to check if the device is enabled to wake up the system
@@ -34,7 +34,7 @@ Description:
 
 What:          /sys/devices/.../power/control
 Date:          January 2009
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../power/control attribute allows the user
                space to control the run-time power management of the device.
@@ -53,7 +53,7 @@ Description:
 
 What:          /sys/devices/.../power/async
 Date:          January 2009
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../async attribute allows the user space to
                enable or diasble the device's suspend and resume callbacks to
@@ -79,7 +79,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_count
 Date:          September 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_count attribute contains the number
                of signaled wakeup events associated with the device.  This
@@ -88,7 +88,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_active_count
 Date:          September 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_active_count attribute contains the
                number of times the processing of wakeup events associated with
@@ -98,7 +98,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_abort_count
 Date:          February 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_abort_count attribute contains the
                number of times the processing of a wakeup event associated with
@@ -109,7 +109,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_expire_count
 Date:          February 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_expire_count attribute contains the
                number of times a wakeup event associated with the device has
@@ -119,7 +119,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_active
 Date:          September 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_active attribute contains either 1,
                or 0, depending on whether or not a wakeup event associated with
@@ -129,7 +129,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_total_time_ms
 Date:          September 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_total_time_ms attribute contains
                the total time of processing wakeup events associated with the
@@ -139,7 +139,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_max_time_ms
 Date:          September 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_max_time_ms attribute contains
                the maximum time of processing a single wakeup event associated
@@ -149,7 +149,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_last_time_ms
 Date:          September 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_last_time_ms attribute contains
                the value of the monotonic clock corresponding to the time of
@@ -160,7 +160,7 @@ Description:
 
 What:          /sys/devices/.../power/wakeup_prevent_sleep_time_ms
 Date:          February 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute
                contains the total time the device has been preventing
@@ -189,7 +189,7 @@ Description:
 
 What:          /sys/devices/.../power/pm_qos_latency_us
 Date:          March 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../power/pm_qos_resume_latency_us attribute
                contains the PM QoS resume latency limit for the given device,
@@ -207,7 +207,7 @@ Description:
 
 What:          /sys/devices/.../power/pm_qos_no_power_off
 Date:          September 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../power/pm_qos_no_power_off attribute
                is used for manipulating the PM QoS "no power off" flag.  If
@@ -222,7 +222,7 @@ Description:
 
 What:          /sys/devices/.../power/pm_qos_remote_wakeup
 Date:          September 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/devices/.../power/pm_qos_remote_wakeup attribute
                is used for manipulating the PM QoS "remote wakeup required"
index 2177726..205a738 100644 (file)
@@ -1,6 +1,6 @@
 What:          /sys/power/
 Date:          August 2006
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power directory will contain files that will
                provide a unified interface to the power management
@@ -8,7 +8,7 @@ Description:
 
 What:          /sys/power/state
 Date:          August 2006
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/state file controls the system power state.
                Reading from this file returns what states are supported,
@@ -22,7 +22,7 @@ Description:
 
 What:          /sys/power/disk
 Date:          September 2006
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/disk file controls the operating mode of the
                suspend-to-disk mechanism.  Reading from this file returns
@@ -67,7 +67,7 @@ Description:
 
 What:          /sys/power/image_size
 Date:          August 2006
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/image_size file controls the size of the image
                created by the suspend-to-disk mechanism.  It can be written a
@@ -84,7 +84,7 @@ Description:
 
 What:          /sys/power/pm_trace
 Date:          August 2006
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/pm_trace file controls the code which saves the
                last PM event point in the RTC across reboots, so that you can
@@ -133,7 +133,7 @@ Description:
 
 What:          /sys/power/pm_async
 Date:          January 2009
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/pm_async file controls the switch allowing the
                user space to enable or disable asynchronous suspend and resume
@@ -146,7 +146,7 @@ Description:
 
 What:          /sys/power/wakeup_count
 Date:          July 2010
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/wakeup_count file allows user space to put the
                system into a sleep state while taking into account the
@@ -161,7 +161,7 @@ Description:
 
 What:          /sys/power/reserved_size
 Date:          May 2011
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/reserved_size file allows user space to control
                the amount of memory reserved for allocations made by device
@@ -175,7 +175,7 @@ Description:
 
 What:          /sys/power/autosleep
 Date:          April 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/autosleep file can be written one of the strings
                returned by reads from /sys/power/state.  If that happens, a
@@ -192,7 +192,7 @@ Description:
 
 What:          /sys/power/wake_lock
 Date:          February 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/wake_lock file allows user space to create
                wakeup source objects and activate them on demand (if one of
@@ -219,7 +219,7 @@ Description:
 
 What:          /sys/power/wake_unlock
 Date:          February 2012
-Contact:       Rafael J. Wysocki <rjw@sisk.pl>
+Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
                The /sys/power/wake_unlock file allows user space to deactivate
                wakeup sources created with the help of /sys/power/wake_lock.
index f403ec3..46ad6fa 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_scan_request
 !Finclude/net/cfg80211.h cfg80211_scan_done
 !Finclude/net/cfg80211.h cfg80211_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss_frame
-!Finclude/net/cfg80211.h cfg80211_inform_bss
+!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
+!Finclude/net/cfg80211.h cfg80211_inform_bss_width
 !Finclude/net/cfg80211.h cfg80211_unlink_bss
 !Finclude/net/cfg80211.h cfg80211_find_ie
 !Finclude/net/cfg80211.h ieee80211_bss_get_ie
index febbb1b..784841c 100644 (file)
@@ -4,4 +4,4 @@ CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel.
 
 When to use this method is described in detail on the
 Linux/ACPI home page:
-http://www.lesswatts.org/projects/acpi/overridingDSDT.php
+https://01.org/linux-acpi/documentation/overriding-dsdt
index 264e984..d9995f1 100644 (file)
@@ -18,17 +18,17 @@ this byte for application use, with the following caveats:
            parameters containing user virtual addresses *must* have
            their top byte cleared before trapping to the kernel.
 
-       (2) Tags are not guaranteed to be preserved when delivering
-           signals. This means that signal handlers in applications
-           making use of tags cannot rely on the tag information for
-           user virtual addresses being maintained for fields inside
-           siginfo_t. One exception to this rule is for signals raised
-           in response to debug exceptions, where the tag information
+       (2) Non-zero tags are not preserved when delivering signals.
+           This means that signal handlers in applications making use
+           of tags cannot rely on the tag information for user virtual
+           addresses being maintained for fields inside siginfo_t.
+           One exception to this rule is for signals raised in response
+           to watchpoint debug exceptions, where the tag information
            will be preserved.
 
        (3) Special care should be taken when using tagged pointers,
            since it is likely that C compilers will not hazard two
-           addresses differing only in the upper bits.
+           virtual addresses differing only in the upper byte.
 
 The architecture prevents the use of a tagged PC, so the upper byte will
 be set to a sign-extension of bit 55 on exception return.
index d18ecd8..929d990 100644 (file)
@@ -6,6 +6,8 @@ capability.txt
        - Generic Block Device Capability (/sys/block/<device>/capability)
 cfq-iosched.txt
        - CFQ IO scheduler tunables
+cmdline-partition.txt
+       - how to specify block device partitions on kernel command line
 data-integrity.txt
        - Block data integrity
 deadline-iosched.txt
index 2bbf4cc..525b9f6 100644 (file)
@@ -1,9 +1,9 @@
-Embedded device command line partition
+Embedded device command line partition parsing
 =====================================================================
 
-Read block device partition table from command line.
-The partition used for fixed block device (eMMC) embedded device.
-It is no MBR, save storage space. Bootloader can be easily accessed
+Support for reading the block device partition table from the command line.
+It is typically used for fixed block (eMMC) embedded devices.
+It has no MBR, so saves storage space. Bootloader can be easily accessed
 by absolute address of data on the block device.
 Users can easily change the partition.
 
index 4848db8..8a4da64 100644 (file)
@@ -71,7 +71,7 @@ static int netlink_send(int s, struct cn_msg *msg)
        nlh->nlmsg_seq = seq++;
        nlh->nlmsg_pid = getpid();
        nlh->nlmsg_type = NLMSG_DONE;
-       nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
+       nlh->nlmsg_len = size;
        nlh->nlmsg_flags = 0;
 
        m = NLMSG_DATA(nlh);
diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt
deleted file mode 100644 (file)
index eb24693..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-*** Memory binding ***
-
-The /memory node provides basic information about the address and size
-of the physical memory. This node is usually filled or updated by the
-bootloader, depending on the actual memory configuration of the given
-hardware.
-
-The memory layout is described by the following node:
-
-/ {
-       #address-cells = <(n)>;
-       #size-cells = <(m)>;
-       memory {
-               device_type = "memory";
-               reg =  <(baseaddr1) (size1)
-                       (baseaddr2) (size2)
-                       ...
-                       (baseaddrN) (sizeN)>;
-       };
-       ...
-};
-
-A memory node follows the typical device tree rules for "reg" property:
-n:             number of cells used to store base address value
-m:             number of cells used to store size value
-baseaddrX:     defines a base address of the defined memory bank
-sizeX:         the size of the defined memory bank
-
-
-More than one memory bank can be defined.
-
-
-*** Reserved memory regions ***
-
-In /memory/reserved-memory node one can create child nodes describing
-particular reserved (excluded from normal use) memory regions. Such
-memory regions are usually designed for the special usage by various
-device drivers. A good example are contiguous memory allocations or
-memory sharing with other operating system on the same hardware board.
-Those special memory regions might depend on the board configuration and
-devices used on the target system.
-
-Parameters for each memory region can be encoded into the device tree
-with the following convention:
-
-[(label):] (name) {
-       compatible = "linux,contiguous-memory-region", "reserved-memory-region";
-       reg = <(address) (size)>;
-       (linux,default-contiguous-region);
-};
-
-compatible:    one or more of:
-       - "linux,contiguous-memory-region" - enables binding of this
-         region to Contiguous Memory Allocator (special region for
-         contiguous memory allocations, shared with movable system
-         memory, Linux kernel-specific).
-       - "reserved-memory-region" - compatibility is defined, given
-         region is assigned for exclusive usage for by the respective
-         devices.
-
-reg:   standard property defining the base address and size of
-       the memory region
-
-linux,default-contiguous-region: property indicating that the region
-       is the default region for all contiguous memory
-       allocations, Linux specific (optional)
-
-It is optional to specify the base address, so if one wants to use
-autoconfiguration of the base address, '0' can be specified as a base
-address in the 'reg' property.
-
-The /memory/reserved-memory node must contain the same #address-cells
-and #size-cells value as the root node.
-
-
-*** Device node's properties ***
-
-Once regions in the /memory/reserved-memory node have been defined, they
-may be referenced by other device nodes. Bindings that wish to reference
-memory regions should explicitly document their use of the following
-property:
-
-memory-region = <&phandle_to_defined_region>;
-
-This property indicates that the device driver should use the memory
-region pointed by the given phandle.
-
-
-*** Example ***
-
-This example defines a memory consisting of 4 memory banks. 3 contiguous
-regions are defined for Linux kernel, one default of all device drivers
-(named contig_mem, placed at 0x72000000, 64MiB), one dedicated to the
-framebuffer device (labelled display_mem, placed at 0x78000000, 8MiB)
-and one for multimedia processing (labelled multimedia_mem, placed at
-0x77000000, 64MiB). 'display_mem' region is then assigned to fb@12300000
-device for DMA memory allocations (Linux kernel drivers will use CMA is
-available or dma-exclusive usage otherwise). 'multimedia_mem' is
-assigned to scaler@12500000 and codec@12600000 devices for contiguous
-memory allocations when CMA driver is enabled.
-
-The reason for creating a separate region for framebuffer device is to
-match the framebuffer base address to the one configured by bootloader,
-so once Linux kernel drivers starts no glitches on the displayed boot
-logo appears. Scaller and codec drivers should share the memory
-allocations.
-
-/ {
-       #address-cells = <1>;
-       #size-cells = <1>;
-
-       /* ... */
-
-       memory {
-               reg =  <0x40000000 0x10000000
-                       0x50000000 0x10000000
-                       0x60000000 0x10000000
-                       0x70000000 0x10000000>;
-
-               reserved-memory {
-                       #address-cells = <1>;
-                       #size-cells = <1>;
-
-                       /*
-                        * global autoconfigured region for contiguous allocations
-                        * (used only with Contiguous Memory Allocator)
-                        */
-                       contig_region@0 {
-                               compatible = "linux,contiguous-memory-region";
-                               reg = <0x0 0x4000000>;
-                               linux,default-contiguous-region;
-                       };
-
-                       /*
-                        * special region for framebuffer
-                        */
-                       display_region: region@78000000 {
-                               compatible = "linux,contiguous-memory-region", "reserved-memory-region";
-                               reg = <0x78000000 0x800000>;
-                       };
-
-                       /*
-                        * special region for multimedia processing devices
-                        */
-                       multimedia_region: region@77000000 {
-                               compatible = "linux,contiguous-memory-region";
-                               reg = <0x77000000 0x4000000>;
-                       };
-               };
-       };
-
-       /* ... */
-
-       fb0: fb@12300000 {
-               status = "okay";
-               memory-region = <&display_region>;
-       };
-
-       scaler: scaler@12500000 {
-               status = "okay";
-               memory-region = <&multimedia_region>;
-       };
-
-       codec: codec@12600000 {
-               status = "okay";
-               memory-region = <&multimedia_region>;
-       };
-};
index 6d1c098..c67b975 100644 (file)
@@ -1,11 +1,11 @@
-* Samsung Exynos specific extensions to the Synopsis Designware Mobile
+* Samsung Exynos specific extensions to the Synopsys Designware Mobile
   Storage Host Controller
 
-The Synopsis designware mobile storage host controller is used to interface
+The Synopsys designware mobile storage host controller is used to interface
 a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
-differences between the core Synopsis dw mshc controller properties described
-by synopsis-dw-mshc.txt and the properties used by the Samsung Exynos specific
-extensions to the Synopsis Designware Mobile Storage Host Controller.
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the Samsung Exynos specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
 
 Required Properties:
 
index 8a3d91d..c559f3f 100644 (file)
@@ -1,11 +1,11 @@
-* Rockchip specific extensions to the Synopsis Designware Mobile
+* Rockchip specific extensions to the Synopsys Designware Mobile
   Storage Host Controller
 
-The Synopsis designware mobile storage host controller is used to interface
+The Synopsys designware mobile storage host controller is used to interface
 a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
-differences between the core Synopsis dw mshc controller properties described
-by synopsis-dw-mshc.txt and the properties used by the Rockchip specific
-extensions to the Synopsis Designware Mobile Storage Host Controller.
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the Rockchip specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
 
 Required Properties:
 
diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt
deleted file mode 100644 (file)
index cdcebea..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-* Synopsis Designware Mobile Storage Host Controller
-
-The Synopsis designware mobile storage host controller is used to interface
-a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
-differences between the core mmc properties described by mmc.txt and the
-properties used by the Synopsis Designware Mobile Storage Host Controller.
-
-Required Properties:
-
-* compatible: should be
-       - snps,dw-mshc: for controllers compliant with synopsis dw-mshc.
-* #address-cells: should be 1.
-* #size-cells: should be 0.
-
-# Slots: The slot specific information are contained within child-nodes with
-  each child-node representing a supported slot. There should be atleast one
-  child node representing a card slot. The name of the child node representing
-  the slot is recommended to be slot@n where n is the unique number of the slot
-  connnected to the controller. The following are optional properties which
-  can be included in the slot child node.
-
-       * reg: specifies the physical slot number. The valid values of this
-         property is 0 to (num-slots -1), where num-slots is the value
-         specified by the num-slots property.
-
-       * bus-width: as documented in mmc core bindings.
-
-       * wp-gpios: specifies the write protect gpio line. The format of the
-         gpio specifier depends on the gpio controller. If a GPIO is not used
-         for write-protect, this property is optional.
-
-       * disable-wp: If the wp-gpios property isn't present then (by default)
-         we'd assume that the write protect is hooked up directly to the
-         controller's special purpose write protect line (accessible via
-         the WRTPRT register).  However, it's possible that we simply don't
-         want write protect.  In that case specify 'disable-wp'.
-         NOTE: This property is not required for slots known to always
-         connect to eMMC or SDIO cards.
-
-Optional properties:
-
-* clocks: from common clock binding: handle to biu and ciu clocks for the
-  bus interface unit clock and the card interface unit clock.
-
-* clock-names: from common clock binding: Shall be "biu" and "ciu".
-  If the biu clock is missing we'll simply skip enabling it.  If the
-  ciu clock is missing we'll just assume that the clock is running at
-  clock-frequency.  It is an error to omit both the ciu clock and the
-  clock-frequency.
-
-* clock-frequency: should be the frequency (in Hz) of the ciu clock.  If this
-  is specified and the ciu clock is specified then we'll try to set the ciu
-  clock to this at probe time.
-
-* num-slots: specifies the number of slots supported by the controller.
-  The number of physical slots actually used could be equal or less than the
-  value specified by num-slots. If this property is not specified, the value
-  of num-slot property is assumed to be 1.
-
-* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
-  specified, the default value of the fifo size is determined from the
-  controller registers.
-
-* card-detect-delay: Delay in milli-seconds before detecting card after card
-  insert event. The default value is 0.
-
-* supports-highspeed: Enables support for high speed cards (up to 50MHz)
-
-* broken-cd: as documented in mmc core bindings.
-
-* vmmc-supply: The phandle to the regulator to use for vmmc.  If this is
-  specified we'll defer probe until we can find this regulator.
-
-Aliases:
-
-- All the MSHC controller nodes should be represented in the aliases node using
-  the following format 'mshc{n}' where n is a unique number for the alias.
-
-Example:
-
-The MSHC controller node can be split into two portions, SoC specific and
-board specific portions as listed below.
-
-       dwmmc0@12200000 {
-               compatible = "snps,dw-mshc";
-               clocks = <&clock 351>, <&clock 132>;
-               clock-names = "biu", "ciu";
-               reg = <0x12200000 0x1000>;
-               interrupts = <0 75 0>;
-               #address-cells = <1>;
-               #size-cells = <0>;
-       };
-
-       dwmmc0@12200000 {
-               clock-frequency = <400000000>;
-               num-slots = <1>;
-               supports-highspeed;
-               broken-cd;
-               fifo-depth = <0x80>;
-               card-detect-delay = <200>;
-               vmmc-supply = <&buck8>;
-
-               slot@0 {
-                       reg = <0>;
-                       bus-width = <8>;
-               };
-       };
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
new file mode 100644 (file)
index 0000000..066a78b
--- /dev/null
@@ -0,0 +1,107 @@
+* Synopsys Designware Mobile Storage Host Controller
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core mmc properties described by mmc.txt and the
+properties used by the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+       - snps,dw-mshc: for controllers compliant with synopsys dw-mshc.
+* #address-cells: should be 1.
+* #size-cells: should be 0.
+
+# Slots: The slot specific information are contained within child-nodes with
+  each child-node representing a supported slot. There should be atleast one
+  child node representing a card slot. The name of the child node representing
+  the slot is recommended to be slot@n where n is the unique number of the slot
+  connnected to the controller. The following are optional properties which
+  can be included in the slot child node.
+
+       * reg: specifies the physical slot number. The valid values of this
+         property is 0 to (num-slots -1), where num-slots is the value
+         specified by the num-slots property.
+
+       * bus-width: as documented in mmc core bindings.
+
+       * wp-gpios: specifies the write protect gpio line. The format of the
+         gpio specifier depends on the gpio controller. If a GPIO is not used
+         for write-protect, this property is optional.
+
+       * disable-wp: If the wp-gpios property isn't present then (by default)
+         we'd assume that the write protect is hooked up directly to the
+         controller's special purpose write protect line (accessible via
+         the WRTPRT register).  However, it's possible that we simply don't
+         want write protect.  In that case specify 'disable-wp'.
+         NOTE: This property is not required for slots known to always
+         connect to eMMC or SDIO cards.
+
+Optional properties:
+
+* clocks: from common clock binding: handle to biu and ciu clocks for the
+  bus interface unit clock and the card interface unit clock.
+
+* clock-names: from common clock binding: Shall be "biu" and "ciu".
+  If the biu clock is missing we'll simply skip enabling it.  If the
+  ciu clock is missing we'll just assume that the clock is running at
+  clock-frequency.  It is an error to omit both the ciu clock and the
+  clock-frequency.
+
+* clock-frequency: should be the frequency (in Hz) of the ciu clock.  If this
+  is specified and the ciu clock is specified then we'll try to set the ciu
+  clock to this at probe time.
+
+* num-slots: specifies the number of slots supported by the controller.
+  The number of physical slots actually used could be equal or less than the
+  value specified by num-slots. If this property is not specified, the value
+  of num-slot property is assumed to be 1.
+
+* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
+  specified, the default value of the fifo size is determined from the
+  controller registers.
+
+* card-detect-delay: Delay in milli-seconds before detecting card after card
+  insert event. The default value is 0.
+
+* supports-highspeed: Enables support for high speed cards (up to 50MHz)
+
+* broken-cd: as documented in mmc core bindings.
+
+* vmmc-supply: The phandle to the regulator to use for vmmc.  If this is
+  specified we'll defer probe until we can find this regulator.
+
+Aliases:
+
+- All the MSHC controller nodes should be represented in the aliases node using
+  the following format 'mshc{n}' where n is a unique number for the alias.
+
+Example:
+
+The MSHC controller node can be split into two portions, SoC specific and
+board specific portions as listed below.
+
+       dwmmc0@12200000 {
+               compatible = "snps,dw-mshc";
+               clocks = <&clock 351>, <&clock 132>;
+               clock-names = "biu", "ciu";
+               reg = <0x12200000 0x1000>;
+               interrupts = <0 75 0>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+       };
+
+       dwmmc0@12200000 {
+               clock-frequency = <400000000>;
+               num-slots = <1>;
+               supports-highspeed;
+               broken-cd;
+               fifo-depth = <0x80>;
+               card-detect-delay = <200>;
+               vmmc-supply = <&buck8>;
+
+               slot@0 {
+                       reg = <0>;
+                       bus-width = <8>;
+               };
+       };
index df204e1..6a2a116 100644 (file)
@@ -9,12 +9,15 @@ compulsory and any optional properties, common to all SD/MMC drivers, as
 described in mmc.txt, can be used. Additionally the following tmio_mmc-specific
 optional bindings can be used.
 
+Required properties:
+- compatible:  "renesas,sdhi-shmobile" - a generic sh-mobile SDHI unit
+               "renesas,sdhi-sh7372" - SDHI IP on SH7372 SoC
+               "renesas,sdhi-sh73a0" - SDHI IP on SH73A0 SoC
+               "renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
+               "renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC
+               "renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC
+               "renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
+               "renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
+
 Optional properties:
 - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
-
-When used with Renesas SDHI hardware, the following compatibility strings
-configure various model-specific properties:
-
-"renesas,sh7372-sdhi": (default) compatible with SH7372
-"renesas,r8a7740-sdhi":        compatible with R8A7740: certain MMC/SD commands have to
-                       wait for the interface to become idle.
diff --git a/Documentation/devicetree/bindings/net/cpsw-phy-sel.txt b/Documentation/devicetree/bindings/net/cpsw-phy-sel.txt
new file mode 100644 (file)
index 0000000..7ff57a1
--- /dev/null
@@ -0,0 +1,28 @@
+TI CPSW Phy mode Selection Device Tree Bindings
+-----------------------------------------------
+
+Required properties:
+- compatible           : Should be "ti,am3352-cpsw-phy-sel"
+- reg                  : physical base address and size of the cpsw
+                         registers map
+- reg-names            : names of the register map given in "reg" node
+
+Optional properties:
+-rmii-clock-ext                : If present, the driver will configure the RMII
+                         interface to external clock usage
+
+Examples:
+
+       phy_sel: cpsw-phy-sel@44e10650 {
+               compatible = "ti,am3352-cpsw-phy-sel";
+               reg= <0x44e10650 0x4>;
+               reg-names = "gmii-sel";
+       };
+
+(or)
+       phy_sel: cpsw-phy-sel@44e10650 {
+               compatible = "ti,am3352-cpsw-phy-sel";
+               reg= <0x44e10650 0x4>;
+               reg-names = "gmii-sel";
+               rmii-clock-ext;
+       };
index 2c6be03..d2ea460 100644 (file)
@@ -86,6 +86,7 @@ General Properties:
 
 Clock Properties:
 
+  - fsl,cksel        Timer reference clock source.
   - fsl,tclk-period  Timer reference clock period in nanoseconds.
   - fsl,tmr-prsc     Prescaler, divides the output clock.
   - fsl,tmr-add      Frequency compensation value.
@@ -97,7 +98,7 @@ Clock Properties:
   clock. You must choose these carefully for the clock to work right.
   Here is how to figure good values:
 
-  TimerOsc     = system clock               MHz
+  TimerOsc     = selected reference clock   MHz
   tclk_period  = desired clock period       nanoseconds
   NominalFreq  = 1000 / tclk_period         MHz
   FreqDivRatio = TimerOsc / NominalFreq     (must be greater that 1.0)
@@ -114,6 +115,20 @@ Clock Properties:
   Pulse Per Second (PPS) signal, since this will be offered to the PPS
   subsystem to synchronize the Linux clock.
 
+  Reference clock source is determined by the value, which is holded
+  in CKSEL bits in TMR_CTRL register. "fsl,cksel" property keeps the
+  value, which will be directly written in those bits, that is why,
+  according to reference manual, the next clock sources can be used:
+
+  <0> - external high precision timer reference clock (TSEC_TMR_CLK
+        input is used for this purpose);
+  <1> - eTSEC system clock;
+  <2> - eTSEC1 transmit clock;
+  <3> - RTC clock input.
+
+  When this attribute is not used, eTSEC system clock will serve as
+  IEEE 1588 timer reference clock.
+
 Example:
 
        ptp_clock@24E00 {
@@ -121,6 +136,7 @@ Example:
                reg = <0x24E00 0xB0>;
                interrupts = <12 0x8 13 0x8>;
                interrupt-parent = < &ipic >;
+               fsl,cksel       = <1>;
                fsl,tclk-period = <10>;
                fsl,tmr-prsc    = <100>;
                fsl,tmr-add     = <0x999999A4>;
index eabcb4b..e216af3 100644 (file)
@@ -1,4 +1,4 @@
-* Synopsis Designware PCIe interface
+* Synopsys Designware PCIe interface
 
 Required properties:
 - compatible: should contain "snps,dw-pcie" to identify the
diff --git a/Documentation/devicetree/bindings/serial/qca,ar9330-uart.txt b/Documentation/devicetree/bindings/serial/qca,ar9330-uart.txt
new file mode 100644 (file)
index 0000000..c5e032c
--- /dev/null
@@ -0,0 +1,34 @@
+* Qualcomm Atheros AR9330 High-Speed UART
+
+Required properties:
+
+- compatible: Must be "qca,ar9330-uart"
+
+- reg: Specifies the physical base address of the controller and
+  the length of the memory mapped region.
+
+- interrupt-parent: The phandle for the interrupt controller that
+  services interrupts for this device.
+
+- interrupts: Specifies the interrupt source of the parent interrupt
+  controller. The format of the interrupt specifier depends on the
+  parent interrupt controller.
+
+Additional requirements:
+
+  Each UART port must have an alias correctly numbered in "aliases"
+  node.
+
+Example:
+
+       aliases {
+               serial0 = &uart0;
+       };
+
+       uart0: uart@18020000 {
+               compatible = "qca,ar9330-uart";
+               reg = <0x18020000 0x14>;
+
+               interrupt-parent = <&intc>;
+               interrupts = <3>;
+       };
diff --git a/Documentation/devicetree/bindings/tty/serial/qca,ar9330-uart.txt b/Documentation/devicetree/bindings/tty/serial/qca,ar9330-uart.txt
deleted file mode 100644 (file)
index c5e032c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-* Qualcomm Atheros AR9330 High-Speed UART
-
-Required properties:
-
-- compatible: Must be "qca,ar9330-uart"
-
-- reg: Specifies the physical base address of the controller and
-  the length of the memory mapped region.
-
-- interrupt-parent: The phandle for the interrupt controller that
-  services interrupts for this device.
-
-- interrupts: Specifies the interrupt source of the parent interrupt
-  controller. The format of the interrupt specifier depends on the
-  parent interrupt controller.
-
-Additional requirements:
-
-  Each UART port must have an alias correctly numbered in "aliases"
-  node.
-
-Example:
-
-       aliases {
-               serial0 = &uart0;
-       };
-
-       uart0: uart@18020000 {
-               compatible = "qca,ar9330-uart";
-               reg = <0x18020000 0x14>;
-
-               interrupt-parent = <&intc>;
-               interrupts = <3>;
-       };
index 1a036cd..fcbb736 100644 (file)
@@ -480,6 +480,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Format: <io>,<irq>,<mode>
                        See header of drivers/net/hamradio/baycom_ser_hdx.c.
 
+       blkdevparts=    Manual partition parsing of block device(s) for
+                       embedded devices based on command line input.
+                       See Documentation/block/cmdline-partition.txt
+
        boot_delay=     Milliseconds to delay each printk during boot.
                        Values larger than 10 seconds (10000) are changed to
                        no delay (0).
@@ -1357,7 +1361,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        pages. In the event, a node is too small to have both
                        kernelcore and Movable pages, kernelcore pages will
                        take priority and other nodes will have a larger number
-                       of kernelcore pages.  The Movable zone is used for the
+                       of Movable pages.  The Movable zone is used for the
                        allocation of pages that may be reclaimed or moved
                        by the page migration subsystem.  This means that
                        HugeTLB pages may not be allocated from this zone.
@@ -3485,6 +3489,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                                the unplug protocol
                        never -- do not unplug even if version check succeeds
 
+       xen_nopvspin    [X86,XEN]
+                       Disables the ticketlock slowpath using Xen PV
+                       optimizations.
+
        xirc2ps_cs=     [NET,PCMCIA]
                        Format:
                        <irq>,<irq_mask>,<io>,<full_duplex>,<do_sound>,<lockup_hack>[,<irq2>[,<irq3>[,<irq4>]]]
index c1d8204..89490be 100644 (file)
@@ -69,8 +69,7 @@ folder:
 # aggregated_ogms        gw_bandwidth           log_level
 # ap_isolation           gw_mode                orig_interval
 # bonding                gw_sel_class           routing_algo
-# bridge_loop_avoidance  hop_penalty            vis_mode
-# fragmentation
+# bridge_loop_avoidance  hop_penalty            fragmentation
 
 
 There is a special folder for debugging information:
@@ -78,7 +77,7 @@ There is a special folder for debugging information:
 # ls /sys/kernel/debug/batman_adv/bat0/
 # bla_backbone_table  log                 transtable_global
 # bla_claim_table     originators         transtable_local
-# gateways            socket              vis_data
+# gateways            socket
 
 Some of the files contain all sort of status information  regard-
 ing  the  mesh  network.  For  example, you can view the table of
@@ -127,51 +126,6 @@ ously assigned to interfaces now used by batman advanced, e.g.
 # ifconfig eth0 0.0.0.0
 
 
-VISUALIZATION
--------------
-
-If you want topology visualization, at least one mesh  node  must
-be configured as VIS-server:
-
-# echo "server" > /sys/class/net/bat0/mesh/vis_mode
-
-Each  node  is  either configured as "server" or as "client" (de-
-fault: "client").  Clients send their topology data to the server
-next to them, and server synchronize with other servers. If there
-is no server configured (default) within the  mesh,  no  topology
-information   will  be  transmitted.  With  these  "synchronizing
-servers", there can be 1 or more vis servers sharing the same (or
-at least very similar) data.
-
-When  configured  as  server,  you can get a topology snapshot of
-your mesh:
-
-# cat /sys/kernel/debug/batman_adv/bat0/vis_data
-
-This raw output is intended to be easily parsable and convertable
-with  other tools. Have a look at the batctl README if you want a
-vis output in dot or json format for instance and how those  out-
-puts could then be visualised in an image.
-
-The raw format consists of comma separated values per entry where
-each entry is giving information about a  certain  source  inter-
-face.  Each  entry can/has to have the following values:
--> "mac" - mac address of an originator's source interface
-           (each line begins with it)
--> "TQ mac  value"  -  src mac's link quality towards mac address
-                       of a neighbor originator's interface which
-                       is being used for routing
--> "TT mac" - TT announced by source mac
--> "PRIMARY" - this  is a primary interface
--> "SEC mac" - secondary mac address of source
-               (requires preceding PRIMARY)
-
-The TQ value has a range from 4 to 255 with 255 being  the  best.
-The TT entries are showing which hosts are connected to the mesh
-via bat0 or being bridged into the mesh network.  The PRIMARY/SEC
-values are only applied on primary interfaces
-
-
 LOGGING/DEBUGGING
 -----------------
 
@@ -245,5 +199,5 @@ Mailing-list:   b.a.t.m.a.n@open-mesh.org (optional  subscription
 
 You can also contact the Authors:
 
-Marek  Lindner  <lindner_marek@yahoo.de>
-Simon  Wunderlich  <siwu@hrz.tu-chemnitz.de>
+Marek  Lindner  <mareklindner@neomailbox.ch>
+Simon  Wunderlich  <sw@simonwunderlich.de>
index 9b28e71..2cdb8b6 100644 (file)
@@ -639,6 +639,15 @@ num_unsol_na
        are generated by the ipv4 and ipv6 code and the numbers of
        repetitions cannot be set independently.
 
+packets_per_slave
+
+       Specify the number of packets to transmit through a slave before
+       moving to the next one. When set to 0 then a slave is chosen at
+       random.
+
+       The valid range is 0 - 65535; the default value is 1. This option
+       has effect only in balance-rr mode.
+
 primary
 
        A string (eth0, eth2, etc) specifying which slave is the
@@ -743,21 +752,16 @@ xmit_hash_policy
                protocol information to generate the hash.
 
                Uses XOR of hardware MAC addresses and IP addresses to
-               generate the hash.  The IPv4 formula is
-
-               (((source IP XOR dest IP) AND 0xffff) XOR
-                       ( source MAC XOR destination MAC ))
-                               modulo slave count
-
-               The IPv6 formula is
+               generate the hash.  The formula is
 
-               hash = (source ip quad 2 XOR dest IP quad 2) XOR
-                      (source ip quad 3 XOR dest IP quad 3) XOR
-                      (source ip quad 4 XOR dest IP quad 4)
+               hash = source MAC XOR destination MAC
+               hash = hash XOR source IP XOR destination IP
+               hash = hash XOR (hash RSHIFT 16)
+               hash = hash XOR (hash RSHIFT 8)
+               And then hash is reduced modulo slave count.
 
-               (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash)
-                       XOR (source MAC XOR destination MAC))
-                               modulo slave count
+               If the protocol is IPv6 then the source and destination
+               addresses are first hashed using ipv6_addr_hash.
 
                This algorithm will place all traffic to a particular
                network peer on the same slave.  For non-IP traffic,
@@ -779,21 +783,16 @@ xmit_hash_policy
                slaves, although a single connection will not span
                multiple slaves.
 
-               The formula for unfragmented IPv4 TCP and UDP packets is
+               The formula for unfragmented TCP and UDP packets is
 
-               ((source port XOR dest port) XOR
-                        ((source IP XOR dest IP) AND 0xffff)
-                               modulo slave count
+               hash = source port, destination port (as in the header)
+               hash = hash XOR source IP XOR destination IP
+               hash = hash XOR (hash RSHIFT 16)
+               hash = hash XOR (hash RSHIFT 8)
+               And then hash is reduced modulo slave count.
 
-               The formula for unfragmented IPv6 TCP and UDP packets is
-
-               hash = (source port XOR dest port) XOR
-                      ((source ip quad 2 XOR dest IP quad 2) XOR
-                       (source ip quad 3 XOR dest IP quad 3) XOR
-                       (source ip quad 4 XOR dest IP quad 4))
-
-               ((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash)
-                       modulo slave count
+               If the protocol is IPv6 then the source and destination
+               addresses are first hashed using ipv6_addr_hash.
 
                For fragmented TCP or UDP packets and all other IPv4 and
                IPv6 protocol traffic, the source and destination port
@@ -801,10 +800,6 @@ xmit_hash_policy
                formula is the same as for the layer2 transmit hash
                policy.
 
-               The IPv4 policy is intended to mimic the behavior of
-               certain switches, notably Cisco switches with PFC2 as
-               well as some Foundry and IBM products.
-
                This algorithm is not fully 802.3ad compliant.  A
                single TCP or UDP conversation containing both
                fragmented and unfragmented packets will see packets
@@ -815,6 +810,26 @@ xmit_hash_policy
                conversations.  Other implementations of 802.3ad may
                or may not tolerate this noncompliance.
 
+       encap2+3
+
+               This policy uses the same formula as layer2+3 but it
+               relies on skb_flow_dissect to obtain the header fields
+               which might result in the use of inner headers if an
+               encapsulation protocol is used. For example this will
+               improve the performance for tunnel users because the
+               packets will be distributed according to the encapsulated
+               flows.
+
+       encap3+4
+
+               This policy uses the same formula as layer3+4 but it
+               relies on skb_flow_dissect to obtain the header fields
+               which might result in the use of inner headers if an
+               encapsulation protocol is used. For example this will
+               improve the performance for tunnel users because the
+               packets will be distributed according to the encapsulated
+               flows.
+
        The default value is layer2.  This option was added in bonding
        version 2.6.3.  In earlier versions of bonding, this parameter
        does not exist, and the layer2 policy is the only policy.  The
index 820f553..4c07241 100644 (file)
@@ -25,6 +25,12 @@ This file contains
       4.1.5 RAW socket option CAN_RAW_FD_FRAMES
       4.1.6 RAW socket returned message flags
     4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
+      4.2.1 Broadcast Manager operations
+      4.2.2 Broadcast Manager message flags
+      4.2.3 Broadcast Manager transmission timers
+      4.2.4 Broadcast Manager message sequence transmission
+      4.2.5 Broadcast Manager receive filter timers
+      4.2.6 Broadcast Manager multiplex message receive filter
     4.3 connected transport protocols (SOCK_SEQPACKET)
     4.4 unconnected transport protocols (SOCK_DGRAM)
 
@@ -593,6 +599,217 @@ solution for a couple of reasons:
       In order to receive such messages, CAN_RAW_RECV_OWN_MSGS must be set.
 
   4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
+
+  The Broadcast Manager protocol provides a command based configuration
+  interface to filter and send (e.g. cyclic) CAN messages in kernel space.
+
+  Receive filters can be used to down sample frequent messages; detect events
+  such as message contents changes, packet length changes, and do time-out
+  monitoring of received messages.
+
+  Periodic transmission tasks of CAN frames or a sequence of CAN frames can be
+  created and modified at runtime; both the message content and the two
+  possible transmit intervals can be altered.
+
+  A BCM socket is not intended for sending individual CAN frames using the
+  struct can_frame as known from the CAN_RAW socket. Instead a special BCM
+  configuration message is defined. The basic BCM configuration message used
+  to communicate with the broadcast manager and the available operations are
+  defined in the linux/can/bcm.h include. The BCM message consists of a
+  message header with a command ('opcode') followed by zero or more CAN frames.
+  The broadcast manager sends responses to user space in the same form:
+
+    struct bcm_msg_head {
+            __u32 opcode;                   /* command */
+            __u32 flags;                    /* special flags */
+            __u32 count;                    /* run 'count' times with ival1 */
+            struct timeval ival1, ival2;    /* count and subsequent interval */
+            canid_t can_id;                 /* unique can_id for task */
+            __u32 nframes;                  /* number of can_frames following */
+            struct can_frame frames[0];
+    };
+
+  The aligned payload 'frames' uses the same basic CAN frame structure defined
+  at the beginning of section 4 and in the include/linux/can.h include. All
+  messages to the broadcast manager from user space have this structure.
+
+  Note a CAN_BCM socket must be connected instead of bound after socket
+  creation (example without error checking):
+
+    int s;
+    struct sockaddr_can addr;
+    struct ifreq ifr;
+
+    s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
+
+    strcpy(ifr.ifr_name, "can0");
+    ioctl(s, SIOCGIFINDEX, &ifr);
+
+    addr.can_family = AF_CAN;
+    addr.can_ifindex = ifr.ifr_ifindex;
+
+    connect(s, (struct sockaddr *)&addr, sizeof(addr))
+
+    (..)
+
+  The broadcast manager socket is able to handle any number of in flight
+  transmissions or receive filters concurrently. The different RX/TX jobs are
+  distinguished by the unique can_id in each BCM message. However additional
+  CAN_BCM sockets are recommended to communicate on multiple CAN interfaces.
+  When the broadcast manager socket is bound to 'any' CAN interface (=> the
+  interface index is set to zero) the configured receive filters apply to any
+  CAN interface unless the sendto() syscall is used to overrule the 'any' CAN
+  interface index. When using recvfrom() instead of read() to retrieve BCM
+  socket messages the originating CAN interface is provided in can_ifindex.
+
+  4.2.1 Broadcast Manager operations
+
+  The opcode defines the operation for the broadcast manager to carry out,
+  or details the broadcast managers response to several events, including
+  user requests.
+
+  Transmit Operations (user space to broadcast manager):
+
+    TX_SETUP:   Create (cyclic) transmission task.
+
+    TX_DELETE:  Remove (cyclic) transmission task, requires only can_id.
+
+    TX_READ:    Read properties of (cyclic) transmission task for can_id.
+
+    TX_SEND:    Send one CAN frame.
+
+  Transmit Responses (broadcast manager to user space):
+
+    TX_STATUS:  Reply to TX_READ request (transmission task configuration).
+
+    TX_EXPIRED: Notification when counter finishes sending at initial interval
+      'ival1'. Requires the TX_COUNTEVT flag to be set at TX_SETUP.
+
+  Receive Operations (user space to broadcast manager):
+
+    RX_SETUP:   Create RX content filter subscription.
+
+    RX_DELETE:  Remove RX content filter subscription, requires only can_id.
+
+    RX_READ:    Read properties of RX content filter subscription for can_id.
+
+  Receive Responses (broadcast manager to user space):
+
+    RX_STATUS:  Reply to RX_READ request (filter task configuration).
+
+    RX_TIMEOUT: Cyclic message is detected to be absent (timer ival1 expired).
+
+    RX_CHANGED: BCM message with updated CAN frame (detected content change).
+      Sent on first message received or on receipt of revised CAN messages.
+
+  4.2.2 Broadcast Manager message flags
+
+  When sending a message to the broadcast manager the 'flags' element may
+  contain the following flag definitions which influence the behaviour:
+
+    SETTIMER:           Set the values of ival1, ival2 and count
+
+    STARTTIMER:         Start the timer with the actual values of ival1, ival2
+      and count. Starting the timer leads simultaneously to emit a CAN frame.
+
+    TX_COUNTEVT:        Create the message TX_EXPIRED when count expires
+
+    TX_ANNOUNCE:        A change of data by the process is emitted immediately.
+
+    TX_CP_CAN_ID:       Copies the can_id from the message header to each
+      subsequent frame in frames. This is intended as usage simplification. For
+      TX tasks the unique can_id from the message header may differ from the
+      can_id(s) stored for transmission in the subsequent struct can_frame(s).
+
+    RX_FILTER_ID:       Filter by can_id alone, no frames required (nframes=0).
+
+    RX_CHECK_DLC:       A change of the DLC leads to an RX_CHANGED.
+
+    RX_NO_AUTOTIMER:    Prevent automatically starting the timeout monitor.
+
+    RX_ANNOUNCE_RESUME: If passed at RX_SETUP and a receive timeout occured, a
+      RX_CHANGED message will be generated when the (cyclic) receive restarts.
+
+    TX_RESET_MULTI_IDX: Reset the index for the multiple frame transmission.
+
+    RX_RTR_FRAME:       Send reply for RTR-request (placed in op->frames[0]).
+
+  4.2.3 Broadcast Manager transmission timers
+
+  Periodic transmission configurations may use up to two interval timers.
+  In this case the BCM sends a number of messages ('count') at an interval
+  'ival1', then continuing to send at another given interval 'ival2'. When
+  only one timer is needed 'count' is set to zero and only 'ival2' is used.
+  When SET_TIMER and START_TIMER flag were set the timers are activated.
+  The timer values can be altered at runtime when only SET_TIMER is set.
+
+  4.2.4 Broadcast Manager message sequence transmission
+
+  Up to 256 CAN frames can be transmitted in a sequence in the case of a cyclic
+  TX task configuration. The number of CAN frames is provided in the 'nframes'
+  element of the BCM message head. The defined number of CAN frames are added
+  as array to the TX_SETUP BCM configuration message.
+
+    /* create a struct to set up a sequence of four CAN frames */
+    struct {
+            struct bcm_msg_head msg_head;
+            struct can_frame frame[4];
+    } mytxmsg;
+
+    (..)
+    mytxmsg.nframes = 4;
+    (..)
+
+    write(s, &mytxmsg, sizeof(mytxmsg));
+
+  With every transmission the index in the array of CAN frames is increased
+  and set to zero at index overflow.
+
+  4.2.5 Broadcast Manager receive filter timers
+
+  The timer values ival1 or ival2 may be set to non-zero values at RX_SETUP.
+  When the SET_TIMER flag is set the timers are enabled:
+
+  ival1: Send RX_TIMEOUT when a received message is not received again within
+    the given time. When START_TIMER is set at RX_SETUP the timeout detection
+    is activated directly - even without a former CAN frame reception.
+
+  ival2: Throttle the received message rate down to the value of ival2. This
+    is useful to reduce messages for the application when the signal inside the
+    CAN frame is stateless as state changes within the ival2 periode may get
+    lost.
+
+  4.2.6 Broadcast Manager multiplex message receive filter
+
+  To filter for content changes in multiplex message sequences an array of more
+  than one CAN frames can be passed in a RX_SETUP configuration message. The
+  data bytes of the first CAN frame contain the mask of relevant bits that
+  have to match in the subsequent CAN frames with the received CAN frame.
+  If one of the subsequent CAN frames is matching the bits in that frame data
+  mark the relevant content to be compared with the previous received content.
+  Up to 257 CAN frames (multiplex filter bit mask CAN frame plus 256 CAN
+  filters) can be added as array to the TX_SETUP BCM configuration message.
+
+    /* usually used to clear CAN frame data[] - beware of endian problems! */
+    #define U64_DATA(p) (*(unsigned long long*)(p)->data)
+
+    struct {
+            struct bcm_msg_head msg_head;
+            struct can_frame frame[5];
+    } msg;
+
+    msg.msg_head.opcode  = RX_SETUP;
+    msg.msg_head.can_id  = 0x42;
+    msg.msg_head.flags   = 0;
+    msg.msg_head.nframes = 5;
+    U64_DATA(&msg.frame[0]) = 0xFF00000000000000ULL; /* MUX mask */
+    U64_DATA(&msg.frame[1]) = 0x01000000000000FFULL; /* data mask (MUX 0x01) */
+    U64_DATA(&msg.frame[2]) = 0x0200FFFF000000FFULL; /* data mask (MUX 0x02) */
+    U64_DATA(&msg.frame[3]) = 0x330000FFFFFF0003ULL; /* data mask (MUX 0x33) */
+    U64_DATA(&msg.frame[4]) = 0x4F07FC0FF0000000ULL; /* data mask (MUX 0x4F) */
+
+    write(s, &msg, sizeof(msg));
+
   4.3 connected transport protocols (SOCK_SEQPACKET)
   4.4 unconnected transport protocols (SOCK_DGRAM)
 
index d718bc2..bf5dbe3 100644 (file)
@@ -18,8 +18,8 @@ Introduction
 Datagram Congestion Control Protocol (DCCP) is an unreliable, connection
 oriented protocol designed to solve issues present in UDP and TCP, particularly
 for real-time and multimedia (streaming) traffic.
-It divides into a base protocol (RFC 4340) and plugable congestion control
-modules called CCIDs. Like plugable TCP congestion control, at least one CCID
+It divides into a base protocol (RFC 4340) and pluggable congestion control
+modules called CCIDs. Like pluggable TCP congestion control, at least one CCID
 needs to be enabled in order for the protocol to function properly. In the Linux
 implementation, this is the TCP-like CCID2 (RFC 4341). Additional CCIDs, such as
 the TCP-friendly CCID3 (RFC 4342), are optional.
index 13a3212..f862cf3 100644 (file)
@@ -103,7 +103,7 @@ Additional Configurations
   PRO/100 Family of Adapters is e100.
 
   As an example, if you install the e100 driver for two PRO/100 adapters
-  (eth0 and eth1), add the following to a configuraton file in /etc/modprobe.d/
+  (eth0 and eth1), add the following to a configuration file in /etc/modprobe.d/
 
        alias eth0 e100
        alias eth1 e100
index 09eb573..22bbc72 100644 (file)
@@ -4,7 +4,7 @@
 
 Introduction
 ============
-The IEEE 802.15.4 working group focuses on standartization of bottom
+The IEEE 802.15.4 working group focuses on standardization of bottom
 two layers: Medium Access Control (MAC) and Physical (PHY). And there
 are mainly two options available for upper layers:
  - ZigBee - proprietary protocol from ZigBee Alliance
@@ -66,7 +66,7 @@ net_device, with .type = ARPHRD_IEEE802154. Data is exchanged with socket family
 code via plain sk_buffs. On skb reception skb->cb must contain additional
 info as described in the struct ieee802154_mac_cb. During packet transmission
 the skb->cb is used to provide additional data to device's header_ops->create
-function. Be aware, that this data can be overriden later (when socket code
+function. Be aware that this data can be overridden later (when socket code
 submits skb to qdisc), so if you need something from that cb later, you should
 store info in the skb->data on your own.
 
index a46d785..8b8a057 100644 (file)
@@ -267,17 +267,6 @@ tcp_max_orphans - INTEGER
        more aggressively. Let me to remind again: each orphan eats
        up to ~64K of unswappable memory.
 
-tcp_max_ssthresh - INTEGER
-       Limited Slow-Start for TCP with large congestion windows (cwnd) defined in
-       RFC3742. Limited slow-start is a mechanism to limit growth of the cwnd
-       on the region where cwnd is larger than tcp_max_ssthresh. TCP increases cwnd
-       by at most tcp_max_ssthresh segments, and by at least tcp_max_ssthresh/2
-       segments per RTT when the cwnd is above tcp_max_ssthresh.
-       If TCP connection increased cwnd to thousands (or tens of thousands) segments,
-       and thousands of packets were being dropped during slow-start, you can set
-       tcp_max_ssthresh to improve performance for new TCP connection.
-       Default: 0 (off)
-
 tcp_max_syn_backlog - INTEGER
        Maximal number of remembered connection requests, which have not
        received an acknowledgment from connecting client.
@@ -451,7 +440,7 @@ tcp_fastopen - INTEGER
        connect() to perform a TCP handshake automatically.
 
        The values (bitmap) are
-       1: Enables sending data in the opening SYN on the client.
+       1: Enables sending data in the opening SYN on the client w/ MSG_FASTOPEN.
        2: Enables TCP Fast Open on the server side, i.e., allowing data in
           a SYN packet to be accepted and passed to the application before
           3-way hand shake finishes.
@@ -464,7 +453,7 @@ tcp_fastopen - INTEGER
           different ways of setting max_qlen without the TCP_FASTOPEN socket
           option.
 
-       Default: 0
+       Default: 1
 
        Note that the client & server side Fast Open flags (1 and 2
        respectively) must be also enabled before the rest of flags can take
index e63fc1f..c74434d 100644 (file)
@@ -197,7 +197,7 @@ state information because the file format is subject to change. It is
 implemented to provide extra debug information to help diagnose
 problems.) Users should use the netlink API.
 
-/proc/net/pppol2tp is also provided for backwards compaibility with
+/proc/net/pppol2tp is also provided for backwards compatibility with
 the original pppol2tp driver. It lists information about L2TPv2
 tunnels and sessions only. Its use is discouraged.
 
index d9112f0..0fe1c6e 100644 (file)
@@ -4,23 +4,23 @@ Information you need to know about netdev
 
 Q: What is netdev?
 
-A: It is a mailing list for all network related linux stuff.  This includes
+A: It is a mailing list for all network-related Linux stuff.  This includes
    anything found under net/  (i.e. core code like IPv6) and drivers/net
-   (i.e. hardware specific drivers) in the linux source tree.
+   (i.e. hardware specific drivers) in the Linux source tree.
 
    Note that some subsystems (e.g. wireless drivers) which have a high volume
    of traffic have their own specific mailing lists.
 
-   The netdev list is managed (like many other linux mailing lists) through
+   The netdev list is managed (like many other Linux mailing lists) through
    VGER ( http://vger.kernel.org/ ) and archives can be found below:
 
        http://marc.info/?l=linux-netdev
        http://www.spinics.net/lists/netdev/
 
-   Aside from subsystems like that mentioned above, all network related linux
-   development (i.e. RFC, review, comments, etc) takes place on netdev.
+   Aside from subsystems like that mentioned above, all network-related Linux
+   development (i.e. RFC, review, comments, etc.) takes place on netdev.
 
-Q: How do the changes posted to netdev make their way into linux?
+Q: How do the changes posted to netdev make their way into Linux?
 
 A: There are always two trees (git repositories) in play.  Both are driven
    by David Miller, the main network maintainer.  There is the "net" tree,
@@ -35,7 +35,7 @@ A: There are always two trees (git repositories) in play.  Both are driven
 Q: How often do changes from these trees make it to the mainline Linus tree?
 
 A: To understand this, you need to know a bit of background information
-   on the cadence of linux development.  Each new release starts off with
+   on the cadence of Linux development.  Each new release starts off with
    a two week "merge window" where the main maintainers feed their new
    stuff to Linus for merging into the mainline tree.  After the two weeks,
    the merge window is closed, and it is called/tagged "-rc1".  No new
@@ -46,7 +46,7 @@ A: To understand this, you need to know a bit of background information
    things are in a state of churn), and a week after the last vX.Y-rcN
    was done, the official "vX.Y" is released.
 
-   Relating that to netdev:  At the beginning of the 2 week merge window,
+   Relating that to netdev:  At the beginning of the 2-week merge window,
    the net-next tree will be closed - no new changes/features.  The
    accumulated new content of the past ~10 weeks will be passed onto
    mainline/Linus via a pull request for vX.Y -- at the same time,
@@ -59,16 +59,16 @@ A: To understand this, you need to know a bit of background information
    IMPORTANT:  Do not send new net-next content to netdev during the
    period during which net-next tree is closed.
 
-   Shortly after the two weeks have passed, (and vX.Y-rc1 is released) the
+   Shortly after the two weeks have passed (and vX.Y-rc1 is released), the
    tree for net-next reopens to collect content for the next (vX.Y+1) release.
 
    If you aren't subscribed to netdev and/or are simply unsure if net-next
    has re-opened yet, simply check the net-next git repository link above for
-   any new networking related commits.
+   any new networking-related commits.
 
    The "net" tree continues to collect fixes for the vX.Y content, and
    is fed back to Linus at regular (~weekly) intervals.  Meaning that the
-   focus for "net" is on stablilization and bugfixes.
+   focus for "net" is on stabilization and bugfixes.
 
    Finally, the vX.Y gets released, and the whole cycle starts over.
 
@@ -217,7 +217,7 @@ A: Attention to detail.  Re-read your own work as if you were the
    to why it happens, and then if necessary, explain why the fix proposed
    is the best way to get things done.   Don't mangle whitespace, and as
    is common, don't mis-indent function arguments that span multiple lines.
-   If it is your 1st patch, mail it to yourself so you can test apply
+   If it is your first patch, mail it to yourself so you can test apply
    it to an unpatched tree to confirm infrastructure didn't mangle it.
 
    Finally, go back and read Documentation/SubmittingPatches to be
index c7ecc70..0b1cf6b 100644 (file)
@@ -10,12 +10,12 @@ network devices.
 struct net_device allocation rules
 ==================================
 Network device structures need to persist even after module is unloaded and
-must be allocated with kmalloc.  If device has registered successfully,
-it will be freed on last use by free_netdev.  This is required to handle the
-pathologic case cleanly (example: rmmod mydriver </sys/class/net/myeth/mtu )
+must be allocated with alloc_netdev_mqs() and friends.
+If device has registered successfully, it will be freed on last use
+by free_netdev(). This is required to handle the pathologic case cleanly
+(example: rmmod mydriver </sys/class/net/myeth/mtu )
 
-There are routines in net_init.c to handle the common cases of
-alloc_etherdev, alloc_netdev.  These reserve extra space for driver
+alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver
 private data which gets freed when the network device is freed. If
 separately allocated data is attached to the network device
 (netdev_priv(dev)) then it is up to the module exit handler to free that.
index 5333788..b261229 100644 (file)
@@ -45,7 +45,7 @@ processing.
 
 Conversion of the reception path involves calling poll() on the file
 descriptor, once the socket is readable the frames from the ring are
-processsed in order until no more messages are available, as indicated by
+processed in order until no more messages are available, as indicated by
 a status word in the frame header.
 
 On kernel side, in order to make use of memory mapped I/O on receive, the
@@ -56,7 +56,7 @@ Dumps of kernel databases automatically support memory mapped I/O.
 
 Conversion of the transmit path involves changing message construction to
 use memory from the TX ring instead of (usually) a buffer declared on the
-stack and setting up the frame header approriately. Optionally poll() can
+stack and setting up the frame header appropriately. Optionally poll() can
 be used to wait for free frames in the TX ring.
 
 Structured and definitions for using memory mapped I/O are contained in
@@ -231,7 +231,7 @@ Ring setup:
        if (setsockopt(fd, NETLINK_TX_RING, &req, sizeof(req)) < 0)
                exit(1)
 
-       /* Calculate size of each invididual ring */
+       /* Calculate size of each individual ring */
        ring_size = req.nm_block_nr * req.nm_block_size;
 
        /* Map RX/TX rings. The TX ring is located after the RX ring */
index 9769457..355c6d8 100644 (file)
@@ -89,8 +89,8 @@ packets. The name 'carrier' and the inversion are historical, think of
 it as lower layer.
 
 Note that for certain kind of soft-devices, which are not managing any
-real hardware, there is possible to set this bit from userpsace.
-One should use TVL IFLA_CARRIER to do so.
+real hardware, it is possible to set this bit from userspace.  One
+should use TVL IFLA_CARRIER to do so.
 
 netif_carrier_ok() can be used to query that bit.
 
index 60d05eb..b89bc82 100644 (file)
@@ -144,7 +144,7 @@ An overview of the RxRPC protocol:
  (*) Calls use ACK packets to handle reliability.  Data packets are also
      explicitly sequenced per call.
 
- (*) There are two types of positive acknowledgement: hard-ACKs and soft-ACKs.
+ (*) There are two types of positive acknowledgment: hard-ACKs and soft-ACKs.
      A hard-ACK indicates to the far side that all the data received to a point
      has been received and processed; a soft-ACK indicates that the data has
      been received but may yet be discarded and re-requested.  The sender may
index 457b8bb..cdd916d 100644 (file)
@@ -160,7 +160,7 @@ Where:
  o pmt: core has the embedded power module (optional).
  o force_sf_dma_mode: force DMA to use the Store and Forward mode
                     instead of the Threshold.
- o force_thresh_dma_mode: force DMA to use the Shreshold mode other than
+ o force_thresh_dma_mode: force DMA to use the Threshold mode other than
                     the Store and Forward mode.
  o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode.
  o fix_mac_speed: this callback is used for modifying some syscfg registers
@@ -175,7 +175,7 @@ Where:
             registers.
  o custom_cfg/custom_data: this is a custom configuration that can be passed
                           while initializing the resources.
- o bsp_priv: another private poiter.
+ o bsp_priv: another private pointer.
 
 For MDIO bus The we have:
 
@@ -271,7 +271,7 @@ reset procedure etc).
  o dwmac1000_dma.c:  dma functions for the GMAC chip;
  o dwmac1000.h: specific header file for the GMAC;
  o dwmac100_core: MAC 100 core and dma code;
- o dwmac100_dma.c: dma funtions for the MAC chip;
+ o dwmac100_dma.c: dma functions for the MAC chip;
  o dwmac1000.h: specific header file for the MAC;
  o dwmac_lib.c: generic DMA functions shared among chips;
  o enh_desc.c: functions for handling enhanced descriptors;
@@ -364,4 +364,4 @@ Auto-negotiated Link Parter Ability.
 10) TODO:
  o XGMAC is not supported.
  o Complete the TBI & RTBI support.
- o extened VLAN support for 3.70a SYNP GMAC.
+ o extend VLAN support for 3.70a SYNP GMAC.
index 9a8041d..97282da 100644 (file)
@@ -68,7 +68,7 @@ Module parameters
 
 There are several parameters which may be provided to the driver when
 its module is loaded.  These are usually placed in /etc/modprobe.d/*.conf
-configuretion files.  Example:
+configuration files.  Example:
 
 options 3c59x debug=3 rx_copybreak=300
 
@@ -178,7 +178,7 @@ max_interrupt_work=N
 
   The driver's interrupt service routine can handle many receive and
   transmit packets in a single invocation.  It does this in a loop. 
-  The value of max_interrupt_work governs how mnay times the interrupt
+  The value of max_interrupt_work governs how many times the interrupt
   service routine will loop.  The default value is 32 loops.  If this
   is exceeded the interrupt service routine gives up and generates a
   warning message "eth0: Too much work in interrupt".
index 78f662e..7f213b5 100644 (file)
@@ -105,7 +105,7 @@ reduced by the following measures or a combination thereof:
     later.
     The lapb module interface was modified to support this. Its
     data_indication() method should now transparently pass the
-    netif_rx() return value to the (lapb mopdule) caller.
+    netif_rx() return value to the (lapb module) caller.
 (2) Drivers for kernel versions 2.2.x should always check the global
     variable netdev_dropping when a new frame is received. The driver
     should only call netif_rx() if netdev_dropping is zero. Otherwise
index f59ded0..a74d0a8 100644 (file)
@@ -100,6 +100,11 @@ static long ppb_to_scaled_ppm(int ppb)
        return (long) (ppb * 65.536);
 }
 
+static int64_t pctns(struct ptp_clock_time *t)
+{
+       return t->sec * 1000000000LL + t->nsec;
+}
+
 static void usage(char *progname)
 {
        fprintf(stderr,
@@ -112,6 +117,8 @@ static void usage(char *progname)
                " -f val     adjust the ptp clock frequency by 'val' ppb\n"
                " -g         get the ptp clock time\n"
                " -h         prints this message\n"
+               " -k val     measure the time offset between system and phc clock\n"
+               "            for 'val' times (Maximum 25)\n"
                " -p val     enable output with a period of 'val' nanoseconds\n"
                " -P val     enable or disable (val=1|0) the system clock PPS\n"
                " -s         set the ptp clock time from the system time\n"
@@ -133,8 +140,12 @@ int main(int argc, char *argv[])
        struct itimerspec timeout;
        struct sigevent sigevent;
 
+       struct ptp_clock_time *pct;
+       struct ptp_sys_offset *sysoff;
+
+
        char *progname;
-       int c, cnt, fd;
+       int i, c, cnt, fd;
 
        char *device = DEVICE;
        clockid_t clkid;
@@ -144,14 +155,19 @@ int main(int argc, char *argv[])
        int extts = 0;
        int gettime = 0;
        int oneshot = 0;
+       int pct_offset = 0;
+       int n_samples = 0;
        int periodic = 0;
        int perout = -1;
        int pps = -1;
        int settime = 0;
 
+       int64_t t1, t2, tp;
+       int64_t interval, offset;
+
        progname = strrchr(argv[0], '/');
        progname = progname ? 1+progname : argv[0];
-       while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghp:P:sSt:v"))) {
+       while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghk:p:P:sSt:v"))) {
                switch (c) {
                case 'a':
                        oneshot = atoi(optarg);
@@ -174,6 +190,10 @@ int main(int argc, char *argv[])
                case 'g':
                        gettime = 1;
                        break;
+               case 'k':
+                       pct_offset = 1;
+                       n_samples = atoi(optarg);
+                       break;
                case 'p':
                        perout = atoi(optarg);
                        break;
@@ -376,6 +396,47 @@ int main(int argc, char *argv[])
                }
        }
 
+       if (pct_offset) {
+               if (n_samples <= 0 || n_samples > 25) {
+                       puts("n_samples should be between 1 and 25");
+                       usage(progname);
+                       return -1;
+               }
+
+               sysoff = calloc(1, sizeof(*sysoff));
+               if (!sysoff) {
+                       perror("calloc");
+                       return -1;
+               }
+               sysoff->n_samples = n_samples;
+
+               if (ioctl(fd, PTP_SYS_OFFSET, sysoff))
+                       perror("PTP_SYS_OFFSET");
+               else
+                       puts("system and phc clock time offset request okay");
+
+               pct = &sysoff->ts[0];
+               for (i = 0; i < sysoff->n_samples; i++) {
+                       t1 = pctns(pct+2*i);
+                       tp = pctns(pct+2*i+1);
+                       t2 = pctns(pct+2*i+2);
+                       interval = t2 - t1;
+                       offset = (t2 + t1) / 2 - tp;
+
+                       printf("system time: %ld.%ld\n",
+                               (pct+2*i)->sec, (pct+2*i)->nsec);
+                       printf("phc    time: %ld.%ld\n",
+                               (pct+2*i+1)->sec, (pct+2*i+1)->nsec);
+                       printf("system time: %ld.%ld\n",
+                               (pct+2*i+2)->sec, (pct+2*i+2)->nsec);
+                       printf("system/phc clock time offset is %ld ns\n"
+                               "system     clock time delay  is %ld ns\n",
+                               offset, interval);
+               }
+
+               free(sysoff);
+       }
+
        close(fd);
        return 0;
 }
index a46ddb8..85c362d 100644 (file)
@@ -28,6 +28,7 @@ ALC269/270/275/276/28x/29x
   alc269-dmic          Enable ALC269(VA) digital mic workaround
   alc271-dmic          Enable ALC271X digital mic workaround
   inv-dmic             Inverted internal mic workaround
+  headset-mic          Indicates a combined headset (headphone+mic) jack
   lenovo-dock          Enables docking station I/O for some Lenovos
   dell-headset-multi   Headset jack, which can also be used as mic-in
   dell-headset-dock    Headset jack (without mic-in), and also dock I/O
@@ -296,6 +297,12 @@ Cirrus Logic CS4206/4207
   imac27       IMac 27 Inch
   auto         BIOS setup (default)
 
+Cirrus Logic CS4208
+===================
+  mba6         MacBook Air 6,1 and 6,2
+  gpio0                Enable GPIO 0 amp
+  auto         BIOS setup (default)
+
 VIA VT17xx/VT18xx/VT20xx
 ========================
   auto         BIOS setup (default)
index e61c2e8..ea07b93 100644 (file)
@@ -237,11 +237,11 @@ F:        drivers/platform/x86/acer-wmi.c
 
 ACPI
 M:     Len Brown <lenb@kernel.org>
-M:     Rafael J. Wysocki <rjw@sisk.pl>
+M:     Rafael J. Wysocki <rjw@rjwysocki.net>
 L:     linux-acpi@vger.kernel.org
-W:     http://www.lesswatts.org/projects/acpi/
-Q:     http://patchwork.kernel.org/project/linux-acpi/list/
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
+W:     https://01.org/linux-acpi
+Q:     https://patchwork.kernel.org/project/linux-acpi/list/
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
 S:     Supported
 F:     drivers/acpi/
 F:     drivers/pnp/pnpacpi/
@@ -256,21 +256,21 @@ F:        drivers/pci/*/*/*acpi*
 ACPI FAN DRIVER
 M:     Zhang Rui <rui.zhang@intel.com>
 L:     linux-acpi@vger.kernel.org
-W:     http://www.lesswatts.org/projects/acpi/
+W:     https://01.org/linux-acpi
 S:     Supported
 F:     drivers/acpi/fan.c
 
 ACPI THERMAL DRIVER
 M:     Zhang Rui <rui.zhang@intel.com>
 L:     linux-acpi@vger.kernel.org
-W:     http://www.lesswatts.org/projects/acpi/
+W:     https://01.org/linux-acpi
 S:     Supported
 F:     drivers/acpi/*thermal*
 
 ACPI VIDEO DRIVER
 M:     Zhang Rui <rui.zhang@intel.com>
 L:     linux-acpi@vger.kernel.org
-W:     http://www.lesswatts.org/projects/acpi/
+W:     https://01.org/linux-acpi
 S:     Supported
 F:     drivers/acpi/video.c
 
@@ -824,15 +824,21 @@ S:        Maintained
 F:     arch/arm/mach-gemini/
 
 ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
-M:     Barry Song <baohua.song@csr.com>
+M:     Barry Song <baohua@kernel.org>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/baohua/linux.git
 S:     Maintained
 F:     arch/arm/mach-prima2/
+F:     drivers/clk/clk-prima2.c
+F:     drivers/clocksource/timer-prima2.c
+F:     drivers/clocksource/timer-marco.c
 F:     drivers/dma/sirf-dma.c
 F:     drivers/i2c/busses/i2c-sirf.c
+F:     drivers/input/misc/sirfsoc-onkey.c
+F:     drivers/irqchip/irq-sirfsoc.c
 F:     drivers/mmc/host/sdhci-sirf.c
 F:     drivers/pinctrl/sirf/
+F:     drivers/rtc/rtc-sirfsoc.c
 F:     drivers/spi/spi-sirf.c
 
 ARM/EBSA110 MACHINE SUPPORT
@@ -1003,6 +1009,7 @@ ARM/Marvell Armada 370 and Armada XP SOC support
 M:     Jason Cooper <jason@lakedaemon.net>
 M:     Andrew Lunn <andrew@lunn.ch>
 M:     Gregory Clement <gregory.clement@free-electrons.com>
+M:     Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     arch/arm/mach-mvebu/
@@ -1010,6 +1017,7 @@ F:        arch/arm/mach-mvebu/
 ARM/Marvell Dove/Kirkwood/MV78xx0/Orion SOC support
 M:     Jason Cooper <jason@lakedaemon.net>
 M:     Andrew Lunn <andrew@lunn.ch>
+M:     Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     arch/arm/mach-dove/
@@ -1142,6 +1150,13 @@ F:       drivers/net/ethernet/i825xx/ether1*
 F:     drivers/net/ethernet/seeq/ether3*
 F:     drivers/scsi/arm/
 
+ARM/Rockchip SoC support
+M:     Heiko Stuebner <heiko@sntech.de>
+L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:     Maintained
+F:     arch/arm/mach-rockchip/
+F:     drivers/*/*rockchip*
+
 ARM/SHARK MACHINE SUPPORT
 M:     Alexander Schulz <alex@shark-linux.de>
 W:     http://www.shark-linux.de/shark.html
@@ -1652,9 +1667,9 @@ F:        drivers/video/backlight/
 F:     include/linux/backlight.h
 
 BATMAN ADVANCED
-M:     Marek Lindner <lindner_marek@yahoo.de>
-M:     Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
-M:     Antonio Quartulli <ordex@autistici.org>
+M:     Marek Lindner <mareklindner@neomailbox.ch>
+M:     Simon Wunderlich <sw@simonwunderlich.de>
+M:     Antonio Quartulli <antonio@meshcoding.com>
 L:     b.a.t.m.a.n@lists.open-mesh.org
 W:     http://www.open-mesh.org/
 S:     Maintained
@@ -1785,6 +1800,7 @@ F:        include/net/bluetooth/
 
 BONDING DRIVER
 M:     Jay Vosburgh <fubar@us.ibm.com>
+M:     Veaceslav Falico <vfalico@redhat.com>
 M:     Andy Gospodarek <andy@greyhouse.net>
 L:     netdev@vger.kernel.org
 W:     http://sourceforge.net/projects/bonding/
@@ -1806,13 +1822,14 @@ F:      drivers/net/ethernet/broadcom/bnx2.*
 F:     drivers/net/ethernet/broadcom/bnx2_*
 
 BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER
-M:     Eilon Greenstein <eilong@broadcom.com>
+M:     Ariel Elior <ariele@broadcom.com>
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/broadcom/bnx2x/
 
 BROADCOM BCM281XX/BCM11XXX ARM ARCHITECTURE
-M:     Christian Daudt <csd@broadcom.com>
+M:     Christian Daudt <bcm@fixthebug.org>
+L:     bcm-kernel-feedback-list@broadcom.com
 T:     git git://git.github.com/broadcom/bcm11351
 S:     Maintained
 F:     arch/arm/mach-bcm/
@@ -2293,7 +2310,7 @@ S:        Maintained
 F:     drivers/net/ethernet/ti/cpmac.c
 
 CPU FREQUENCY DRIVERS
-M:     Rafael J. Wysocki <rjw@sisk.pl>
+M:     Rafael J. Wysocki <rjw@rjwysocki.net>
 M:     Viresh Kumar <viresh.kumar@linaro.org>
 L:     cpufreq@vger.kernel.org
 L:     linux-pm@vger.kernel.org
@@ -2324,7 +2341,7 @@ S:      Maintained
 F:      drivers/cpuidle/cpuidle-big_little.c
 
 CPUIDLE DRIVERS
-M:     Rafael J. Wysocki <rjw@sisk.pl>
+M:     Rafael J. Wysocki <rjw@rjwysocki.net>
 M:     Daniel Lezcano <daniel.lezcano@linaro.org>
 L:     linux-pm@vger.kernel.org
 S:     Maintained
@@ -2639,6 +2656,18 @@ F:       include/linux/device-mapper.h
 F:     include/linux/dm-*.h
 F:     include/uapi/linux/dm-*.h
 
+DIGI NEO AND CLASSIC PCI PRODUCTS
+M:     Lidza Louina <lidza.louina@gmail.com>
+L:     driverdev-devel@linuxdriverproject.org
+S:     Maintained
+F:     drivers/staging/dgnc/
+
+DIGI EPCA PCI PRODUCTS
+M:     Lidza Louina <lidza.louina@gmail.com>
+L:     driverdev-devel@linuxdriverproject.org
+S:     Maintained
+F:     drivers/staging/dgap/
+
 DIOLAN U2C-12 I2C DRIVER
 M:     Guenter Roeck <linux@roeck-us.net>
 L:     linux-i2c@vger.kernel.org
@@ -2699,6 +2728,8 @@ T:        git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git
 DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
 M:     Vinod Koul <vinod.koul@intel.com>
 M:     Dan Williams <dan.j.williams@intel.com>
+L:     dmaengine@vger.kernel.org
+Q:     https://patchwork.kernel.org/project/linux-dmaengine/list/
 S:     Supported
 F:     drivers/dma/
 F:     include/linux/dma*
@@ -2802,7 +2833,7 @@ M:        Terje Bergström <tbergstrom@nvidia.com>
 L:     dri-devel@lists.freedesktop.org
 L:     linux-tegra@vger.kernel.org
 T:     git git://anongit.freedesktop.org/tegra/linux.git
-S:     Maintained
+S:     Supported
 F:     drivers/gpu/host1x/
 F:     include/uapi/drm/tegra_drm.h
 F:     Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
@@ -3534,7 +3565,7 @@ F:        fs/freevxfs/
 
 FREEZER
 M:     Pavel Machek <pavel@ucw.cz>
-M:     "Rafael J. Wysocki" <rjw@sisk.pl>
+M:     "Rafael J. Wysocki" <rjw@rjwysocki.net>
 L:     linux-pm@vger.kernel.org
 S:     Supported
 F:     Documentation/power/freezing-of-tasks.txt
@@ -3605,6 +3636,12 @@ L:       linux-scsi@vger.kernel.org
 S:     Odd Fixes (e.g., new signatures)
 F:     drivers/scsi/fdomain.*
 
+GCOV BASED KERNEL PROFILING
+M:     Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+S:     Maintained
+F:     kernel/gcov/
+F:     Documentation/gcov.txt
+
 GDT SCSI DISK ARRAY CONTROLLER DRIVER
 M:     Achim Leubner <achim_leubner@adaptec.com>
 L:     linux-scsi@vger.kernel.org
@@ -3870,7 +3907,7 @@ F:        drivers/video/hgafb.c
 
 HIBERNATION (aka Software Suspend, aka swsusp)
 M:     Pavel Machek <pavel@ucw.cz>
-M:     "Rafael J. Wysocki" <rjw@sisk.pl>
+M:     "Rafael J. Wysocki" <rjw@rjwysocki.net>
 L:     linux-pm@vger.kernel.org
 S:     Supported
 F:     arch/x86/power/
@@ -4320,7 +4357,7 @@ F:        drivers/video/i810/
 INTEL MENLOW THERMAL DRIVER
 M:     Sujith Thomas <sujith.thomas@intel.com>
 L:     platform-driver-x86@vger.kernel.org
-W:     http://www.lesswatts.org/projects/acpi/
+W:     https://01.org/linux-acpi
 S:     Supported
 F:     drivers/platform/x86/intel_menlow.c
 
@@ -4332,7 +4369,10 @@ F:       arch/x86/kernel/microcode_intel.c
 
 INTEL I/OAT DMA DRIVER
 M:     Dan Williams <dan.j.williams@intel.com>
-S:     Maintained
+M:     Dave Jiang <dave.jiang@intel.com>
+L:     dmaengine@vger.kernel.org
+Q:     https://patchwork.kernel.org/project/linux-dmaengine/list/
+S:     Supported
 F:     drivers/dma/ioat*
 
 INTEL IOMMU (VT-d)
@@ -4457,6 +4497,13 @@ L:       linux-serial@vger.kernel.org
 S:     Maintained
 F:     drivers/tty/serial/ioc3_serial.c
 
+IOMMU DRIVERS
+M:     Joerg Roedel <joro@8bytes.org>
+L:     iommu@lists.linux-foundation.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
+S:     Maintained
+F:     drivers/iommu/
+
 IP MASQUERADING
 M:     Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar>
 S:     Maintained
@@ -5306,7 +5353,7 @@ S:        Orphan
 F:     drivers/net/wireless/libertas/
 
 MARVELL MV643XX ETHERNET DRIVER
-M:     Lennert Buytenhek <buytenh@wantstofly.org>
+M:     Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/marvell/mv643xx_eth.*
@@ -6595,7 +6642,7 @@ S:        Obsolete
 F:     drivers/net/wireless/prism54/
 
 PROMISE SATA TX2/TX4 CONTROLLER LIBATA DRIVER
-M:     Mikael Pettersson <mikpe@it.uu.se>
+M:     Mikael Pettersson <mikpelinux@gmail.com>
 L:     linux-ide@vger.kernel.org
 S:     Maintained
 F:     drivers/ata/sata_promise.*
@@ -6816,6 +6863,14 @@ L:       linux-hexagon@vger.kernel.org
 S:     Supported
 F:     arch/hexagon/
 
+QUALCOMM WCN36XX WIRELESS DRIVER
+M:     Eugene Krasnikov <k.eugene.e@gmail.com>
+L:     wcn36xx@lists.infradead.org
+W:     http://wireless.kernel.org/en/users/Drivers/wcn36xx
+T:     git git://github.com/KrasnikovEugene/wcn36xx.git
+S:     Supported
+F:     drivers/net/wireless/ath/wcn36xx/
+
 QUICKCAM PARALLEL PORT WEBCAMS
 M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
@@ -7258,9 +7313,9 @@ F:        include/linux/sched.h
 F:     include/uapi/linux/sched.h
 
 SCORE ARCHITECTURE
-M:     Chen Liqin <liqin.chen@sunplusct.com>
+M:     Chen Liqin <liqin.linux@gmail.com>
 M:     Lennox Wu <lennox.wu@gmail.com>
-W:     http://www.sunplusct.com
+W:     http://www.sunplus.com
 S:     Supported
 F:     arch/score/
 
@@ -7790,6 +7845,13 @@ F:       Documentation/sound/alsa/soc/
 F:     sound/soc/
 F:     include/sound/soc*
 
+SOUND - DMAENGINE HELPERS
+M:     Lars-Peter Clausen <lars@metafoo.de>
+S:     Supported
+F:     include/sound/dmaengine_pcm.h
+F:     sound/core/pcm_dmaengine.c
+F:     sound/soc/soc-generic-dmaengine-pcm.c
+
 SPARC + UltraSPARC (sparc/sparc64)
 M:     "David S. Miller" <davem@davemloft.net>
 L:     sparclinux@vger.kernel.org
@@ -8069,7 +8131,7 @@ F:        drivers/sh/
 SUSPEND TO RAM
 M:     Len Brown <len.brown@intel.com>
 M:     Pavel Machek <pavel@ucw.cz>
-M:     "Rafael J. Wysocki" <rjw@sisk.pl>
+M:     "Rafael J. Wysocki" <rjw@rjwysocki.net>
 L:     linux-pm@vger.kernel.org
 S:     Supported
 F:     Documentation/power/
@@ -8262,14 +8324,72 @@ L:      linux-media@vger.kernel.org
 S:     Maintained
 F:     drivers/media/rc/ttusbir.c
 
-TEGRA SUPPORT
+TEGRA ARCHITECTURE SUPPORT
 M:     Stephen Warren <swarren@wwwdotorg.org>
+M:     Thierry Reding <thierry.reding@gmail.com>
 L:     linux-tegra@vger.kernel.org
 Q:     http://patchwork.ozlabs.org/project/linux-tegra/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra.git
 S:     Supported
 N:     [^a-z]tegra
 
+TEGRA ASOC DRIVER
+M:     Stephen Warren <swarren@wwwdotorg.org>
+S:     Supported
+F:     sound/soc/tegra/
+
+TEGRA CLOCK DRIVER
+M:     Peter De Schrijver <pdeschrijver@nvidia.com>
+M:     Prashant Gaikwad <pgaikwad@nvidia.com>
+S:     Supported
+F:     drivers/clk/tegra/
+
+TEGRA DMA DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/dma/tegra20-apb-dma.c
+
+TEGRA GPIO DRIVER
+M:     Stephen Warren <swarren@wwwdotorg.org>
+S:     Supported
+F:     drivers/gpio/gpio-tegra.c
+
+TEGRA I2C DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/i2c/busses/i2c-tegra.c
+
+TEGRA IOMMU DRIVERS
+M:     Hiroshi Doyu <hdoyu@nvidia.com>
+S:     Supported
+F:     drivers/iommu/tegra*
+
+TEGRA KBC DRIVER
+M:     Rakesh Iyer <riyer@nvidia.com>
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/input/keyboard/tegra-kbc.c
+
+TEGRA PINCTRL DRIVER
+M:     Stephen Warren <swarren@wwwdotorg.org>
+S:     Supported
+F:     drivers/pinctrl/pinctrl-tegra*
+
+TEGRA PWM DRIVER
+M:     Thierry Reding <thierry.reding@gmail.com>
+S:     Supported
+F:     drivers/pwm/pwm-tegra.c
+
+TEGRA SERIAL DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/tty/serial/serial-tegra.c
+
+TEGRA SPI DRIVER
+M:     Laxman Dewangan <ldewangan@nvidia.com>
+S:     Supported
+F:     drivers/spi/spi-tegra*
+
 TEHUTI ETHERNET DRIVER
 M:     Andy Gospodarek <andy@greyhouse.net>
 L:     netdev@vger.kernel.org
@@ -8724,9 +8844,8 @@ F:        Documentation/hid/hiddev.txt
 F:     drivers/hid/usbhid/
 
 USB/IP DRIVERS
-M:     Matt Mooney <mfm@muteddisk.com>
 L:     linux-usb@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/staging/usbip/
 
 USB ISP116X DRIVER
@@ -8806,61 +8925,14 @@ W:      http://pegasus2.sourceforge.net/
 S:     Maintained
 F:     drivers/net/usb/rtl8150.c
 
-USB SERIAL BELKIN F5U103 DRIVER
-M:     William Greathouse <wgreathouse@smva.com>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/belkin_sa.*
-
-USB SERIAL CYPRESS M8 DRIVER
-M:     Lonnie Mendez <dignome@gmail.com>
+USB SERIAL SUBSYSTEM
+M:     Johan Hovold <jhovold@gmail.com>
 L:     linux-usb@vger.kernel.org
 S:     Maintained
-W:     http://geocities.com/i0xox0i
-W:     http://firstlight.net/cvs
-F:     drivers/usb/serial/cypress_m8.*
-
-USB SERIAL CYBERJACK DRIVER
-M:     Matthias Bruestle and Harald Welte <support@reiner-sct.com>
-W:     http://www.reiner-sct.de/support/treiber_cyberjack.php
-S:     Maintained
-F:     drivers/usb/serial/cyberjack.c
-
-USB SERIAL DIGI ACCELEPORT DRIVER
-M:     Peter Berger <pberger@brimson.com>
-M:     Al Borchers <alborchers@steinerpoint.com>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/digi_acceleport.c
-
-USB SERIAL DRIVER
-M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-L:     linux-usb@vger.kernel.org
-S:     Supported
 F:     Documentation/usb/usb-serial.txt
-F:     drivers/usb/serial/generic.c
-F:     drivers/usb/serial/usb-serial.c
+F:     drivers/usb/serial/
 F:     include/linux/usb/serial.h
 
-USB SERIAL EMPEG EMPEG-CAR MARK I/II DRIVER
-M:     Gary Brubaker <xavyer@ix.netcom.com>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/empeg.c
-
-USB SERIAL KEYSPAN DRIVER
-M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-L:     linux-usb@vger.kernel.org
-S:     Maintained
-F:     drivers/usb/serial/*keyspan*
-
-USB SERIAL WHITEHEAT DRIVER
-M:     Support Department <support@connecttech.com>
-L:     linux-usb@vger.kernel.org
-W:     http://www.connecttech.com
-S:     Supported
-F:     drivers/usb/serial/whiteheat*
-
 USB SMSC75XX ETHERNET DRIVER
 M:     Steve Glendinning <steve.glendinning@shawell.net>
 L:     netdev@vger.kernel.org
@@ -9366,6 +9438,7 @@ F:        arch/arm64/include/asm/xen/
 
 XEN NETWORK BACKEND DRIVER
 M:     Ian Campbell <ian.campbell@citrix.com>
+M:     Wei Liu <wei.liu2@citrix.com>
 L:     xen-devel@lists.xenproject.org (moderated for non-subscribers)
 L:     netdev@vger.kernel.org
 S:     Supported
index de004ce..67077ad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 12
 SUBLEVEL = 0
-EXTRAVERSION = -rc1
+EXTRAVERSION =
 NAME = One Giant Leap for Frogkind
 
 # *DOCUMENTATION*
index 1feb169..af2cc6e 100644 (file)
@@ -286,9 +286,6 @@ config HAVE_PERF_USER_STACK_DUMP
 config HAVE_ARCH_JUMP_LABEL
        bool
 
-config HAVE_ARCH_MUTEX_CPU_RELAX
-       bool
-
 config HAVE_RCU_TABLE_FREE
        bool
 
index 467de01..e3a1491 100644 (file)
@@ -81,6 +81,8 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
-#define SO_BUSY_POLL                   46
+#define SO_BUSY_POLL           46
+
+#define SO_MAX_PACING_RATE     47
 
 #endif /* _UAPI_ASM_SOCKET_H */
index f158197..b6a8c2d 100644 (file)
@@ -45,7 +45,14 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock)
 
 static inline void arch_spin_unlock(arch_spinlock_t *lock)
 {
-       lock->slock = __ARCH_SPIN_LOCK_UNLOCKED__;
+       unsigned int tmp = __ARCH_SPIN_LOCK_UNLOCKED__;
+
+       __asm__ __volatile__(
+       "       ex  %0, [%1]            \n"
+       : "+r" (tmp)
+       : "r"(&(lock->slock))
+       : "memory");
+
        smp_mb();
 }
 
index 3242082..30c9baf 100644 (file)
@@ -43,7 +43,7 @@
  * Because it essentially checks if buffer end is within limit and @len is
  * non-ngeative, which implies that buffer start will be within limit too.
  *
- * The reason for rewriting being, for majorit yof cases, @len is generally
+ * The reason for rewriting being, for majoritof cases, @len is generally
  * compile time constant, causing first sub-expression to be compile time
  * subsumed.
  *
@@ -53,7 +53,7 @@
  *
  */
 #define __user_ok(addr, sz)    (((sz) <= TASK_SIZE) && \
-                                (((addr)+(sz)) <= get_fs()))
+                                ((addr) <= (get_fs() - (sz))))
 #define __access_ok(addr, sz)  (unlikely(__kernel_ok) || \
                                 likely(__user_ok((addr), (sz))))
 
index 3332385..5d76706 100644 (file)
@@ -102,7 +102,7 @@ static int genregs_set(struct task_struct *target,
        REG_IGNORE_ONE(pad2);
        REG_IN_CHUNK(callee, efa, cregs);       /* callee_regs[r25..r13] */
        REG_IGNORE_ONE(efa);                    /* efa update invalid */
-       REG_IN_ONE(stop_pc, &ptregs->ret);      /* stop_pc: PC update */
+       REG_IGNORE_ONE(stop_pc);                        /* PC updated via @ret */
 
        return ret;
 }
index ee6ef2f..7e95e1a 100644 (file)
@@ -101,7 +101,6 @@ SYSCALL_DEFINE0(rt_sigreturn)
 {
        struct rt_sigframe __user *sf;
        unsigned int magic;
-       int err;
        struct pt_regs *regs = current_pt_regs();
 
        /* Always make any pending restarted system calls return -EINTR */
@@ -119,15 +118,16 @@ SYSCALL_DEFINE0(rt_sigreturn)
        if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))
                goto badframe;
 
-       err = restore_usr_regs(regs, sf);
-       err |= __get_user(magic, &sf->sigret_magic);
-       if (err)
+       if (__get_user(magic, &sf->sigret_magic))
                goto badframe;
 
        if (unlikely(is_do_ss_needed(magic)))
                if (restore_altstack(&sf->uc.uc_stack))
                        goto badframe;
 
+       if (restore_usr_regs(regs, sf))
+               goto badframe;
+
        /* Don't restart from sigreturn */
        syscall_wont_restart(regs);
 
@@ -190,6 +190,15 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info,
        if (!sf)
                return 1;
 
+       /*
+        * w/o SA_SIGINFO, struct ucontext is partially populated (only
+        * uc_mcontext/uc_sigmask) for kernel's normal user state preservation
+        * during signal handler execution. This works for SA_SIGINFO as well
+        * although the semantics are now overloaded (the same reg state can be
+        * inspected by userland: but are they allowed to fiddle with it ?
+        */
+       err |= stash_usr_regs(sf, regs, set);
+
        /*
         * SA_SIGINFO requires 3 args to signal handler:
         *  #1: sig-no (common to any handler)
@@ -213,14 +222,6 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info,
                magic = MAGIC_SIGALTSTK;
        }
 
-       /*
-        * w/o SA_SIGINFO, struct ucontext is partially populated (only
-        * uc_mcontext/uc_sigmask) for kernel's normal user state preservation
-        * during signal handler execution. This works for SA_SIGINFO as well
-        * although the semantics are now overloaded (the same reg state can be
-        * inspected by userland: but are they allowed to fiddle with it ?
-        */
-       err |= stash_usr_regs(sf, regs, set);
        err |= __put_user(magic, &sf->sigret_magic);
        if (err)
                return err;
index 0e51e69..3fde7de 100644 (file)
@@ -227,12 +227,9 @@ void __attribute__((weak)) arc_local_timer_setup(unsigned int cpu)
 {
        struct clock_event_device *clk = &per_cpu(arc_clockevent_device, cpu);
 
-       clockevents_calc_mult_shift(clk, arc_get_core_freq(), 5);
-
-       clk->max_delta_ns = clockevent_delta2ns(ARC_TIMER_MAX, clk);
        clk->cpumask = cpumask_of(cpu);
-
-       clockevents_register_device(clk);
+       clockevents_config_and_register(clk, arc_get_core_freq(),
+                                       0, ARC_TIMER_MAX);
 
        /*
         * setup the per-cpu timer IRQ handler - for all cpus
index 28d1700..7ff5b5c 100644 (file)
@@ -245,6 +245,12 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs,
                regs->status32 &= ~STATUS_DE_MASK;
        } else {
                regs->ret += state.instr_len;
+
+               /* handle zero-overhead-loop */
+               if ((regs->ret == regs->lp_end) && (regs->lp_count)) {
+                       regs->ret = regs->lp_start;
+                       regs->lp_count--;
+               }
        }
 
        return 0;
index d63f3de..0c14d8a 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/pgalloc.h>
 #include <asm/mmu.h>
 
-static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address)
+static int handle_vmalloc_fault(unsigned long address)
 {
        /*
         * Synchronize this task's top level page-table
@@ -27,7 +27,7 @@ static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address)
        pud_t *pud, *pud_k;
        pmd_t *pmd, *pmd_k;
 
-       pgd = pgd_offset_fast(mm, address);
+       pgd = pgd_offset_fast(current->active_mm, address);
        pgd_k = pgd_offset_k(address);
 
        if (!pgd_present(*pgd_k))
@@ -72,7 +72,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address)
         * nothing more.
         */
        if (address >= VMALLOC_START && address <= VMALLOC_END) {
-               ret = handle_vmalloc_fault(mm, address);
+               ret = handle_vmalloc_fault(address);
                if (unlikely(ret))
                        goto bad_area_nosemaphore;
                else
index 3f7714d..1ad6fb6 100644 (file)
@@ -2217,8 +2217,7 @@ config NEON
 
 config KERNEL_MODE_NEON
        bool "Support for NEON in kernel mode"
-       default n
-       depends on NEON
+       depends on NEON && AEABI
        help
          Say Y to include support for NEON in kernel mode.
 
index a37a50f..db50b62 100644 (file)
@@ -296,10 +296,15 @@ archprepare:
 # Convert bzImage to zImage
 bzImage: zImage
 
-zImage Image xipImage bootpImage uImage: vmlinux
+BOOT_TARGETS   = zImage Image xipImage bootpImage uImage
+INSTALL_TARGETS        = zinstall uinstall install
+
+PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS)
+
+$(BOOT_TARGETS): vmlinux
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
 
-zinstall uinstall install: vmlinux
+$(INSTALL_TARGETS):
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
 
 %.dtb: | scripts
index 84aa2ca..ec2f806 100644 (file)
@@ -95,24 +95,24 @@ initrd:
        @test "$(INITRD)" != "" || \
        (echo You must specify INITRD; exit -1)
 
-install: $(obj)/Image
-       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+install:
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" \
        $(obj)/Image System.map "$(INSTALL_PATH)"
 
-zinstall: $(obj)/zImage
-       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+zinstall:
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" \
        $(obj)/zImage System.map "$(INSTALL_PATH)"
 
-uinstall: $(obj)/uImage
-       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+uinstall:
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" \
        $(obj)/uImage System.map "$(INSTALL_PATH)"
 
 zi:
-       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" \
        $(obj)/zImage System.map "$(INSTALL_PATH)"
 
 i:
-       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+       $(CONFIG_SHELL) $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" \
        $(obj)/Image System.map "$(INSTALL_PATH)"
 
 subdir-            := bootp compressed dts
index cc0f1fb..802720e 100644 (file)
@@ -41,6 +41,8 @@ dtb-$(CONFIG_ARCH_AT91)       += sama5d33ek.dtb
 dtb-$(CONFIG_ARCH_AT91)        += sama5d34ek.dtb
 dtb-$(CONFIG_ARCH_AT91)        += sama5d35ek.dtb
 
+dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb
+
 dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
 dtb-$(CONFIG_ARCH_BCM) += bcm11351-brt.dtb \
        bcm28155-ap.dtb
@@ -183,6 +185,7 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
        am335x-evm.dtb \
        am335x-evmsk.dtb \
        am335x-bone.dtb \
+       am335x-boneblack.dtb \
        am3517-evm.dtb \
        am3517_mt_ventoux.dtb \
        am43x-epos-evm.dtb
diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi
new file mode 100644 (file)
index 0000000..2f66ded
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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.
+ */
+
+/ {
+       model = "TI AM335x BeagleBone";
+       compatible = "ti,am335x-bone", "ti,am33xx";
+
+       cpus {
+               cpu@0 {
+                       cpu0-supply = <&dcdc2_reg>;
+               };
+       };
+
+       memory {
+               device_type = "memory";
+               reg = <0x80000000 0x10000000>; /* 256 MB */
+       };
+
+       am33xx_pinmux: pinmux@44e10800 {
+               pinctrl-names = "default";
+               pinctrl-0 = <&clkout2_pin>;
+
+               user_leds_s0: user_leds_s0 {
+                       pinctrl-single,pins = <
+                               0x54 (PIN_OUTPUT_PULLDOWN | MUX_MODE7)  /* gpmc_a5.gpio1_21 */
+                               0x58 (PIN_OUTPUT_PULLUP | MUX_MODE7)    /* gpmc_a6.gpio1_22 */
+                               0x5c (PIN_OUTPUT_PULLDOWN | MUX_MODE7)  /* gpmc_a7.gpio1_23 */
+                               0x60 (PIN_OUTPUT_PULLUP | MUX_MODE7)    /* gpmc_a8.gpio1_24 */
+                       >;
+               };
+
+               i2c0_pins: pinmux_i2c0_pins {
+                       pinctrl-single,pins = <
+                               0x188 (PIN_INPUT_PULLUP | MUX_MODE0)    /* i2c0_sda.i2c0_sda */
+                               0x18c (PIN_INPUT_PULLUP | MUX_MODE0)    /* i2c0_scl.i2c0_scl */
+                       >;
+               };
+
+               uart0_pins: pinmux_uart0_pins {
+                       pinctrl-single,pins = <
+                               0x170 (PIN_INPUT_PULLUP | MUX_MODE0)    /* uart0_rxd.uart0_rxd */
+                               0x174 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */
+                       >;
+               };
+
+               clkout2_pin: pinmux_clkout2_pin {
+                       pinctrl-single,pins = <
+                               0x1b4 (PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr1.clkout2 */
+                       >;
+               };
+
+               cpsw_default: cpsw_default {
+                       pinctrl-single,pins = <
+                               /* Slave 1 */
+                               0x110 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxerr.mii1_rxerr */
+                               0x114 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */
+                               0x118 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxdv.mii1_rxdv */
+                               0x11c (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */
+                               0x120 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */
+                               0x124 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */
+                               0x128 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */
+                               0x12c (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_txclk.mii1_txclk */
+                               0x130 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxclk.mii1_rxclk */
+                               0x134 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd3.mii1_rxd3 */
+                               0x138 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd2.mii1_rxd2 */
+                               0x13c (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd1.mii1_rxd1 */
+                               0x140 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd0.mii1_rxd0 */
+                       >;
+               };
+
+               cpsw_sleep: cpsw_sleep {
+                       pinctrl-single,pins = <
+                               /* Slave 1 reset value */
+                               0x110 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x114 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x118 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x11c (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x120 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x124 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x128 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x12c (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x130 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x13c (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x140 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                       >;
+               };
+
+               davinci_mdio_default: davinci_mdio_default {
+                       pinctrl-single,pins = <
+                               /* MDIO */
+                               0x148 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0)    /* mdio_data.mdio_data */
+                               0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0)                   /* mdio_clk.mdio_clk */
+                       >;
+               };
+
+               davinci_mdio_sleep: davinci_mdio_sleep {
+                       pinctrl-single,pins = <
+                               /* MDIO reset value */
+                               0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                               0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7)
+                       >;
+               };
+       };
+
+       ocp {
+               uart0: serial@44e09000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&uart0_pins>;
+
+                       status = "okay";
+               };
+
+               musb: usb@47400000 {
+                       status = "okay";
+
+                       control@44e10000 {
+                               status = "okay";
+                       };
+
+                       usb-phy@47401300 {
+                               status = "okay";
+                       };
+
+                       usb-phy@47401b00 {
+                               status = "okay";
+                       };
+
+                       usb@47401000 {
+                               status = "okay";
+                       };
+
+                       usb@47401800 {
+                               status = "okay";
+                               dr_mode = "host";
+                       };
+
+                       dma-controller@07402000  {
+                               status = "okay";
+                       };
+               };
+
+               i2c0: i2c@44e0b000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&i2c0_pins>;
+
+                       status = "okay";
+                       clock-frequency = <400000>;
+
+                       tps: tps@24 {
+                               reg = <0x24>;
+                       };
+
+               };
+       };
+
+       leds {
+               pinctrl-names = "default";
+               pinctrl-0 = <&user_leds_s0>;
+
+               compatible = "gpio-leds";
+
+               led@2 {
+                       label = "beaglebone:green:heartbeat";
+                       gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
+                       linux,default-trigger = "heartbeat";
+                       default-state = "off";
+               };
+
+               led@3 {
+                       label = "beaglebone:green:mmc0";
+                       gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>;
+                       linux,default-trigger = "mmc0";
+                       default-state = "off";
+               };
+
+               led@4 {
+                       label = "beaglebone:green:usr2";
+                       gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
+                       default-state = "off";
+               };
+
+               led@5 {
+                       label = "beaglebone:green:usr3";
+                       gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>;
+                       default-state = "off";
+               };
+       };
+};
+
+/include/ "tps65217.dtsi"
+
+&tps {
+       regulators {
+               dcdc1_reg: regulator@0 {
+                       regulator-always-on;
+               };
+
+               dcdc2_reg: regulator@1 {
+                       /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */
+                       regulator-name = "vdd_mpu";
+                       regulator-min-microvolt = <925000>;
+                       regulator-max-microvolt = <1325000>;
+                       regulator-boot-on;
+                       regulator-always-on;
+               };
+
+               dcdc3_reg: regulator@2 {
+                       /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */
+                       regulator-name = "vdd_core";
+                       regulator-min-microvolt = <925000>;
+                       regulator-max-microvolt = <1150000>;
+                       regulator-boot-on;
+                       regulator-always-on;
+               };
+
+               ldo1_reg: regulator@3 {
+                       regulator-always-on;
+               };
+
+               ldo2_reg: regulator@4 {
+                       regulator-always-on;
+               };
+
+               ldo3_reg: regulator@5 {
+                       regulator-always-on;
+               };
+
+               ldo4_reg: regulator@6 {
+                       regulator-always-on;
+               };
+       };
+};
+
+&cpsw_emac0 {
+       phy_id = <&davinci_mdio>, <0>;
+       phy-mode = "mii";
+};
+
+&cpsw_emac1 {
+       phy_id = <&davinci_mdio>, <1>;
+       phy-mode = "mii";
+};
+
+&mac {
+       pinctrl-names = "default", "sleep";
+       pinctrl-0 = <&cpsw_default>;
+       pinctrl-1 = <&cpsw_sleep>;
+
+};
+
+&davinci_mdio {
+       pinctrl-names = "default", "sleep";
+       pinctrl-0 = <&davinci_mdio_default>;
+       pinctrl-1 = <&davinci_mdio_sleep>;
+};
index d318987..7993c48 100644 (file)
@@ -8,258 +8,4 @@
 /dts-v1/;
 
 #include "am33xx.dtsi"
-
-/ {
-       model = "TI AM335x BeagleBone";
-       compatible = "ti,am335x-bone", "ti,am33xx";
-
-       cpus {
-               cpu@0 {
-                       cpu0-supply = <&dcdc2_reg>;
-               };
-       };
-
-       memory {
-               device_type = "memory";
-               reg = <0x80000000 0x10000000>; /* 256 MB */
-       };
-
-       am33xx_pinmux: pinmux@44e10800 {
-               pinctrl-names = "default";
-               pinctrl-0 = <&clkout2_pin>;
-
-               user_leds_s0: user_leds_s0 {
-                       pinctrl-single,pins = <
-                               0x54 (PIN_OUTPUT_PULLDOWN | MUX_MODE7)  /* gpmc_a5.gpio1_21 */
-                               0x58 (PIN_OUTPUT_PULLUP | MUX_MODE7)    /* gpmc_a6.gpio1_22 */
-                               0x5c (PIN_OUTPUT_PULLDOWN | MUX_MODE7)  /* gpmc_a7.gpio1_23 */
-                               0x60 (PIN_OUTPUT_PULLUP | MUX_MODE7)    /* gpmc_a8.gpio1_24 */
-                       >;
-               };
-
-               i2c0_pins: pinmux_i2c0_pins {
-                       pinctrl-single,pins = <
-                               0x188 (PIN_INPUT_PULLUP | MUX_MODE0)    /* i2c0_sda.i2c0_sda */
-                               0x18c (PIN_INPUT_PULLUP | MUX_MODE0)    /* i2c0_scl.i2c0_scl */
-                       >;
-               };
-
-               uart0_pins: pinmux_uart0_pins {
-                       pinctrl-single,pins = <
-                               0x170 (PIN_INPUT_PULLUP | MUX_MODE0)    /* uart0_rxd.uart0_rxd */
-                               0x174 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */
-                       >;
-               };
-
-               clkout2_pin: pinmux_clkout2_pin {
-                       pinctrl-single,pins = <
-                               0x1b4 (PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr1.clkout2 */
-                       >;
-               };
-
-               cpsw_default: cpsw_default {
-                       pinctrl-single,pins = <
-                               /* Slave 1 */
-                               0x110 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxerr.mii1_rxerr */
-                               0x114 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */
-                               0x118 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxdv.mii1_rxdv */
-                               0x11c (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */
-                               0x120 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */
-                               0x124 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */
-                               0x128 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */
-                               0x12c (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_txclk.mii1_txclk */
-                               0x130 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxclk.mii1_rxclk */
-                               0x134 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd3.mii1_rxd3 */
-                               0x138 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd2.mii1_rxd2 */
-                               0x13c (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd1.mii1_rxd1 */
-                               0x140 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd0.mii1_rxd0 */
-                       >;
-               };
-
-               cpsw_sleep: cpsw_sleep {
-                       pinctrl-single,pins = <
-                               /* Slave 1 reset value */
-                               0x110 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x114 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x118 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x11c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x120 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x124 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x128 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x12c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x130 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x13c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x140 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                       >;
-               };
-
-               davinci_mdio_default: davinci_mdio_default {
-                       pinctrl-single,pins = <
-                               /* MDIO */
-                               0x148 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0)    /* mdio_data.mdio_data */
-                               0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0)                   /* mdio_clk.mdio_clk */
-                       >;
-               };
-
-               davinci_mdio_sleep: davinci_mdio_sleep {
-                       pinctrl-single,pins = <
-                               /* MDIO reset value */
-                               0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                               0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                       >;
-               };
-       };
-
-       ocp {
-               uart0: serial@44e09000 {
-                       pinctrl-names = "default";
-                       pinctrl-0 = <&uart0_pins>;
-
-                       status = "okay";
-               };
-
-               musb: usb@47400000 {
-                       status = "okay";
-
-                       control@44e10000 {
-                               status = "okay";
-                       };
-
-                       usb-phy@47401300 {
-                               status = "okay";
-                       };
-
-                       usb-phy@47401b00 {
-                               status = "okay";
-                       };
-
-                       usb@47401000 {
-                               status = "okay";
-                       };
-
-                       usb@47401800 {
-                               status = "okay";
-                               dr_mode = "host";
-                       };
-
-                       dma-controller@07402000  {
-                               status = "okay";
-                       };
-               };
-
-               i2c0: i2c@44e0b000 {
-                       pinctrl-names = "default";
-                       pinctrl-0 = <&i2c0_pins>;
-
-                       status = "okay";
-                       clock-frequency = <400000>;
-
-                       tps: tps@24 {
-                               reg = <0x24>;
-                       };
-
-               };
-       };
-
-       leds {
-               pinctrl-names = "default";
-               pinctrl-0 = <&user_leds_s0>;
-
-               compatible = "gpio-leds";
-
-               led@2 {
-                       label = "beaglebone:green:heartbeat";
-                       gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
-                       linux,default-trigger = "heartbeat";
-                       default-state = "off";
-               };
-
-               led@3 {
-                       label = "beaglebone:green:mmc0";
-                       gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>;
-                       linux,default-trigger = "mmc0";
-                       default-state = "off";
-               };
-
-               led@4 {
-                       label = "beaglebone:green:usr2";
-                       gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
-                       default-state = "off";
-               };
-
-               led@5 {
-                       label = "beaglebone:green:usr3";
-                       gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>;
-                       default-state = "off";
-               };
-       };
-};
-
-/include/ "tps65217.dtsi"
-
-&tps {
-       regulators {
-               dcdc1_reg: regulator@0 {
-                       regulator-always-on;
-               };
-
-               dcdc2_reg: regulator@1 {
-                       /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */
-                       regulator-name = "vdd_mpu";
-                       regulator-min-microvolt = <925000>;
-                       regulator-max-microvolt = <1325000>;
-                       regulator-boot-on;
-                       regulator-always-on;
-               };
-
-               dcdc3_reg: regulator@2 {
-                       /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */
-                       regulator-name = "vdd_core";
-                       regulator-min-microvolt = <925000>;
-                       regulator-max-microvolt = <1150000>;
-                       regulator-boot-on;
-                       regulator-always-on;
-               };
-
-               ldo1_reg: regulator@3 {
-                       regulator-always-on;
-               };
-
-               ldo2_reg: regulator@4 {
-                       regulator-always-on;
-               };
-
-               ldo3_reg: regulator@5 {
-                       regulator-always-on;
-               };
-
-               ldo4_reg: regulator@6 {
-                       regulator-always-on;
-               };
-       };
-};
-
-&cpsw_emac0 {
-       phy_id = <&davinci_mdio>, <0>;
-       phy-mode = "mii";
-};
-
-&cpsw_emac1 {
-       phy_id = <&davinci_mdio>, <1>;
-       phy-mode = "mii";
-};
-
-&mac {
-       pinctrl-names = "default", "sleep";
-       pinctrl-0 = <&cpsw_default>;
-       pinctrl-1 = <&cpsw_sleep>;
-
-};
-
-&davinci_mdio {
-       pinctrl-names = "default", "sleep";
-       pinctrl-0 = <&davinci_mdio_default>;
-       pinctrl-1 = <&davinci_mdio_sleep>;
-};
+#include "am335x-bone-common.dtsi"
diff --git a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts
new file mode 100644 (file)
index 0000000..197cadf
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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.
+ */
+/dts-v1/;
+
+#include "am33xx.dtsi"
+#include "am335x-bone-common.dtsi"
+
+&ldo3_reg {
+       regulator-min-microvolt = <1800000>;
+       regulator-max-microvolt = <1800000>;
+       regulator-always-on;
+};
index f9c5da9..d76ae24 100644 (file)
                                /* Filled in by U-Boot */
                                mac-address = [ 00 00 00 00 00 00 ];
                        };
+
+                       phy_sel: cpsw-phy-sel@44e10650 {
+                               compatible = "ti,am3352-cpsw-phy-sel";
+                               reg= <0x44e10650 0x4>;
+                               reg-names = "gmii-sel";
+                       };
                };
 
                ocmcram: ocmcram@40300000 {
index 05e4485..8ac2ac1 100644 (file)
        };
 
        soc {
+               ranges = <MBUS_ID(0xf0, 0x01) 0 0xd0000000 0x100000
+                         MBUS_ID(0x01, 0xe0) 0 0xfff00000 0x100000>;
+
+               pcie-controller {
+                       status = "okay";
+
+                       /* Connected to Marvell SATA controller */
+                       pcie@1,0 {
+                               /* Port 0, Lane 0 */
+                               status = "okay";
+                       };
+
+                       /* Connected to FL1009 USB 3.0 controller */
+                       pcie@2,0 {
+                               /* Port 1, Lane 0 */
+                               status = "okay";
+                       };
+               };
+
                internal-regs {
                        serial@12000 {
                                clock-frequency = <200000000>;
                                        marvell,pins = "mpp56";
                                        marvell,function = "gpio";
                                };
+
+                               poweroff: poweroff {
+                                       marvell,pins = "mpp8";
+                                       marvell,function = "gpio";
+                               };
                        };
 
                        mdio {
                                        pwm_polarity = <0>;
                                };
                        };
-
-                       pcie-controller {
-                               status = "okay";
-
-                               /* Connected to Marvell SATA controller */
-                               pcie@1,0 {
-                                       /* Port 0, Lane 0 */
-                                       status = "okay";
-                               };
-
-                               /* Connected to FL1009 USB 3.0 controller */
-                               pcie@2,0 {
-                                       /* Port 1, Lane 0 */
-                                       status = "okay";
-                               };
-                       };
                };
        };
 
                button@1 {
                        label = "Power Button";
                        linux,code = <116>;     /* KEY_POWER */
-                       gpios = <&gpio1 30 1>;
+                       gpios = <&gpio1 30 0>;
                };
 
                button@2 {
                };
        };
 
+       gpio_poweroff {
+               compatible = "gpio-poweroff";
+               pinctrl-0 = <&poweroff>;
+               pinctrl-names = "default";
+               gpios = <&gpio0 8 1>;
+       };
+
 };
index def125c..3058522 100644 (file)
@@ -70,6 +70,8 @@
 
                        timer@20300 {
                                compatible = "marvell,armada-xp-timer";
+                               clocks = <&coreclk 2>, <&refclk>;
+                               clock-names = "nbclk", "fixed";
                        };
 
                        coreclk: mvebu-sar@18230 {
                        };
                };
        };
+
+       clocks {
+               /* 25 MHz reference crystal */
+               refclk: oscillator {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <25000000>;
+               };
+       };
 };
index cf78ac0..e74dc15 100644 (file)
                                                         AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE>;  /* PA8 periph A */
                                        };
 
-                                       pinctrl_uart2_rts: uart2_rts-0 {
+                                       pinctrl_usart2_rts: usart2_rts-0 {
                                                atmel,pins =
                                                        <AT91_PIOB 0 AT91_PERIPH_B AT91_PINCTRL_NONE>;  /* PB0 periph B */
                                        };
 
-                                       pinctrl_uart2_cts: uart2_cts-0 {
+                                       pinctrl_usart2_cts: usart2_cts-0 {
                                                atmel,pins =
                                                        <AT91_PIOB 1 AT91_PERIPH_B AT91_PINCTRL_NONE>;  /* PB1 periph B */
                                        };
                                interrupts = <12 IRQ_TYPE_LEVEL_HIGH 0>;
                                dmas = <&dma0 1 AT91_DMA_CFG_PER_ID(0)>;
                                dma-names = "rxtx";
+                               pinctrl-names = "default";
                                #address-cells = <1>;
                                #size-cells = <0>;
                                status = "disabled";
                                interrupts = <26 IRQ_TYPE_LEVEL_HIGH 0>;
                                dmas = <&dma1 1 AT91_DMA_CFG_PER_ID(0)>;
                                dma-names = "rxtx";
+                               pinctrl-names = "default";
                                #address-cells = <1>;
                                #size-cells = <0>;
                                status = "disabled";
index 8678e0c..6db4f81 100644 (file)
                                interrupts = <17>;
                                fifosize = <128>;
                                clocks = <&clks 13>;
+                               sirf,uart-dma-rx-channel = <21>;
+                               sirf,uart-dma-tx-channel = <2>;
                        };
 
                        uart1: uart@b0060000 {
                                interrupts = <19>;
                                fifosize = <128>;
                                clocks = <&clks 15>;
+                               sirf,uart-dma-rx-channel = <6>;
+                               sirf,uart-dma-tx-channel = <7>;
                        };
 
                        usp0: usp@b0080000 {
                                compatible = "sirf,prima2-usp";
                                reg = <0xb0080000 0x10000>;
                                interrupts = <20>;
+                               fifosize = <128>;
                                clocks = <&clks 28>;
+                               sirf,usp-dma-rx-channel = <17>;
+                               sirf,usp-dma-tx-channel = <18>;
                        };
 
                        usp1: usp@b0090000 {
                                compatible = "sirf,prima2-usp";
                                reg = <0xb0090000 0x10000>;
                                interrupts = <21>;
+                               fifosize = <128>;
                                clocks = <&clks 29>;
+                               sirf,usp-dma-rx-channel = <14>;
+                               sirf,usp-dma-tx-channel = <15>;
                        };
 
                        dmac0: dma-controller@b00b0000 {
                                compatible = "sirf,prima2-vip";
                                reg = <0xb00C0000 0x10000>;
                                clocks = <&clks 31>;
+                               interrupts = <14>;
+                               sirf,vip-dma-rx-channel = <16>;
                        };
 
                        spi0: spi@b00d0000 {
index 7d7cc77..bbac42a 100644 (file)
                             <1 14 0xf08>,
                             <1 11 0xf08>,
                             <1 10 0xf08>;
+               /* Unfortunately we need this since some versions of U-Boot
+                * on Exynos don't set the CNTFRQ register, so we need the
+                * value from DT.
+                */
+               clock-frequency = <24000000>;
        };
 
        mct@101C0000 {
index c037c22..b7a1c6d 100644 (file)
                                compatible = "fsl,imx27-cspi";
                                reg = <0x1000e000 0x1000>;
                                interrupts = <16>;
-                               clocks = <&clks 53>, <&clks 53>;
+                               clocks = <&clks 53>, <&clks 60>;
                                clock-names = "ipg", "per";
                                status = "disabled";
                        };
                                compatible = "fsl,imx27-cspi";
                                reg = <0x1000f000 0x1000>;
                                interrupts = <15>;
-                               clocks = <&clks 52>, <&clks 52>;
+                               clocks = <&clks 52>, <&clks 60>;
                                clock-names = "ipg", "per";
                                status = "disabled";
                        };
                                compatible = "fsl,imx27-cspi";
                                reg = <0x10017000 0x1000>;
                                interrupts = <6>;
-                               clocks = <&clks 51>, <&clks 51>;
+                               clocks = <&clks 51>, <&clks 60>;
                                clock-names = "ipg", "per";
                                status = "disabled";
                        };
index a85abb4..54cee65 100644 (file)
                                compatible = "fsl,imx51-pata", "fsl,imx27-pata";
                                reg = <0x83fe0000 0x4000>;
                                interrupts = <70>;
-                               clocks = <&clks 161>;
+                               clocks = <&clks 172>;
                                status = "disabled";
                        };
 
index c0e38a4..9bbe82b 100644 (file)
 #define MX6QDL_PAD_EIM_D29__ECSPI4_SS0              0x0c8 0x3dc 0x824 0x2 0x1
 #define MX6QDL_PAD_EIM_D29__UART2_RTS_B             0x0c8 0x3dc 0x924 0x4 0x1
 #define MX6QDL_PAD_EIM_D29__UART2_CTS_B             0x0c8 0x3dc 0x000 0x4 0x0
-#define MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B         0x0c4 0x3dc 0x000 0x4 0x0
-#define MX6QDL_PAD_EIM_D29__UART2_DTE_CTS_B         0x0c4 0x3dc 0x924 0x4 0x1
+#define MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B         0x0c8 0x3dc 0x000 0x4 0x0
+#define MX6QDL_PAD_EIM_D29__UART2_DTE_CTS_B         0x0c8 0x3dc 0x924 0x4 0x1
 #define MX6QDL_PAD_EIM_D29__GPIO3_IO29              0x0c8 0x3dc 0x000 0x5 0x0
 #define MX6QDL_PAD_EIM_D29__IPU2_CSI1_VSYNC         0x0c8 0x3dc 0x8e4 0x6 0x0
 #define MX6QDL_PAD_EIM_D29__IPU1_DI0_PIN14          0x0c8 0x3dc 0x000 0x7 0x0
index ff1aea0..72693a6 100644 (file)
@@ -9,11 +9,6 @@
        model = "ARM Integrator/CP";
        compatible = "arm,integrator-cp";
 
-       aliases {
-               arm,timer-primary = &timer2;
-               arm,timer-secondary = &timer1;
-       };
-
        chosen {
                bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
        };
        };
 
        timer0: timer@13000000 {
+               /* TIMER0 runs @ 25MHz */
                compatible = "arm,integrator-cp-timer";
+               status = "disabled";
        };
 
        timer1: timer@13000100 {
+               /* TIMER1 runs @ 1MHz */
                compatible = "arm,integrator-cp-timer";
        };
 
        timer2: timer@13000200 {
+               /* TIMER2 runs @ 1MHz */
                compatible = "arm,integrator-cp-timer";
        };
 
index cf7aeaf..1335b2e 100644 (file)
@@ -13,6 +13,7 @@
                cpu@0 {
                        device_type = "cpu";
                        compatible = "marvell,feroceon";
+                       reg = <0>;
                        clocks = <&core_clk 1>, <&core_clk 3>, <&gate_clk 11>;
                        clock-names = "cpu_clk", "ddrclk", "powersave";
                };
                xor@60900 {
                        compatible = "marvell,orion-xor";
                        reg = <0x60900 0x100
-                              0xd0B00 0x100>;
+                              0x60B00 0x100>;
                        status = "okay";
                        clocks = <&gate_clk 16>;
 
index afdb164..2816bf6 100644 (file)
@@ -11,7 +11,7 @@
 
 / {
        model = "TI OMAP3 BeagleBoard xM";
-       compatible = "ti,omap3-beagle-xm, ti,omap3-beagle", "ti,omap3";
+       compatible = "ti,omap3-beagle-xm", "ti,omap36xx", "ti,omap3";
 
        cpus {
                cpu@0 {
index bc48b11..2326d11 100644 (file)
                >;
        };
 
+       mcbsp2_pins: pinmux_mcbsp2_pins {
+               pinctrl-single,pins = <
+                       0x10c (PIN_INPUT | MUX_MODE0)           /* mcbsp2_fsx.mcbsp2_fsx */
+                       0x10e (PIN_INPUT | MUX_MODE0)           /* mcbsp2_clkx.mcbsp2_clkx */
+                       0x110 (PIN_INPUT | MUX_MODE0)           /* mcbsp2_dr.mcbsp2.dr */
+                       0x112 (PIN_OUTPUT | MUX_MODE0)          /* mcbsp2_dx.mcbsp2_dx */
+               >;
+       };
+
        mmc1_pins: pinmux_mmc1_pins {
                pinctrl-single,pins = <
                        0x114 (PIN_INPUT_PULLUP | MUX_MODE0)    /* sdmmc1_clk.sdmmc1_clk */
        clock-frequency = <400000>;
 };
 
+&mcbsp2 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&mcbsp2_pins>;
+};
+
 &mmc1 {
       pinctrl-names = "default";
       pinctrl-0 = <&mmc1_pins>;
index 7d95cda..b41bd57 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <0>;
                        pinctrl-single,register-width = <16>;
-                       pinctrl-single,function-mask = <0x7f1f>;
+                       pinctrl-single,function-mask = <0xff1f>;
                };
 
                omap3_pmx_wkup: pinmux@0x48002a00 {
                        #address-cells = <1>;
                        #size-cells = <0>;
                        pinctrl-single,register-width = <16>;
-                       pinctrl-single,function-mask = <0x7f1f>;
+                       pinctrl-single,function-mask = <0xff1f>;
                };
 
                gpio1: gpio@48310000 {
index faa95b5..814ab67 100644 (file)
         */
                clock-frequency = <19200000>;
        };
+
+       /* regulator for wl12xx on sdio5 */
+       wl12xx_vmmc: wl12xx_vmmc {
+               pinctrl-names = "default";
+               pinctrl-0 = <&wl12xx_gpio>;
+               compatible = "regulator-fixed";
+               regulator-name = "vwl1271";
+               regulator-min-microvolt = <1800000>;
+               regulator-max-microvolt = <1800000>;
+               gpio = <&gpio2 11 0>;
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
 };
 
 &omap4_pmx_wkup {
                        0x1c (PIN_OUTPUT | MUX_MODE3)   /* gpio_wk8 */
                >;
        };
+
+       /*
+        * wl12xx GPIO outputs for WLAN_EN, BT_EN, FM_EN, BT_WAKEUP
+        * REVISIT: Are the pull-ups needed for GPIO 48 and 49?
+        */
+       wl12xx_gpio: pinmux_wl12xx_gpio {
+               pinctrl-single,pins = <
+                       0x26 (PIN_OUTPUT | MUX_MODE3)           /* gpmc_a19.gpio_43 */
+                       0x2c (PIN_OUTPUT | MUX_MODE3)           /* gpmc_a22.gpio_46 */
+                       0x30 (PIN_OUTPUT_PULLUP | MUX_MODE3)    /* gpmc_a24.gpio_48 */
+                       0x32 (PIN_OUTPUT_PULLUP | MUX_MODE3)    /* gpmc_a25.gpio_49 */
+               >;
+       };
+
+       /* wl12xx GPIO inputs and SDIO pins */
+       wl12xx_pins: pinmux_wl12xx_pins {
+               pinctrl-single,pins = <
+                       0x38 (PIN_INPUT | MUX_MODE3)            /* gpmc_ncs2.gpio_52 */
+                       0x3a (PIN_INPUT | MUX_MODE3)            /* gpmc_ncs3.gpio_53 */
+                       0x108 (PIN_OUTPUT | MUX_MODE0)          /* sdmmc5_clk.sdmmc5_clk */
+                       0x10a (PIN_INPUT_PULLUP | MUX_MODE0)    /* sdmmc5_cmd.sdmmc5_cmd */
+                       0x10c (PIN_INPUT_PULLUP | MUX_MODE0)    /* sdmmc5_dat0.sdmmc5_dat0 */
+                       0x10e (PIN_INPUT_PULLUP | MUX_MODE0)    /* sdmmc5_dat1.sdmmc5_dat1 */
+                       0x110 (PIN_INPUT_PULLUP | MUX_MODE0)    /* sdmmc5_dat2.sdmmc5_dat2 */
+                       0x112 (PIN_INPUT_PULLUP | MUX_MODE0)    /* sdmmc5_dat3.sdmmc5_dat3 */
+               >;
+       };
 };
 
 &i2c1 {
 };
 
 &mmc5 {
-       ti,non-removable;
+       pinctrl-names = "default";
+       pinctrl-0 = <&wl12xx_pins>;
+       vmmc-supply = <&wl12xx_vmmc>;
+       non-removable;
        bus-width = <4>;
+       cap-power-off-card;
 };
 
 &emif1 {
index 7951b4e..4f78380 100644 (file)
                        "DMic", "Digital Mic",
                        "Digital Mic", "Digital Mic1 Bias";
        };
+
+       /* regulator for wl12xx on sdio5 */
+       wl12xx_vmmc: wl12xx_vmmc {
+               pinctrl-names = "default";
+               pinctrl-0 = <&wl12xx_gpio>;
+               compatible = "regulator-fixed";
+               regulator-name = "vwl1271";
+               regulator-min-microvolt = <1800000>;
+               regulator-max-microvolt = <1800000>;
+               gpio = <&gpio2 22 0>;
+               startup-delay-us = <70000>;
+               enable-active-high;
+       };
 };
 
 &omap4_pmx_wkup {
                        0xf0 (PIN_INPUT_PULLUP | MUX_MODE0)     /* i2c4_sda */
                >;
        };
+
+       /* wl12xx GPIO output for WLAN_EN */
+       wl12xx_gpio: pinmux_wl12xx_gpio {
+               pinctrl-single,pins = <
+                       0x3c (PIN_OUTPUT | MUX_MODE3)           /* gpmc_nwp.gpio_54 */
+               >;
+       };
+
+       /* wl12xx GPIO inputs and SDIO pins */
+       wl12xx_pins: pinmux_wl12xx_pins {
+               pinctrl-single,pins = <
+                       0x3a (PIN_INPUT | MUX_MODE3)            /* gpmc_ncs3.gpio_53 */
+                       0x108 (PIN_OUTPUT | MUX_MODE3)          /* sdmmc5_clk.sdmmc5_clk */
+                       0x10a (PIN_INPUT_PULLUP | MUX_MODE3)    /* sdmmc5_cmd.sdmmc5_cmd */
+                       0x10c (PIN_INPUT_PULLUP | MUX_MODE3)    /* sdmmc5_dat0.sdmmc5_dat0 */
+                       0x10e (PIN_INPUT_PULLUP | MUX_MODE3)    /* sdmmc5_dat1.sdmmc5_dat1 */
+                       0x110 (PIN_INPUT_PULLUP | MUX_MODE3)    /* sdmmc5_dat2.sdmmc5_dat2 */
+                       0x112 (PIN_INPUT_PULLUP | MUX_MODE3)    /* sdmmc5_dat3.sdmmc5_dat3 */
+               >;
+       };
 };
 
 &i2c1 {
 };
 
 &mmc5 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&wl12xx_pins>;
+       vmmc-supply = <&wl12xx_vmmc>;
+       non-removable;
        bus-width = <4>;
-       ti,non-removable;
+       cap-power-off-card;
 };
 
 &emif1 {
index 07be2cd..7cdea1b 100644 (file)
                omap_dwc3@4a020000 {
                        compatible = "ti,dwc3";
                        ti,hwmods = "usb_otg_ss";
-                       reg = <0x4a020000 0x1000>;
+                       reg = <0x4a020000 0x10000>;
                        interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                        ranges;
                        dwc3@4a030000 {
                                compatible = "snps,dwc3";
-                               reg = <0x4a030000 0x1000>;
+                               reg = <0x4a030000 0x10000>;
                                interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
                                usb-phy = <&usb2_phy>, <&usb3_phy>;
                                tx-fifo-resize;
                        };
                };
 
-               ocp2scp {
+               ocp2scp@4a080000 {
                        compatible = "ti,omap-ocp2scp";
                        #address-cells = <1>;
                        #size-cells = <1>;
+                       reg = <0x4a080000 0x20>;
                        ranges;
                        ti,hwmods = "ocp2scp1";
                        usb2_phy: usb2phy@4a084000 {
index bbeb623..27ed9f5 100644 (file)
                        compatible = "simple-bus";
                        #address-cells = <1>;
                        #size-cells = <1>;
-                       ranges = <0xb0000000 0xb0000000 0x180000>;
+                       ranges = <0xb0000000 0xb0000000 0x180000>,
+                              <0x56000000 0x56000000 0x1b00000>;
 
                        timer@b0020000 {
                                compatible = "sirf,prima2-tick";
                        uart0: uart@b0050000 {
                                cell-index = <0>;
                                compatible = "sirf,prima2-uart";
-                               reg = <0xb0050000 0x10000>;
+                               reg = <0xb0050000 0x1000>;
                                interrupts = <17>;
+                               fifosize = <128>;
                                clocks = <&clks 13>;
+                               sirf,uart-dma-rx-channel = <21>;
+                               sirf,uart-dma-tx-channel = <2>;
                        };
 
                        uart1: uart@b0060000 {
                                cell-index = <1>;
                                compatible = "sirf,prima2-uart";
-                               reg = <0xb0060000 0x10000>;
+                               reg = <0xb0060000 0x1000>;
                                interrupts = <18>;
+                               fifosize = <32>;
                                clocks = <&clks 14>;
                        };
 
                        uart2: uart@b0070000 {
                                cell-index = <2>;
                                compatible = "sirf,prima2-uart";
-                               reg = <0xb0070000 0x10000>;
+                               reg = <0xb0070000 0x1000>;
                                interrupts = <19>;
+                               fifosize = <128>;
                                clocks = <&clks 15>;
+                               sirf,uart-dma-rx-channel = <6>;
+                               sirf,uart-dma-tx-channel = <7>;
                        };
 
                        usp0: usp@b0080000 {
                                compatible = "sirf,prima2-usp";
                                reg = <0xb0080000 0x10000>;
                                interrupts = <20>;
+                               fifosize = <128>;
                                clocks = <&clks 28>;
+                               sirf,usp-dma-rx-channel = <17>;
+                               sirf,usp-dma-tx-channel = <18>;
                        };
 
                        usp1: usp@b0090000 {
                                compatible = "sirf,prima2-usp";
                                reg = <0xb0090000 0x10000>;
                                interrupts = <21>;
+                               fifosize = <128>;
                                clocks = <&clks 29>;
+                               sirf,usp-dma-rx-channel = <14>;
+                               sirf,usp-dma-tx-channel = <15>;
                        };
 
                        usp2: usp@b00a0000 {
                                compatible = "sirf,prima2-usp";
                                reg = <0xb00a0000 0x10000>;
                                interrupts = <22>;
+                               fifosize = <128>;
                                clocks = <&clks 30>;
+                               sirf,usp-dma-rx-channel = <10>;
+                               sirf,usp-dma-tx-channel = <11>;
                        };
 
                        dmac0: dma-controller@b00b0000 {
                                compatible = "sirf,prima2-vip";
                                reg = <0xb00C0000 0x10000>;
                                clocks = <&clks 31>;
+                               interrupts = <14>;
+                               sirf,vip-dma-rx-channel = <16>;
                        };
 
                        spi0: spi@b00d0000 {
index 6c26caa..658fcc5 100644 (file)
        };
 
        sdhi0: sdhi@ee100000 {
-               compatible = "renesas,r8a73a4-sdhi";
+               compatible = "renesas,sdhi-r8a73a4";
                reg = <0 0xee100000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 165 4>;
        };
 
        sdhi1: sdhi@ee120000 {
-               compatible = "renesas,r8a73a4-sdhi";
+               compatible = "renesas,sdhi-r8a73a4";
                reg = <0 0xee120000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 166 4>;
        };
 
        sdhi2: sdhi@ee140000 {
-               compatible = "renesas,r8a73a4-sdhi";
+               compatible = "renesas,sdhi-r8a73a4";
                reg = <0 0xee140000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 167 4>;
index 45ac404..3577aba 100644 (file)
@@ -96,6 +96,5 @@
        pfc: pfc@fffc0000 {
                compatible = "renesas,pfc-r8a7778";
                reg = <0xfffc000 0x118>;
-               #gpio-range-cells = <3>;
        };
 };
index 23a6244..ebbe507 100644 (file)
        pfc: pfc@fffc0000 {
                compatible = "renesas,pfc-r8a7779";
                reg = <0xfffc0000 0x23c>;
-               #gpio-range-cells = <3>;
        };
 
        thermal@ffc48000 {
index 3b879e7..413b4c2 100644 (file)
        pfc: pfc@e6060000 {
                compatible = "renesas,pfc-r8a7790";
                reg = <0 0xe6060000 0 0x250>;
-               #gpio-range-cells = <3>;
        };
 
        sdhi0: sdhi@ee100000 {
-               compatible = "renesas,r8a7790-sdhi";
+               compatible = "renesas,sdhi-r8a7790";
                reg = <0 0xee100000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 165 4>;
        };
 
        sdhi1: sdhi@ee120000 {
-               compatible = "renesas,r8a7790-sdhi";
+               compatible = "renesas,sdhi-r8a7790";
                reg = <0 0xee120000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 166 4>;
        };
 
        sdhi2: sdhi@ee140000 {
-               compatible = "renesas,r8a7790-sdhi";
+               compatible = "renesas,sdhi-r8a7790";
                reg = <0 0xee140000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 167 4>;
        };
 
        sdhi3: sdhi@ee160000 {
-               compatible = "renesas,r8a7790-sdhi";
+               compatible = "renesas,sdhi-r8a7790";
                reg = <0 0xee160000 0 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 168 4>;
index ba59a58..3955c76 100644 (file)
        };
 
        sdhi0: sdhi@ee100000 {
-               compatible = "renesas,r8a7740-sdhi";
+               compatible = "renesas,sdhi-r8a7740";
                reg = <0xee100000 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 83 4
 
        /* SDHI1 and SDHI2 have no CD pins, no need for CD IRQ */
        sdhi1: sdhi@ee120000 {
-               compatible = "renesas,r8a7740-sdhi";
+               compatible = "renesas,sdhi-r8a7740";
                reg = <0xee120000 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 88 4
        };
 
        sdhi2: sdhi@ee140000 {
-               compatible = "renesas,r8a7740-sdhi";
+               compatible = "renesas,sdhi-r8a7740";
                reg = <0xee140000 0x100>;
                interrupt-parent = <&gic>;
                interrupts = <0 104 4
index 06ea7d4..2a45092 100644 (file)
 #   $4 - default install path (blank if root directory)
 #
 
+verify () {
+       if [ ! -f "$1" ]; then
+               echo ""                                                   1>&2
+               echo " *** Missing file: $1"                              1>&2
+               echo ' *** You need to run "make" before "make install".' 1>&2
+               echo ""                                                   1>&2
+               exit 1
+       fi
+}
+
+# Make sure the files actually exist
+verify "$2"
+verify "$3"
+
 # User may have a custom install script
 if [ -x ~/bin/${INSTALLKERNEL} ]; then exec ~/bin/${INSTALLKERNEL} "$@"; fi
 if [ -x /sbin/${INSTALLKERNEL} ]; then exec /sbin/${INSTALLKERNEL} "$@"; fi
index 117f955..8e1a024 100644 (file)
@@ -269,6 +269,11 @@ static const struct edmacc_param dummy_paramset = {
        .ccnt = 1,
 };
 
+static const struct of_device_id edma_of_ids[] = {
+       { .compatible = "ti,edma3", },
+       {}
+};
+
 /*****************************************************************************/
 
 static void map_dmach_queue(unsigned ctlr, unsigned ch_no,
@@ -560,14 +565,38 @@ static int reserve_contiguous_slots(int ctlr, unsigned int id,
 static int prepare_unused_channel_list(struct device *dev, void *data)
 {
        struct platform_device *pdev = to_platform_device(dev);
-       int i, ctlr;
+       int i, count, ctlr;
+       struct of_phandle_args  dma_spec;
 
+       if (dev->of_node) {
+               count = of_property_count_strings(dev->of_node, "dma-names");
+               if (count < 0)
+                       return 0;
+               for (i = 0; i < count; i++) {
+                       if (of_parse_phandle_with_args(dev->of_node, "dmas",
+                                                      "#dma-cells", i,
+                                                      &dma_spec))
+                               continue;
+
+                       if (!of_match_node(edma_of_ids, dma_spec.np)) {
+                               of_node_put(dma_spec.np);
+                               continue;
+                       }
+
+                       clear_bit(EDMA_CHAN_SLOT(dma_spec.args[0]),
+                                 edma_cc[0]->edma_unused);
+                       of_node_put(dma_spec.np);
+               }
+               return 0;
+       }
+
+       /* For non-OF case */
        for (i = 0; i < pdev->num_resources; i++) {
                if ((pdev->resource[i].flags & IORESOURCE_DMA) &&
                                (int)pdev->resource[i].start >= 0) {
                        ctlr = EDMA_CTLR(pdev->resource[i].start);
                        clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start),
-                                       edma_cc[ctlr]->edma_unused);
+                                 edma_cc[ctlr]->edma_unused);
                }
        }
 
@@ -1762,11 +1791,6 @@ static int edma_probe(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id edma_of_ids[] = {
-       { .compatible = "ti,edma3", },
-       {}
-};
-
 static struct platform_driver edma_driver = {
        .driver = {
                .name   = "edma",
index 370236d..9902509 100644 (file)
@@ -51,7 +51,8 @@ void mcpm_cpu_power_down(void)
 {
        phys_reset_t phys_reset;
 
-       BUG_ON(!platform_ops);
+       if (WARN_ON_ONCE(!platform_ops || !platform_ops->power_down))
+               return;
        BUG_ON(!irqs_disabled());
 
        /*
@@ -93,7 +94,8 @@ void mcpm_cpu_suspend(u64 expected_residency)
 {
        phys_reset_t phys_reset;
 
-       BUG_ON(!platform_ops);
+       if (WARN_ON_ONCE(!platform_ops || !platform_ops->suspend))
+               return;
        BUG_ON(!irqs_disabled());
 
        /* Very similar to mcpm_cpu_power_down() */
index d56c932..025f6ce 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <asm/mach/sharpsl_param.h>
+#include <asm/memory.h>
 
 /*
  * Certain hardware parameters determined at the time of device manufacture,
  */
 #ifdef CONFIG_ARCH_SA1100
 #define PARAM_BASE     0xe8ffc000
+#define param_start(x) (void *)(x)
 #else
 #define PARAM_BASE     0xa0000a00
+#define param_start(x) __va(x)
 #endif
 #define MAGIC_CHG(a,b,c,d) ( ( d << 24 ) | ( c << 16 )  | ( b << 8 ) | a )
 
@@ -41,7 +44,7 @@ EXPORT_SYMBOL(sharpsl_param);
 
 void sharpsl_save_param(void)
 {
-       memcpy(&sharpsl_param, (void *)PARAM_BASE, sizeof(struct sharpsl_param_info));
+       memcpy(&sharpsl_param, param_start(PARAM_BASE), sizeof(struct sharpsl_param_info));
 
        if (sharpsl_param.comadj_keyword != COMADJ_MAGIC)
                sharpsl_param.comadj=-1;
index 6e572c6..119fc37 100644 (file)
@@ -36,6 +36,7 @@ CONFIG_ARCH_TEGRA_114_SOC=y
 CONFIG_TEGRA_PCI=y
 CONFIG_TEGRA_EMC_SCALING_ENABLE=y
 CONFIG_ARCH_U8500=y
+CONFIG_MACH_HREFV60=y
 CONFIG_MACH_SNOWBALL=y
 CONFIG_MACH_UX500_DT=y
 CONFIG_ARCH_VEXPRESS=y
@@ -46,6 +47,7 @@ CONFIG_ARCH_ZYNQ=y
 CONFIG_SMP=y
 CONFIG_HIGHPTE=y
 CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
 CONFIG_NET=y
 CONFIG_UNIX=y
 CONFIG_INET=y
@@ -133,6 +135,7 @@ CONFIG_MMC=y
 CONFIG_MMC_ARMMMCI=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
 CONFIG_MMC_SDHCI_TEGRA=y
 CONFIG_MMC_SDHCI_SPEAR=y
 CONFIG_MMC_OMAP=y
index 19d6cd6..3a14ea8 100644 (file)
@@ -148,7 +148,7 @@ AES_Te:
 @               const AES_KEY *key) {
 .align 5
 ENTRY(AES_encrypt)
-       sub     r3,pc,#8                @ AES_encrypt
+       adr     r3,AES_encrypt
        stmdb   sp!,{r1,r4-r12,lr}
        mov     r12,r0          @ inp
        mov     r11,r2
@@ -381,7 +381,7 @@ _armv4_AES_encrypt:
 .align 5
 ENTRY(private_AES_set_encrypt_key)
 _armv4_AES_set_encrypt_key:
-       sub     r3,pc,#8                @ AES_set_encrypt_key
+       adr     r3,_armv4_AES_set_encrypt_key
        teq     r0,#0
        moveq   r0,#-1
        beq     .Labrt
@@ -843,7 +843,7 @@ AES_Td:
 @               const AES_KEY *key) {
 .align 5
 ENTRY(AES_decrypt)
-       sub     r3,pc,#8                @ AES_decrypt
+       adr     r3,AES_decrypt
        stmdb   sp!,{r1,r4-r12,lr}
        mov     r12,r0          @ inp
        mov     r11,r2
index d3db398..59ceae8 100644 (file)
@@ -31,5 +31,4 @@ generic-y += termbits.h
 generic-y += termios.h
 generic-y += timex.h
 generic-y += trace_clock.h
-generic-y += types.h
 generic-y += unaligned.h
index bfc198c..863c892 100644 (file)
@@ -16,7 +16,7 @@
 
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-       asm goto("1:\n\t"
+       asm_volatile_goto("1:\n\t"
                 JUMP_LABEL_NOP "\n\t"
                 ".pushsection __jump_table,  \"aw\"\n\t"
                 ".word 1b, %l[l_yes], %c0\n\t"
index 0f7b762..fc82a88 100644 (file)
@@ -76,8 +76,11 @@ int mcpm_cpu_power_up(unsigned int cpu, unsigned int cluster);
  *
  * This must be called with interrupts disabled.
  *
- * This does not return.  Re-entry in the kernel is expected via
- * mcpm_entry_point.
+ * On success this does not return.  Re-entry in the kernel is expected
+ * via mcpm_entry_point.
+ *
+ * This will return if mcpm_platform_register() has not been called
+ * previously in which case the caller should take appropriate action.
  */
 void mcpm_cpu_power_down(void);
 
@@ -98,8 +101,11 @@ void mcpm_cpu_power_down(void);
  *
  * This must be called with interrupts disabled.
  *
- * This does not return.  Re-entry in the kernel is expected via
- * mcpm_entry_point.
+ * On success this does not return.  Re-entry in the kernel is expected
+ * via mcpm_entry_point.
+ *
+ * This will return if mcpm_platform_register() has not been called
+ * previously in which case the caller should take appropriate action.
  */
 void mcpm_cpu_suspend(u64 expected_residency);
 
index f1d96d4..73ddd72 100644 (file)
@@ -57,6 +57,9 @@ static inline void syscall_get_arguments(struct task_struct *task,
                                         unsigned int i, unsigned int n,
                                         unsigned long *args)
 {
+       if (n == 0)
+               return;
+
        if (i + n > SYSCALL_MAX_ARGS) {
                unsigned long *args_bad = args + SYSCALL_MAX_ARGS - i;
                unsigned int n_bad = n + i - SYSCALL_MAX_ARGS;
@@ -81,6 +84,9 @@ static inline void syscall_set_arguments(struct task_struct *task,
                                         unsigned int i, unsigned int n,
                                         const unsigned long *args)
 {
+       if (n == 0)
+               return;
+
        if (i + n > SYSCALL_MAX_ARGS) {
                pr_warning("%s called with max args %d, handling only %d\n",
                           __func__, i + n, SYSCALL_MAX_ARGS);
index 7e1f760..72abdc5 100644 (file)
 #include <asm/unified.h>
 #include <asm/compiler.h>
 
+#if __LINUX_ARM_ARCH__ < 6
+#include <asm-generic/uaccess-unaligned.h>
+#else
+#define __get_user_unaligned __get_user
+#define __put_user_unaligned __put_user
+#endif
+
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
 
index 74ad15d..bc6bd96 100644 (file)
@@ -442,10 +442,10 @@ local_restart:
        ldrcc   pc, [tbl, scno, lsl #2]         @ call sys_* routine
 
        add     r1, sp, #S_OFF
-       cmp     scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
+2:     cmp     scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
        eor     r0, scno, #__NR_SYSCALL_BASE    @ put OS number back
        bcs     arm_syscall
-2:     mov     why, #0                         @ no longer a real syscall
+       mov     why, #0                         @ no longer a real syscall
        b       sys_ni_syscall                  @ not private func
 
 #if defined(CONFIG_OABI_COMPAT) || !defined(CONFIG_AEABI)
index de23a9b..39f89fb 100644 (file)
 #ifdef CONFIG_CONTEXT_TRACKING
        .if     \save
        stmdb   sp!, {r0-r3, ip, lr}
-       bl      user_exit
+       bl      context_tracking_user_exit
        ldmia   sp!, {r0-r3, ip, lr}
        .else
-       bl      user_exit
+       bl      context_tracking_user_exit
        .endif
 #endif
        .endm
 #ifdef CONFIG_CONTEXT_TRACKING
        .if     \save
        stmdb   sp!, {r0-r3, ip, lr}
-       bl      user_enter
+       bl      context_tracking_user_enter
        ldmia   sp!, {r0-r3, ip, lr}
        .else
-       bl      user_enter
+       bl      context_tracking_user_enter
        .endif
 #endif
        .endm
index 2c7cc1e..476de57 100644 (file)
@@ -487,7 +487,26 @@ __fixup_smp:
        mrc     p15, 0, r0, c0, c0, 5   @ read MPIDR
        and     r0, r0, #0xc0000000     @ multiprocessing extensions and
        teq     r0, #0x80000000         @ not part of a uniprocessor system?
-       moveq   pc, lr                  @ yes, assume SMP
+       bne    __fixup_smp_on_up        @ no, assume UP
+
+       @ Core indicates it is SMP. Check for Aegis SOC where a single
+       @ Cortex-A9 CPU is present but SMP operations fault.
+       mov     r4, #0x41000000
+       orr     r4, r4, #0x0000c000
+       orr     r4, r4, #0x00000090
+       teq     r3, r4                  @ Check for ARM Cortex-A9
+       movne   pc, lr                  @ Not ARM Cortex-A9,
+
+       @ If a future SoC *does* use 0x0 as the PERIPH_BASE, then the
+       @ below address check will need to be #ifdef'd or equivalent
+       @ for the Aegis platform.
+       mrc     p15, 4, r0, c15, c0     @ get SCU base address
+       teq     r0, #0x0                @ '0' on actual UP A9 hardware
+       beq     __fixup_smp_on_up       @ So its an A9 UP
+       ldr     r0, [r0, #4]            @ read SCU Config
+       and     r0, r0, #0x3            @ number of CPUs
+       teq     r0, #0x0                @ is 1?
+       movne   pc, lr
 
 __fixup_smp_on_up:
        adr     r0, 1f
index 71e08ba..c02ba4a 100644 (file)
@@ -58,14 +58,14 @@ static const struct kvm_irq_level a15_vtimer_irq = {
  */
 int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
 {
-       struct kvm_regs *cpu_reset;
+       struct kvm_regs *reset_regs;
        const struct kvm_irq_level *cpu_vtimer_irq;
 
        switch (vcpu->arch.target) {
        case KVM_ARM_TARGET_CORTEX_A15:
                if (vcpu->vcpu_id > a15_max_cpu_idx)
                        return -EINVAL;
-               cpu_reset = &a15_regs_reset;
+               reset_regs = &a15_regs_reset;
                vcpu->arch.midr = read_cpuid_id();
                cpu_vtimer_irq = &a15_vtimer_irq;
                break;
@@ -74,7 +74,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
        }
 
        /* Reset core registers */
-       memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs));
+       memcpy(&vcpu->arch.regs, reset_regs, sizeof(vcpu->arch.regs));
 
        /* Reset CP15 registers */
        kvm_reset_coprocs(vcpu);
index 180b302..f607deb 100644 (file)
@@ -93,7 +93,7 @@ static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id)
 
 static struct irqaction at91rm9200_timer_irq = {
        .name           = "at91_tick",
-       .flags          = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+       .flags          = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
        .handler        = at91rm9200_timer_interrupt,
        .irq            = NR_IRQS_LEGACY + AT91_ID_SYS,
 };
index 3a4bc2e..bb39232 100644 (file)
@@ -171,7 +171,7 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
 
 static struct irqaction at91sam926x_pit_irq = {
        .name           = "at91_tick",
-       .flags          = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+       .flags          = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
        .handler        = at91sam926x_pit_interrupt,
        .irq            = NR_IRQS_LEGACY + AT91_ID_SYS,
 };
index 721a1a3..c40c1e2 100644 (file)
 #include "at91_rstc.h"
                        .arm
 
+/*
+ * at91_ramc_base is an array void*
+ * init at NULL if only one DDR controler is present in or DT
+ */
                        .globl  at91sam9g45_restart
 
 at91sam9g45_restart:
                        ldr     r5, =at91_ramc_base             @ preload constants
                        ldr     r0, [r5]
+                       ldr     r5, [r5, #4]                    @ ddr1
+                       cmp     r5, #0
                        ldr     r4, =at91_rstc_base
                        ldr     r1, [r4]
 
@@ -30,6 +36,8 @@ at91sam9g45_restart:
 
                        .balign 32                              @ align to cache line
 
+                       strne   r2, [r5, #AT91_DDRSDRC_RTR]     @ disable DDR1 access
+                       strne   r3, [r5, #AT91_DDRSDRC_LPR]     @ power down DDR1
                        str     r2, [r0, #AT91_DDRSDRC_RTR]     @ disable DDR0 access
                        str     r3, [r0, #AT91_DDRSDRC_LPR]     @ power down DDR0
                        str     r4, [r1, #AT91_RSTC_CR]         @ reset processor
index 2919eba..c0e637a 100644 (file)
@@ -57,7 +57,7 @@ static irqreturn_t at91x40_timer_interrupt(int irq, void *dev_id)
 
 static struct irqaction at91x40_timer_irq = {
        .name           = "at91_tick",
-       .flags          = IRQF_DISABLED | IRQF_TIMER,
+       .flags          = IRQF_TIMER,
        .handler        = at91x40_timer_interrupt
 };
 
index 92b7f77..4078ba9 100644 (file)
@@ -176,7 +176,7 @@ static struct at24_platform_data eeprom_info = {
        .context        = (void *)0x7f00,
 };
 
-static struct snd_platform_data dm365_evm_snd_data = {
+static struct snd_platform_data dm365_evm_snd_data __maybe_unused = {
        .asp_chan_q = EVENTQ_3,
 };
 
index 52b8571..ce402cd 100644 (file)
@@ -15,8 +15,6 @@
 
 #include <mach/hardware.h>
 
-#include <linux/platform_device.h>
-
 #define DAVINCI_UART0_BASE     (IO_PHYS + 0x20000)
 #define DAVINCI_UART1_BASE     (IO_PHYS + 0x20400)
 #define DAVINCI_UART2_BASE     (IO_PHYS + 0x20800)
@@ -39,6 +37,8 @@
 #define UART_DM646X_SCR_TX_WATERMARK   0x08
 
 #ifndef __ASSEMBLY__
+#include <linux/platform_device.h>
+
 extern int davinci_serial_init(struct platform_device *);
 #endif
 
index deb4b80..0d40b35 100644 (file)
@@ -90,6 +90,7 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
        init.ops = &clk_fixup_mux_ops;
        init.parent_names = parents;
        init.num_parents = num_parents;
+       init.flags = 0;
 
        fixup_mux->mux.reg = reg;
        fixup_mux->mux.shift = shift;
index c3cfa41..c6b40f3 100644 (file)
@@ -285,7 +285,7 @@ int __init mx27_clocks_init(unsigned long fref)
        clk_register_clkdev(clk[ata_ahb_gate], "ata", NULL);
        clk_register_clkdev(clk[rtc_ipg_gate], NULL, "imx21-rtc");
        clk_register_clkdev(clk[scc_ipg_gate], "scc", NULL);
-       clk_register_clkdev(clk[cpu_div], NULL, "cpufreq-cpu0.0");
+       clk_register_clkdev(clk[cpu_div], NULL, "cpu0");
        clk_register_clkdev(clk[emi_ahb_gate], "emi_ahb" , NULL);
 
        mxc_timer_init(MX27_IO_ADDRESS(MX27_GPT1_BASE_ADDR), MX27_INT_GPT1);
index 1a56a33..7c0dc45 100644 (file)
@@ -328,7 +328,7 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
        clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "imx-ssi.1");
        clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "imx-ssi.2");
        clk_register_clkdev(clk[sdma_gate], NULL, "imx35-sdma");
-       clk_register_clkdev(clk[cpu_podf], NULL, "cpufreq-cpu0.0");
+       clk_register_clkdev(clk[cpu_podf], NULL, "cpu0");
        clk_register_clkdev(clk[iim_gate], "iim", NULL);
        clk_register_clkdev(clk[dummy], NULL, "imx2-wdt.0");
        clk_register_clkdev(clk[dummy], NULL, "imx2-wdt.1");
@@ -397,7 +397,7 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc,
                                mx51_spdif_xtal_sel, ARRAY_SIZE(mx51_spdif_xtal_sel));
        clk[spdif1_sel] = imx_clk_mux("spdif1_sel", MXC_CCM_CSCMR2, 2, 2,
                                spdif_sel, ARRAY_SIZE(spdif_sel));
-       clk[spdif1_pred] = imx_clk_divider("spdif1_podf", "spdif1_sel", MXC_CCM_CDCDR, 16, 3);
+       clk[spdif1_pred] = imx_clk_divider("spdif1_pred", "spdif1_sel", MXC_CCM_CDCDR, 16, 3);
        clk[spdif1_podf] = imx_clk_divider("spdif1_podf", "spdif1_pred", MXC_CCM_CDCDR, 9, 6);
        clk[spdif1_com_sel] = imx_clk_mux("spdif1_com_sel", MXC_CCM_CSCMR2, 5, 1,
                                mx51_spdif1_com_sel, ARRAY_SIZE(mx51_spdif1_com_sel));
index 85a1b51..90372a2 100644 (file)
@@ -233,10 +233,15 @@ put_node:
        of_node_put(np);
 }
 
-static void __init imx6q_opp_init(struct device *cpu_dev)
+static void __init imx6q_opp_init(void)
 {
        struct device_node *np;
+       struct device *cpu_dev = get_cpu_device(0);
 
+       if (!cpu_dev) {
+               pr_warn("failed to get cpu0 device\n");
+               return;
+       }
        np = of_node_get(cpu_dev->of_node);
        if (!np) {
                pr_warn("failed to find cpu0 node\n");
@@ -268,7 +273,7 @@ static void __init imx6q_init_late(void)
                imx6q_cpuidle_init();
 
        if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) {
-               imx6q_opp_init(&imx6q_cpufreq_pdev.dev);
+               imx6q_opp_init();
                platform_device_register(&imx6q_cpufreq_pdev);
        }
 }
index 64ff37e..80c177c 100644 (file)
@@ -117,6 +117,17 @@ void __init imx_init_l2cache(void)
        /* Configure the L2 PREFETCH and POWER registers */
        val = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
        val |= 0x70800000;
+       /*
+        * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0
+        * The L2 cache controller(PL310) version on the i.MX6DL/SOLO/SL is r3p2
+        * But according to ARM PL310 errata: 752271
+        * ID: 752271: Double linefill feature can cause data corruption
+        * Fault Status: Present in: r3p0, r3p1, r3p1-50rel0. Fixed in r3p2
+        * Workaround: The only workaround to this erratum is to disable the
+        * double linefill feature. This is the default behavior.
+        */
+       if (cpu_is_imx6q())
+               val &= ~(1 << 30 | 1 << 23);
        writel_relaxed(val, l2x0_base + L2X0_PREFETCH_CTRL);
        val = L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN;
        writel_relaxed(val, l2x0_base + L2X0_POWER_CTRL);
index 755fd29..06a9e2e 100644 (file)
@@ -1,2 +1,9 @@
 /* Simple oneliner include to the PCIv3 early init */
+#ifdef CONFIG_PCI
 extern int pci_v3_early_init(void);
+#else
+static inline int pci_v3_early_init(void)
+{
+       return 0;
+}
+#endif
index 4c24303..58adf2f 100644 (file)
@@ -140,6 +140,7 @@ int __init coherency_init(void)
                coherency_base = of_iomap(np, 0);
                coherency_cpu_base = of_iomap(np, 1);
                set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
+               of_node_put(np);
        }
 
        return 0;
@@ -147,9 +148,14 @@ int __init coherency_init(void)
 
 static int __init coherency_late_init(void)
 {
-       if (of_find_matching_node(NULL, of_coherency_table))
+       struct device_node *np;
+
+       np = of_find_matching_node(NULL, of_coherency_table);
+       if (np) {
                bus_register_notifier(&platform_bus_type,
                                      &mvebu_hwcc_platform_nb);
+               of_node_put(np);
+       }
        return 0;
 }
 
index 3cc4bef..27fc4f0 100644 (file)
@@ -67,6 +67,7 @@ int __init armada_370_xp_pmsu_init(void)
                pr_info("Initializing Power Management Service Unit\n");
                pmsu_mp_base = of_iomap(np, 0);
                pmsu_reset_base = of_iomap(np, 1);
+               of_node_put(np);
        }
 
        return 0;
index f875124..5175083 100644 (file)
@@ -98,6 +98,7 @@ static int __init mvebu_system_controller_init(void)
                BUG_ON(!match);
                system_controller_base = of_iomap(np, 0);
                mvebu_sc = (struct mvebu_system_controller *)match->data;
+               of_node_put(np);
        }
 
        return 0;
index 39c7838..87162e1 100644 (file)
@@ -129,6 +129,24 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)")
        .restart        = omap3xxx_restart,
 MACHINE_END
 
+static const char *omap36xx_boards_compat[] __initdata = {
+       "ti,omap36xx",
+       NULL,
+};
+
+DT_MACHINE_START(OMAP36XX_DT, "Generic OMAP36xx (Flattened Device Tree)")
+       .reserve        = omap_reserve,
+       .map_io         = omap3_map_io,
+       .init_early     = omap3630_init_early,
+       .init_irq       = omap_intc_of_init,
+       .handle_irq     = omap3_intc_handle_irq,
+       .init_machine   = omap_generic_init,
+       .init_late      = omap3_init_late,
+       .init_time      = omap3_sync32k_timer_init,
+       .dt_compat      = omap36xx_boards_compat,
+       .restart        = omap3xxx_restart,
+MACHINE_END
+
 static const char *omap3_gp_boards_compat[] __initdata = {
        "ti,omap3-beagle",
        "timll,omap3-devkit8000",
index c3270c0..f6fe388 100644 (file)
@@ -167,38 +167,47 @@ static struct lp55xx_led_config rx51_lp5523_led_config[] = {
                .name           = "lp5523:kb1",
                .chan_nr        = 0,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:kb2",
                .chan_nr        = 1,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:kb3",
                .chan_nr        = 2,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:kb4",
                .chan_nr        = 3,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:b",
                .chan_nr        = 4,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:g",
                .chan_nr        = 5,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:r",
                .chan_nr        = 6,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:kb5",
                .chan_nr        = 7,
                .led_current    = 50,
+               .max_current    = 100,
        }, {
                .name           = "lp5523:kb6",
                .chan_nr        = 8,
                .led_current    = 50,
+               .max_current    = 100,
        }
 };
 
index 1d5b529..b237950 100644 (file)
@@ -1632,7 +1632,7 @@ static struct omap_clk omap44xx_clks[] = {
        CLK(NULL,       "auxclk5_src_ck",               &auxclk5_src_ck),
        CLK(NULL,       "auxclk5_ck",                   &auxclk5_ck),
        CLK(NULL,       "auxclkreq5_ck",                &auxclkreq5_ck),
-       CLK("omap-gpmc",        "fck",                  &dummy_ck),
+       CLK("50000000.gpmc",    "fck",                  &dummy_ck),
        CLK("omap_i2c.1",       "ick",                  &dummy_ck),
        CLK("omap_i2c.2",       "ick",                  &dummy_ck),
        CLK("omap_i2c.3",       "ick",                  &dummy_ck),
index c443f2e..4c8982a 100644 (file)
@@ -143,7 +143,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
         * Call idle CPU cluster PM exit notifier chain
         * to restore GIC and wakeupgen context.
         */
-       if ((cx->mpu_state == PWRDM_POWER_RET) &&
+       if (dev->cpu == 0 && (cx->mpu_state == PWRDM_POWER_RET) &&
                (cx->mpu_logic_state == PWRDM_POWER_OFF))
                cpu_cluster_pm_exit();
 
index 64b5a83..8b6876c 100644 (file)
@@ -272,9 +272,19 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base)
        struct gpmc_timings t;
        int ret;
 
-       if (gpmc_onenand_data->of_node)
+       if (gpmc_onenand_data->of_node) {
                gpmc_read_settings_dt(gpmc_onenand_data->of_node,
                                      &onenand_async);
+               if (onenand_async.sync_read || onenand_async.sync_write) {
+                       if (onenand_async.sync_write)
+                               gpmc_onenand_data->flags |=
+                                       ONENAND_SYNC_READWRITE;
+                       else
+                               gpmc_onenand_data->flags |= ONENAND_SYNC_READ;
+                       onenand_async.sync_read = false;
+                       onenand_async.sync_write = false;
+               }
+       }
 
        omap2_onenand_set_async_mode(onenand_base);
 
index 9f4795a..579697a 100644 (file)
@@ -1491,8 +1491,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
         */
        ret = gpmc_cs_remap(cs, res.start);
        if (ret < 0) {
-               dev_err(&pdev->dev, "cannot remap GPMC CS %d to 0x%x\n",
-                       cs, res.start);
+               dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
+                       cs, &res.start);
                goto err;
        }
 
index 5d2080e..16f78a9 100644 (file)
@@ -28,7 +28,7 @@
 #define OMAP_PULL_UP                   (1 << 4)
 #define OMAP_ALTELECTRICALSEL          (1 << 5)
 
-/* 34xx specific mux bit defines */
+/* omap3/4/5 specific mux bit defines */
 #define OMAP_INPUT_EN                  (1 << 8)
 #define OMAP_OFF_EN                    (1 << 9)
 #define OMAP_OFFOUT_EN                 (1 << 10)
@@ -36,8 +36,6 @@
 #define OMAP_OFF_PULL_EN               (1 << 12)
 #define OMAP_OFF_PULL_UP               (1 << 13)
 #define OMAP_WAKEUP_EN                 (1 << 14)
-
-/* 44xx specific mux bit defines */
 #define OMAP_WAKEUP_EVENT              (1 << 15)
 
 /* Active pin states */
index c53609f..be271f1 100644 (file)
@@ -620,7 +620,7 @@ static struct omap_mux __initdata omap3_muxmodes[] = {
                "uart1_rts", "ssi1_flag_tx", NULL, NULL,
                "gpio_149", NULL, NULL, "safe_mode"),
        _OMAP3_MUXENTRY(UART1_RX, 151,
-               "uart1_rx", "ss1_wake_tx", "mcbsp1_clkr", "mcspi4_clk",
+               "uart1_rx", "ssi1_wake_tx", "mcbsp1_clkr", "mcspi4_clk",
                "gpio_151", NULL, NULL, "safe_mode"),
        _OMAP3_MUXENTRY(UART1_TX, 148,
                "uart1_tx", "ssi1_dat_tx", NULL, NULL,
index 8708b2a..8912110 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * OMAP4 SMP source file. It contains platform specific fucntions
+ * OMAP4 SMP source file. It contains platform specific functions
  * needed for the linux smp kernel.
  *
  * Copyright (C) 2009 Texas Instruments, Inc.
index f99f68e..b69dd9a 100644 (file)
@@ -158,7 +158,7 @@ static int omap_device_build_from_dt(struct platform_device *pdev)
        }
 
        od = omap_device_alloc(pdev, hwmods, oh_cnt);
-       if (!od) {
+       if (IS_ERR(od)) {
                dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n",
                        oh_name);
                ret = PTR_ERR(od);
index fa74a06..ead48fa 100644 (file)
@@ -628,7 +628,7 @@ void __init omap4_local_timer_init(void)
 #endif /* CONFIG_HAVE_ARM_TWD */
 #endif /* CONFIG_ARCH_OMAP4 */
 
-#ifdef CONFIG_SOC_OMAP5
+#if defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX)
 void __init omap5_realtime_timer_init(void)
 {
        omap4_sync32k_timer_init();
@@ -636,7 +636,7 @@ void __init omap5_realtime_timer_init(void)
 
        clocksource_of_init();
 }
-#endif /* CONFIG_SOC_OMAP5 */
+#endif /* CONFIG_SOC_OMAP5 || CONFIG_SOC_DRA7XX */
 
 /**
  * omap_timer_init - build and register timer device with an
index 612a456..7fb96eb 100644 (file)
@@ -289,7 +289,7 @@ static void collie_flash_exit(void)
 }
 
 static struct flash_platform_data collie_flash_data = {
-       .map_name       = "cfi_probe",
+       .map_name       = "jedec_probe",
        .init           = collie_flash_init,
        .set_vpp        = collie_set_vpp,
        .exit           = collie_flash_exit,
index 5bd1479..7f8f607 100644 (file)
@@ -1108,9 +1108,9 @@ static const struct pinctrl_map eva_pinctrl_map[] = {
        PIN_MAP_MUX_GROUP_DEFAULT("asoc-simple-card.1", "pfc-r8a7740",
                                  "fsib_mclk_in", "fsib"),
        /* GETHER */
-       PIN_MAP_MUX_GROUP_DEFAULT("sh-eth", "pfc-r8a7740",
+       PIN_MAP_MUX_GROUP_DEFAULT("r8a7740-gether", "pfc-r8a7740",
                                  "gether_mii", "gether"),
-       PIN_MAP_MUX_GROUP_DEFAULT("sh-eth", "pfc-r8a7740",
+       PIN_MAP_MUX_GROUP_DEFAULT("r8a7740-gether", "pfc-r8a7740",
                                  "gether_int", "gether"),
        /* HDMI */
        PIN_MAP_MUX_GROUP_DEFAULT("sh-mobile-hdmi", "pfc-r8a7740",
index ffb6f0a..5930af8 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/pinctrl/machine.h>
 #include <linux/platform_data/gpio-rcar.h>
 #include <linux/platform_device.h>
+#include <linux/phy.h>
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
 #include <linux/sh_eth.h>
@@ -155,6 +156,30 @@ static void __init lager_add_standard_devices(void)
                                          &ether_pdata, sizeof(ether_pdata));
 }
 
+/*
+ * Ether LEDs on the Lager board are named LINK and ACTIVE which corresponds
+ * to non-default 01 setting of the Micrel KSZ8041 PHY control register 1 bits
+ * 14-15. We have to set them back to 01 from the default 00 value each time
+ * the PHY is reset. It's also important because the PHY's LED0 signal is
+ * connected to SoC's ETH_LINK signal and in the PHY's default mode it will
+ * bounce on and off after each packet, which we apparently want to avoid.
+ */
+static int lager_ksz8041_fixup(struct phy_device *phydev)
+{
+       u16 phyctrl1 = phy_read(phydev, 0x1e);
+
+       phyctrl1 &= ~0xc000;
+       phyctrl1 |= 0x4000;
+       return phy_write(phydev, 0x1e, phyctrl1);
+}
+
+static void __init lager_init(void)
+{
+       lager_add_standard_devices();
+
+       phy_register_fixup_for_id("r8a7790-ether-ff:01", lager_ksz8041_fixup);
+}
+
 static const char *lager_boards_compat_dt[] __initdata = {
        "renesas,lager",
        NULL,
@@ -163,6 +188,6 @@ static const char *lager_boards_compat_dt[] __initdata = {
 DT_MACHINE_START(LAGER_DT, "lager")
        .init_early     = r8a7790_init_delay,
        .init_time      = r8a7790_timer_init,
-       .init_machine   = lager_add_standard_devices,
+       .init_machine   = lager_init,
        .dt_compat      = lager_boards_compat_dt,
 MACHINE_END
index 8ea5ef6..5bd2e85 100644 (file)
@@ -555,7 +555,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_CON_ID("pll2h",                  &pll2h_clk),
 
        /* CPU clock */
-       CLKDEV_DEV_ID("cpufreq-cpu0",           &z_clk),
+       CLKDEV_DEV_ID("cpu0",                   &z_clk),
 
        /* DIV6 */
        CLKDEV_CON_ID("zb",                     &div6_clks[DIV6_ZB]),
index 1942eae..c92c023 100644 (file)
@@ -616,7 +616,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("smp_twd", &twd_clk), /* smp_twd */
 
        /* DIV4 clocks */
-       CLKDEV_DEV_ID("cpufreq-cpu0", &div4_clks[DIV4_Z]),
+       CLKDEV_DEV_ID("cpu0", &div4_clks[DIV4_Z]),
 
        /* DIV6 clocks */
        CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]),
index a85adcd..a165986 100644 (file)
@@ -1,7 +1,3 @@
-menu "ST-Ericsson AB U300/U335 Platform"
-
-comment "ST-Ericsson Mobile Platform Products"
-
 config ARCH_U300
        bool "ST-Ericsson U300 Series" if ARCH_MULTI_V5
        depends on MMU
@@ -25,7 +21,9 @@ config ARCH_U300
        help
          Support for ST-Ericsson U300 series mobile platforms.
 
-comment "ST-Ericsson U300/U335 Feature Selections"
+if ARCH_U300
+
+menu "ST-Ericsson AB U300/U335 Platform"
 
 config MACH_U300
        depends on ARCH_U300
@@ -53,3 +51,5 @@ config MACH_U300_SPIDUMMY
                SPI framework and ARM PL022 support.
 
 endmenu
+
+endif
index 82ccf1d..264f894 100644 (file)
@@ -69,6 +69,7 @@ static int __init ux500_l2x0_init(void)
         * some SMI service available.
         */
        outer_cache.disable = NULL;
+       outer_cache.set_debug = NULL;
 
        return 0;
 }
index 7aeb5d6..e6eb481 100644 (file)
@@ -131,6 +131,16 @@ static void tc2_pm_down(u64 residency)
        } else
                BUG();
 
+       /*
+        * If the CPU is committed to power down, make sure
+        * the power controller will be in charge of waking it
+        * up upon IRQ, ie IRQ lines are cut from GIC CPU IF
+        * to the CPU by disabling the GIC CPU IF to prevent wfi
+        * from completing execution behind power controller back
+        */
+       if (!skip_wfi)
+               gic_cpu_if_down();
+
        if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
                arch_spin_unlock(&tc2_pm_lock);
 
@@ -231,7 +241,6 @@ static void tc2_pm_suspend(u64 residency)
        cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
        cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
        ve_spc_set_resume_addr(cluster, cpu, virt_to_phys(mcpm_entry_point));
-       gic_cpu_if_down();
        tc2_pm_down(residency);
 }
 
index f5e1a84..1272ed2 100644 (file)
@@ -1232,7 +1232,8 @@ __iommu_create_mapping(struct device *dev, struct page **pages, size_t size)
                                break;
 
                len = (j - i) << PAGE_SHIFT;
-               ret = iommu_map(mapping->domain, iova, phys, len, 0);
+               ret = iommu_map(mapping->domain, iova, phys, len,
+                               IOMMU_READ|IOMMU_WRITE);
                if (ret < 0)
                        goto fail;
                iova += len;
@@ -1431,6 +1432,27 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
                                         GFP_KERNEL);
 }
 
+static int __dma_direction_to_prot(enum dma_data_direction dir)
+{
+       int prot;
+
+       switch (dir) {
+       case DMA_BIDIRECTIONAL:
+               prot = IOMMU_READ | IOMMU_WRITE;
+               break;
+       case DMA_TO_DEVICE:
+               prot = IOMMU_READ;
+               break;
+       case DMA_FROM_DEVICE:
+               prot = IOMMU_WRITE;
+               break;
+       default:
+               prot = 0;
+       }
+
+       return prot;
+}
+
 /*
  * Map a part of the scatter-gather list into contiguous io address space
  */
@@ -1444,6 +1466,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
        int ret = 0;
        unsigned int count;
        struct scatterlist *s;
+       int prot;
 
        size = PAGE_ALIGN(size);
        *handle = DMA_ERROR_CODE;
@@ -1460,7 +1483,9 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
                        !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
                        __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
 
-               ret = iommu_map(mapping->domain, iova, phys, len, 0);
+               prot = __dma_direction_to_prot(dir);
+
+               ret = iommu_map(mapping->domain, iova, phys, len, prot);
                if (ret < 0)
                        goto fail;
                count += len >> PAGE_SHIFT;
@@ -1665,19 +1690,7 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p
        if (dma_addr == DMA_ERROR_CODE)
                return dma_addr;
 
-       switch (dir) {
-       case DMA_BIDIRECTIONAL:
-               prot = IOMMU_READ | IOMMU_WRITE;
-               break;
-       case DMA_TO_DEVICE:
-               prot = IOMMU_READ;
-               break;
-       case DMA_FROM_DEVICE:
-               prot = IOMMU_WRITE;
-               break;
-       default:
-               prot = 0;
-       }
+       prot = __dma_direction_to_prot(dir);
 
        ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, prot);
        if (ret < 0)
index febaee7..18ec4c5 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/nodemask.h>
 #include <linux/initrd.h>
 #include <linux/of_fdt.h>
-#include <linux/of_reserved_mem.h>
 #include <linux/highmem.h>
 #include <linux/gfp.h>
 #include <linux/memblock.h>
@@ -379,8 +378,6 @@ void __init arm_memblock_init(struct meminfo *mi,
        if (mdesc->reserve)
                mdesc->reserve();
 
-       early_init_dt_scan_reserved_mem();
-
        /*
         * reserve memory for DMA contigouos allocations,
         * must come from DMA area inside low memory
index f50d223..99b44e0 100644 (file)
@@ -930,4 +930,5 @@ void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter)
                module_free(NULL, fp->bpf_func);
+       kfree(fp);
 }
index 1a6bfe9..835c559 100644 (file)
@@ -6,13 +6,6 @@ config FRAME_POINTER
        bool
        default y
 
-config DEBUG_STACK_USAGE
-       bool "Enable stack utilization instrumentation"
-       depends on DEBUG_KERNEL
-       help
-         Enables the display of the minimum amount of free stack which each
-         task has ever had available in the sysrq-T output.
-
 config EARLY_PRINTK
        bool "Early printk support"
        default y
index 5b3e832..31c81e9 100644 (file)
@@ -42,7 +42,7 @@ CONFIG_IP_PNP_BOOTP=y
 # CONFIG_WIRELESS is not set
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
-# CONFIG_BLK_DEV is not set
+CONFIG_BLK_DEV=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
 CONFIG_BLK_DEV_SD=y
@@ -72,6 +72,7 @@ CONFIG_LOGO=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
 # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
 # CONFIG_EXT3_FS_XATTR is not set
 CONFIG_FUSE_FS=y
@@ -90,3 +91,5 @@ CONFIG_DEBUG_KERNEL=y
 CONFIG_DEBUG_INFO=y
 # CONFIG_FTRACE is not set
 CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_VIRTIO_BLK=y
index 6d4482f..e2950b0 100644 (file)
@@ -43,6 +43,6 @@
                                 COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
                                 COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV)
 
-extern unsigned int elf_hwcap;
+extern unsigned long elf_hwcap;
 #endif
 #endif
index edb3d5c..7ecc2b2 100644 (file)
@@ -166,9 +166,10 @@ do {                                                                       \
 
 #define get_user(x, ptr)                                               \
 ({                                                                     \
+       __typeof__(*(ptr)) __user *__p = (ptr);                         \
        might_fault();                                                  \
-       access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) ?                 \
-               __get_user((x), (ptr)) :                                \
+       access_ok(VERIFY_READ, __p, sizeof(*__p)) ?                     \
+               __get_user((x), __p) :                                  \
                ((x) = 0, -EFAULT);                                     \
 })
 
@@ -227,9 +228,10 @@ do {                                                                       \
 
 #define put_user(x, ptr)                                               \
 ({                                                                     \
+       __typeof__(*(ptr)) __user *__p = (ptr);                         \
        might_fault();                                                  \
-       access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) ?                \
-               __put_user((x), (ptr)) :                                \
+       access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ?                    \
+               __put_user((x), __p) :                                  \
                -EFAULT;                                                \
 })
 
index 1f2e4d5..bb785d2 100644 (file)
@@ -80,8 +80,10 @@ void fpsimd_thread_switch(struct task_struct *next)
 
 void fpsimd_flush_thread(void)
 {
+       preempt_disable();
        memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
        fpsimd_load_state(&current->thread.fpsimd_state);
+       preempt_enable();
 }
 
 #ifdef CONFIG_KERNEL_MODE_NEON
index 57fb55c..7ae8a1f 100644 (file)
@@ -143,15 +143,26 @@ void machine_restart(char *cmd)
 
 void __show_regs(struct pt_regs *regs)
 {
-       int i;
+       int i, top_reg;
+       u64 lr, sp;
+
+       if (compat_user_mode(regs)) {
+               lr = regs->compat_lr;
+               sp = regs->compat_sp;
+               top_reg = 12;
+       } else {
+               lr = regs->regs[30];
+               sp = regs->sp;
+               top_reg = 29;
+       }
 
        show_regs_print_info(KERN_DEFAULT);
        print_symbol("PC is at %s\n", instruction_pointer(regs));
-       print_symbol("LR is at %s\n", regs->regs[30]);
+       print_symbol("LR is at %s\n", lr);
        printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n",
-              regs->pc, regs->regs[30], regs->pstate);
-       printk("sp : %016llx\n", regs->sp);
-       for (i = 29; i >= 0; i--) {
+              regs->pc, lr, regs->pstate);
+       printk("sp : %016llx\n", sp);
+       for (i = top_reg; i >= 0; i--) {
                printk("x%-2d: %016llx ", i, regs->regs[i]);
                if (i % 2 == 0)
                        printk("\n");
index 12ad8f3..055cfb8 100644 (file)
@@ -57,7 +57,7 @@
 unsigned int processor_id;
 EXPORT_SYMBOL(processor_id);
 
-unsigned int elf_hwcap __read_mostly;
+unsigned long elf_hwcap __read_mostly;
 EXPORT_SYMBOL_GPL(elf_hwcap);
 
 static const char *cpu_name;
index 6d6acf1..c23751b 100644 (file)
@@ -130,7 +130,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
        force_sig_info(sig, &si, tsk);
 }
 
-void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 {
        struct task_struct *tsk = current;
        struct mm_struct *mm = tsk->active_mm;
index 8ae80a1..19da91e 100644 (file)
@@ -35,7 +35,7 @@
  */
 ENTRY(__cpu_flush_user_tlb_range)
        vma_vm_mm x3, x2                        // get vma->vm_mm
-       mmid    x3, x3                          // get vm_mm->context.id
+       mmid    w3, x3                          // get vm_mm->context.id
        dsb     sy
        lsr     x0, x0, #12                     // align address
        lsr     x1, x1, #12
index d22af85..fd79807 100644 (file)
@@ -1,5 +1,19 @@
 
 generic-y      += clkdev.h
+generic-y       += cputime.h
+generic-y       += delay.h
+generic-y       += device.h
+generic-y       += div64.h
+generic-y       += emergency-restart.h
 generic-y      += exec.h
-generic-y      += trace_clock.h
+generic-y       += futex.h
+generic-y       += irq_regs.h
 generic-y      += param.h
+generic-y       += local.h
+generic-y       += local64.h
+generic-y       += percpu.h
+generic-y       += scatterlist.h
+generic-y       += sections.h
+generic-y       += topology.h
+generic-y      += trace_clock.h
+generic-y       += xor.h
diff --git a/arch/avr32/include/asm/cputime.h b/arch/avr32/include/asm/cputime.h
deleted file mode 100644 (file)
index e87e0f8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_CPUTIME_H
-#define __ASM_AVR32_CPUTIME_H
-
-#include <asm-generic/cputime.h>
-
-#endif /* __ASM_AVR32_CPUTIME_H */
diff --git a/arch/avr32/include/asm/delay.h b/arch/avr32/include/asm/delay.h
deleted file mode 100644 (file)
index 9670e12..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/delay.h>
diff --git a/arch/avr32/include/asm/device.h b/arch/avr32/include/asm/device.h
deleted file mode 100644 (file)
index d8f9872..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Arch specific extensions to struct device
- *
- * This file is released under the GPLv2
- */
-#include <asm-generic/device.h>
-
diff --git a/arch/avr32/include/asm/div64.h b/arch/avr32/include/asm/div64.h
deleted file mode 100644 (file)
index d7ddd4f..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_DIV64_H
-#define __ASM_AVR32_DIV64_H
-
-#include <asm-generic/div64.h>
-
-#endif /* __ASM_AVR32_DIV64_H */
diff --git a/arch/avr32/include/asm/emergency-restart.h b/arch/avr32/include/asm/emergency-restart.h
deleted file mode 100644 (file)
index 3e7e014..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_EMERGENCY_RESTART_H
-#define __ASM_AVR32_EMERGENCY_RESTART_H
-
-#include <asm-generic/emergency-restart.h>
-
-#endif /* __ASM_AVR32_EMERGENCY_RESTART_H */
diff --git a/arch/avr32/include/asm/futex.h b/arch/avr32/include/asm/futex.h
deleted file mode 100644 (file)
index 10419f1..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_FUTEX_H
-#define __ASM_AVR32_FUTEX_H
-
-#include <asm-generic/futex.h>
-
-#endif /* __ASM_AVR32_FUTEX_H */
diff --git a/arch/avr32/include/asm/irq_regs.h b/arch/avr32/include/asm/irq_regs.h
deleted file mode 100644 (file)
index 3dd9c0b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/avr32/include/asm/local.h b/arch/avr32/include/asm/local.h
deleted file mode 100644 (file)
index 1c16196..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_LOCAL_H
-#define __ASM_AVR32_LOCAL_H
-
-#include <asm-generic/local.h>
-
-#endif /* __ASM_AVR32_LOCAL_H */
diff --git a/arch/avr32/include/asm/local64.h b/arch/avr32/include/asm/local64.h
deleted file mode 100644 (file)
index 36c93b5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
diff --git a/arch/avr32/include/asm/percpu.h b/arch/avr32/include/asm/percpu.h
deleted file mode 100644 (file)
index 69227b4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_PERCPU_H
-#define __ASM_AVR32_PERCPU_H
-
-#include <asm-generic/percpu.h>
-
-#endif /* __ASM_AVR32_PERCPU_H */
diff --git a/arch/avr32/include/asm/scatterlist.h b/arch/avr32/include/asm/scatterlist.h
deleted file mode 100644 (file)
index a5902d9..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_SCATTERLIST_H
-#define __ASM_AVR32_SCATTERLIST_H
-
-#include <asm-generic/scatterlist.h>
-
-#endif /* __ASM_AVR32_SCATTERLIST_H */
diff --git a/arch/avr32/include/asm/sections.h b/arch/avr32/include/asm/sections.h
deleted file mode 100644 (file)
index aa14252..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_SECTIONS_H
-#define __ASM_AVR32_SECTIONS_H
-
-#include <asm-generic/sections.h>
-
-#endif /* __ASM_AVR32_SECTIONS_H */
diff --git a/arch/avr32/include/asm/topology.h b/arch/avr32/include/asm/topology.h
deleted file mode 100644 (file)
index 5b766cb..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_AVR32_TOPOLOGY_H
-#define __ASM_AVR32_TOPOLOGY_H
-
-#include <asm-generic/topology.h>
-
-#endif /* __ASM_AVR32_TOPOLOGY_H */
diff --git a/arch/avr32/include/asm/xor.h b/arch/avr32/include/asm/xor.h
deleted file mode 100644 (file)
index 99c87aa..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_XOR_H
-#define _ASM_XOR_H
-
-#include <asm-generic/xor.h>
-
-#endif
index 11c4259..4399364 100644 (file)
@@ -76,4 +76,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* __ASM_AVR32_SOCKET_H */
index c273100..42a53e7 100644 (file)
@@ -289,7 +289,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
                memset(childregs, 0, sizeof(struct pt_regs));
                p->thread.cpu_context.r0 = arg;
                p->thread.cpu_context.r1 = usp; /* fn */
-               p->thread.cpu_context.r2 = syscall_return;
+               p->thread.cpu_context.r2 = (unsigned long)syscall_return;
                p->thread.cpu_context.pc = (unsigned long)ret_from_kernel_thread;
                childregs->sr = MODE_SUPERVISOR;
        } else {
index 869a1c6..12f828a 100644 (file)
@@ -98,7 +98,14 @@ static void comparator_mode(enum clock_event_mode mode,
        case CLOCK_EVT_MODE_SHUTDOWN:
                sysreg_write(COMPARE, 0);
                pr_debug("%s: stop\n", evdev->name);
-               cpu_idle_poll_ctrl(false);
+               if (evdev->mode == CLOCK_EVT_MODE_ONESHOT ||
+                   evdev->mode == CLOCK_EVT_MODE_RESUME) {
+                       /*
+                        * Only disable idle poll if we have forced that
+                        * in a previous call.
+                        */
+                       cpu_idle_poll_ctrl(false);
+               }
                break;
        default:
                BUG();
index eb723e5..13829aa 100644 (file)
@@ -78,6 +78,8 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_SOCKET_H */
 
 
index f0cb1c3..5d42997 100644 (file)
@@ -76,5 +76,7 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_SOCKET_H */
 
index 9490758..214ccaf 100644 (file)
@@ -76,4 +76,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_SOCKET_H */
index 556d070..c25302f 100644 (file)
@@ -85,4 +85,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_IA64_SOCKET_H */
index 24be7c8..5296665 100644 (file)
@@ -76,4 +76,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_M32R_SOCKET_H */
index 4a9baa9..9969dba 100644 (file)
@@ -276,7 +276,7 @@ static struct platform_device mtx1_pci_host = {
        .resource       = alchemy_pci_host_res,
 };
 
-static struct __initdata platform_device * mtx1_devs[] = {
+static struct platform_device *mtx1_devs[] __initdata = {
        &mtx1_pci_host,
        &mtx1_gpio_leds,
        &mtx1_wdt,
index 51680d1..d445d06 100644 (file)
 
 /*
  * MIPS32, MIPS64, VR5500, IDT32332, IDT32334 and maybe a few other
- * pre-MIPS32/MIPS53 processors have CLO, CLZ. The IDT RC64574 is 64-bit and
+ * pre-MIPS32/MIPS64 processors have CLO, CLZ. The IDT RC64574 is 64-bit and
  * has CLO and CLZ but not DCLO nor DCLZ.  For 64-bit kernels
  * cpu_has_clo_clz also indicates the availability of DCLO and DCLZ.
  */
index 4d6d77e..e194f95 100644 (file)
@@ -22,7 +22,7 @@
 
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-       asm goto("1:\tnop\n\t"
+       asm_volatile_goto("1:\tnop\n\t"
                "nop\n\t"
                ".pushsection __jump_table,  \"aw\"\n\t"
                WORD_INSN " 1b, %l[l_yes], %0\n\t"
index 61c01f0..0df9787 100644 (file)
@@ -94,4 +94,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 4204d76..029e002 100644 (file)
@@ -73,7 +73,7 @@
 3:
 
 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
-       PTR_L   t8, __stack_chk_guard
+       PTR_LA  t8, __stack_chk_guard
        LONG_L  t9, TASK_STACK_CANARY(a1)
        LONG_S  t9, 0(t8)
 #endif
index 45f1ffc..24cdf64 100644 (file)
@@ -971,11 +971,11 @@ static const struct mips_perf_event mipsxx74Kcore_cache_map
 [C(LL)] = {
        [C(OP_READ)] = {
                [C(RESULT_ACCESS)]      = { 0x1c, CNTR_ODD, P },
-               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN | CNTR_ODD, P },
+               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN, P },
        },
        [C(OP_WRITE)] = {
                [C(RESULT_ACCESS)]      = { 0x1c, CNTR_ODD, P },
-               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN | CNTR_ODD, P },
+               [C(RESULT_MISS)]        = { 0x1d, CNTR_EVEN, P },
        },
 },
 [C(ITLB)] = {
index 38af83f..20b7b04 100644 (file)
@@ -67,7 +67,7 @@ LEAF(resume)
 1:
 
 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
-       PTR_L   t8, __stack_chk_guard
+       PTR_LA  t8, __stack_chk_guard
        LONG_L  t9, TASK_STACK_CANARY(a1)
        LONG_S  t9, 0(t8)
 #endif
index 921238a..078de5e 100644 (file)
@@ -69,7 +69,7 @@
 1:
 
 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
-       PTR_L   t8, __stack_chk_guard
+       PTR_LA  t8, __stack_chk_guard
        LONG_L  t9, TASK_STACK_CANARY(a1)
        LONG_S  t9, 0(t8)
 #endif
index 627883b..bc6f96f 100644 (file)
@@ -609,6 +609,7 @@ static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size)
                        r4k_blast_scache();
                else
                        blast_scache_range(addr, addr + size);
+               preempt_enable();
                __sync();
                return;
        }
@@ -650,6 +651,7 @@ static void r4k_dma_cache_inv(unsigned long addr, unsigned long size)
                         */
                        blast_inv_scache_range(addr, addr + size);
                }
+               preempt_enable();
                __sync();
                return;
        }
index f25a7e9..5f8b955 100644 (file)
@@ -308,12 +308,10 @@ static void mips_dma_sync_sg_for_cpu(struct device *dev,
 {
        int i;
 
-       /* Make sure that gcc doesn't leave the empty loop body.  */
-       for (i = 0; i < nelems; i++, sg++) {
-               if (cpu_needs_post_dma_flush(dev))
+       if (cpu_needs_post_dma_flush(dev))
+               for (i = 0; i < nelems; i++, sg++)
                        __dma_sync(sg_page(sg), sg->offset, sg->length,
                                   direction);
-       }
 }
 
 static void mips_dma_sync_sg_for_device(struct device *dev,
@@ -321,12 +319,10 @@ static void mips_dma_sync_sg_for_device(struct device *dev,
 {
        int i;
 
-       /* Make sure that gcc doesn't leave the empty loop body.  */
-       for (i = 0; i < nelems; i++, sg++) {
-               if (!plat_device_is_coherent(dev))
+       if (!plat_device_is_coherent(dev))
+               for (i = 0; i < nelems; i++, sg++)
                        __dma_sync(sg_page(sg), sg->offset, sg->length,
                                   direction);
-       }
 }
 
 int mips_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
index c69da37..5b28e81 100644 (file)
@@ -473,7 +473,7 @@ static void __init fill_ipi_map(void)
 {
        int cpu;
 
-       for (cpu = 0; cpu < NR_CPUS; cpu++) {
+       for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
                fill_ipi_map1(gic_resched_int_base, cpu, GIC_CPU_INT1);
                fill_ipi_map1(gic_call_int_base, cpu, GIC_CPU_INT2);
        }
@@ -574,8 +574,9 @@ void __init arch_init_irq(void)
                /* FIXME */
                int i;
 #if defined(CONFIG_MIPS_MT_SMP)
-               gic_call_int_base = GIC_NUM_INTRS - NR_CPUS;
-               gic_resched_int_base = gic_call_int_base - NR_CPUS;
+               gic_call_int_base = GIC_NUM_INTRS -
+                       (NR_CPUS - nr_cpu_ids) * 2 - nr_cpu_ids;
+               gic_resched_int_base = gic_call_int_base - nr_cpu_ids;
                fill_ipi_map();
 #endif
                gic_init(GIC_BASE_ADDR, GIC_ADDRSPACE_SZ, gic_intr_map,
@@ -599,7 +600,7 @@ void __init arch_init_irq(void)
                printk("CPU%d: status register now %08x\n", smp_processor_id(), read_c0_status());
                write_c0_status(0x1100dc00);
                printk("CPU%d: status register frc %08x\n", smp_processor_id(), read_c0_status());
-               for (i = 0; i < NR_CPUS; i++) {
+               for (i = 0; i < nr_cpu_ids; i++) {
                        arch_init_ipiirq(MIPS_GIC_IRQ_BASE +
                                         GIC_RESCHED_INT(i), &irq_resched);
                        arch_init_ipiirq(MIPS_GIC_IRQ_BASE +
index e49241a..2027857 100644 (file)
@@ -126,7 +126,7 @@ static int rt_timer_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       rt->membase = devm_request_and_ioremap(&pdev->dev, res);
+       rt->membase = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(rt->membase))
                return PTR_ERR(rt->membase);
 
index e2a2b20..71dedca 100644 (file)
@@ -76,4 +76,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_SOCKET_H */
index eb59bfe..93c9980 100644 (file)
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
-
-#include <linux/of.h>  /* linux/of.h gets to determine #include ordering */
-
 #ifndef _ASM_OPENRISC_PROM_H
 #define _ASM_OPENRISC_PROM_H
-#ifdef __KERNEL__
-#ifndef __ASSEMBLY__
 
-#include <linux/types.h>
-#include <asm/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/atomic.h>
-#include <linux/of_irq.h>
-#include <linux/of_fdt.h>
-#include <linux/of_address.h>
-#include <linux/proc_fs.h>
-#include <linux/platform_device.h>
 #define HAVE_ARCH_DEVTREE_FIXUPS
 
-/* Other Prototypes */
-extern int early_uartlite_console(void);
-
-/* Parse the ibm,dma-window property of an OF node into the busno, phys and
- * size parameters.
- */
-void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop,
-               unsigned long *busno, unsigned long *phys, unsigned long *size);
-
-extern void kdump_move_device_tree(void);
-
-/* Get the MAC address */
-extern const void *of_get_mac_address(struct device_node *np);
-
-/**
- * of_irq_map_pci - Resolve the interrupt for a PCI device
- * @pdev:      the device whose interrupt is to be resolved
- * @out_irq:   structure of_irq filled by this function
- *
- * This function resolves the PCI interrupt for a given PCI device. If a
- * device-node exists for a given pci_dev, it will use normal OF tree
- * walking. If not, it will implement standard swizzling and walk up the
- * PCI tree until an device-node is found, at which point it will finish
- * resolving using the OF tree walking.
- */
-struct pci_dev;
-extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq);
-
-#endif /* __ASSEMBLY__ */
-#endif /* __KERNEL__ */
 #endif /* _ASM_OPENRISC_PROM_H */
index 0f90569..9387cc2 100644 (file)
@@ -40,6 +40,8 @@ CONFIG_IP_NF_QUEUE=m
 CONFIG_LLC2=m
 CONFIG_NET_PKTGEN=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_PARPORT=y
index b647b18..9002532 100644 (file)
@@ -79,6 +79,8 @@ CONFIG_IP_DCCP=m
 CONFIG_LLC2=m
 CONFIG_NET_PKTGEN=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_BLK_DEV_UMEM=m
index e289f5b..f1a0c25 100644 (file)
@@ -4,6 +4,7 @@ CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
 CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
@@ -27,6 +28,8 @@ CONFIG_IP_PNP_BOOTP=y
 # CONFIG_INET_LRO is not set
 CONFIG_IPV6=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
index 311ca36..ec1b014 100644 (file)
@@ -5,6 +5,7 @@ CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
 CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_EXPERT=y
 CONFIG_KALLSYMS_ALL=y
@@ -39,6 +40,8 @@ CONFIG_NETFILTER_DEBUG=y
 CONFIG_IP_NF_QUEUE=m
 CONFIG_NET_PKTGEN=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_BLK_DEV_UMEM=m
index f110063..e1c8d20 100644 (file)
@@ -62,6 +62,8 @@ CONFIG_TIPC=m
 CONFIG_LLC2=m
 CONFIG_DNS_RESOLVER=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 CONFIG_PARPORT=y
 CONFIG_PARPORT_PC=y
index dfe88f6..ba61495 100644 (file)
@@ -49,6 +49,8 @@ CONFIG_INET6_ESP=y
 CONFIG_INET6_IPCOMP=y
 CONFIG_LLC2=m
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_STANDALONE is not set
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_PARPORT=y
index 1945f99..4736020 100644 (file)
@@ -6,7 +6,7 @@ struct pt_regs;
 
 /* traps.c */
 void parisc_terminate(char *msg, struct pt_regs *regs,
-               int code, unsigned long offset);
+               int code, unsigned long offset) __noreturn __cold;
 
 /* mm/fault.c */
 void do_page_fault(struct pt_regs *regs, unsigned long code,
index 71700e6..7c614d0 100644 (file)
@@ -75,6 +75,8 @@
 
 #define SO_BUSY_POLL           0x4027
 
+#define SO_MAX_PACING_RATE     0x4048
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index 37aabd7..d2d5825 100644 (file)
@@ -195,6 +195,8 @@ common_stext:
        ldw             MEM_PDC_HI(%r0),%r6
        depd            %r6, 31, 32, %r3        /* move to upper word */
 
+       mfctl           %cr30,%r6               /* PCX-W2 firmware bug */
+
        ldo             PDC_PSW(%r0),%arg0              /* 21 */
        ldo             PDC_PSW_SET_DEFAULTS(%r0),%arg1 /* 2 */
        ldo             PDC_PSW_WIDE_BIT(%r0),%arg2     /* 2 */
@@ -203,6 +205,8 @@ common_stext:
        copy            %r0,%arg3
 
 stext_pdc_ret:
+       mtctl           %r6,%cr30               /* restore task thread info */
+
        /* restore rfi target address*/
        ldd             TI_TASK-THREAD_SZ_ALGN(%sp), %r10
        tophys_r1       %r10
index 8a252f2..2b96602 100644 (file)
@@ -72,7 +72,6 @@ enum ipi_message_type {
        IPI_NOP=0,
        IPI_RESCHEDULE=1,
        IPI_CALL_FUNC,
-       IPI_CALL_FUNC_SINGLE,
        IPI_CPU_START,
        IPI_CPU_STOP,
        IPI_CPU_TEST
@@ -164,11 +163,6 @@ ipi_interrupt(int irq, void *dev_id)
                                generic_smp_call_function_interrupt();
                                break;
 
-                       case IPI_CALL_FUNC_SINGLE:
-                               smp_debug(100, KERN_DEBUG "CPU%d IPI_CALL_FUNC_SINGLE\n", this_cpu);
-                               generic_smp_call_function_single_interrupt();
-                               break;
-
                        case IPI_CPU_START:
                                smp_debug(100, KERN_DEBUG "CPU%d IPI_CPU_START\n", this_cpu);
                                break;
@@ -260,7 +254,7 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask)
 
 void arch_send_call_function_single_ipi(int cpu)
 {
-       send_IPI_single(cpu, IPI_CALL_FUNC_SINGLE);
+       send_IPI_single(cpu, IPI_CALL_FUNC);
 }
 
 /*
index 04e47c6..1cd1d0c 100644 (file)
@@ -291,11 +291,6 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
        do_exit(SIGSEGV);
 }
 
-int syscall_ipi(int (*syscall) (struct pt_regs *), struct pt_regs *regs)
-{
-       return syscall(regs);
-}
-
 /* gdb uses break 4,8 */
 #define GDB_BREAK_INSN 0x10004
 static void handle_gdb_break(struct pt_regs *regs, int wot)
@@ -805,14 +800,14 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
        else {
 
            /*
-            * The kernel should never fault on its own address space.
+            * The kernel should never fault on its own address space,
+            * unless pagefault_disable() was called before.
             */
 
-           if (fault_space == 0
+           if (fault_space == 0 && !in_atomic())
            {
                pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
                parisc_terminate("Kernel Fault", regs, code, fault_address);
-       
            }
        }
 
index ac4370b..b5507ec 100644 (file)
@@ -56,7 +56,7 @@
 #ifdef __KERNEL__
 #include <linux/module.h>
 #include <linux/compiler.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #define s_space "%%sr1"
 #define d_space "%%sr2"
 #else
@@ -524,4 +524,17 @@ EXPORT_SYMBOL(copy_to_user);
 EXPORT_SYMBOL(copy_from_user);
 EXPORT_SYMBOL(copy_in_user);
 EXPORT_SYMBOL(memcpy);
+
+long probe_kernel_read(void *dst, const void *src, size_t size)
+{
+       unsigned long addr = (unsigned long)src;
+
+       if (size < 0 || addr < PAGE_SIZE)
+               return -EFAULT;
+
+       /* check for I/O space F_EXTEND(0xfff00000) access as well? */
+
+       return __probe_kernel_read(dst, src, size);
+}
+
 #endif
index d10d27a..0293588 100644 (file)
@@ -171,17 +171,25 @@ void do_page_fault(struct pt_regs *regs, unsigned long code,
                              unsigned long address)
 {
        struct vm_area_struct *vma, *prev_vma;
-       struct task_struct *tsk = current;
-       struct mm_struct *mm = tsk->mm;
+       struct task_struct *tsk;
+       struct mm_struct *mm;
        unsigned long acc_type;
        int fault;
-       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+       unsigned int flags;
 
-       if (in_atomic() || !mm)
+       if (in_atomic())
                goto no_context;
 
+       tsk = current;
+       mm = tsk->mm;
+       if (!mm)
+               goto no_context;
+
+       flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
        if (user_mode(regs))
                flags |= FAULT_FLAG_USER;
+
+       acc_type = parisc_acctyp(code, regs->iir);
        if (acc_type & VM_WRITE)
                flags |= FAULT_FLAG_WRITE;
 retry:
@@ -196,8 +204,6 @@ retry:
 
 good_area:
 
-       acc_type = parisc_acctyp(code,regs->iir);
-
        if ((vma->vm_flags & acc_type) != acc_type)
                goto bad_area;
 
index 6a15c96..15ca225 100644 (file)
@@ -74,7 +74,7 @@ src-wlib-$(CONFIG_8xx) += mpc8xx.c planetcore.c
 src-wlib-$(CONFIG_PPC_82xx) += pq2.c fsl-soc.c planetcore.c
 src-wlib-$(CONFIG_EMBEDDED6xx) += mv64x60.c mv64x60_i2c.c ugecon.c
 
-src-plat-y := of.c
+src-plat-y := of.c epapr.c
 src-plat-$(CONFIG_40x) += fixed-head.S ep405.c cuboot-hotfoot.c \
                                treeboot-walnut.c cuboot-acadia.c \
                                cuboot-kilauea.c simpleboot.c \
@@ -97,7 +97,7 @@ src-plat-$(CONFIG_EMBEDDED6xx) += cuboot-pq2.c cuboot-mpc7448hpc2.c \
                                        prpmc2800.c
 src-plat-$(CONFIG_AMIGAONE) += cuboot-amigaone.c
 src-plat-$(CONFIG_PPC_PS3) += ps3-head.S ps3-hvcall.S ps3.c
-src-plat-$(CONFIG_EPAPR_BOOT) += epapr.c
+src-plat-$(CONFIG_EPAPR_BOOT) += epapr.c epapr-wrapper.c
 
 src-wlib := $(sort $(src-wlib-y))
 src-plat := $(sort $(src-plat-y))
diff --git a/arch/powerpc/boot/epapr-wrapper.c b/arch/powerpc/boot/epapr-wrapper.c
new file mode 100644 (file)
index 0000000..c101910
--- /dev/null
@@ -0,0 +1,9 @@
+extern void epapr_platform_init(unsigned long r3, unsigned long r4,
+                               unsigned long r5, unsigned long r6,
+                               unsigned long r7);
+
+void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                  unsigned long r6, unsigned long r7)
+{
+       epapr_platform_init(r3, r4, r5, r6, r7);
+}
index 06c1961..02e91aa 100644 (file)
@@ -48,8 +48,8 @@ static void platform_fixups(void)
                       fdt_addr, fdt_totalsize((void *)fdt_addr), ima_size);
 }
 
-void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
-                  unsigned long r6, unsigned long r7)
+void epapr_platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                        unsigned long r6, unsigned long r7)
 {
        epapr_magic = r6;
        ima_size = r7;
index 61d9899..62e2f43 100644 (file)
@@ -26,6 +26,9 @@
 
 static unsigned long claim_base;
 
+void epapr_platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                        unsigned long r6, unsigned long r7);
+
 static void *of_try_claim(unsigned long size)
 {
        unsigned long addr = 0;
@@ -61,7 +64,7 @@ static void of_image_hdr(const void *hdr)
        }
 }
 
-void platform_init(unsigned long a1, unsigned long a2, void *promptr)
+static void of_platform_init(unsigned long a1, unsigned long a2, void *promptr)
 {
        platform_ops.image_hdr = of_image_hdr;
        platform_ops.malloc = of_try_claim;
@@ -81,3 +84,14 @@ void platform_init(unsigned long a1, unsigned long a2, void *promptr)
                loader_info.initrd_size = a2;
        }
 }
+
+void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                  unsigned long r6, unsigned long r7)
+{
+       /* Detect OF vs. ePAPR boot */
+       if (r5)
+               of_platform_init(r3, r4, (void *)r5);
+       else
+               epapr_platform_init(r3, r4, r5, r6, r7);
+}
+
index 6761c74..cd7af84 100755 (executable)
@@ -148,18 +148,18 @@ make_space=y
 
 case "$platform" in
 pseries)
-    platformo=$object/of.o
+    platformo="$object/of.o $object/epapr.o"
     link_address='0x4000000'
     ;;
 maple)
-    platformo=$object/of.o
+    platformo="$object/of.o $object/epapr.o"
     link_address='0x400000'
     ;;
 pmac|chrp)
-    platformo=$object/of.o
+    platformo="$object/of.o $object/epapr.o"
     ;;
 coff)
-    platformo="$object/crt0.o $object/of.o"
+    platformo="$object/crt0.o $object/of.o $object/epapr.o"
     lds=$object/zImage.coff.lds
     link_address='0x500000'
     pie=
@@ -253,6 +253,7 @@ treeboot-iss4xx-mpic)
     platformo="$object/treeboot-iss4xx.o"
     ;;
 epapr)
+    platformo="$object/epapr.o $object/epapr-wrapper.o"
     link_address='0x20000000'
     pie=-pie
     ;;
index 0e40843..41f13ce 100644 (file)
@@ -69,9 +69,9 @@ extern struct thread_info *softirq_ctx[NR_CPUS];
 
 extern void irq_ctx_init(void);
 extern void call_do_softirq(struct thread_info *tp);
-extern int call_handle_irq(int irq, void *p1,
-                          struct thread_info *tp, void *func);
+extern void call_do_irq(struct pt_regs *regs, struct thread_info *tp);
 extern void do_IRQ(struct pt_regs *regs);
+extern void __do_irq(struct pt_regs *regs);
 
 int irq_choose_cpu(const struct cpumask *mask);
 
index ae098c4..f016bb6 100644 (file)
@@ -19,7 +19,7 @@
 
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-       asm goto("1:\n\t"
+       asm_volatile_goto("1:\n\t"
                 "nop\n\t"
                 ".pushsection __jump_table,  \"aw\"\n\t"
                 JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t"
index e378ccc..ce4de5a 100644 (file)
@@ -149,8 +149,6 @@ typedef struct {
 
 struct thread_struct {
        unsigned long   ksp;            /* Kernel stack pointer */
-       unsigned long   ksp_limit;      /* if ksp <= ksp_limit stack overflow */
-
 #ifdef CONFIG_PPC64
        unsigned long   ksp_vsid;
 #endif
@@ -162,6 +160,7 @@ struct thread_struct {
 #endif
 #ifdef CONFIG_PPC32
        void            *pgdir;         /* root of page-table tree */
+       unsigned long   ksp_limit;      /* if ksp <= ksp_limit stack overflow */
 #endif
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
        /*
@@ -321,7 +320,6 @@ struct thread_struct {
 #else
 #define INIT_THREAD  { \
        .ksp = INIT_SP, \
-       .ksp_limit = INIT_SP_LIMIT, \
        .regs = (struct pt_regs *)INIT_SP - 1, /* XXX bogus, I think */ \
        .fs = KERNEL_DS, \
        .fpr = {{0}}, \
index a6d7446..fa69832 100644 (file)
@@ -83,4 +83,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index d8958be..502c7a4 100644 (file)
@@ -80,10 +80,11 @@ int main(void)
        DEFINE(TASKTHREADPPR, offsetof(struct task_struct, thread.ppr));
 #else
        DEFINE(THREAD_INFO, offsetof(struct task_struct, stack));
+       DEFINE(THREAD_INFO_GAP, _ALIGN_UP(sizeof(struct thread_info), 16));
+       DEFINE(KSP_LIMIT, offsetof(struct thread_struct, ksp_limit));
 #endif /* CONFIG_PPC64 */
 
        DEFINE(KSP, offsetof(struct thread_struct, ksp));
-       DEFINE(KSP_LIMIT, offsetof(struct thread_struct, ksp_limit));
        DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
 #ifdef CONFIG_BOOKE
        DEFINE(THREAD_NORMSAVES, offsetof(struct thread_struct, normsave[0]));
index 0adab06..572bb5b 100644 (file)
@@ -661,7 +661,7 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid)
        /* number of bytes needed for the bitmap */
        sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long);
 
-       page = alloc_pages_node(nid, GFP_ATOMIC, get_order(sz));
+       page = alloc_pages_node(nid, GFP_KERNEL, get_order(sz));
        if (!page)
                panic("iommu_init_table: Can't allocate %ld bytes\n", sz);
        tbl->it_map = page_address(page);
index c69440c..c7cb8c2 100644 (file)
@@ -441,50 +441,6 @@ void migrate_irqs(void)
 }
 #endif
 
-static inline void handle_one_irq(unsigned int irq)
-{
-       struct thread_info *curtp, *irqtp;
-       unsigned long saved_sp_limit;
-       struct irq_desc *desc;
-
-       desc = irq_to_desc(irq);
-       if (!desc)
-               return;
-
-       /* Switch to the irq stack to handle this */
-       curtp = current_thread_info();
-       irqtp = hardirq_ctx[smp_processor_id()];
-
-       if (curtp == irqtp) {
-               /* We're already on the irq stack, just handle it */
-               desc->handle_irq(irq, desc);
-               return;
-       }
-
-       saved_sp_limit = current->thread.ksp_limit;
-
-       irqtp->task = curtp->task;
-       irqtp->flags = 0;
-
-       /* Copy the softirq bits in preempt_count so that the
-        * softirq checks work in the hardirq context. */
-       irqtp->preempt_count = (irqtp->preempt_count & ~SOFTIRQ_MASK) |
-                              (curtp->preempt_count & SOFTIRQ_MASK);
-
-       current->thread.ksp_limit = (unsigned long)irqtp +
-               _ALIGN_UP(sizeof(struct thread_info), 16);
-
-       call_handle_irq(irq, desc, irqtp, desc->handle_irq);
-       current->thread.ksp_limit = saved_sp_limit;
-       irqtp->task = NULL;
-
-       /* Set any flag that may have been set on the
-        * alternate stack
-        */
-       if (irqtp->flags)
-               set_bits(irqtp->flags, &curtp->flags);
-}
-
 static inline void check_stack_overflow(void)
 {
 #ifdef CONFIG_DEBUG_STACKOVERFLOW
@@ -501,9 +457,9 @@ static inline void check_stack_overflow(void)
 #endif
 }
 
-void do_IRQ(struct pt_regs *regs)
+void __do_irq(struct pt_regs *regs)
 {
-       struct pt_regs *old_regs = set_irq_regs(regs);
+       struct irq_desc *desc;
        unsigned int irq;
 
        irq_enter();
@@ -519,18 +475,57 @@ void do_IRQ(struct pt_regs *regs)
         */
        irq = ppc_md.get_irq();
 
-       /* We can hard enable interrupts now */
+       /* We can hard enable interrupts now to allow perf interrupts */
        may_hard_irq_enable();
 
        /* And finally process it */
-       if (irq != NO_IRQ)
-               handle_one_irq(irq);
-       else
+       if (unlikely(irq == NO_IRQ))
                __get_cpu_var(irq_stat).spurious_irqs++;
+       else {
+               desc = irq_to_desc(irq);
+               if (likely(desc))
+                       desc->handle_irq(irq, desc);
+       }
 
        trace_irq_exit(regs);
 
        irq_exit();
+}
+
+void do_IRQ(struct pt_regs *regs)
+{
+       struct pt_regs *old_regs = set_irq_regs(regs);
+       struct thread_info *curtp, *irqtp, *sirqtp;
+
+       /* Switch to the irq stack to handle this */
+       curtp = current_thread_info();
+       irqtp = hardirq_ctx[raw_smp_processor_id()];
+       sirqtp = softirq_ctx[raw_smp_processor_id()];
+
+       /* Already there ? */
+       if (unlikely(curtp == irqtp || curtp == sirqtp)) {
+               __do_irq(regs);
+               set_irq_regs(old_regs);
+               return;
+       }
+
+       /* Prepare the thread_info in the irq stack */
+       irqtp->task = curtp->task;
+       irqtp->flags = 0;
+
+       /* Copy the preempt_count so that the [soft]irq checks work. */
+       irqtp->preempt_count = curtp->preempt_count;
+
+       /* Switch stack and call */
+       call_do_irq(regs, irqtp);
+
+       /* Restore stack limit */
+       irqtp->task = NULL;
+
+       /* Copy back updates to the thread_info */
+       if (irqtp->flags)
+               set_bits(irqtp->flags, &curtp->flags);
+
        set_irq_regs(old_regs);
 }
 
@@ -592,28 +587,22 @@ void irq_ctx_init(void)
                memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
                tp = softirq_ctx[i];
                tp->cpu = i;
-               tp->preempt_count = 0;
 
                memset((void *)hardirq_ctx[i], 0, THREAD_SIZE);
                tp = hardirq_ctx[i];
                tp->cpu = i;
-               tp->preempt_count = HARDIRQ_OFFSET;
        }
 }
 
 static inline void do_softirq_onstack(void)
 {
        struct thread_info *curtp, *irqtp;
-       unsigned long saved_sp_limit = current->thread.ksp_limit;
 
        curtp = current_thread_info();
        irqtp = softirq_ctx[smp_processor_id()];
        irqtp->task = curtp->task;
        irqtp->flags = 0;
-       current->thread.ksp_limit = (unsigned long)irqtp +
-                                   _ALIGN_UP(sizeof(struct thread_info), 16);
        call_do_softirq(irqtp);
-       current->thread.ksp_limit = saved_sp_limit;
        irqtp->task = NULL;
 
        /* Set any flag that may have been set on the
index 777d999..2b0ad98 100644 (file)
 
        .text
 
+/*
+ * We store the saved ksp_limit in the unused part
+ * of the STACK_FRAME_OVERHEAD
+ */
 _GLOBAL(call_do_softirq)
        mflr    r0
        stw     r0,4(r1)
+       lwz     r10,THREAD+KSP_LIMIT(r2)
+       addi    r11,r3,THREAD_INFO_GAP
        stwu    r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r3)
        mr      r1,r3
+       stw     r10,8(r1)
+       stw     r11,THREAD+KSP_LIMIT(r2)
        bl      __do_softirq
+       lwz     r10,8(r1)
        lwz     r1,0(r1)
        lwz     r0,4(r1)
+       stw     r10,THREAD+KSP_LIMIT(r2)
        mtlr    r0
        blr
 
-_GLOBAL(call_handle_irq)
+_GLOBAL(call_do_irq)
        mflr    r0
        stw     r0,4(r1)
-       mtctr   r6
-       stwu    r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r5)
-       mr      r1,r5
-       bctrl
+       lwz     r10,THREAD+KSP_LIMIT(r2)
+       addi    r11,r3,THREAD_INFO_GAP
+       stwu    r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r4)
+       mr      r1,r4
+       stw     r10,8(r1)
+       stw     r11,THREAD+KSP_LIMIT(r2)
+       bl      __do_irq
+       lwz     r10,8(r1)
        lwz     r1,0(r1)
        lwz     r0,4(r1)
+       stw     r10,THREAD+KSP_LIMIT(r2)
        mtlr    r0
        blr
 
index 971d7e7..e59caf8 100644 (file)
@@ -40,14 +40,12 @@ _GLOBAL(call_do_softirq)
        mtlr    r0
        blr
 
-_GLOBAL(call_handle_irq)
-       ld      r8,0(r6)
+_GLOBAL(call_do_irq)
        mflr    r0
        std     r0,16(r1)
-       mtctr   r8
-       stdu    r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r5)
-       mr      r1,r5
-       bctrl
+       stdu    r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r4)
+       mr      r1,r4
+       bl      .__do_irq
        ld      r1,0(r1)
        ld      r0,16(r1)
        mtlr    r0
index 6f428da..96d2fdf 100644 (file)
@@ -1000,9 +1000,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
        kregs = (struct pt_regs *) sp;
        sp -= STACK_FRAME_OVERHEAD;
        p->thread.ksp = sp;
+#ifdef CONFIG_PPC32
        p->thread.ksp_limit = (unsigned long)task_stack_page(p) +
                                _ALIGN_UP(sizeof(struct thread_info), 16);
-
+#endif
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
        p->thread.ptrace_bps[0] = NULL;
 #endif
index 12e656f..5fe2842 100644 (file)
@@ -196,6 +196,8 @@ static int __initdata mem_reserve_cnt;
 
 static cell_t __initdata regbuf[1024];
 
+static bool rtas_has_query_cpu_stopped;
+
 
 /*
  * Error results ... some OF calls will return "-1" on error, some
@@ -1574,6 +1576,11 @@ static void __init prom_instantiate_rtas(void)
        prom_setprop(rtas_node, "/rtas", "linux,rtas-entry",
                     &val, sizeof(val));
 
+       /* Check if it supports "query-cpu-stopped-state" */
+       if (prom_getprop(rtas_node, "query-cpu-stopped-state",
+                        &val, sizeof(val)) != PROM_ERROR)
+               rtas_has_query_cpu_stopped = true;
+
 #if defined(CONFIG_PPC_POWERNV) && defined(__BIG_ENDIAN__)
        /* PowerVN takeover hack */
        prom_rtas_data = base;
@@ -1815,6 +1822,18 @@ static void __init prom_hold_cpus(void)
                = (void *) LOW_ADDR(__secondary_hold_acknowledge);
        unsigned long secondary_hold = LOW_ADDR(__secondary_hold);
 
+       /*
+        * On pseries, if RTAS supports "query-cpu-stopped-state",
+        * we skip this stage, the CPUs will be started by the
+        * kernel using RTAS.
+        */
+       if ((of_platform == PLATFORM_PSERIES ||
+            of_platform == PLATFORM_PSERIES_LPAR) &&
+           rtas_has_query_cpu_stopped) {
+               prom_printf("prom_hold_cpus: skipped\n");
+               return;
+       }
+
        prom_debug("prom_hold_cpus: start...\n");
        prom_debug("    1) spinloop       = 0x%x\n", (unsigned long)spinloop);
        prom_debug("    1) *spinloop      = 0x%x\n", *spinloop);
@@ -3011,6 +3030,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         * On non-powermacs, put all CPUs in spin-loops.
         *
         * PowerMacs use a different mechanism to spin CPUs
+        *
+        * (This must be done after instanciating RTAS)
         */
        if (of_platform != PLATFORM_POWERMAC &&
            of_platform != PLATFORM_OPAL)
index 27a90b9..b4e6676 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/machdep.h>
 #include <asm/smp.h>
 #include <asm/pmc.h>
+#include <asm/firmware.h>
 
 #include "cacheinfo.h"
 
@@ -179,15 +180,25 @@ SYSFS_PMCSETUP(spurr, SPRN_SPURR);
 SYSFS_PMCSETUP(dscr, SPRN_DSCR);
 SYSFS_PMCSETUP(pir, SPRN_PIR);
 
+/*
+  Lets only enable read for phyp resources and
+  enable write when needed with a separate function.
+  Lets be conservative and default to pseries.
+*/
 static DEVICE_ATTR(mmcra, 0600, show_mmcra, store_mmcra);
 static DEVICE_ATTR(spurr, 0400, show_spurr, NULL);
 static DEVICE_ATTR(dscr, 0600, show_dscr, store_dscr);
-static DEVICE_ATTR(purr, 0600, show_purr, store_purr);
+static DEVICE_ATTR(purr, 0400, show_purr, store_purr);
 static DEVICE_ATTR(pir, 0400, show_pir, NULL);
 
 unsigned long dscr_default = 0;
 EXPORT_SYMBOL(dscr_default);
 
+static void add_write_permission_dev_attr(struct device_attribute *attr)
+{
+       attr->attr.mode |= 0200;
+}
+
 static ssize_t show_dscr_default(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -394,8 +405,11 @@ static void register_cpu_online(unsigned int cpu)
        if (cpu_has_feature(CPU_FTR_MMCRA))
                device_create_file(s, &dev_attr_mmcra);
 
-       if (cpu_has_feature(CPU_FTR_PURR))
+       if (cpu_has_feature(CPU_FTR_PURR)) {
+               if (!firmware_has_feature(FW_FEATURE_LPAR))
+                       add_write_permission_dev_attr(&dev_attr_purr);
                device_create_file(s, &dev_attr_purr);
+       }
 
        if (cpu_has_feature(CPU_FTR_SPURR))
                device_create_file(s, &dev_attr_spurr);
index 7b60b98..cd809ea 100644 (file)
@@ -79,6 +79,11 @@ _GLOBAL(tm_abort)
        TABORT(R3)
        blr
 
+       .section        ".toc","aw"
+DSCR_DEFAULT:
+       .tc dscr_default[TC],dscr_default
+
+       .section        ".text"
 
 /* void tm_reclaim(struct thread_struct *thread,
  *                 unsigned long orig_msr,
@@ -123,6 +128,7 @@ _GLOBAL(tm_reclaim)
        mr      r15, r14
        ori     r15, r15, MSR_FP
        li      r16, MSR_RI
+       ori     r16, r16, MSR_EE /* IRQs hard off */
        andc    r15, r15, r16
        oris    r15, r15, MSR_VEC@h
 #ifdef CONFIG_VSX
@@ -187,11 +193,18 @@ dont_backup_fp:
        std     r1, PACATMSCRATCH(r13)
        ld      r1, PACAR1(r13)
 
+       /* Store the PPR in r11 and reset to decent value */
+       std     r11, GPR11(r1)                  /* Temporary stash */
+       mfspr   r11, SPRN_PPR
+       HMT_MEDIUM
+
        /* Now get some more GPRS free */
        std     r7, GPR7(r1)                    /* Temporary stash */
        std     r12, GPR12(r1)                  /* ''   ''    ''   */
        ld      r12, STACK_PARAM(0)(r1)         /* Param 0, thread_struct * */
 
+       std     r11, THREAD_TM_PPR(r12)         /* Store PPR and free r11 */
+
        addi    r7, r12, PT_CKPT_REGS           /* Thread's ckpt_regs */
 
        /* Make r7 look like an exception frame so that we
@@ -203,15 +216,19 @@ dont_backup_fp:
        SAVE_GPR(0, r7)                         /* user r0 */
        SAVE_GPR(2, r7)                 /* user r2 */
        SAVE_4GPRS(3, r7)                       /* user r3-r6 */
-       SAVE_4GPRS(8, r7)                       /* user r8-r11 */
+       SAVE_GPR(8, r7)                         /* user r8 */
+       SAVE_GPR(9, r7)                         /* user r9 */
+       SAVE_GPR(10, r7)                        /* user r10 */
        ld      r3, PACATMSCRATCH(r13)          /* user r1 */
        ld      r4, GPR7(r1)                    /* user r7 */
-       ld      r5, GPR12(r1)                   /* user r12 */
-       GET_SCRATCH0(6)                         /* user r13 */
+       ld      r5, GPR11(r1)                   /* user r11 */
+       ld      r6, GPR12(r1)                   /* user r12 */
+       GET_SCRATCH0(8)                         /* user r13 */
        std     r3, GPR1(r7)
        std     r4, GPR7(r7)
-       std     r5, GPR12(r7)
-       std     r6, GPR13(r7)
+       std     r5, GPR11(r7)
+       std     r6, GPR12(r7)
+       std     r8, GPR13(r7)
 
        SAVE_NVGPRS(r7)                         /* user r14-r31 */
 
@@ -234,14 +251,12 @@ dont_backup_fp:
        std     r6, _XER(r7)
 
 
-       /* ******************** TAR, PPR, DSCR ********** */
+       /* ******************** TAR, DSCR ********** */
        mfspr   r3, SPRN_TAR
-       mfspr   r4, SPRN_PPR
-       mfspr   r5, SPRN_DSCR
+       mfspr   r4, SPRN_DSCR
 
        std     r3, THREAD_TM_TAR(r12)
-       std     r4, THREAD_TM_PPR(r12)
-       std     r5, THREAD_TM_DSCR(r12)
+       std     r4, THREAD_TM_DSCR(r12)
 
        /* MSR and flags:  We don't change CRs, and we don't need to alter
         * MSR.
@@ -258,7 +273,7 @@ dont_backup_fp:
        std     r3, THREAD_TM_TFHAR(r12)
        std     r4, THREAD_TM_TFIAR(r12)
 
-       /* AMR and PPR are checkpointed too, but are unsupported by Linux. */
+       /* AMR is checkpointed too, but is unsupported by Linux. */
 
        /* Restore original MSR/IRQ state & clear TM mode */
        ld      r14, TM_FRAME_L0(r1)            /* Orig MSR */
@@ -274,6 +289,12 @@ dont_backup_fp:
        mtcr    r4
        mtlr    r0
        ld      r2, 40(r1)
+
+       /* Load system default DSCR */
+       ld      r4, DSCR_DEFAULT@toc(r2)
+       ld      r0, 0(r4)
+       mtspr   SPRN_DSCR, r0
+
        blr
 
 
@@ -358,25 +379,24 @@ dont_restore_fp:
 
 restore_gprs:
 
-       /* ******************** TAR, PPR, DSCR ********** */
-       ld      r4, THREAD_TM_TAR(r3)
-       ld      r5, THREAD_TM_PPR(r3)
-       ld      r6, THREAD_TM_DSCR(r3)
+       /* ******************** CR,LR,CCR,MSR ********** */
+       ld      r4, _CTR(r7)
+       ld      r5, _LINK(r7)
+       ld      r6, _CCR(r7)
+       ld      r8, _XER(r7)
 
-       mtspr   SPRN_TAR,       r4
-       mtspr   SPRN_PPR,       r5
-       mtspr   SPRN_DSCR,      r6
+       mtctr   r4
+       mtlr    r5
+       mtcr    r6
+       mtxer   r8
 
-       /* ******************** CR,LR,CCR,MSR ********** */
-       ld      r3, _CTR(r7)
-       ld      r4, _LINK(r7)
-       ld      r5, _CCR(r7)
-       ld      r6, _XER(r7)
+       /* ******************** TAR ******************** */
+       ld      r4, THREAD_TM_TAR(r3)
+       mtspr   SPRN_TAR,       r4
 
-       mtctr   r3
-       mtlr    r4
-       mtcr    r5
-       mtxer   r6
+       /* Load up the PPR and DSCR in GPRs only at this stage */
+       ld      r5, THREAD_TM_DSCR(r3)
+       ld      r6, THREAD_TM_PPR(r3)
 
        /* Clear the MSR RI since we are about to change R1.  EE is already off
         */
@@ -384,19 +404,26 @@ restore_gprs:
        mtmsrd  r4, 1
 
        REST_4GPRS(0, r7)                       /* GPR0-3 */
-       REST_GPR(4, r7)                         /* GPR4-6 */
-       REST_GPR(5, r7)
-       REST_GPR(6, r7)
+       REST_GPR(4, r7)                         /* GPR4 */
        REST_4GPRS(8, r7)                       /* GPR8-11 */
        REST_2GPRS(12, r7)                      /* GPR12-13 */
 
        REST_NVGPRS(r7)                         /* GPR14-31 */
 
-       ld      r7, GPR7(r7)                    /* GPR7 */
+       /* Load up PPR and DSCR here so we don't run with user values for long
+        */
+       mtspr   SPRN_DSCR, r5
+       mtspr   SPRN_PPR, r6
+
+       REST_GPR(5, r7)                         /* GPR5-7 */
+       REST_GPR(6, r7)
+       ld      r7, GPR7(r7)
 
        /* Commit register state as checkpointed state: */
        TRECHKPT
 
+       HMT_MEDIUM
+
        /* Our transactional state has now changed.
         *
         * Now just get out of here.  Transactional (current) state will be
@@ -419,6 +446,12 @@ restore_gprs:
        mtcr    r4
        mtlr    r0
        ld      r2, 40(r1)
+
+       /* Load system default DSCR */
+       ld      r4, DSCR_DEFAULT@toc(r2)
+       ld      r0, 0(r4)
+       mtspr   SPRN_DSCR, r0
+
        blr
 
        /* ****************************************************************** */
index 78a3506..d38cc08 100644 (file)
@@ -1530,11 +1530,15 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
        const char *cp;
 
        dn = dev->of_node;
-       if (!dn)
-               return -ENODEV;
+       if (!dn) {
+               strcat(buf, "\n");
+               return strlen(buf);
+       }
        cp = of_get_property(dn, "compatible", NULL);
-       if (!cp)
-               return -ENODEV;
+       if (!cp) {
+               strcat(buf, "\n");
+               return strlen(buf);
+       }
 
        return sprintf(buf, "vio:T%sS%s\n", vio_dev->type, cp);
 }
index 294b7af..c71103b 100644 (file)
@@ -1066,7 +1066,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 BEGIN_FTR_SECTION
        mfspr   r8, SPRN_DSCR
        ld      r7, HSTATE_DSCR(r13)
-       std     r8, VCPU_DSCR(r7)
+       std     r8, VCPU_DSCR(r9)
        mtspr   SPRN_DSCR, r7
 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
index 1c6a9d7..c65593a 100644 (file)
@@ -332,6 +332,13 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
        unsigned long hva;
        int pfnmap = 0;
        int tsize = BOOK3E_PAGESZ_4K;
+       int ret = 0;
+       unsigned long mmu_seq;
+       struct kvm *kvm = vcpu_e500->vcpu.kvm;
+
+       /* used to check for invalidations in progress */
+       mmu_seq = kvm->mmu_notifier_seq;
+       smp_rmb();
 
        /*
         * Translate guest physical to true physical, acquiring
@@ -449,6 +456,12 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
                gvaddr &= ~((tsize_pages << PAGE_SHIFT) - 1);
        }
 
+       spin_lock(&kvm->mmu_lock);
+       if (mmu_notifier_retry(kvm, mmu_seq)) {
+               ret = -EAGAIN;
+               goto out;
+       }
+
        kvmppc_e500_ref_setup(ref, gtlbe, pfn);
 
        kvmppc_e500_setup_stlbe(&vcpu_e500->vcpu, gtlbe, tsize,
@@ -457,10 +470,13 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
        /* Clear i-cache for new pages */
        kvmppc_mmu_flush_icache(pfn);
 
+out:
+       spin_unlock(&kvm->mmu_lock);
+
        /* Drop refcount on page, so that mmu notifiers can clear it */
        kvm_release_pfn_clean(pfn);
 
-       return 0;
+       return ret;
 }
 
 /* XXX only map the one-one case, for now use TLB0 */
index 167f725..57a0720 100644 (file)
@@ -226,19 +226,35 @@ _GLOBAL(csum_partial)
        blr
 
 
-       .macro source
+       .macro srcnr
 100:
        .section __ex_table,"a"
        .align 3
-       .llong 100b,.Lsrc_error
+       .llong 100b,.Lsrc_error_nr
        .previous
        .endm
 
-       .macro dest
+       .macro source
+150:
+       .section __ex_table,"a"
+       .align 3
+       .llong 150b,.Lsrc_error
+       .previous
+       .endm
+
+       .macro dstnr
 200:
        .section __ex_table,"a"
        .align 3
-       .llong 200b,.Ldest_error
+       .llong 200b,.Ldest_error_nr
+       .previous
+       .endm
+
+       .macro dest
+250:
+       .section __ex_table,"a"
+       .align 3
+       .llong 250b,.Ldest_error
        .previous
        .endm
 
@@ -269,16 +285,16 @@ _GLOBAL(csum_partial_copy_generic)
        rldicl. r6,r3,64-1,64-2         /* r6 = (r3 & 0x3) >> 1 */
        beq     .Lcopy_aligned
 
-       li      r7,4
-       sub     r6,r7,r6
+       li      r9,4
+       sub     r6,r9,r6
        mtctr   r6
 
 1:
-source;        lhz     r6,0(r3)                /* align to doubleword */
+srcnr; lhz     r6,0(r3)                /* align to doubleword */
        subi    r5,r5,2
        addi    r3,r3,2
        adde    r0,r0,r6
-dest;  sth     r6,0(r4)
+dstnr; sth     r6,0(r4)
        addi    r4,r4,2
        bdnz    1b
 
@@ -392,10 +408,10 @@ dest;     std     r16,56(r4)
 
        mtctr   r6
 3:
-source;        ld      r6,0(r3)
+srcnr; ld      r6,0(r3)
        addi    r3,r3,8
        adde    r0,r0,r6
-dest;  std     r6,0(r4)
+dstnr; std     r6,0(r4)
        addi    r4,r4,8
        bdnz    3b
 
@@ -405,10 +421,10 @@ dest;     std     r6,0(r4)
        srdi.   r6,r5,2
        beq     .Lcopy_tail_halfword
 
-source;        lwz     r6,0(r3)
+srcnr; lwz     r6,0(r3)
        addi    r3,r3,4
        adde    r0,r0,r6
-dest;  stw     r6,0(r4)
+dstnr; stw     r6,0(r4)
        addi    r4,r4,4
        subi    r5,r5,4
 
@@ -416,10 +432,10 @@ dest;     stw     r6,0(r4)
        srdi.   r6,r5,1
        beq     .Lcopy_tail_byte
 
-source;        lhz     r6,0(r3)
+srcnr; lhz     r6,0(r3)
        addi    r3,r3,2
        adde    r0,r0,r6
-dest;  sth     r6,0(r4)
+dstnr; sth     r6,0(r4)
        addi    r4,r4,2
        subi    r5,r5,2
 
@@ -427,10 +443,10 @@ dest;     sth     r6,0(r4)
        andi.   r6,r5,1
        beq     .Lcopy_finish
 
-source;        lbz     r6,0(r3)
+srcnr; lbz     r6,0(r3)
        sldi    r9,r6,8                 /* Pad the byte out to 16 bits */
        adde    r0,r0,r9
-dest;  stb     r6,0(r4)
+dstnr; stb     r6,0(r4)
 
 .Lcopy_finish:
        addze   r0,r0                   /* add in final carry */
@@ -440,6 +456,11 @@ dest;      stb     r6,0(r4)
        blr
 
 .Lsrc_error:
+       ld      r14,STK_REG(R14)(r1)
+       ld      r15,STK_REG(R15)(r1)
+       ld      r16,STK_REG(R16)(r1)
+       addi    r1,r1,STACKFRAMESIZE
+.Lsrc_error_nr:
        cmpdi   0,r7,0
        beqlr
        li      r6,-EFAULT
@@ -447,6 +468,11 @@ dest;      stb     r6,0(r4)
        blr
 
 .Ldest_error:
+       ld      r14,STK_REG(R14)(r1)
+       ld      r15,STK_REG(R15)(r1)
+       ld      r16,STK_REG(R16)(r1)
+       addi    r1,r1,STACKFRAMESIZE
+.Ldest_error_nr:
        cmpdi   0,r8,0
        beqlr
        li      r6,-EFAULT
index a7ee978..b1faa15 100644 (file)
@@ -1505,6 +1505,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                 */
                if ((ra == 1) && !(regs->msr & MSR_PR) \
                        && (val3 >= (regs->gpr[1] - STACK_INT_FRAME_SIZE))) {
+#ifdef CONFIG_PPC32
                        /*
                         * Check if we will touch kernel sack overflow
                         */
@@ -1513,7 +1514,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                                err = -EINVAL;
                                break;
                        }
-
+#endif /* CONFIG_PPC32 */
                        /*
                         * Check if we already set since that means we'll
                         * lose the previous value.
index d0cd9e4..8ed035d 100644 (file)
@@ -300,5 +300,9 @@ void vmemmap_free(unsigned long start, unsigned long end)
 {
 }
 
+void register_page_bootmem_memmap(unsigned long section_nr,
+                                 struct page *start_page, unsigned long size)
+{
+}
 #endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
index 1cf9c5b..3fa93dc 100644 (file)
@@ -297,12 +297,21 @@ void __init paging_init(void)
 }
 #endif /* ! CONFIG_NEED_MULTIPLE_NODES */
 
+static void __init register_page_bootmem_info(void)
+{
+       int i;
+
+       for_each_online_node(i)
+               register_page_bootmem_info_node(NODE_DATA(i));
+}
+
 void __init mem_init(void)
 {
 #ifdef CONFIG_SWIOTLB
        swiotlb_init(0);
 #endif
 
+       register_page_bootmem_info();
        high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
        set_max_mapnr(max_pfn);
        free_all_bootmem();
index bf56e33..2345bdb 100644 (file)
@@ -691,4 +691,5 @@ void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter)
                module_free(NULL, fp->bpf_func);
+       kfree(fp);
 }
index 2ee4a70..a3f7abd 100644 (file)
 #define MMCR1_UNIT_SHIFT(pmc)          (60 - (4 * ((pmc) - 1)))
 #define MMCR1_COMBINE_SHIFT(pmc)       (35 - ((pmc) - 1))
 #define MMCR1_PMCSEL_SHIFT(pmc)                (24 - (((pmc) - 1)) * 8)
+#define MMCR1_FAB_SHIFT                        36
 #define MMCR1_DC_QUAL_SHIFT            47
 #define MMCR1_IC_QUAL_SHIFT            46
 
@@ -388,8 +389,8 @@ static int power8_compute_mmcr(u64 event[], int n_ev,
                 * the threshold bits are used for the match value.
                 */
                if (event_is_fab_match(event[i])) {
-                       mmcr1 |= (event[i] >> EVENT_THR_CTL_SHIFT) &
-                                 EVENT_THR_CTL_MASK;
+                       mmcr1 |= ((event[i] >> EVENT_THR_CTL_SHIFT) &
+                                 EVENT_THR_CTL_MASK) << MMCR1_FAB_SHIFT;
                } else {
                        val = (event[i] >> EVENT_THR_CTL_SHIFT) & EVENT_THR_CTL_MASK;
                        mmcra |= val << MMCRA_THR_CTL_SHIFT;
index 1c1771a..24f58cb 100644 (file)
@@ -233,18 +233,24 @@ static void __init smp_init_pseries(void)
 
        alloc_bootmem_cpumask_var(&of_spin_mask);
 
-       /* Mark threads which are still spinning in hold loops. */
-       if (cpu_has_feature(CPU_FTR_SMT)) {
-               for_each_present_cpu(i) { 
-                       if (cpu_thread_in_core(i) == 0)
-                               cpumask_set_cpu(i, of_spin_mask);
-               }
-       } else {
-               cpumask_copy(of_spin_mask, cpu_present_mask);
+       /*
+        * Mark threads which are still spinning in hold loops
+        *
+        * We know prom_init will not have started them if RTAS supports
+        * query-cpu-stopped-state.
+        */
+       if (rtas_token("query-cpu-stopped-state") == RTAS_UNKNOWN_SERVICE) {
+               if (cpu_has_feature(CPU_FTR_SMT)) {
+                       for_each_present_cpu(i) {
+                               if (cpu_thread_in_core(i) == 0)
+                                       cpumask_set_cpu(i, of_spin_mask);
+                       }
+               } else
+                       cpumask_copy(of_spin_mask, cpu_present_mask);
+
+               cpumask_clear_cpu(boot_cpuid, of_spin_mask);
        }
 
-       cpumask_clear_cpu(boot_cpuid, of_spin_mask);
-
        /* Non-lpar has additional take/give timebase */
        if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
                smp_ops->give_timebase = rtas_give_timebase;
index dcc6ac2..7143793 100644 (file)
@@ -93,6 +93,7 @@ config S390
        select ARCH_INLINE_WRITE_UNLOCK_IRQ
        select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
        select ARCH_SAVE_PAGE_KEYS if HIBERNATION
+       select ARCH_USE_CMPXCHG_LOCKREF
        select ARCH_WANT_IPC_PARSE_VERSION
        select BUILDTIME_EXTABLE_SORT
        select CLONE_BACKWARDS2
@@ -102,7 +103,6 @@ config S390
        select GENERIC_TIME_VSYSCALL_OLD
        select HAVE_ALIGNED_STRUCT_PAGE if SLUB
        select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
-       select HAVE_ARCH_MUTEX_CPU_RELAX
        select HAVE_ARCH_SECCOMP_FILTER
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT
index 6c32190..346b1c8 100644 (file)
@@ -15,7 +15,7 @@
 
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-       asm goto("0:    brcl 0,0\n"
+       asm_volatile_goto("0:   brcl 0,0\n"
                ".pushsection __jump_table, \"aw\"\n"
                ASM_ALIGN "\n"
                ASM_PTR " 0b, %l[label], %0\n"
index 688271f..458c1f7 100644 (file)
@@ -7,5 +7,3 @@
  */
 
 #include <asm-generic/mutex-dec.h>
-
-#define arch_mutex_cpu_relax() barrier()
index 9b60a36..2204400 100644 (file)
@@ -748,7 +748,9 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
 
 static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
 {
-       if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_WRITE)) {
+       if (!MACHINE_HAS_ESOP &&
+           (pte_val(entry) & _PAGE_PRESENT) &&
+           (pte_val(entry) & _PAGE_WRITE)) {
                /*
                 * Without enhanced suppression-on-protection force
                 * the dirty bit on for all writable ptes.
index 0eb3750..ca7821f 100644 (file)
@@ -198,6 +198,8 @@ static inline void cpu_relax(void)
        barrier();
 }
 
+#define arch_mutex_cpu_relax()  barrier()
+
 static inline void psw_set_key(unsigned int key)
 {
        asm volatile("spka 0(%0)" : : "d" (key));
index 701fe8c..83e5d21 100644 (file)
@@ -44,6 +44,11 @@ extern void arch_spin_lock_wait_flags(arch_spinlock_t *, unsigned long flags);
 extern int arch_spin_trylock_retry(arch_spinlock_t *);
 extern void arch_spin_relax(arch_spinlock_t *lock);
 
+static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
+{
+       return lock.owner_cpu == 0;
+}
+
 static inline void arch_spin_lock(arch_spinlock_t *lp)
 {
        int old;
index 8ad8af9..819b94d 100644 (file)
@@ -71,30 +71,30 @@ static inline void local_tick_enable(unsigned long long comp)
 
 typedef unsigned long long cycles_t;
 
-static inline unsigned long long get_tod_clock(void)
-{
-       unsigned long long clk;
-
-#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
-       asm volatile(".insn s,0xb27c0000,%0" : "=Q" (clk) : : "cc");
-#else
-       asm volatile("stck %0" : "=Q" (clk) : : "cc");
-#endif
-       return clk;
-}
-
 static inline void get_tod_clock_ext(char *clk)
 {
        asm volatile("stcke %0" : "=Q" (*clk) : : "cc");
 }
 
-static inline unsigned long long get_tod_clock_xt(void)
+static inline unsigned long long get_tod_clock(void)
 {
        unsigned char clk[16];
        get_tod_clock_ext(clk);
        return *((unsigned long long *)&clk[1]);
 }
 
+static inline unsigned long long get_tod_clock_fast(void)
+{
+#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
+       unsigned long long clk;
+
+       asm volatile("stckf %0" : "=Q" (clk) : : "cc");
+       return clk;
+#else
+       return get_tod_clock();
+#endif
+}
+
 static inline cycles_t get_cycles(void)
 {
        return (cycles_t) get_tod_clock() >> 2;
@@ -125,7 +125,7 @@ extern u64 sched_clock_base_cc;
  */
 static inline unsigned long long get_tod_clock_monotonic(void)
 {
-       return get_tod_clock_xt() - sched_clock_base_cc;
+       return get_tod_clock() - sched_clock_base_cc;
 }
 
 /**
index 9249449..c286c2e 100644 (file)
@@ -82,4 +82,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _ASM_SOCKET_H */
index 1389b63..adaa9e9 100644 (file)
@@ -99,7 +99,7 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
                        break;
                }
        }
-       return err;
+       return err ? -EFAULT : 0;
 }
 
 int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
@@ -148,7 +148,7 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
                        break;
                }
        }
-       return err;
+       return err ? -EFAULT : 0;
 }
 
 static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
index c84f33d..7dd2172 100644 (file)
@@ -40,28 +40,26 @@ static inline void *load_real_addr(void *addr)
 }
 
 /*
- * Copy up to one page to vmalloc or real memory
+ * Copy real to virtual or real memory
  */
-static ssize_t copy_page_real(void *buf, void *src, size_t csize)
+static int copy_from_realmem(void *dest, void *src, size_t count)
 {
-       size_t size;
+       unsigned long size;
+       int rc;
 
-       if (is_vmalloc_addr(buf)) {
-               BUG_ON(csize >= PAGE_SIZE);
-               /* If buf is not page aligned, copy first part */
-               size = min(roundup(__pa(buf), PAGE_SIZE) - __pa(buf), csize);
-               if (size) {
-                       if (memcpy_real(load_real_addr(buf), src, size))
-                               return -EFAULT;
-                       buf += size;
-                       src += size;
-               }
-               /* Copy second part */
-               size = csize - size;
-               return (size) ? memcpy_real(load_real_addr(buf), src, size) : 0;
-       } else {
-               return memcpy_real(buf, src, csize);
-       }
+       if (!count)
+               return 0;
+       if (!is_vmalloc_or_module_addr(dest))
+               return memcpy_real(dest, src, count);
+       do {
+               size = min(count, PAGE_SIZE - (__pa(dest) & ~PAGE_MASK));
+               if (memcpy_real(load_real_addr(dest), src, size))
+                       return -EFAULT;
+               count -= size;
+               dest += size;
+               src += size;
+       } while (count);
+       return 0;
 }
 
 /*
@@ -114,7 +112,7 @@ static ssize_t copy_oldmem_page_kdump(char *buf, size_t csize,
                rc = copy_to_user_real((void __force __user *) buf,
                                       (void *) src, csize);
        else
-               rc = copy_page_real(buf, (void *) src, csize);
+               rc = copy_from_realmem(buf, (void *) src, csize);
        return (rc == 0) ? rc : csize;
 }
 
@@ -210,7 +208,7 @@ int copy_from_oldmem(void *dest, void *src, size_t count)
        if (OLDMEM_BASE) {
                if ((unsigned long) src < OLDMEM_SIZE) {
                        copied = min(count, OLDMEM_SIZE - (unsigned long) src);
-                       rc = memcpy_real(dest, src + OLDMEM_BASE, copied);
+                       rc = copy_from_realmem(dest, src + OLDMEM_BASE, copied);
                        if (rc)
                                return rc;
                }
@@ -223,7 +221,7 @@ int copy_from_oldmem(void *dest, void *src, size_t count)
                                return rc;
                }
        }
-       return memcpy_real(dest + copied, src + copied, count - copied);
+       return copy_from_realmem(dest + copied, src + copied, count - copied);
 }
 
 /*
index f1279dc..17d62fe 100644 (file)
@@ -867,7 +867,7 @@ static inline void
 debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level,
                        int exception)
 {
-       active->id.stck = get_tod_clock();
+       active->id.stck = get_tod_clock_fast();
        active->id.fields.cpuid = smp_processor_id();
        active->caller = __builtin_return_address(0);
        active->id.fields.exception = exception;
index cc30d1f..0dc2b6d 100644 (file)
@@ -266,6 +266,7 @@ sysc_sigpending:
        tm      __TI_flags+3(%r12),_TIF_SYSCALL
        jno     sysc_return
        lm      %r2,%r7,__PT_R2(%r11)   # load svc arguments
+       l       %r10,__TI_sysc_table(%r12)      # 31 bit system call table
        xr      %r8,%r8                 # svc 0 returns -ENOSYS
        clc     __PT_INT_CODE+2(2,%r11),BASED(.Lnr_syscalls+2)
        jnl     sysc_nr_ok              # invalid svc number -> do svc 0
index 2b2188b..e5b43c9 100644 (file)
@@ -297,6 +297,7 @@ sysc_sigpending:
        tm      __TI_flags+7(%r12),_TIF_SYSCALL
        jno     sysc_return
        lmg     %r2,%r7,__PT_R2(%r11)   # load svc arguments
+       lg      %r10,__TI_sysc_table(%r12)      # address of system call table
        lghi    %r8,0                   # svc 0 returns -ENOSYS
        llgh    %r1,__PT_INT_CODE+2(%r11)       # load new svc number
        cghi    %r1,NR_syscalls
index 0ce9fb2..d86e64e 100644 (file)
@@ -67,6 +67,11 @@ static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
        case 0xac:      /* stnsm */
        case 0xad:      /* stosm */
                return -EINVAL;
+       case 0xc6:
+               switch (insn[0] & 0x0f) {
+               case 0x00: /* exrl   */
+                       return -EINVAL;
+               }
        }
        switch (insn[0]) {
        case 0x0101:    /* pr    */
@@ -180,7 +185,6 @@ static int __kprobes is_insn_relative_long(kprobe_opcode_t *insn)
                break;
        case 0xc6:
                switch (insn[0] & 0x0f) {
-               case 0x00: /* exrl   */
                case 0x02: /* pfdrl  */
                case 0x04: /* cghrl  */
                case 0x05: /* chrl   */
index 7f35cb3..7f1f7ac 100644 (file)
@@ -385,7 +385,7 @@ static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
        }
 
        if ((!rc) && (vcpu->arch.sie_block->ckc <
-               get_tod_clock() + vcpu->arch.sie_block->epoch)) {
+               get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) {
                if ((!psw_extint_disabled(vcpu)) &&
                        (vcpu->arch.sie_block->gcr[0] & 0x800ul))
                        rc = 1;
@@ -425,7 +425,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
                goto no_timer;
        }
 
-       now = get_tod_clock() + vcpu->arch.sie_block->epoch;
+       now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch;
        if (vcpu->arch.sie_block->ckc < now) {
                __unset_cpu_idle(vcpu);
                return 0;
@@ -515,7 +515,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
        }
 
        if ((vcpu->arch.sie_block->ckc <
-               get_tod_clock() + vcpu->arch.sie_block->epoch))
+               get_tod_clock_fast() + vcpu->arch.sie_block->epoch))
                __try_deliver_ckc_interrupt(vcpu);
 
        if (atomic_read(&fi->active)) {
index 57c87d7..a9f3d00 100644 (file)
@@ -44,7 +44,7 @@ static void __udelay_disabled(unsigned long long usecs)
        do {
                set_clock_comparator(end);
                vtime_stop_cpu();
-       } while (get_tod_clock() < end);
+       } while (get_tod_clock_fast() < end);
        lockdep_on();
        __ctl_load(cr0, 0, 0);
        __ctl_load(cr6, 6, 6);
@@ -55,7 +55,7 @@ static void __udelay_enabled(unsigned long long usecs)
 {
        u64 clock_saved, end;
 
-       end = get_tod_clock() + (usecs << 12);
+       end = get_tod_clock_fast() + (usecs << 12);
        do {
                clock_saved = 0;
                if (end < S390_lowcore.clock_comparator) {
@@ -65,7 +65,7 @@ static void __udelay_enabled(unsigned long long usecs)
                vtime_stop_cpu();
                if (clock_saved)
                        local_tick_enable(clock_saved);
-       } while (get_tod_clock() < end);
+       } while (get_tod_clock_fast() < end);
 }
 
 /*
@@ -109,8 +109,8 @@ void udelay_simple(unsigned long long usecs)
 {
        u64 end;
 
-       end = get_tod_clock() + (usecs << 12);
-       while (get_tod_clock() < end)
+       end = get_tod_clock_fast() + (usecs << 12);
+       while (get_tod_clock_fast() < end)
                cpu_relax();
 }
 
@@ -120,10 +120,10 @@ void __ndelay(unsigned long long nsecs)
 
        nsecs <<= 9;
        do_div(nsecs, 125);
-       end = get_tod_clock() + nsecs;
+       end = get_tod_clock_fast() + nsecs;
        if (nsecs & ~0xfffUL)
                __udelay(nsecs >> 12);
-       while (get_tod_clock() < end)
+       while (get_tod_clock_fast() < end)
                barrier();
 }
 EXPORT_SYMBOL(__ndelay);
index 7092392..a5df511 100644 (file)
@@ -881,7 +881,9 @@ void bpf_jit_free(struct sk_filter *fp)
        struct bpf_binary_header *header = (void *)addr;
 
        if (fp->bpf_func == sk_run_filter)
-               return;
+               goto free_filter;
        set_memory_rw(addr, header->pages);
        module_free(NULL, header);
+free_filter:
+       kfree(fp);
 }
index a1be70d..305f7ee 100644 (file)
@@ -2,6 +2,7 @@ menu "Machine selection"
 
 config SCORE
        def_bool y
+       select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_SHOW
        select GENERIC_IOMAP
        select GENERIC_ATOMIC64
@@ -110,3 +111,6 @@ source "security/Kconfig"
 source "crypto/Kconfig"
 
 source "lib/Kconfig"
+
+config NO_IOMEM
+       def_bool y
index 974aefe..9e3e060 100644 (file)
@@ -20,8 +20,8 @@ cflags-y += -G0 -pipe -mel -mnhwloop -D__SCOREEL__ \
 #
 KBUILD_AFLAGS += $(cflags-y)
 KBUILD_CFLAGS += $(cflags-y)
-KBUILD_AFLAGS_MODULE += -mlong-calls
-KBUILD_CFLAGS_MODULE += -mlong-calls
+KBUILD_AFLAGS_MODULE +=
+KBUILD_CFLAGS_MODULE +=
 LDFLAGS += --oformat elf32-littlescore
 LDFLAGS_vmlinux        += -G0 -static -nostdlib
 
index f909ac3..961bd64 100644 (file)
@@ -184,48 +184,57 @@ static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
                                __wsum sum)
 {
        __asm__ __volatile__(
-               ".set\tnoreorder\t\t\t# csum_ipv6_magic\n\t"
-               ".set\tnoat\n\t"
-               "addu\t%0, %5\t\t\t# proto (long in network byte order)\n\t"
-               "sltu\t$1, %0, %5\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %6\t\t\t# csum\n\t"
-               "sltu\t$1, %0, %6\n\t"
-               "lw\t%1, 0(%2)\t\t\t# four words source address\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 4(%2)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 8(%2)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 12(%2)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 0(%3)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 4(%3)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 8(%3)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "lw\t%1, 12(%3)\n\t"
-               "addu\t%0, $1\n\t"
-               "addu\t%0, %1\n\t"
-               "sltu\t$1, %0, %1\n\t"
-               "addu\t%0, $1\t\t\t# Add final carry\n\t"
-               ".set\tnoat\n\t"
-               ".set\tnoreorder"
+               ".set\tvolatile\t\t\t# csum_ipv6_magic\n\t"
+               "add\t%0, %0, %5\t\t\t# proto (long in network byte order)\n\t"
+               "cmp.c\t%5, %0\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %6\t\t\t# csum\n\t"
+               "cmp.c\t%6, %0\n\t"
+               "lw\t%1, [%2, 0]\t\t\t# four words source address\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "1:lw\t%1, [%2, 4]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "lw\t%1, [%2,8]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "lw\t%1, [%2, 12]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0,%1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "lw\t%1, [%3, 0]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "lw\t%1, [%3, 4]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "lw\t%1, [%3, 8]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "lw\t%1, [%3, 12]\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:add\t%0, %0, %1\n\t"
+               "cmp.c\t%1, %0\n\t"
+               "bleu 1f\n\t"
+               "addi\t%0, 0x1\n\t"
+               "1:\n\t"
+               ".set\toptimize"
                : "=r" (sum), "=r" (proto)
                : "r" (saddr), "r" (daddr),
                  "0" (htonl(len)), "1" (htonl(proto)), "r" (sum));
index fbbfd71..574c882 100644 (file)
@@ -5,5 +5,4 @@
 
 #define virt_to_bus    virt_to_phys
 #define bus_to_virt    phys_to_virt
-
 #endif /* _ASM_SCORE_IO_H */
index 059a61b..716b3fd 100644 (file)
@@ -2,7 +2,7 @@
 #define _ASM_SCORE_PGALLOC_H
 
 #include <linux/mm.h>
-
+#include <linux/highmem.h>
 static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd,
        pte_t *pte)
 {
index 7234ed0..befb87d 100644 (file)
@@ -264,7 +264,7 @@ resume_kernel:
        disable_irq
        lw      r8, [r28, TI_PRE_COUNT]
        cmpz.c  r8
-       bne     r8, restore_all
+       bne     restore_all
 need_resched:
        lw      r8, [r28, TI_FLAGS]
        andri.c r9, r8, _TIF_NEED_RESCHED
@@ -415,7 +415,7 @@ ENTRY(handle_sys)
        sw      r9, [r0, PT_EPC]
 
        cmpi.c  r27, __NR_syscalls      # check syscall number
-       bgeu    illegal_syscall
+       bcs     illegal_syscall
 
        slli    r8, r27, 2              # get syscall routine
        la      r11, sys_call_table
index f4c6d02..a1519ad 100644 (file)
@@ -78,8 +78,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
        p->thread.reg0 = (unsigned long) childregs;
        if (unlikely(p->flags & PF_KTHREAD)) {
                memset(childregs, 0, sizeof(struct pt_regs));
-               p->thread->reg12 = usp;
-               p->thread->reg13 = arg;
+               p->thread.reg12 = usp;
+               p->thread.reg13 = arg;
                p->thread.reg3 = (unsigned long) ret_from_kernel_thread;
        } else {
                *childregs = *current_pt_regs();
index 2137ad6..78c4fdb 100644 (file)
@@ -506,12 +506,17 @@ config SUN_OPENPROMFS
          Only choose N if you know in advance that you will not need to modify
          OpenPROM settings on the running system.
 
-# Makefile helper
+# Makefile helpers
 config SPARC64_PCI
        bool
        default y
        depends on SPARC64 && PCI
 
+config SPARC64_PCI_MSI
+       bool
+       default y
+       depends on SPARC64_PCI && PCI_MSI
+
 endmenu
 
 menu "Executable file formats"
index e204f90..7c90c50 100644 (file)
@@ -254,7 +254,7 @@ static int sun_fd_request_irq(void)
                once = 1;
 
                error = request_irq(FLOPPY_IRQ, sparc_floppy_irq,
-                                   IRQF_DISABLED, "floppy", NULL);
+                                   0, "floppy", NULL);
 
                return ((error == 0) ? 0 : -1);
        }
index 5080d16..ec2e2e2 100644 (file)
@@ -9,7 +9,7 @@
 
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-               asm goto("1:\n\t"
+               asm_volatile_goto("1:\n\t"
                         "nop\n\t"
                         "nop\n\t"
                         ".pushsection __jump_table,  \"aw\"\n\t"
index 4e1d66c..0f21e9a 100644 (file)
@@ -72,6 +72,8 @@
 
 #define SO_BUSY_POLL           0x0030
 
+#define SO_MAX_PACING_RATE     0x0031
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index d432fb2..d15cc17 100644 (file)
@@ -1,3 +1,4 @@
+
 #
 # Makefile for the linux kernel.
 #
@@ -99,7 +100,7 @@ obj-$(CONFIG_STACKTRACE)     += stacktrace.o
 obj-$(CONFIG_SPARC64_PCI)    += pci.o pci_common.o psycho_common.o
 obj-$(CONFIG_SPARC64_PCI)    += pci_psycho.o pci_sabre.o pci_schizo.o
 obj-$(CONFIG_SPARC64_PCI)    += pci_sun4v.o pci_sun4v_asm.o pci_fire.o
-obj-$(CONFIG_PCI_MSI)        += pci_msi.o
+obj-$(CONFIG_SPARC64_PCI_MSI) += pci_msi.o
 
 obj-$(CONFIG_COMPAT)         += sys32.o sys_sparc32.o signal32.o
 
index 62d6b15..dff60ab 100644 (file)
@@ -849,9 +849,8 @@ void ldom_reboot(const char *boot_command)
        if (boot_command && strlen(boot_command)) {
                unsigned long len;
 
-               strcpy(full_boot_str, "boot ");
-               strlcpy(full_boot_str + strlen("boot "), boot_command,
-                       sizeof(full_boot_str + strlen("boot ")));
+               snprintf(full_boot_str, sizeof(full_boot_str), "boot %s",
+                        boot_command);
                len = strlen(full_boot_str);
 
                if (reboot_data_supported) {
index 54df554..e01d75d 100644 (file)
@@ -1249,12 +1249,12 @@ int ldc_bind(struct ldc_channel *lp, const char *name)
        snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name);
        snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name);
 
-       err = request_irq(lp->cfg.rx_irq, ldc_rx, IRQF_DISABLED,
+       err = request_irq(lp->cfg.rx_irq, ldc_rx, 0,
                          lp->rx_irq_name, lp);
        if (err)
                return err;
 
-       err = request_irq(lp->cfg.tx_irq, ldc_tx, IRQF_DISABLED,
+       err = request_irq(lp->cfg.tx_irq, ldc_tx, 0,
                          lp->tx_irq_name, lp);
        if (err) {
                free_irq(lp->cfg.rx_irq, lp);
index 9c7be59..218b6b2 100644 (file)
@@ -808,4 +808,5 @@ void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter)
                module_free(NULL, fp->bpf_func);
+       kfree(fp);
 }
index d385eaa..7097984 100644 (file)
@@ -166,7 +166,7 @@ static inline int atomic_cmpxchg(atomic_t *v, int o, int n)
  *
  * Atomically sets @v to @i and returns old @v
  */
-static inline u64 atomic64_xchg(atomic64_t *v, u64 n)
+static inline long long atomic64_xchg(atomic64_t *v, long long n)
 {
        return xchg64(&v->counter, n);
 }
@@ -180,7 +180,8 @@ static inline u64 atomic64_xchg(atomic64_t *v, u64 n)
  * Atomically checks if @v holds @o and replaces it with @n if so.
  * Returns the old value at @v.
  */
-static inline u64 atomic64_cmpxchg(atomic64_t *v, u64 o, u64 n)
+static inline long long atomic64_cmpxchg(atomic64_t *v, long long o,
+                                       long long n)
 {
        return cmpxchg64(&v->counter, o, n);
 }
index 0d0395b..1ad4a1f 100644 (file)
@@ -80,7 +80,7 @@ static inline void atomic_set(atomic_t *v, int n)
 /* A 64bit atomic type */
 
 typedef struct {
-       u64 __aligned(8) counter;
+       long long counter;
 } atomic64_t;
 
 #define ATOMIC64_INIT(val) { (val) }
@@ -91,14 +91,14 @@ typedef struct {
  *
  * Atomically reads the value of @v.
  */
-static inline u64 atomic64_read(const atomic64_t *v)
+static inline long long atomic64_read(const atomic64_t *v)
 {
        /*
         * Requires an atomic op to read both 32-bit parts consistently.
         * Casting away const is safe since the atomic support routines
         * do not write to memory if the value has not been modified.
         */
-       return _atomic64_xchg_add((u64 *)&v->counter, 0);
+       return _atomic64_xchg_add((long long *)&v->counter, 0);
 }
 
 /**
@@ -108,7 +108,7 @@ static inline u64 atomic64_read(const atomic64_t *v)
  *
  * Atomically adds @i to @v.
  */
-static inline void atomic64_add(u64 i, atomic64_t *v)
+static inline void atomic64_add(long long i, atomic64_t *v)
 {
        _atomic64_xchg_add(&v->counter, i);
 }
@@ -120,7 +120,7 @@ static inline void atomic64_add(u64 i, atomic64_t *v)
  *
  * Atomically adds @i to @v and returns @i + @v
  */
-static inline u64 atomic64_add_return(u64 i, atomic64_t *v)
+static inline long long atomic64_add_return(long long i, atomic64_t *v)
 {
        smp_mb();  /* barrier for proper semantics */
        return _atomic64_xchg_add(&v->counter, i) + i;
@@ -135,7 +135,8 @@ static inline u64 atomic64_add_return(u64 i, atomic64_t *v)
  * Atomically adds @a to @v, so long as @v was not already @u.
  * Returns non-zero if @v was not @u, and zero otherwise.
  */
-static inline u64 atomic64_add_unless(atomic64_t *v, u64 a, u64 u)
+static inline long long atomic64_add_unless(atomic64_t *v, long long a,
+                                       long long u)
 {
        smp_mb();  /* barrier for proper semantics */
        return _atomic64_xchg_add_unless(&v->counter, a, u) != u;
@@ -151,7 +152,7 @@ static inline u64 atomic64_add_unless(atomic64_t *v, u64 a, u64 u)
  * atomic64_set() can't be just a raw store, since it would be lost if it
  * fell between the load and store of one of the other atomic ops.
  */
-static inline void atomic64_set(atomic64_t *v, u64 n)
+static inline void atomic64_set(atomic64_t *v, long long n)
 {
        _atomic64_xchg(&v->counter, n);
 }
@@ -236,11 +237,13 @@ extern struct __get_user __atomic_xchg_add_unless(volatile int *p,
 extern struct __get_user __atomic_or(volatile int *p, int *lock, int n);
 extern struct __get_user __atomic_andn(volatile int *p, int *lock, int n);
 extern struct __get_user __atomic_xor(volatile int *p, int *lock, int n);
-extern u64 __atomic64_cmpxchg(volatile u64 *p, int *lock, u64 o, u64 n);
-extern u64 __atomic64_xchg(volatile u64 *p, int *lock, u64 n);
-extern u64 __atomic64_xchg_add(volatile u64 *p, int *lock, u64 n);
-extern u64 __atomic64_xchg_add_unless(volatile u64 *p,
-                                     int *lock, u64 o, u64 n);
+extern long long __atomic64_cmpxchg(volatile long long *p, int *lock,
+                                       long long o, long long n);
+extern long long __atomic64_xchg(volatile long long *p, int *lock, long long n);
+extern long long __atomic64_xchg_add(volatile long long *p, int *lock,
+                                       long long n);
+extern long long __atomic64_xchg_add_unless(volatile long long *p,
+                                       int *lock, long long o, long long n);
 
 /* Return failure from the atomic wrappers. */
 struct __get_user __atomic_bad_address(int __user *addr);
index 4001d5e..0ccda3c 100644 (file)
@@ -35,10 +35,10 @@ int _atomic_xchg(int *ptr, int n);
 int _atomic_xchg_add(int *v, int i);
 int _atomic_xchg_add_unless(int *v, int a, int u);
 int _atomic_cmpxchg(int *ptr, int o, int n);
-u64 _atomic64_xchg(u64 *v, u64 n);
-u64 _atomic64_xchg_add(u64 *v, u64 i);
-u64 _atomic64_xchg_add_unless(u64 *v, u64 a, u64 u);
-u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n);
+long long _atomic64_xchg(long long *v, long long n);
+long long _atomic64_xchg_add(long long *v, long long i);
+long long _atomic64_xchg_add_unless(long long *v, long long a, long long u);
+long long _atomic64_cmpxchg(long long *v, long long o, long long n);
 
 #define xchg(ptr, n)                                                   \
        ({                                                              \
@@ -53,7 +53,8 @@ u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n);
                if (sizeof(*(ptr)) != 4)                                \
                        __cmpxchg_called_with_bad_pointer();            \
                smp_mb();                                               \
-               (typeof(*(ptr)))_atomic_cmpxchg((int *)ptr, (int)o, (int)n); \
+               (typeof(*(ptr)))_atomic_cmpxchg((int *)ptr, (int)o,     \
+                                               (int)n);                \
        })
 
 #define xchg64(ptr, n)                                                 \
@@ -61,7 +62,8 @@ u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n);
                if (sizeof(*(ptr)) != 8)                                \
                        __xchg_called_with_bad_pointer();               \
                smp_mb();                                               \
-               (typeof(*(ptr)))_atomic64_xchg((u64 *)(ptr), (u64)(n)); \
+               (typeof(*(ptr)))_atomic64_xchg((long long *)(ptr),      \
+                                               (long long)(n));        \
        })
 
 #define cmpxchg64(ptr, o, n)                                           \
@@ -69,7 +71,8 @@ u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n);
                if (sizeof(*(ptr)) != 8)                                \
                        __cmpxchg_called_with_bad_pointer();            \
                smp_mb();                                               \
-               (typeof(*(ptr)))_atomic64_cmpxchg((u64 *)ptr, (u64)o, (u64)n); \
+               (typeof(*(ptr)))_atomic64_cmpxchg((long long *)ptr,     \
+                                       (long long)o, (long long)n);    \
        })
 
 #else
@@ -81,10 +84,11 @@ u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n);
                switch (sizeof(*(ptr))) {                               \
                case 4:                                                 \
                        __x = (typeof(__x))(unsigned long)              \
-                               __insn_exch4((ptr), (u32)(unsigned long)(n)); \
+                               __insn_exch4((ptr),                     \
+                                       (u32)(unsigned long)(n));       \
                        break;                                          \
                case 8:                                                 \
-                       __x = (typeof(__x))                     \
+                       __x = (typeof(__x))                             \
                                __insn_exch((ptr), (unsigned long)(n)); \
                        break;                                          \
                default:                                                \
@@ -103,10 +107,12 @@ u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n);
                switch (sizeof(*(ptr))) {                               \
                case 4:                                                 \
                        __x = (typeof(__x))(unsigned long)              \
-                               __insn_cmpexch4((ptr), (u32)(unsigned long)(n)); \
+                               __insn_cmpexch4((ptr),                  \
+                                       (u32)(unsigned long)(n));       \
                        break;                                          \
                case 8:                                                 \
-                       __x = (typeof(__x))__insn_cmpexch((ptr), (u64)(n)); \
+                       __x = (typeof(__x))__insn_cmpexch((ptr),        \
+                                               (long long)(n));        \
                        break;                                          \
                default:                                                \
                        __cmpxchg_called_with_bad_pointer();            \
index 63294f5..4f7ae39 100644 (file)
 #ifndef _ASM_TILE_PERCPU_H
 #define _ASM_TILE_PERCPU_H
 
-register unsigned long __my_cpu_offset __asm__("tp");
-#define __my_cpu_offset __my_cpu_offset
-#define set_my_cpu_offset(tp) (__my_cpu_offset = (tp))
+register unsigned long my_cpu_offset_reg asm("tp");
+
+#ifdef CONFIG_PREEMPT
+/*
+ * For full preemption, we can't just use the register variable
+ * directly, since we need barrier() to hazard against it, causing the
+ * compiler to reload anything computed from a previous "tp" value.
+ * But we also don't want to use volatile asm, since we'd like the
+ * compiler to be able to cache the value across multiple percpu reads.
+ * So we use a fake stack read as a hazard against barrier().
+ * The 'U' constraint is like 'm' but disallows postincrement.
+ */
+static inline unsigned long __my_cpu_offset(void)
+{
+       unsigned long tp;
+       register unsigned long *sp asm("sp");
+       asm("move %0, tp" : "=r" (tp) : "U" (*sp));
+       return tp;
+}
+#define __my_cpu_offset __my_cpu_offset()
+#else
+/*
+ * We don't need to hazard against barrier() since "tp" doesn't ever
+ * change with PREEMPT_NONE, and with PREEMPT_VOLUNTARY it only
+ * changes at function call points, at which we are already re-reading
+ * the value of "tp" due to "my_cpu_offset_reg" being a global variable.
+ */
+#define __my_cpu_offset my_cpu_offset_reg
+#endif
+
+#define set_my_cpu_offset(tp) (my_cpu_offset_reg = (tp))
 
 #include <asm-generic/percpu.h>
 
index df27a1f..531f4c3 100644 (file)
@@ -66,7 +66,7 @@ static struct hardwall_type hardwall_types[] = {
                0,
                "udn",
                LIST_HEAD_INIT(hardwall_types[HARDWALL_UDN].list),
-               __SPIN_LOCK_INITIALIZER(hardwall_types[HARDWALL_UDN].lock),
+               __SPIN_LOCK_UNLOCKED(hardwall_types[HARDWALL_UDN].lock),
                NULL
        },
 #ifndef __tilepro__
@@ -77,7 +77,7 @@ static struct hardwall_type hardwall_types[] = {
                1,  /* disabled pending hypervisor support */
                "idn",
                LIST_HEAD_INIT(hardwall_types[HARDWALL_IDN].list),
-               __SPIN_LOCK_INITIALIZER(hardwall_types[HARDWALL_IDN].lock),
+               __SPIN_LOCK_UNLOCKED(hardwall_types[HARDWALL_IDN].lock),
                NULL
        },
        {  /* access to user-space IPI */
@@ -87,7 +87,7 @@ static struct hardwall_type hardwall_types[] = {
                0,
                "ipi",
                LIST_HEAD_INIT(hardwall_types[HARDWALL_IPI].list),
-               __SPIN_LOCK_INITIALIZER(hardwall_types[HARDWALL_IPI].lock),
+               __SPIN_LOCK_UNLOCKED(hardwall_types[HARDWALL_IPI].lock),
                NULL
        },
 #endif
index 088d5c1..2cbe6d5 100644 (file)
@@ -815,6 +815,9 @@ STD_ENTRY(interrupt_return)
        }
        bzt     r28, 1f
        bnz     r29, 1f
+       /* Disable interrupts explicitly for preemption. */
+       IRQ_DISABLE(r20,r21)
+       TRACE_IRQS_OFF
        jal     preempt_schedule_irq
        FEEDBACK_REENTER(interrupt_return)
 1:
index ec755d3..b8fc497 100644 (file)
@@ -841,6 +841,9 @@ STD_ENTRY(interrupt_return)
        }
        beqzt   r28, 1f
        bnez    r29, 1f
+       /* Disable interrupts explicitly for preemption. */
+       IRQ_DISABLE(r20,r21)
+       TRACE_IRQS_OFF
        jal     preempt_schedule_irq
        FEEDBACK_REENTER(interrupt_return)
 1:
index 362284a..c93977a 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/mmzone.h>
 #include <linux/dcache.h>
 #include <linux/fs.h>
+#include <linux/string.h>
 #include <asm/backtrace.h>
 #include <asm/page.h>
 #include <asm/ucontext.h>
@@ -332,21 +333,18 @@ static void describe_addr(struct KBacktraceIterator *kbt,
        }
 
        if (vma->vm_file) {
-               char *s;
                p = d_path(&vma->vm_file->f_path, buf, bufsize);
                if (IS_ERR(p))
                        p = "?";
-               s = strrchr(p, '/');
-               if (s)
-                       p = s+1;
+               name = kbasename(p);
        } else {
-               p = "anon";
+               name = "anon";
        }
 
        /* Generate a string description of the vma info. */
-       namelen = strlen(p);
+       namelen = strlen(name);
        remaining = (bufsize - 1) - namelen;
-       memmove(buf, p, namelen);
+       memmove(buf, name, namelen);
        snprintf(buf + namelen, remaining, "[%lx+%lx] ",
                 vma->vm_start, vma->vm_end - vma->vm_start);
 }
index 759efa3..c89b211 100644 (file)
@@ -107,19 +107,19 @@ unsigned long _atomic_xor(volatile unsigned long *p, unsigned long mask)
 EXPORT_SYMBOL(_atomic_xor);
 
 
-u64 _atomic64_xchg(u64 *v, u64 n)
+long long _atomic64_xchg(long long *v, long long n)
 {
        return __atomic64_xchg(v, __atomic_setup(v), n);
 }
 EXPORT_SYMBOL(_atomic64_xchg);
 
-u64 _atomic64_xchg_add(u64 *v, u64 i)
+long long _atomic64_xchg_add(long long *v, long long i)
 {
        return __atomic64_xchg_add(v, __atomic_setup(v), i);
 }
 EXPORT_SYMBOL(_atomic64_xchg_add);
 
-u64 _atomic64_xchg_add_unless(u64 *v, u64 a, u64 u)
+long long _atomic64_xchg_add_unless(long long *v, long long a, long long u)
 {
        /*
         * Note: argument order is switched here since it is easier
@@ -130,7 +130,7 @@ u64 _atomic64_xchg_add_unless(u64 *v, u64 a, u64 u)
 }
 EXPORT_SYMBOL(_atomic64_xchg_add_unless);
 
-u64 _atomic64_cmpxchg(u64 *v, u64 o, u64 n)
+long long _atomic64_cmpxchg(long long *v, long long o, long long n)
 {
        return __atomic64_cmpxchg(v, __atomic_setup(v), o, n);
 }
index 829df49..41ebbfe 100644 (file)
@@ -40,9 +40,11 @@ static ssize_t exitcode_proc_write(struct file *file,
                const char __user *buffer, size_t count, loff_t *pos)
 {
        char *end, buf[sizeof("nnnnn\0")];
+       size_t size;
        int tmp;
 
-       if (copy_from_user(buf, buffer, count))
+       size = min(count, sizeof(buf));
+       if (copy_from_user(buf, buffer, size))
                return -EFAULT;
 
        tmp = simple_strtol(buf, &end, 0);
index ee2fb9d..f67e839 100644 (file)
@@ -860,7 +860,7 @@ source "kernel/Kconfig.preempt"
 
 config X86_UP_APIC
        bool "Local APIC support on uniprocessors"
-       depends on X86_32 && !SMP && !X86_32_NON_STANDARD
+       depends on X86_32 && !SMP && !X86_32_NON_STANDARD && !PCI_MSI
        ---help---
          A local APIC (Advanced Programmable Interrupt Controller) is an
          integrated interrupt controller in the CPU. If you have a single-CPU
@@ -885,11 +885,11 @@ config X86_UP_IOAPIC
 
 config X86_LOCAL_APIC
        def_bool y
-       depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC
+       depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
 
 config X86_IO_APIC
        def_bool y
-       depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_IOAPIC
+       depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_IOAPIC || PCI_MSI
 
 config X86_VISWS_APIC
        def_bool y
@@ -1033,6 +1033,7 @@ config X86_REBOOTFIXUPS
 
 config MICROCODE
        tristate "CPU microcode loading support"
+       depends on CPU_SUP_AMD || CPU_SUP_INTEL
        select FW_LOADER
        ---help---
 
index d3f5c63..89270b4 100644 (file)
@@ -374,7 +374,7 @@ static __always_inline __pure bool __static_cpu_has(u16 bit)
                 * Catch too early usage of this before alternatives
                 * have run.
                 */
-               asm goto("1: jmp %l[t_warn]\n"
+               asm_volatile_goto("1: jmp %l[t_warn]\n"
                         "2:\n"
                         ".section .altinstructions,\"a\"\n"
                         " .long 1b - .\n"
@@ -388,7 +388,7 @@ static __always_inline __pure bool __static_cpu_has(u16 bit)
 
 #endif
 
-               asm goto("1: jmp %l[t_no]\n"
+               asm_volatile_goto("1: jmp %l[t_no]\n"
                         "2:\n"
                         ".section .altinstructions,\"a\"\n"
                         " .long 1b - .\n"
@@ -453,7 +453,7 @@ static __always_inline __pure bool _static_cpu_has_safe(u16 bit)
  * have. Thus, we force the jump to the widest, 4-byte, signed relative
  * offset even though the last would often fit in less bytes.
  */
-               asm goto("1: .byte 0xe9\n .long %l[t_dynamic] - 2f\n"
+               asm_volatile_goto("1: .byte 0xe9\n .long %l[t_dynamic] - 2f\n"
                         "2:\n"
                         ".section .altinstructions,\"a\"\n"
                         " .long 1b - .\n"              /* src offset */
index 64507f3..6a2cefb 100644 (file)
@@ -18,7 +18,7 @@
 
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-       asm goto("1:"
+       asm_volatile_goto("1:"
                ".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t"
                ".pushsection __jump_table,  \"aw\" \n\t"
                _ASM_ALIGN "\n\t"
index e7e6751..07537a4 100644 (file)
@@ -20,7 +20,7 @@
 static inline void __mutex_fastpath_lock(atomic_t *v,
                                         void (*fail_fn)(atomic_t *))
 {
-       asm volatile goto(LOCK_PREFIX "   decl %0\n"
+       asm_volatile_goto(LOCK_PREFIX "   decl %0\n"
                          "   jns %l[exit]\n"
                          : : "m" (v->counter)
                          : "memory", "cc"
@@ -75,7 +75,7 @@ static inline int __mutex_fastpath_lock_retval(atomic_t *count)
 static inline void __mutex_fastpath_unlock(atomic_t *v,
                                           void (*fail_fn)(atomic_t *))
 {
-       asm volatile goto(LOCK_PREFIX "   incl %0\n"
+       asm_volatile_goto(LOCK_PREFIX "   incl %0\n"
                          "   jg %l[exit]\n"
                          : : "m" (v->counter)
                          : "memory", "cc"
index 0da5200..b3e18f8 100644 (file)
@@ -128,7 +128,8 @@ do {                                                        \
 do {                                                                   \
        typedef typeof(var) pao_T__;                                    \
        const int pao_ID__ = (__builtin_constant_p(val) &&              \
-                             ((val) == 1 || (val) == -1)) ? (val) : 0; \
+                             ((val) == 1 || (val) == -1)) ?            \
+                               (int)(val) : 0;                         \
        if (0) {                                                        \
                pao_T__ pao_tmp__;                                      \
                pao_tmp__ = (val);                                      \
index 6aef9fb..b913915 100644 (file)
@@ -79,30 +79,38 @@ static inline int phys_to_machine_mapping_valid(unsigned long pfn)
        return get_phys_to_machine(pfn) != INVALID_P2M_ENTRY;
 }
 
-static inline unsigned long mfn_to_pfn(unsigned long mfn)
+static inline unsigned long mfn_to_pfn_no_overrides(unsigned long mfn)
 {
        unsigned long pfn;
-       int ret = 0;
+       int ret;
 
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return mfn;
 
-       if (unlikely(mfn >= machine_to_phys_nr)) {
-               pfn = ~0;
-               goto try_override;
-       }
-       pfn = 0;
+       if (unlikely(mfn >= machine_to_phys_nr))
+               return ~0;
+
        /*
         * The array access can fail (e.g., device space beyond end of RAM).
         * In such cases it doesn't matter what we return (we return garbage),
         * but we must handle the fault without crashing!
         */
        ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
-try_override:
-       /* ret might be < 0 if there are no entries in the m2p for mfn */
        if (ret < 0)
-               pfn = ~0;
-       else if (get_phys_to_machine(pfn) != mfn)
+               return ~0;
+
+       return pfn;
+}
+
+static inline unsigned long mfn_to_pfn(unsigned long mfn)
+{
+       unsigned long pfn;
+
+       if (xen_feature(XENFEAT_auto_translated_physmap))
+               return mfn;
+
+       pfn = mfn_to_pfn_no_overrides(mfn);
+       if (get_phys_to_machine(pfn) != mfn) {
                /*
                 * If this appears to be a foreign mfn (because the pfn
                 * doesn't map back to the mfn), then check the local override
@@ -111,6 +119,7 @@ try_override:
                 * m2p_find_override_pfn returns ~0 if it doesn't find anything.
                 */
                pfn = m2p_find_override_pfn(mfn, ~0);
+       }
 
        /* 
         * pfn is ~0 if there are no entries in the m2p for mfn or if the
index 1191ac1..a419814 100644 (file)
@@ -113,7 +113,7 @@ static int __init early_get_pnodeid(void)
                break;
        case UV3_HUB_PART_NUMBER:
        case UV3_HUB_PART_NUMBER_X:
-               uv_min_hub_revision_id += UV3_HUB_REVISION_BASE - 1;
+               uv_min_hub_revision_id += UV3_HUB_REVISION_BASE;
                break;
        }
 
index 8355c84..8a87a32 100644 (file)
@@ -1276,16 +1276,16 @@ void perf_events_lapic_init(void)
 static int __kprobes
 perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
-       int ret;
        u64 start_clock;
        u64 finish_clock;
+       int ret;
 
        if (!atomic_read(&active_events))
                return NMI_DONE;
 
-       start_clock = local_clock();
+       start_clock = sched_clock();
        ret = x86_pmu.handle_irq(regs);
-       finish_clock = local_clock();
+       finish_clock = sched_clock();
 
        perf_sample_event_took(finish_clock - start_clock);
 
@@ -1506,7 +1506,7 @@ static int __init init_hw_perf_events(void)
                err = amd_pmu_init();
                break;
        default:
-               return 0;
+               err = -ENOTSUPP;
        }
        if (err != 0) {
                pr_cont("no PMU driver, software events only.\n");
@@ -1883,26 +1883,21 @@ static struct pmu pmu = {
 
 void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now)
 {
-       userpg->cap_usr_time = 0;
-       userpg->cap_usr_time_zero = 0;
-       userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc;
+       userpg->cap_user_time = 0;
+       userpg->cap_user_time_zero = 0;
+       userpg->cap_user_rdpmc = x86_pmu.attr_rdpmc;
        userpg->pmc_width = x86_pmu.cntval_bits;
 
-       if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
+       if (!sched_clock_stable)
                return;
 
-       if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
-               return;
-
-       userpg->cap_usr_time = 1;
+       userpg->cap_user_time = 1;
        userpg->time_mult = this_cpu_read(cyc2ns);
        userpg->time_shift = CYC2NS_SCALE_FACTOR;
        userpg->time_offset = this_cpu_read(cyc2ns_offset) - now;
 
-       if (sched_clock_stable && !check_tsc_disabled()) {
-               userpg->cap_usr_time_zero = 1;
-               userpg->time_zero = this_cpu_read(cyc2ns_offset);
-       }
+       userpg->cap_user_time_zero = 1;
+       userpg->time_zero = this_cpu_read(cyc2ns_offset);
 }
 
 /*
index 9db76c3..f31a165 100644 (file)
@@ -2325,6 +2325,7 @@ __init int intel_pmu_init(void)
                break;
 
        case 55: /* Atom 22nm "Silvermont" */
+       case 77: /* Avoton "Silvermont" */
                memcpy(hw_cache_event_ids, slm_hw_cache_event_ids,
                        sizeof(hw_cache_event_ids));
                memcpy(hw_cache_extra_regs, slm_hw_cache_extra_regs,
index 8ed4458..4118f9f 100644 (file)
@@ -2706,14 +2706,14 @@ static void uncore_pmu_init_hrtimer(struct intel_uncore_box *box)
        box->hrtimer.function = uncore_pmu_hrtimer;
 }
 
-struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type, int cpu)
+static struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type, int node)
 {
        struct intel_uncore_box *box;
        int i, size;
 
        size = sizeof(*box) + type->num_shared_regs * sizeof(struct intel_uncore_extra_reg);
 
-       box = kzalloc_node(size, GFP_KERNEL, cpu_to_node(cpu));
+       box = kzalloc_node(size, GFP_KERNEL, node);
        if (!box)
                return NULL;
 
@@ -3031,7 +3031,7 @@ static int uncore_validate_group(struct intel_uncore_pmu *pmu,
        struct intel_uncore_box *fake_box;
        int ret = -EINVAL, n;
 
-       fake_box = uncore_alloc_box(pmu->type, smp_processor_id());
+       fake_box = uncore_alloc_box(pmu->type, NUMA_NO_NODE);
        if (!fake_box)
                return -ENOMEM;
 
@@ -3294,7 +3294,7 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
        }
 
        type = pci_uncores[UNCORE_PCI_DEV_TYPE(id->driver_data)];
-       box = uncore_alloc_box(type, 0);
+       box = uncore_alloc_box(type, NUMA_NO_NODE);
        if (!box)
                return -ENOMEM;
 
@@ -3499,7 +3499,7 @@ static int uncore_cpu_prepare(int cpu, int phys_id)
                        if (pmu->func_id < 0)
                                pmu->func_id = j;
 
-                       box = uncore_alloc_box(type, cpu);
+                       box = uncore_alloc_box(type, cpu_to_node(cpu));
                        if (!box)
                                return -ENOMEM;
 
index ee11b7d..26d5a55 100644 (file)
@@ -42,15 +42,27 @@ static void __jump_label_transform(struct jump_entry *entry,
                                   int init)
 {
        union jump_code_union code;
+       const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
        const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];
 
        if (type == JUMP_LABEL_ENABLE) {
-               /*
-                * We are enabling this jump label. If it is not a nop
-                * then something must have gone wrong.
-                */
-               if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) != 0))
-                       bug_at((void *)entry->code, __LINE__);
+               if (init) {
+                       /*
+                        * Jump label is enabled for the first time.
+                        * So we expect a default_nop...
+                        */
+                       if (unlikely(memcmp((void *)entry->code, default_nop, 5)
+                                    != 0))
+                               bug_at((void *)entry->code, __LINE__);
+               } else {
+                       /*
+                        * ...otherwise expect an ideal_nop. Otherwise
+                        * something went horribly wrong.
+                        */
+                       if (unlikely(memcmp((void *)entry->code, ideal_nop, 5)
+                                    != 0))
+                               bug_at((void *)entry->code, __LINE__);
+               }
 
                code.jump = 0xe9;
                code.offset = entry->target -
@@ -63,7 +75,6 @@ static void __jump_label_transform(struct jump_entry *entry,
                 * are converting the default nop to the ideal nop.
                 */
                if (init) {
-                       const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
                        if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0))
                                bug_at((void *)entry->code, __LINE__);
                } else {
index 697b93a..b2046e4 100644 (file)
@@ -609,7 +609,7 @@ static struct dentry *d_kvm_debug;
 
 struct dentry *kvm_init_debugfs(void)
 {
-       d_kvm_debug = debugfs_create_dir("kvm", NULL);
+       d_kvm_debug = debugfs_create_dir("kvm-guest", NULL);
        if (!d_kvm_debug)
                printk(KERN_WARNING "Could not create 'kvm' debugfs directory\n");
 
@@ -775,11 +775,22 @@ void __init kvm_spinlock_init(void)
        if (!kvm_para_has_feature(KVM_FEATURE_PV_UNHALT))
                return;
 
-       printk(KERN_INFO "KVM setup paravirtual spinlock\n");
+       pv_lock_ops.lock_spinning = PV_CALLEE_SAVE(kvm_lock_spinning);
+       pv_lock_ops.unlock_kick = kvm_unlock_kick;
+}
+
+static __init int kvm_spinlock_init_jump(void)
+{
+       if (!kvm_para_available())
+               return 0;
+       if (!kvm_para_has_feature(KVM_FEATURE_PV_UNHALT))
+               return 0;
 
        static_key_slow_inc(&paravirt_ticketlocks_enabled);
+       printk(KERN_INFO "KVM setup paravirtual spinlock\n");
 
-       pv_lock_ops.lock_spinning = PV_CALLEE_SAVE(kvm_lock_spinning);
-       pv_lock_ops.unlock_kick = kvm_unlock_kick;
+       return 0;
 }
+early_initcall(kvm_spinlock_init_jump);
+
 #endif /* CONFIG_PARAVIRT_SPINLOCKS */
index 7123b5d..af99f71 100644 (file)
@@ -216,6 +216,7 @@ int apply_microcode_amd(int cpu)
        /* need to apply patch? */
        if (rev >= mc_amd->hdr.patch_id) {
                c->microcode = rev;
+               uci->cpu_sig.rev = rev;
                return 0;
        }
 
index ba77ebc..6fcb49c 100644 (file)
@@ -113,10 +113,10 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2
                u64 before, delta, whole_msecs;
                int remainder_ns, decimal_msecs, thishandled;
 
-               before = local_clock();
+               before = sched_clock();
                thishandled = a->handler(type, regs);
                handled += thishandled;
-               delta = local_clock() - before;
+               delta = sched_clock() - before;
                trace_nmi_handler(a->handler, (int)delta, thishandled);
 
                if (delta < nmi_longest_ns)
index 563ed91..7e920bf 100644 (file)
@@ -326,6 +326,14 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6320"),
                },
        },
+       {       /* Handle problems with rebooting on the Latitude E5410. */
+               .callback = set_pci_reboot,
+               .ident = "Dell Latitude E5410",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5410"),
+               },
+       },
        {       /* Handle problems with rebooting on the Latitude E5420. */
                .callback = set_pci_reboot,
                .ident = "Dell Latitude E5420",
@@ -352,12 +360,28 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
        },
        {       /* Handle problems with rebooting on the Precision M6600. */
                .callback = set_pci_reboot,
-               .ident = "Dell OptiPlex 990",
+               .ident = "Dell Precision M6600",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"),
                },
        },
+       {       /* Handle problems with rebooting on the Dell PowerEdge C6100. */
+               .callback = set_pci_reboot,
+               .ident = "Dell PowerEdge C6100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "C6100"),
+               },
+       },
+       {       /* Some C6100 machines were shipped with vendor being 'Dell'. */
+               .callback = set_pci_reboot,
+               .ident = "Dell PowerEdge C6100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "C6100"),
+               },
+       },
        { }
 };
 
index 22513e9..86179d4 100644 (file)
@@ -72,14 +72,14 @@ __init int create_simplefb(const struct screen_info *si,
         * the part that is occupied by the framebuffer */
        len = mode->height * mode->stride;
        len = PAGE_ALIGN(len);
-       if (len > si->lfb_size << 16) {
+       if (len > (u64)si->lfb_size << 16) {
                printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n");
                return -EINVAL;
        }
 
        /* setup IORESOURCE_MEM as framebuffer memory */
        memset(&res, 0, sizeof(res));
-       res.flags = IORESOURCE_MEM;
+       res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
        res.name = simplefb_resname;
        res.start = si->lfb_base;
        res.end = si->lfb_base + len - 1;
index a1216de..2b2fce1 100644 (file)
@@ -3255,25 +3255,29 @@ static void vmx_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
 
 static void ept_load_pdptrs(struct kvm_vcpu *vcpu)
 {
+       struct kvm_mmu *mmu = vcpu->arch.walk_mmu;
+
        if (!test_bit(VCPU_EXREG_PDPTR,
                      (unsigned long *)&vcpu->arch.regs_dirty))
                return;
 
        if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
-               vmcs_write64(GUEST_PDPTR0, vcpu->arch.mmu.pdptrs[0]);
-               vmcs_write64(GUEST_PDPTR1, vcpu->arch.mmu.pdptrs[1]);
-               vmcs_write64(GUEST_PDPTR2, vcpu->arch.mmu.pdptrs[2]);
-               vmcs_write64(GUEST_PDPTR3, vcpu->arch.mmu.pdptrs[3]);
+               vmcs_write64(GUEST_PDPTR0, mmu->pdptrs[0]);
+               vmcs_write64(GUEST_PDPTR1, mmu->pdptrs[1]);
+               vmcs_write64(GUEST_PDPTR2, mmu->pdptrs[2]);
+               vmcs_write64(GUEST_PDPTR3, mmu->pdptrs[3]);
        }
 }
 
 static void ept_save_pdptrs(struct kvm_vcpu *vcpu)
 {
+       struct kvm_mmu *mmu = vcpu->arch.walk_mmu;
+
        if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
-               vcpu->arch.mmu.pdptrs[0] = vmcs_read64(GUEST_PDPTR0);
-               vcpu->arch.mmu.pdptrs[1] = vmcs_read64(GUEST_PDPTR1);
-               vcpu->arch.mmu.pdptrs[2] = vmcs_read64(GUEST_PDPTR2);
-               vcpu->arch.mmu.pdptrs[3] = vmcs_read64(GUEST_PDPTR3);
+               mmu->pdptrs[0] = vmcs_read64(GUEST_PDPTR0);
+               mmu->pdptrs[1] = vmcs_read64(GUEST_PDPTR1);
+               mmu->pdptrs[2] = vmcs_read64(GUEST_PDPTR2);
+               mmu->pdptrs[3] = vmcs_read64(GUEST_PDPTR3);
        }
 
        __set_bit(VCPU_EXREG_PDPTR,
@@ -5345,7 +5349,9 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
         * There are errata that may cause this bit to not be set:
         * AAK134, BY25.
         */
-       if (exit_qualification & INTR_INFO_UNBLOCK_NMI)
+       if (!(to_vmx(vcpu)->idt_vectoring_info & VECTORING_INFO_VALID_MASK) &&
+                       cpu_has_virtual_nmis() &&
+                       (exit_qualification & INTR_INFO_UNBLOCK_NMI))
                vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, GUEST_INTR_STATE_NMI);
 
        gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
@@ -7775,10 +7781,6 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
                vmcs_write64(GUEST_PDPTR1, vmcs12->guest_pdptr1);
                vmcs_write64(GUEST_PDPTR2, vmcs12->guest_pdptr2);
                vmcs_write64(GUEST_PDPTR3, vmcs12->guest_pdptr3);
-               __clear_bit(VCPU_EXREG_PDPTR,
-                               (unsigned long *)&vcpu->arch.regs_avail);
-               __clear_bit(VCPU_EXREG_PDPTR,
-                               (unsigned long *)&vcpu->arch.regs_dirty);
        }
 
        kvm_register_write(vcpu, VCPU_REGS_RSP, vmcs12->guest_rsp);
index 79c216a..26328e8 100644 (file)
@@ -772,13 +772,23 @@ out:
        return;
 }
 
+static void bpf_jit_free_deferred(struct work_struct *work)
+{
+       struct sk_filter *fp = container_of(work, struct sk_filter, work);
+       unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
+       struct bpf_binary_header *header = (void *)addr;
+
+       set_memory_rw(addr, header->pages);
+       module_free(NULL, header);
+       kfree(fp);
+}
+
 void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter) {
-               unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
-               struct bpf_binary_header *header = (void *)addr;
-
-               set_memory_rw(addr, header->pages);
-               module_free(NULL, header);
+               INIT_WORK(&fp->work, bpf_jit_free_deferred);
+               schedule_work(&fp->work);
+       } else {
+               kfree(fp);
        }
 }
index 5596c7b..082e881 100644 (file)
@@ -700,7 +700,7 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
        if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
                return -ENODEV;
 
-       if (start > end || !addr)
+       if (start > end)
                return -EINVAL;
 
        mutex_lock(&pci_mmcfg_lock);
@@ -716,6 +716,11 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
                return -EEXIST;
        }
 
+       if (!addr) {
+               mutex_unlock(&pci_mmcfg_lock);
+               return -EINVAL;
+       }
+
        rc = -EBUSY;
        cfg = pci_mmconfig_alloc(seg, start, end, addr);
        if (cfg == NULL) {
index 90f6ed1..c7e22ab 100644 (file)
@@ -912,10 +912,13 @@ void __init efi_enter_virtual_mode(void)
 
        for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
                md = p;
-               if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
-                   md->type != EFI_BOOT_SERVICES_CODE &&
-                   md->type != EFI_BOOT_SERVICES_DATA)
-                       continue;
+               if (!(md->attribute & EFI_MEMORY_RUNTIME)) {
+#ifdef CONFIG_X86_64
+                       if (md->type != EFI_BOOT_SERVICES_CODE &&
+                           md->type != EFI_BOOT_SERVICES_DATA)
+#endif
+                               continue;
+               }
 
                size = md->num_pages << EFI_PAGE_SHIFT;
                end = md->phys_addr + size;
index 8b901e8..a61c7d5 100644 (file)
@@ -879,7 +879,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,
        unsigned long uninitialized_var(address);
        unsigned level;
        pte_t *ptep = NULL;
-       int ret = 0;
 
        pfn = page_to_pfn(page);
        if (!PageHighMem(page)) {
@@ -926,8 +925,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
         * frontend pages while they are being shared with the backend,
         * because mfn_to_pfn (that ends up being called by GUPF) will
         * return the backend pfn rather than the frontend pfn. */
-       ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
-       if (ret == 0 && get_phys_to_machine(pfn) == mfn)
+       pfn = mfn_to_pfn_no_overrides(mfn);
+       if (get_phys_to_machine(pfn) == mfn)
                set_phys_to_machine(pfn, FOREIGN_FRAME(mfn));
 
        return 0;
@@ -942,7 +941,6 @@ int m2p_remove_override(struct page *page,
        unsigned long uninitialized_var(address);
        unsigned level;
        pte_t *ptep = NULL;
-       int ret = 0;
 
        pfn = page_to_pfn(page);
        mfn = get_phys_to_machine(pfn);
@@ -1029,8 +1027,8 @@ int m2p_remove_override(struct page *page,
         * the original pfn causes mfn_to_pfn(mfn) to return the frontend
         * pfn again. */
        mfn &= ~FOREIGN_FRAME_BIT;
-       ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
-       if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
+       pfn = mfn_to_pfn_no_overrides(mfn);
+       if (get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
                        m2p_find_override(mfn) == NULL)
                set_phys_to_machine(pfn, mfn);
 
index d1e4777..31d0475 100644 (file)
@@ -278,6 +278,15 @@ static void __init xen_smp_prepare_boot_cpu(void)
                   old memory can be recycled */
                make_lowmem_page_readwrite(xen_initial_gdt);
 
+#ifdef CONFIG_X86_32
+               /*
+                * Xen starts us with XEN_FLAT_RING1_DS, but linux code
+                * expects __USER_DS
+                */
+               loadsegment(ds, __USER_DS);
+               loadsegment(es, __USER_DS);
+#endif
+
                xen_filter_cpu_maps();
                xen_setup_vcpu_info_placement();
        }
index 253f63f..be6b860 100644 (file)
@@ -259,6 +259,14 @@ void xen_uninit_lock_cpu(int cpu)
 }
 
 
+/*
+ * Our init of PV spinlocks is split in two init functions due to us
+ * using paravirt patching and jump labels patching and having to do
+ * all of this before SMP code is invoked.
+ *
+ * The paravirt patching needs to be done _before_ the alternative asm code
+ * is started, otherwise we would not patch the core kernel code.
+ */
 void __init xen_init_spinlocks(void)
 {
 
@@ -267,12 +275,26 @@ void __init xen_init_spinlocks(void)
                return;
        }
 
-       static_key_slow_inc(&paravirt_ticketlocks_enabled);
-
        pv_lock_ops.lock_spinning = PV_CALLEE_SAVE(xen_lock_spinning);
        pv_lock_ops.unlock_kick = xen_unlock_kick;
 }
 
+/*
+ * While the jump_label init code needs to happend _after_ the jump labels are
+ * enabled and before SMP is started. Hence we use pre-SMP initcall level
+ * init. We cannot do it in xen_init_spinlocks as that is done before
+ * jump labels are activated.
+ */
+static __init int xen_init_spinlocks_jump(void)
+{
+       if (!xen_pvspin)
+               return 0;
+
+       static_key_slow_inc(&paravirt_ticketlocks_enabled);
+       return 0;
+}
+early_initcall(xen_init_spinlocks_jump);
+
 static __init int xen_parse_nopvspin(char *arg)
 {
        xen_pvspin = false;
index c114483..7db5c22 100644 (file)
@@ -87,4 +87,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* _XTENSA_SOCKET_H */
index de1dfa1..21dbe6b 100644 (file)
@@ -1122,7 +1122,7 @@ ENDPROC(fast_syscall_spill_registers)
  * a3: exctable, original value in excsave1
  */
 
-fast_syscall_spill_registers_fixup:
+ENTRY(fast_syscall_spill_registers_fixup)
 
        rsr     a2, windowbase  # get current windowbase (a2 is saved)
        xsr     a0, depc        # restore depc and a0
@@ -1134,22 +1134,26 @@ fast_syscall_spill_registers_fixup:
         */
 
        xsr     a3, excsave1    # get spill-mask
-       slli    a2, a3, 1       # shift left by one
+       slli    a3, a3, 1       # shift left by one
 
-       slli    a3, a2, 32-WSBITS
-       src     a2, a2, a3      # a1 = xxwww1yyxxxwww1yy......
+       slli    a2, a3, 32-WSBITS
+       src     a2, a3, a2      # a2 = xxwww1yyxxxwww1yy......
        wsr     a2, windowstart # set corrected windowstart
 
-       rsr     a3, excsave1
-       l32i    a2, a3, EXC_TABLE_DOUBLE_SAVE   # restore a2
-       l32i    a3, a3, EXC_TABLE_PARAM # original WB (in user task)
+       srli    a3, a3, 1
+       rsr     a2, excsave1
+       l32i    a2, a2, EXC_TABLE_DOUBLE_SAVE   # restore a2
+       xsr     a2, excsave1
+       s32i    a3, a2, EXC_TABLE_DOUBLE_SAVE   # save a3
+       l32i    a3, a2, EXC_TABLE_PARAM # original WB (in user task)
+       xsr     a2, excsave1
 
        /* Return to the original (user task) WINDOWBASE.
         * We leave the following frame behind:
         * a0, a1, a2   same
-        * a3:          trashed (saved in excsave_1)
+        * a3:          trashed (saved in EXC_TABLE_DOUBLE_SAVE)
         * depc:        depc (we have to return to that address)
-        * excsave_1:   a3
+        * excsave_1:   exctable
         */
 
        wsr     a3, windowbase
@@ -1159,9 +1163,9 @@ fast_syscall_spill_registers_fixup:
         *  a0: return address
         *  a1: used, stack pointer
         *  a2: kernel stack pointer
-        *  a3: available, saved in EXCSAVE_1
+        *  a3: available
         *  depc: exception address
-        *  excsave: a3
+        *  excsave: exctable
         * Note: This frame might be the same as above.
         */
 
@@ -1181,9 +1185,12 @@ fast_syscall_spill_registers_fixup:
        rsr     a0, exccause
        addx4   a0, a0, a3                      # find entry in table
        l32i    a0, a0, EXC_TABLE_FAST_USER     # load handler
+       l32i    a3, a3, EXC_TABLE_DOUBLE_SAVE
        jx      a0
 
-fast_syscall_spill_registers_fixup_return:
+ENDPROC(fast_syscall_spill_registers_fixup)
+
+ENTRY(fast_syscall_spill_registers_fixup_return)
 
        /* When we return here, all registers have been restored (a2: DEPC) */
 
@@ -1191,13 +1198,13 @@ fast_syscall_spill_registers_fixup_return:
 
        /* Restore fixup handler. */
 
-       xsr     a3, excsave1
-       movi    a2, fast_syscall_spill_registers_fixup
-       s32i    a2, a3, EXC_TABLE_FIXUP
-       s32i    a0, a3, EXC_TABLE_DOUBLE_SAVE
-       rsr     a2, windowbase
-       s32i    a2, a3, EXC_TABLE_PARAM
-       l32i    a2, a3, EXC_TABLE_KSTK
+       rsr     a2, excsave1
+       s32i    a3, a2, EXC_TABLE_DOUBLE_SAVE
+       movi    a3, fast_syscall_spill_registers_fixup
+       s32i    a3, a2, EXC_TABLE_FIXUP
+       rsr     a3, windowbase
+       s32i    a3, a2, EXC_TABLE_PARAM
+       l32i    a2, a2, EXC_TABLE_KSTK
 
        /* Load WB at the time the exception occurred. */
 
@@ -1206,8 +1213,12 @@ fast_syscall_spill_registers_fixup_return:
        wsr     a3, windowbase
        rsync
 
+       rsr     a3, excsave1
+       l32i    a3, a3, EXC_TABLE_DOUBLE_SAVE
+
        rfde
 
+ENDPROC(fast_syscall_spill_registers_fixup_return)
 
 /*
  * spill all registers.
index 718eca1..98b67d5 100644 (file)
@@ -341,7 +341,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
 
        sp = regs->areg[1];
 
-       if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) {
+       if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && sas_ss_flags(sp) == 0) {
                sp = current->sas_ss_sp + current->sas_ss_size;
        }
 
index 56f88b7..e9e1aad 100644 (file)
@@ -737,7 +737,8 @@ static int __init iss_net_setup(char *str)
                return 1;
        }
 
-       if ((new = alloc_bootmem(sizeof new)) == NULL) {
+       new = alloc_bootmem(sizeof(*new));
+       if (new == NULL) {
                printk("Alloc_bootmem failed\n");
                return 1;
        }
index 7f38e40..2429515 100644 (file)
@@ -99,11 +99,16 @@ config BLK_DEV_THROTTLING
 
        See Documentation/cgroups/blkio-controller.txt for more information.
 
-config CMDLINE_PARSER
+config BLK_CMDLINE_PARSER
        bool "Block device command line partition parser"
        default n
        ---help---
-       Parsing command line, get the partitions information.
+       Enabling this option allows you to specify the partition layout from
+       the kernel boot args.  This is typically of use for embedded devices
+       which don't otherwise have any standardized method for listing the
+       partitions on a block device.
+
+       See Documentation/block/cmdline-partition.txt for more information.
 
 menu "Partition Types"
 
index 4fa4be5..671a83d 100644 (file)
@@ -18,4 +18,4 @@ obj-$(CONFIG_IOSCHED_CFQ)     += cfq-iosched.o
 
 obj-$(CONFIG_BLOCK_COMPAT)     += compat_ioctl.o
 obj-$(CONFIG_BLK_DEV_INTEGRITY)        += blk-integrity.o
-obj-$(CONFIG_CMDLINE_PARSER)   += cmdline-parser.o
+obj-$(CONFIG_BLK_CMDLINE_PARSER)       += cmdline-parser.o
index e90c7c1..4e491d9 100644 (file)
@@ -235,8 +235,13 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg,
        blkg->online = true;
        spin_unlock(&blkcg->lock);
 
-       if (!ret)
+       if (!ret) {
+               if (blkcg == &blkcg_root) {
+                       q->root_blkg = blkg;
+                       q->root_rl.blkg = blkg;
+               }
                return blkg;
+       }
 
        /* @blkg failed fully initialized, use the usual release path */
        blkg_put(blkg);
@@ -334,6 +339,15 @@ static void blkg_destroy(struct blkcg_gq *blkg)
        if (rcu_dereference_raw(blkcg->blkg_hint) == blkg)
                rcu_assign_pointer(blkcg->blkg_hint, NULL);
 
+       /*
+        * If root blkg is destroyed.  Just clear the pointer since root_rl
+        * does not take reference on root blkg.
+        */
+       if (blkcg == &blkcg_root) {
+               blkg->q->root_blkg = NULL;
+               blkg->q->root_rl.blkg = NULL;
+       }
+
        /*
         * Put the reference taken at the time of creation so that when all
         * queues are gone, group can be destroyed.
@@ -360,13 +374,6 @@ static void blkg_destroy_all(struct request_queue *q)
                blkg_destroy(blkg);
                spin_unlock(&blkcg->lock);
        }
-
-       /*
-        * root blkg is destroyed.  Just clear the pointer since
-        * root_rl does not take reference on root blkg.
-        */
-       q->root_blkg = NULL;
-       q->root_rl.blkg = NULL;
 }
 
 /*
@@ -970,8 +977,6 @@ int blkcg_activate_policy(struct request_queue *q,
                ret = PTR_ERR(blkg);
                goto out_unlock;
        }
-       q->root_blkg = blkg;
-       q->root_rl.blkg = blkg;
 
        list_for_each_entry(blkg, &q->blkg_list, q_node)
                cnt++;
index c045053..0a00e4e 100644 (file)
@@ -1549,11 +1549,9 @@ get_rq:
        if (plug) {
                /*
                 * If this is the first request added after a plug, fire
-                * of a plug trace. If others have been added before, check
-                * if we have multiple devices in this plug. If so, make a
-                * note to sort the list before dispatch.
+                * of a plug trace.
                 */
-               if (list_empty(&plug->list))
+               if (!request_count)
                        trace_block_plug(q);
                else {
                        if (request_count >= BLK_MAX_REQUEST_COUNT) {
index e706213..ae4f27d 100644 (file)
@@ -68,9 +68,9 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
        spin_lock_irq(q->queue_lock);
 
        if (unlikely(blk_queue_dying(q))) {
+               rq->cmd_flags |= REQ_QUIET; 
                rq->errors = -ENXIO;
-               if (rq->end_io)
-                       rq->end_io(rq, rq->errors);
+               __blk_end_request_all(rq, rq->errors);
                spin_unlock_irq(q->queue_lock);
                return;
        }
index dabb9d0..434944c 100644 (file)
@@ -1803,7 +1803,7 @@ static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf,
 
        if (samples) {
                v = blkg_stat_read(&cfqg->stats.avg_queue_size_sum);
-               do_div(v, samples);
+               v = div64_u64(v, samples);
        }
        __blkg_prfill_u64(sf, pd, v);
        return 0;
@@ -4358,7 +4358,7 @@ static int cfq_init_queue(struct request_queue *q, struct elevator_type *e)
        if (!eq)
                return -ENOMEM;
 
-       cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node);
+       cfqd = kzalloc_node(sizeof(*cfqd), GFP_KERNEL, q->node);
        if (!cfqd) {
                kobject_put(&eq->kobj);
                return -ENOMEM;
index 20614a3..9ef6640 100644 (file)
@@ -346,7 +346,7 @@ static int deadline_init_queue(struct request_queue *q, struct elevator_type *e)
        if (!eq)
                return -ENOMEM;
 
-       dd = kmalloc_node(sizeof(*dd), GFP_KERNEL | __GFP_ZERO, q->node);
+       dd = kzalloc_node(sizeof(*dd), GFP_KERNEL, q->node);
        if (!dd) {
                kobject_put(&eq->kobj);
                return -ENOMEM;
index 668394d..2bcbd8c 100644 (file)
@@ -155,7 +155,7 @@ struct elevator_queue *elevator_alloc(struct request_queue *q,
 {
        struct elevator_queue *eq;
 
-       eq = kmalloc_node(sizeof(*eq), GFP_KERNEL | __GFP_ZERO, q->node);
+       eq = kzalloc_node(sizeof(*eq), GFP_KERNEL, q->node);
        if (unlikely(!eq))
                goto err;
 
index dadf42b..791f419 100644 (file)
@@ -1252,8 +1252,7 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
 {
        struct gendisk *disk;
 
-       disk = kmalloc_node(sizeof(struct gendisk),
-                               GFP_KERNEL | __GFP_ZERO, node_id);
+       disk = kzalloc_node(sizeof(struct gendisk), GFP_KERNEL, node_id);
        if (disk) {
                if (!init_part_stats(&disk->part0)) {
                        kfree(disk);
index 87a3208..9b29a99 100644 (file)
@@ -263,7 +263,7 @@ config SYSV68_PARTITION
 
 config CMDLINE_PARTITION
        bool "Command line partition support" if PARTITION_ADVANCED
-       select CMDLINE_PARSER
+       select BLK_CMDLINE_PARSER
        help
-         Say Y here if you would read the partitions table from bootargs.
+         Say Y here if you want to read the partition table from bootargs.
          The format for the command line is just like mtdparts.
index 56cf4ff..5141b56 100644 (file)
@@ -2,15 +2,15 @@
  * Copyright (C) 2013 HUAWEI
  * Author: Cai Zhiyong <caizhiyong@huawei.com>
  *
- * Read block device partition table from command line.
- * The partition used for fixed block device (eMMC) embedded device.
- * It is no MBR, save storage space. Bootloader can be easily accessed
+ * Read block device partition table from the command line.
+ * Typically used for fixed block (eMMC) embedded devices.
+ * It has no MBR, so saves storage space. Bootloader can be easily accessed
  * by absolute address of data on the block device.
  * Users can easily change the partition.
  *
  * The format for the command line is just like mtdparts.
  *
- * Verbose config please reference "Documentation/block/cmdline-partition.txt"
+ * For further information, see "Documentation/block/cmdline-partition.txt"
  *
  */
 
index 1eb09ee..a8287b4 100644 (file)
@@ -222,11 +222,16 @@ check_hybrid:
         * the disk size.
         *
         * Hybrid MBRs do not necessarily comply with this.
+        *
+        * Consider a bad value here to be a warning to support dd'ing
+        * an image from a smaller disk to a larger disk.
         */
        if (ret == GPT_MBR_PROTECTIVE) {
                sz = le32_to_cpu(mbr->partition_record[part].size_in_lba);
                if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF)
-                       ret = 0;
+                       pr_debug("GPT: mbr size in lba (%u) different than whole disk (%u).\n",
+                                sz, min_t(uint32_t,
+                                          total_sectors - 1, 0xFFFFFFFF));
        }
 done:
        return ret;
index 22327e6..6efe2ac 100644 (file)
@@ -24,7 +24,7 @@ menuconfig ACPI
          are configured, ACPI is used.
 
          The project home page for the Linux ACPI subsystem is here:
-         <http://www.lesswatts.org/projects/acpi/>
+         <https://01.org/linux-acpi>
 
          Linux support for ACPI is based on Intel Corporation's ACPI
          Component Architecture (ACPI CA).  For more information on the
@@ -123,9 +123,9 @@ config ACPI_BUTTON
        default y
        help
          This driver handles events on the power, sleep, and lid buttons.
-         A daemon reads /proc/acpi/event and perform user-defined actions
-         such as shutting down the system.  This is necessary for
-         software-controlled poweroff.
+         A daemon reads events from input devices or via netlink and
+         performs user-defined actions such as shutting down the system.
+         This is necessary for software-controlled poweroff.
 
          To compile this driver as a module, choose M here:
          the module will be called button.
index f40acef..a6977e1 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/ipmi.h>
 #include <linux/device.h>
 #include <linux/pnp.h>
+#include <linux/spinlock.h>
 
 MODULE_AUTHOR("Zhao Yakui");
 MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
@@ -57,7 +58,7 @@ struct acpi_ipmi_device {
        struct list_head head;
        /* the IPMI request message list */
        struct list_head tx_msg_list;
-       struct mutex    tx_msg_lock;
+       spinlock_t      tx_msg_lock;
        acpi_handle handle;
        struct pnp_dev *pnp_dev;
        ipmi_user_t     user_interface;
@@ -147,6 +148,7 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
        struct kernel_ipmi_msg *msg;
        struct acpi_ipmi_buffer *buffer;
        struct acpi_ipmi_device *device;
+       unsigned long flags;
 
        msg = &tx_msg->tx_message;
        /*
@@ -177,10 +179,10 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
 
        /* Get the msgid */
        device = tx_msg->device;
-       mutex_lock(&device->tx_msg_lock);
+       spin_lock_irqsave(&device->tx_msg_lock, flags);
        device->curr_msgid++;
        tx_msg->tx_msgid = device->curr_msgid;
-       mutex_unlock(&device->tx_msg_lock);
+       spin_unlock_irqrestore(&device->tx_msg_lock, flags);
 }
 
 static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
@@ -242,6 +244,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
        int msg_found = 0;
        struct acpi_ipmi_msg *tx_msg;
        struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+       unsigned long flags;
 
        if (msg->user != ipmi_device->user_interface) {
                dev_warn(&pnp_dev->dev, "Unexpected response is returned. "
@@ -250,7 +253,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
                ipmi_free_recv_msg(msg);
                return;
        }
-       mutex_lock(&ipmi_device->tx_msg_lock);
+       spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
        list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
                if (msg->msgid == tx_msg->tx_msgid) {
                        msg_found = 1;
@@ -258,7 +261,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
                }
        }
 
-       mutex_unlock(&ipmi_device->tx_msg_lock);
+       spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
        if (!msg_found) {
                dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is "
                        "returned.\n", msg->msgid);
@@ -378,6 +381,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
        struct acpi_ipmi_device *ipmi_device = handler_context;
        int err, rem_time;
        acpi_status status;
+       unsigned long flags;
        /*
         * IPMI opregion message.
         * IPMI message is firstly written to the BMC and system software
@@ -395,9 +399,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
                return AE_NO_MEMORY;
 
        acpi_format_ipmi_msg(tx_msg, address, value);
-       mutex_lock(&ipmi_device->tx_msg_lock);
+       spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
        list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
-       mutex_unlock(&ipmi_device->tx_msg_lock);
+       spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
        err = ipmi_request_settime(ipmi_device->user_interface,
                                        &tx_msg->addr,
                                        tx_msg->tx_msgid,
@@ -413,9 +417,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
        status = AE_OK;
 
 end_label:
-       mutex_lock(&ipmi_device->tx_msg_lock);
+       spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
        list_del(&tx_msg->head);
-       mutex_unlock(&ipmi_device->tx_msg_lock);
+       spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
        kfree(tx_msg);
        return status;
 }
@@ -457,7 +461,7 @@ static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device)
 
        INIT_LIST_HEAD(&ipmi_device->head);
 
-       mutex_init(&ipmi_device->tx_msg_lock);
+       spin_lock_init(&ipmi_device->tx_msg_lock);
        INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
        ipmi_install_space_handler(ipmi_device);
 
index 59d3202..a94383d 100644 (file)
@@ -1025,60 +1025,4 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off)
        }
 }
 EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
-
-/**
- * acpi_dev_pm_add_dependent - Add physical device depending for PM.
- * @handle: Handle of ACPI device node.
- * @depdev: Device depending on that node for PM.
- */
-void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev)
-{
-       struct acpi_device_physical_node *dep;
-       struct acpi_device *adev;
-
-       if (!depdev || acpi_bus_get_device(handle, &adev))
-               return;
-
-       mutex_lock(&adev->physical_node_lock);
-
-       list_for_each_entry(dep, &adev->power_dependent, node)
-               if (dep->dev == depdev)
-                       goto out;
-
-       dep = kzalloc(sizeof(*dep), GFP_KERNEL);
-       if (dep) {
-               dep->dev = depdev;
-               list_add_tail(&dep->node, &adev->power_dependent);
-       }
-
- out:
-       mutex_unlock(&adev->physical_node_lock);
-}
-EXPORT_SYMBOL_GPL(acpi_dev_pm_add_dependent);
-
-/**
- * acpi_dev_pm_remove_dependent - Remove physical device depending for PM.
- * @handle: Handle of ACPI device node.
- * @depdev: Device depending on that node for PM.
- */
-void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev)
-{
-       struct acpi_device_physical_node *dep;
-       struct acpi_device *adev;
-
-       if (!depdev || acpi_bus_get_device(handle, &adev))
-               return;
-
-       mutex_lock(&adev->physical_node_lock);
-
-       list_for_each_entry(dep, &adev->power_dependent, node)
-               if (dep->dev == depdev) {
-                       list_del(&dep->node);
-                       kfree(dep);
-                       break;
-               }
-
-       mutex_unlock(&adev->physical_node_lock);
-}
-EXPORT_SYMBOL_GPL(acpi_dev_pm_remove_dependent);
 #endif /* CONFIG_PM */
index 0dbe5cd..c2ad391 100644 (file)
@@ -59,16 +59,9 @@ ACPI_MODULE_NAME("power");
 #define ACPI_POWER_RESOURCE_STATE_ON   0x01
 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
 
-struct acpi_power_dependent_device {
-       struct list_head node;
-       struct acpi_device *adev;
-       struct work_struct work;
-};
-
 struct acpi_power_resource {
        struct acpi_device device;
        struct list_head list_node;
-       struct list_head dependent;
        char *name;
        u32 system_level;
        u32 order;
@@ -233,32 +226,6 @@ static int acpi_power_get_list_state(struct list_head *list, int *state)
        return 0;
 }
 
-static void acpi_power_resume_dependent(struct work_struct *work)
-{
-       struct acpi_power_dependent_device *dep;
-       struct acpi_device_physical_node *pn;
-       struct acpi_device *adev;
-       int state;
-
-       dep = container_of(work, struct acpi_power_dependent_device, work);
-       adev = dep->adev;
-       if (acpi_power_get_inferred_state(adev, &state))
-               return;
-
-       if (state > ACPI_STATE_D0)
-               return;
-
-       mutex_lock(&adev->physical_node_lock);
-
-       list_for_each_entry(pn, &adev->physical_node_list, node)
-               pm_request_resume(pn->dev);
-
-       list_for_each_entry(pn, &adev->power_dependent, node)
-               pm_request_resume(pn->dev);
-
-       mutex_unlock(&adev->physical_node_lock);
-}
-
 static int __acpi_power_on(struct acpi_power_resource *resource)
 {
        acpi_status status = AE_OK;
@@ -283,14 +250,8 @@ static int acpi_power_on_unlocked(struct acpi_power_resource *resource)
                                  resource->name));
        } else {
                result = __acpi_power_on(resource);
-               if (result) {
+               if (result)
                        resource->ref_count--;
-               } else {
-                       struct acpi_power_dependent_device *dep;
-
-                       list_for_each_entry(dep, &resource->dependent, node)
-                               schedule_work(&dep->work);
-               }
        }
        return result;
 }
@@ -390,52 +351,6 @@ static int acpi_power_on_list(struct list_head *list)
        return result;
 }
 
-static void acpi_power_add_dependent(struct acpi_power_resource *resource,
-                                    struct acpi_device *adev)
-{
-       struct acpi_power_dependent_device *dep;
-
-       mutex_lock(&resource->resource_lock);
-
-       list_for_each_entry(dep, &resource->dependent, node)
-               if (dep->adev == adev)
-                       goto out;
-
-       dep = kzalloc(sizeof(*dep), GFP_KERNEL);
-       if (!dep)
-               goto out;
-
-       dep->adev = adev;
-       INIT_WORK(&dep->work, acpi_power_resume_dependent);
-       list_add_tail(&dep->node, &resource->dependent);
-
- out:
-       mutex_unlock(&resource->resource_lock);
-}
-
-static void acpi_power_remove_dependent(struct acpi_power_resource *resource,
-                                       struct acpi_device *adev)
-{
-       struct acpi_power_dependent_device *dep;
-       struct work_struct *work = NULL;
-
-       mutex_lock(&resource->resource_lock);
-
-       list_for_each_entry(dep, &resource->dependent, node)
-               if (dep->adev == adev) {
-                       list_del(&dep->node);
-                       work = &dep->work;
-                       break;
-               }
-
-       mutex_unlock(&resource->resource_lock);
-
-       if (work) {
-               cancel_work_sync(work);
-               kfree(dep);
-       }
-}
-
 static struct attribute *attrs[] = {
        NULL,
 };
@@ -524,8 +439,6 @@ static void acpi_power_expose_hide(struct acpi_device *adev,
 
 void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
 {
-       struct acpi_device_power_state *ps;
-       struct acpi_power_resource_entry *entry;
        int state;
 
        if (adev->wakeup.flags.valid)
@@ -535,16 +448,6 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
        if (!adev->power.flags.power_resources)
                return;
 
-       ps = &adev->power.states[ACPI_STATE_D0];
-       list_for_each_entry(entry, &ps->resources, node) {
-               struct acpi_power_resource *resource = entry->resource;
-
-               if (add)
-                       acpi_power_add_dependent(resource, adev);
-               else
-                       acpi_power_remove_dependent(resource, adev);
-       }
-
        for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++)
                acpi_power_expose_hide(adev,
                                       &adev->power.states[state].resources,
@@ -882,7 +785,6 @@ int acpi_add_power_resource(acpi_handle handle)
        acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER,
                                ACPI_STA_DEFAULT);
        mutex_init(&resource->resource_lock);
-       INIT_LIST_HEAD(&resource->dependent);
        INIT_LIST_HEAD(&resource->list_node);
        resource->name = device->pnp.bus_id;
        strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
@@ -936,8 +838,10 @@ void acpi_resume_power_resources(void)
                mutex_lock(&resource->resource_lock);
 
                result = acpi_power_get_state(resource->device.handle, &state);
-               if (result)
+               if (result) {
+                       mutex_unlock(&resource->resource_lock);
                        continue;
+               }
 
                if (state == ACPI_POWER_RESOURCE_STATE_OFF
                    && resource->ref_count) {
index fbdb82e..fee8a29 100644 (file)
@@ -968,7 +968,7 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(acpi_bus_get_device);
+EXPORT_SYMBOL(acpi_bus_get_device);
 
 int acpi_device_add(struct acpi_device *device,
                    void (*release)(struct device *))
@@ -999,7 +999,6 @@ int acpi_device_add(struct acpi_device *device,
        INIT_LIST_HEAD(&device->wakeup_list);
        INIT_LIST_HEAD(&device->physical_node_list);
        mutex_init(&device->physical_node_lock);
-       INIT_LIST_HEAD(&device->power_dependent);
 
        new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);
        if (!new_bus_id) {
@@ -1121,7 +1120,7 @@ int acpi_bus_register_driver(struct acpi_driver *driver)
 EXPORT_SYMBOL(acpi_bus_register_driver);
 
 /**
- * acpi_bus_unregister_driver - unregisters a driver with the APIC bus
+ * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus
  * @driver: driver to unregister
  *
  * Unregisters a driver with the ACPI bus.  Searches the namespace for all
index 9d715ae..8e28f92 100644 (file)
@@ -1343,7 +1343,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
        else
-               printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n");
+               dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");
 
        if (pi.flags & ATA_FLAG_EM)
                ahci_reset_em(host);
index 2daaee0..7d3b853 100644 (file)
@@ -184,7 +184,7 @@ static int ahci_probe(struct platform_device *pdev)
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
        else
-               printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n");
+               dev_info(dev, "SSS flag set, parallel bus scan disabled\n");
 
        if (pi.flags & ATA_FLAG_EM)
                ahci_reset_em(host);
index acfd0f7..aaac4fb 100644 (file)
@@ -778,8 +778,16 @@ static void ahci_start_port(struct ata_port *ap)
                                rc = ap->ops->transmit_led_message(ap,
                                                               emp->led_state,
                                                               4);
+                               /*
+                                * If busy, give a breather but do not
+                                * release EH ownership by using msleep()
+                                * instead of ata_msleep().  EM Transmit
+                                * bit is busy for the whole host and
+                                * releasing ownership will cause other
+                                * ports to fail the same way.
+                                */
                                if (rc == -EBUSY)
-                                       ata_msleep(ap, 1);
+                                       msleep(1);
                                else
                                        break;
                        }
index 4ba8b04..ab714d2 100644 (file)
@@ -1035,17 +1035,3 @@ void ata_acpi_on_disable(struct ata_device *dev)
 {
        ata_acpi_clear_gtf(dev);
 }
-
-void ata_scsi_acpi_bind(struct ata_device *dev)
-{
-       acpi_handle handle = ata_dev_acpi_handle(dev);
-       if (handle)
-               acpi_dev_pm_add_dependent(handle, &dev->sdev->sdev_gendev);
-}
-
-void ata_scsi_acpi_unbind(struct ata_device *dev)
-{
-       acpi_handle handle = ata_dev_acpi_handle(dev);
-       if (handle)
-               acpi_dev_pm_remove_dependent(handle, &dev->sdev->sdev_gendev);
-}
index c69fcce..370462f 100644 (file)
@@ -1322,14 +1322,14 @@ void ata_eh_qc_complete(struct ata_queued_cmd *qc)
  *     should be retried.  To be used from EH.
  *
  *     SCSI midlayer limits the number of retries to scmd->allowed.
- *     scmd->retries is decremented for commands which get retried
+ *     scmd->allowed is incremented for commands which get retried
  *     due to unrelated failures (qc->err_mask is zero).
  */
 void ata_eh_qc_retry(struct ata_queued_cmd *qc)
 {
        struct scsi_cmnd *scmd = qc->scsicmd;
-       if (!qc->err_mask && scmd->retries)
-               scmd->retries--;
+       if (!qc->err_mask)
+               scmd->allowed++;
        __ata_eh_qc_complete(qc);
 }
 
index 97a0cef..db6dfcf 100644 (file)
@@ -3679,7 +3679,6 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
                        if (!IS_ERR(sdev)) {
                                dev->sdev = sdev;
                                scsi_device_put(sdev);
-                               ata_scsi_acpi_bind(dev);
                        } else {
                                dev->sdev = NULL;
                        }
@@ -3767,8 +3766,6 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
        struct scsi_device *sdev;
        unsigned long flags;
 
-       ata_scsi_acpi_unbind(dev);
-
        /* Alas, we need to grab scan_mutex to ensure SCSI device
         * state doesn't change underneath us and thus
         * scsi_device_get() always succeeds.  The mutex locking can
index eeeb778..45b5ab3 100644 (file)
@@ -121,8 +121,6 @@ extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state);
 extern void ata_acpi_bind_port(struct ata_port *ap);
 extern void ata_acpi_bind_dev(struct ata_device *dev);
 extern acpi_handle ata_dev_acpi_handle(struct ata_device *dev);
-extern void ata_scsi_acpi_bind(struct ata_device *dev);
-extern void ata_scsi_acpi_unbind(struct ata_device *dev);
 #else
 static inline void ata_acpi_dissociate(struct ata_host *host) { }
 static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; }
@@ -133,8 +131,6 @@ static inline void ata_acpi_set_state(struct ata_port *ap,
                                      pm_message_t state) { }
 static inline void ata_acpi_bind_port(struct ata_port *ap) {}
 static inline void ata_acpi_bind_dev(struct ata_device *dev) {}
-static inline void ata_scsi_acpi_bind(struct ata_device *dev) {}
-static inline void ata_scsi_acpi_unbind(struct ata_device *dev) {}
 #endif
 
 /* libata-scsi.c */
index 4bceb88..b33d1f9 100644 (file)
@@ -78,7 +78,7 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev
 
        ap->ioaddr.cmd_addr = cmd_addr;
 
-       if (pnp_port_valid(idev, 1) == 0) {
+       if (pnp_port_valid(idev, 1)) {
                ctl_addr = devm_ioport_map(&idev->dev,
                                           pnp_port_start(idev, 1), 1);
                ap->ioaddr.altstatus_addr = ctl_addr;
index 958ba2a..97f4acb 100644 (file)
@@ -2,7 +2,7 @@
  *  sata_promise.c - Promise SATA
  *
  *  Maintained by:  Tejun Heo <tj@kernel.org>
- *                 Mikael Pettersson <mikpe@it.uu.se>
+ *                 Mikael Pettersson
  *                 Please ALWAYS copy linux-ide@vger.kernel.org
  *                 on emails.
  *
index 49e783e..364eded 100644 (file)
@@ -420,7 +420,6 @@ struct fs_transmit_config {
 #define RC_FLAGS_BFPS_BFP27 (0xd << 17)
 #define RC_FLAGS_BFPS_BFP47 (0xe << 17)
 
-#define RC_FLAGS_BFPS       (0x1 << 17)
 #define RC_FLAGS_BFPP       (0x1 << 21)
 #define RC_FLAGS_TEVC       (0x1 << 22)
 #define RC_FLAGS_TEP        (0x1 << 23)
index c7cfadc..34abf4d 100644 (file)
@@ -2017,7 +2017,7 @@ EXPORT_SYMBOL_GPL(device_move);
  */
 void device_shutdown(void)
 {
-       struct device *dev;
+       struct device *dev, *parent;
 
        spin_lock(&devices_kset->list_lock);
        /*
@@ -2034,7 +2034,7 @@ void device_shutdown(void)
                 * prevent it from being freed because parent's
                 * lock is to be held
                 */
-               get_device(dev->parent);
+               parent = get_device(dev->parent);
                get_device(dev);
                /*
                 * Make sure the device is off the kset list, in the
@@ -2044,8 +2044,8 @@ void device_shutdown(void)
                spin_unlock(&devices_kset->list_lock);
 
                /* hold lock to avoid race with probe/release */
-               if (dev->parent)
-                       device_lock(dev->parent);
+               if (parent)
+                       device_lock(parent);
                device_lock(dev);
 
                /* Don't allow any more runtime suspends */
@@ -2063,11 +2063,11 @@ void device_shutdown(void)
                }
 
                device_unlock(dev);
-               if (dev->parent)
-                       device_unlock(dev->parent);
+               if (parent)
+                       device_unlock(parent);
 
                put_device(dev);
-               put_device(dev->parent);
+               put_device(parent);
 
                spin_lock(&devices_kset->list_lock);
        }
index 9e59f65..bece691 100644 (file)
@@ -333,8 +333,10 @@ store_mem_state(struct device *dev,
                online_type = ONLINE_KEEP;
        else if (!strncmp(buf, "offline", min_t(int, count, 7)))
                online_type = -1;
-       else
-               return -EINVAL;
+       else {
+               ret = -EINVAL;
+               goto err;
+       }
 
        switch (online_type) {
        case ONLINE_KERNEL:
@@ -357,6 +359,7 @@ store_mem_state(struct device *dev,
                ret = -EINVAL; /* should never happen */
        }
 
+err:
        unlock_device_hotplug();
 
        if (ret)
index a355e63..6fb98b5 100644 (file)
@@ -188,8 +188,11 @@ static int bcma_host_pci_probe(struct pci_dev *dev,
                pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
 
        /* SSB needed additional powering up, do we have any AMBA PCI cards? */
-       if (!pci_is_pcie(dev))
-               bcma_err(bus, "PCI card detected, report problems.\n");
+       if (!pci_is_pcie(dev)) {
+               bcma_err(bus, "PCI card detected, they are not supported.\n");
+               err = -ENXIO;
+               goto err_pci_release_regions;
+       }
 
        /* Map MMIO */
        err = -ENOMEM;
@@ -269,6 +272,7 @@ static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend,
 
 static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4313) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
index d2d95ff..edfa251 100644 (file)
@@ -1189,6 +1189,7 @@ static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode,
        int err;
        u32 cp;
 
+       memset(&arg64, 0, sizeof(arg64));
        err = 0;
        err |=
            copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
index 639d26b..2b94403 100644 (file)
@@ -1193,6 +1193,7 @@ out_passthru:
                ida_pci_info_struct pciinfo;
 
                if (!arg) return -EINVAL;
+               memset(&pciinfo, 0, sizeof(pciinfo));
                pciinfo.bus = host->pci_dev->bus->number;
                pciinfo.dev_fn = host->pci_dev->devfn;
                pciinfo.board_id = host->board_id;
index 4afae20..9fe8a87 100644 (file)
@@ -30,3 +30,5 @@ hci_uart-$(CONFIG_BT_HCIUART_LL)      += hci_ll.o
 hci_uart-$(CONFIG_BT_HCIUART_ATH3K)    += hci_ath.o
 hci_uart-$(CONFIG_BT_HCIUART_3WIRE)    += hci_h5.o
 hci_uart-objs                          := $(hci_uart-y)
+
+ccflags-y += -D__CHECK_ENDIAN__
index 0a327f4..6bfc1bb 100644 (file)
@@ -57,7 +57,7 @@ struct ath3k_version {
        unsigned char   reserved[0x07];
 };
 
-static struct usb_device_id ath3k_table[] = {
+static const struct usb_device_id ath3k_table[] = {
        /* Atheros AR3011 */
        { USB_DEVICE(0x0CF3, 0x3000) },
 
@@ -112,7 +112,7 @@ MODULE_DEVICE_TABLE(usb, ath3k_table);
 #define BTUSB_ATH3012          0x80
 /* This table is to load patch and sysconfig files
  * for AR3012 */
-static struct usb_device_id ath3k_blist_tbl[] = {
+static const struct usb_device_id ath3k_blist_tbl[] = {
 
        /* Atheros AR3012 with sflash firmware*/
        { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
index 995aee9..3138699 100644 (file)
@@ -42,7 +42,7 @@
 
 static struct usb_driver bfusb_driver;
 
-static struct usb_device_id bfusb_table[] = {
+static const struct usb_device_id bfusb_table[] = {
        /* AVM BlueFRITZ! USB */
        { USB_DEVICE(0x057c, 0x2200) },
 
@@ -318,7 +318,6 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
                        return -ENOMEM;
                }
 
-               skb->dev = (void *) data->hdev;
                bt_cb(skb)->pkt_type = pkt_type;
 
                data->reassembly = skb;
@@ -333,7 +332,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
                memcpy(skb_put(data->reassembly, len), buf, len);
 
        if (hdr & 0x08) {
-               hci_recv_frame(data->reassembly);
+               hci_recv_frame(data->hdev, data->reassembly);
                data->reassembly = NULL;
        }
 
@@ -465,26 +464,18 @@ static int bfusb_close(struct hci_dev *hdev)
        return 0;
 }
 
-static int bfusb_send_frame(struct sk_buff *skb)
+static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-       struct bfusb_data *data;
+       struct bfusb_data *data = hci_get_drvdata(hdev);
        struct sk_buff *nskb;
        unsigned char buf[3];
        int sent = 0, size, count;
 
        BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
-       data = hci_get_drvdata(hdev);
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -544,11 +535,6 @@ static int bfusb_send_frame(struct sk_buff *skb)
        return 0;
 }
 
-static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
 static int bfusb_load_firmware(struct bfusb_data *data,
                               const unsigned char *firmware, int count)
 {
@@ -699,11 +685,10 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
        hci_set_drvdata(hdev, data);
        SET_HCIDEV_DEV(hdev, &intf->dev);
 
-       hdev->open     = bfusb_open;
-       hdev->close    = bfusb_close;
-       hdev->flush    = bfusb_flush;
-       hdev->send     = bfusb_send_frame;
-       hdev->ioctl    = bfusb_ioctl;
+       hdev->open  = bfusb_open;
+       hdev->close = bfusb_close;
+       hdev->flush = bfusb_flush;
+       hdev->send  = bfusb_send_frame;
 
        if (hci_register_dev(hdev) < 0) {
                BT_ERR("Can't register HCI device");
index 6c3e3d4..57427de 100644 (file)
@@ -399,7 +399,6 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
 
                if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
 
-                       info->rx_skb->dev = (void *) info->hdev;
                        bt_cb(info->rx_skb)->pkt_type = buf[i];
 
                        switch (bt_cb(info->rx_skb)->pkt_type) {
@@ -477,7 +476,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
                                        break;
 
                                case RECV_WAIT_DATA:
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        info->rx_skb = NULL;
                                        break;
 
@@ -659,17 +658,9 @@ static int bluecard_hci_close(struct hci_dev *hdev)
 }
 
 
-static int bluecard_hci_send_frame(struct sk_buff *skb)
+static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       bluecard_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
+       bluecard_info_t *info = hci_get_drvdata(hdev);
 
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
@@ -693,12 +684,6 @@ static int bluecard_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -734,11 +719,10 @@ static int bluecard_open(bluecard_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = bluecard_hci_open;
-       hdev->close    = bluecard_hci_close;
-       hdev->flush    = bluecard_hci_flush;
-       hdev->send     = bluecard_hci_send_frame;
-       hdev->ioctl    = bluecard_hci_ioctl;
+       hdev->open  = bluecard_hci_open;
+       hdev->close = bluecard_hci_close;
+       hdev->flush = bluecard_hci_flush;
+       hdev->send  = bluecard_hci_send_frame;
 
        id = inb(iobase + 0x30);
 
index 2fe4a80..8a31991 100644 (file)
@@ -37,7 +37,7 @@
 
 #define VERSION "0.10"
 
-static struct usb_device_id bpa10x_table[] = {
+static const struct usb_device_id bpa10x_table[] = {
        /* Tektronix BPA 100/105 (Digianswer) */
        { USB_DEVICE(0x08fd, 0x0002) },
 
@@ -129,8 +129,6 @@ static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
                                return -ENOMEM;
                        }
 
-                       skb->dev = (void *) hdev;
-
                        data->rx_skb[queue] = skb;
 
                        scb = (void *) skb->cb;
@@ -155,7 +153,7 @@ static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
                        data->rx_skb[queue] = NULL;
 
                        bt_cb(skb)->pkt_type = scb->type;
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
                }
 
                count -= len; buf += len;
@@ -352,9 +350,8 @@ static int bpa10x_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int bpa10x_send_frame(struct sk_buff *skb)
+static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct bpa10x_data *data = hci_get_drvdata(hdev);
        struct usb_ctrlrequest *dr;
        struct urb *urb;
@@ -366,6 +363,8 @@ static int bpa10x_send_frame(struct sk_buff *skb)
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
+       skb->dev = (void *) hdev;
+
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb)
                return -ENOMEM;
index a1aaa3b..73d8799 100644 (file)
@@ -247,7 +247,6 @@ static void bt3c_receive(bt3c_info_t *info)
 
                if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
 
-                       info->rx_skb->dev = (void *) info->hdev;
                        bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
                        inb(iobase + DATA_H);
                        //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
@@ -318,7 +317,7 @@ static void bt3c_receive(bt3c_info_t *info)
                                        break;
 
                                case RECV_WAIT_DATA:
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        info->rx_skb = NULL;
                                        break;
 
@@ -416,19 +415,11 @@ static int bt3c_hci_close(struct hci_dev *hdev)
 }
 
 
-static int bt3c_hci_send_frame(struct sk_buff *skb)
+static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       bt3c_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+       bt3c_info_t *info = hci_get_drvdata(hdev);
        unsigned long flags;
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -455,12 +446,6 @@ static int bt3c_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -577,11 +562,10 @@ static int bt3c_open(bt3c_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = bt3c_hci_open;
-       hdev->close    = bt3c_hci_close;
-       hdev->flush    = bt3c_hci_flush;
-       hdev->send     = bt3c_hci_send_frame;
-       hdev->ioctl    = bt3c_hci_ioctl;
+       hdev->open  = bt3c_hci_open;
+       hdev->close = bt3c_hci_close;
+       hdev->flush = bt3c_hci_flush;
+       hdev->send  = bt3c_hci_send_frame;
 
        /* Load firmware */
        err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
index 27068d1..f9d1833 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <net/bluetooth/bluetooth.h>
+#include <linux/ctype.h>
+#include <linux/firmware.h>
 
 #define BTM_HEADER_LEN                 4
 #define BTM_UPLD_SIZE                  2312
@@ -41,6 +43,8 @@ struct btmrvl_thread {
 struct btmrvl_device {
        void *card;
        struct hci_dev *hcidev;
+       struct device *dev;
+       const char *cal_data;
 
        u8 dev_type;
 
@@ -91,6 +95,7 @@ struct btmrvl_private {
 #define BT_CMD_HOST_SLEEP_CONFIG       0x59
 #define BT_CMD_HOST_SLEEP_ENABLE       0x5A
 #define BT_CMD_MODULE_CFG_REQ          0x5B
+#define BT_CMD_LOAD_CONFIG_DATA                0x61
 
 /* Sub-commands: Module Bringup/Shutdown Request/Response */
 #define MODULE_BRINGUP_REQ             0xF1
@@ -116,11 +121,8 @@ struct btmrvl_private {
 #define PS_SLEEP                       0x01
 #define PS_AWAKE                       0x00
 
-struct btmrvl_cmd {
-       __le16 ocf_ogf;
-       u8 length;
-       u8 data[4];
-} __packed;
+#define BT_CMD_DATA_SIZE               32
+#define BT_CAL_DATA_SIZE               28
 
 struct btmrvl_event {
        u8 ec;          /* event counter */
index 9a9f518..5cf31c4 100644 (file)
@@ -57,8 +57,7 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
                ocf = hci_opcode_ocf(opcode);
                ogf = hci_opcode_ogf(opcode);
 
-               if (ocf == BT_CMD_MODULE_CFG_REQ &&
-                                       priv->btmrvl_dev.sendcmdflag) {
+               if (priv->btmrvl_dev.sendcmdflag) {
                        priv->btmrvl_dev.sendcmdflag = false;
                        priv->adapter->cmd_complete = true;
                        wake_up_interruptible(&priv->adapter->cmd_wait_q);
@@ -116,7 +115,6 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
                        adapter->hs_state = HS_ACTIVATED;
                        if (adapter->psmode)
                                adapter->ps_state = PS_SLEEP;
-                       wake_up_interruptible(&adapter->cmd_wait_q);
                        BT_DBG("HS ACTIVATED!");
                } else {
                        BT_DBG("HS Enable failed");
@@ -168,45 +166,50 @@ exit:
 }
 EXPORT_SYMBOL_GPL(btmrvl_process_event);
 
-int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 cmd_no,
+                               const void *param, u8 len)
 {
        struct sk_buff *skb;
-       struct btmrvl_cmd *cmd;
-       int ret = 0;
+       struct hci_command_hdr *hdr;
 
-       skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+       skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
        if (skb == NULL) {
                BT_ERR("No free skb");
                return -ENOMEM;
        }
 
-       cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
-       cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ));
-       cmd->length = 1;
-       cmd->data[0] = subcmd;
+       hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE);
+       hdr->opcode = cpu_to_le16(hci_opcode_pack(OGF, cmd_no));
+       hdr->plen = len;
+
+       if (len)
+               memcpy(skb_put(skb, len), param, len);
 
        bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
 
-       skb->dev = (void *) priv->btmrvl_dev.hcidev;
        skb_queue_head(&priv->adapter->tx_queue, skb);
 
        priv->btmrvl_dev.sendcmdflag = true;
 
        priv->adapter->cmd_complete = false;
 
-       BT_DBG("Queue module cfg Command");
-
        wake_up_interruptible(&priv->main_thread.wait_q);
 
        if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
                                priv->adapter->cmd_complete,
-                               msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) {
-               ret = -ETIMEDOUT;
-               BT_ERR("module_cfg_cmd(%x): timeout: %d",
-                                       subcmd, priv->btmrvl_dev.sendcmdflag);
-       }
+                               msecs_to_jiffies(WAIT_UNTIL_CMD_RESP)))
+               return -ETIMEDOUT;
+
+       return 0;
+}
 
-       BT_DBG("module cfg Command done");
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+{
+       int ret;
+
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);
+       if (ret)
+               BT_ERR("module_cfg_cmd(%x) failed\n", subcmd);
 
        return ret;
 }
@@ -214,61 +217,36 @@ EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
 
 int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
 {
-       struct sk_buff *skb;
-       struct btmrvl_cmd *cmd;
-
-       skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
-       if (!skb) {
-               BT_ERR("No free skb");
-               return -ENOMEM;
-       }
-
-       cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
-       cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
-                                                  BT_CMD_HOST_SLEEP_CONFIG));
-       cmd->length = 2;
-       cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
-       cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
+       int ret;
+       u8 param[2];
 
-       bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+       param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
+       param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
 
-       skb->dev = (void *) priv->btmrvl_dev.hcidev;
-       skb_queue_head(&priv->adapter->tx_queue, skb);
+       BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x",
+              param[0], param[1]);
 
-       BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x", cmd->data[0],
-              cmd->data[1]);
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);
+       if (ret)
+               BT_ERR("HSCFG command failed\n");
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd);
 
 int btmrvl_enable_ps(struct btmrvl_private *priv)
 {
-       struct sk_buff *skb;
-       struct btmrvl_cmd *cmd;
-
-       skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
-       if (skb == NULL) {
-               BT_ERR("No free skb");
-               return -ENOMEM;
-       }
-
-       cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
-       cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
-                                       BT_CMD_AUTO_SLEEP_MODE));
-       cmd->length = 1;
+       int ret;
+       u8 param;
 
        if (priv->btmrvl_dev.psmode)
-               cmd->data[0] = BT_PS_ENABLE;
+               param = BT_PS_ENABLE;
        else
-               cmd->data[0] = BT_PS_DISABLE;
+               param = BT_PS_DISABLE;
 
-       bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
-
-       skb->dev = (void *) priv->btmrvl_dev.hcidev;
-       skb_queue_head(&priv->adapter->tx_queue, skb);
-
-       BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, &param, 1);
+       if (ret)
+               BT_ERR("PSMODE command failed\n");
 
        return 0;
 }
@@ -276,37 +254,11 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
 
 int btmrvl_enable_hs(struct btmrvl_private *priv)
 {
-       struct sk_buff *skb;
-       struct btmrvl_cmd *cmd;
-       int ret = 0;
-
-       skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
-       if (skb == NULL) {
-               BT_ERR("No free skb");
-               return -ENOMEM;
-       }
-
-       cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
-       cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE));
-       cmd->length = 0;
-
-       bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
-
-       skb->dev = (void *) priv->btmrvl_dev.hcidev;
-       skb_queue_head(&priv->adapter->tx_queue, skb);
-
-       BT_DBG("Queue hs enable Command");
-
-       wake_up_interruptible(&priv->main_thread.wait_q);
+       int ret;
 
-       if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
-                       priv->adapter->hs_state,
-                       msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) {
-               ret = -ETIMEDOUT;
-               BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state,
-                                               priv->adapter->ps_state,
-                                               priv->adapter->wakeup_tries);
-       }
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
+       if (ret)
+               BT_ERR("Host sleep enable command failed\n");
 
        return ret;
 }
@@ -403,26 +355,12 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv)
        priv->adapter = NULL;
 }
 
-static int btmrvl_ioctl(struct hci_dev *hdev,
-                               unsigned int cmd, unsigned long arg)
+static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       return -ENOIOCTLCMD;
-}
-
-static int btmrvl_send_frame(struct sk_buff *skb)
-{
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-       struct btmrvl_private *priv = NULL;
+       struct btmrvl_private *priv = hci_get_drvdata(hdev);
 
        BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device");
-               return -ENODEV;
-       }
-
-       priv = hci_get_drvdata(hdev);
-
        if (!test_bit(HCI_RUNNING, &hdev->flags)) {
                BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
                print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
@@ -479,6 +417,137 @@ static int btmrvl_open(struct hci_dev *hdev)
        return 0;
 }
 
+/*
+ * This function parses provided calibration data input. It should contain
+ * hex bytes separated by space or new line character. Here is an example.
+ * 00 1C 01 37 FF FF FF FF 02 04 7F 01
+ * CE BA 00 00 00 2D C6 C0 00 00 00 00
+ * 00 F0 00 00
+ */
+static int btmrvl_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 dst_size)
+{
+       const u8 *s = src;
+       u8 *d = dst;
+       int ret;
+       u8 tmp[3];
+
+       tmp[2] = '\0';
+       while ((s - src) <= len - 2) {
+               if (isspace(*s)) {
+                       s++;
+                       continue;
+               }
+
+               if (isxdigit(*s)) {
+                       if ((d - dst) >= dst_size) {
+                               BT_ERR("calibration data file too big!!!");
+                               return -EINVAL;
+                       }
+
+                       memcpy(tmp, s, 2);
+
+                       ret = kstrtou8(tmp, 16, d++);
+                       if (ret < 0)
+                               return ret;
+
+                       s += 2;
+               } else {
+                       return -EINVAL;
+               }
+       }
+       if (d == dst)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int btmrvl_load_cal_data(struct btmrvl_private *priv,
+                               u8 *config_data)
+{
+       int i, ret;
+       u8 data[BT_CMD_DATA_SIZE];
+
+       data[0] = 0x00;
+       data[1] = 0x00;
+       data[2] = 0x00;
+       data[3] = BT_CMD_DATA_SIZE - 4;
+
+       /* Swap cal-data bytes. Each four bytes are swapped. Considering 4
+        * byte SDIO header offset, mapping of input and output bytes will be
+        * {3, 2, 1, 0} -> {0+4, 1+4, 2+4, 3+4},
+        * {7, 6, 5, 4} -> {4+4, 5+4, 6+4, 7+4} */
+       for (i = 4; i < BT_CMD_DATA_SIZE; i++)
+               data[i] = config_data[(i / 4) * 8 - 1 - i];
+
+       print_hex_dump_bytes("Calibration data: ",
+                            DUMP_PREFIX_OFFSET, data, BT_CMD_DATA_SIZE);
+
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
+                                  BT_CMD_DATA_SIZE);
+       if (ret)
+               BT_ERR("Failed to download caibration data\n");
+
+       return 0;
+}
+
+static int
+btmrvl_process_cal_cfg(struct btmrvl_private *priv, u8 *data, u32 size)
+{
+       u8 cal_data[BT_CAL_DATA_SIZE];
+       int ret;
+
+       ret = btmrvl_parse_cal_cfg(data, size, cal_data, sizeof(cal_data));
+       if (ret)
+               return ret;
+
+       ret = btmrvl_load_cal_data(priv, cal_data);
+       if (ret) {
+               BT_ERR("Fail to load calibrate data");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int btmrvl_cal_data_config(struct btmrvl_private *priv)
+{
+       const struct firmware *cfg;
+       int ret;
+       const char *cal_data = priv->btmrvl_dev.cal_data;
+
+       if (!cal_data)
+               return 0;
+
+       ret = request_firmware(&cfg, cal_data, priv->btmrvl_dev.dev);
+       if (ret < 0) {
+               BT_DBG("Failed to get %s file, skipping cal data download",
+                      cal_data);
+               return 0;
+       }
+
+       ret = btmrvl_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size);
+       release_firmware(cfg);
+       return ret;
+}
+
+static int btmrvl_setup(struct hci_dev *hdev)
+{
+       struct btmrvl_private *priv = hci_get_drvdata(hdev);
+
+       btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+
+       if (btmrvl_cal_data_config(priv))
+               BT_ERR("Set cal data failed");
+
+       priv->btmrvl_dev.psmode = 1;
+       btmrvl_enable_ps(priv);
+
+       priv->btmrvl_dev.gpio_gap = 0xffff;
+       btmrvl_send_hscfg_cmd(priv);
+
+       return 0;
+}
+
 /*
  * This function handles the event generated by firmware, rx data
  * received from firmware, and tx data sent from kernel.
@@ -566,14 +635,12 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
        priv->btmrvl_dev.hcidev = hdev;
        hci_set_drvdata(hdev, priv);
 
-       hdev->bus = HCI_SDIO;
-       hdev->open = btmrvl_open;
+       hdev->bus   = HCI_SDIO;
+       hdev->open  = btmrvl_open;
        hdev->close = btmrvl_close;
        hdev->flush = btmrvl_flush;
-       hdev->send = btmrvl_send_frame;
-       hdev->ioctl = btmrvl_ioctl;
-
-       btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+       hdev->send  = btmrvl_send_frame;
+       hdev->setup = btmrvl_setup;
 
        hdev->dev_type = priv->btmrvl_dev.dev_type;
 
index 00da6df..fabcf5b 100644 (file)
@@ -18,7 +18,6 @@
  * this warranty disclaimer.
  **/
 
-#include <linux/firmware.h>
 #include <linux/slab.h>
 
 #include <linux/mmc/sdio_ids.h>
@@ -102,6 +101,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
        .helper         = "mrvl/sd8688_helper.bin",
        .firmware       = "mrvl/sd8688.bin",
+       .cal_data       = NULL,
        .reg            = &btmrvl_reg_8688,
        .sd_blksz_fw_dl = 64,
 };
@@ -109,6 +109,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8787_uapsta.bin",
+       .cal_data       = NULL,
        .reg            = &btmrvl_reg_87xx,
        .sd_blksz_fw_dl = 256,
 };
@@ -116,6 +117,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8797_uapsta.bin",
+       .cal_data       = "mrvl/sd8797_caldata.conf",
        .reg            = &btmrvl_reg_87xx,
        .sd_blksz_fw_dl = 256,
 };
@@ -123,6 +125,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8897_uapsta.bin",
+       .cal_data       = NULL,
        .reg            = &btmrvl_reg_88xx,
        .sd_blksz_fw_dl = 256,
 };
@@ -597,15 +600,14 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
        case HCI_SCODATA_PKT:
        case HCI_EVENT_PKT:
                bt_cb(skb)->pkt_type = type;
-               skb->dev = (void *)hdev;
                skb_put(skb, buf_len);
                skb_pull(skb, SDIO_HEADER_LEN);
 
                if (type == HCI_EVENT_PKT) {
                        if (btmrvl_check_evtpkt(priv, skb))
-                               hci_recv_frame(skb);
+                               hci_recv_frame(hdev, skb);
                } else {
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
                }
 
                hdev->stat.byte_rx += buf_len;
@@ -613,12 +615,11 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
 
        case MRVL_VENDOR_PKT:
                bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
-               skb->dev = (void *)hdev;
                skb_put(skb, buf_len);
                skb_pull(skb, SDIO_HEADER_LEN);
 
                if (btmrvl_process_event(priv, skb))
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
 
                hdev->stat.byte_rx += buf_len;
                break;
@@ -1006,6 +1007,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
                struct btmrvl_sdio_device *data = (void *) id->driver_data;
                card->helper = data->helper;
                card->firmware = data->firmware;
+               card->cal_data = data->cal_data;
                card->reg = data->reg;
                card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
        }
@@ -1034,6 +1036,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
        }
 
        card->priv = priv;
+       priv->btmrvl_dev.dev = &card->func->dev;
+       priv->btmrvl_dev.cal_data = card->cal_data;
 
        /* Initialize the interface specific function pointers */
        priv->hw_host_to_card = btmrvl_sdio_host_to_card;
@@ -1046,12 +1050,6 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
                goto disable_host_int;
        }
 
-       priv->btmrvl_dev.psmode = 1;
-       btmrvl_enable_ps(priv);
-
-       priv->btmrvl_dev.gpio_gap = 0xffff;
-       btmrvl_send_hscfg_cmd(priv);
-
        return 0;
 
 disable_host_int:
@@ -1222,4 +1220,5 @@ MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
 MODULE_FIRMWARE("mrvl/sd8688.bin");
 MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8797_caldata.conf");
 MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
index 43d35a6..6872d9e 100644 (file)
@@ -85,6 +85,7 @@ struct btmrvl_sdio_card {
        u32 ioport;
        const char *helper;
        const char *firmware;
+       const char *cal_data;
        const struct btmrvl_sdio_card_reg *reg;
        u16 sd_blksz_fw_dl;
        u8 rx_unit;
@@ -94,6 +95,7 @@ struct btmrvl_sdio_card {
 struct btmrvl_sdio_device {
        const char *helper;
        const char *firmware;
+       const char *cal_data;
        const struct btmrvl_sdio_card_reg *reg;
        u16 sd_blksz_fw_dl;
 };
index 4a99097..b61440a 100644 (file)
@@ -157,10 +157,9 @@ static int btsdio_rx_packet(struct btsdio_data *data)
 
        data->hdev->stat.byte_rx += len;
 
-       skb->dev = (void *) data->hdev;
        bt_cb(skb)->pkt_type = hdr[3];
 
-       err = hci_recv_frame(skb);
+       err = hci_recv_frame(data->hdev, skb);
        if (err < 0)
                return err;
 
@@ -255,9 +254,8 @@ static int btsdio_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int btsdio_send_frame(struct sk_buff *skb)
+static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct btsdio_data *data = hci_get_drvdata(hdev);
 
        BT_DBG("%s", hdev->name);
index beb262f..a03ecc2 100644 (file)
@@ -198,7 +198,6 @@ static void btuart_receive(btuart_info_t *info)
 
                if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
 
-                       info->rx_skb->dev = (void *) info->hdev;
                        bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
 
                        switch (bt_cb(info->rx_skb)->pkt_type) {
@@ -265,7 +264,7 @@ static void btuart_receive(btuart_info_t *info)
                                        break;
 
                                case RECV_WAIT_DATA:
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        info->rx_skb = NULL;
                                        break;
 
@@ -424,17 +423,9 @@ static int btuart_hci_close(struct hci_dev *hdev)
 }
 
 
-static int btuart_hci_send_frame(struct sk_buff *skb)
+static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       btuart_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
+       btuart_info_t *info = hci_get_drvdata(hdev);
 
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
@@ -458,12 +449,6 @@ static int btuart_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -495,11 +480,10 @@ static int btuart_open(btuart_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = btuart_hci_open;
-       hdev->close    = btuart_hci_close;
-       hdev->flush    = btuart_hci_flush;
-       hdev->send     = btuart_hci_send_frame;
-       hdev->ioctl    = btuart_hci_ioctl;
+       hdev->open  = btuart_hci_open;
+       hdev->close = btuart_hci_close;
+       hdev->flush = btuart_hci_flush;
+       hdev->send  = btuart_hci_send_frame;
 
        spin_lock_irqsave(&(info->lock), flags);
 
index f3dfc0a..c0ff34f 100644 (file)
@@ -50,7 +50,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_ATH3012          0x80
 #define BTUSB_INTEL            0x100
 
-static struct usb_device_id btusb_table[] = {
+static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
        { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
 
@@ -121,7 +121,7 @@ static struct usb_device_id btusb_table[] = {
 
 MODULE_DEVICE_TABLE(usb, btusb_table);
 
-static struct usb_device_id blacklist_table[] = {
+static const struct usb_device_id blacklist_table[] = {
        /* CSR BlueCore devices */
        { USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
 
@@ -716,9 +716,8 @@ static int btusb_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int btusb_send_frame(struct sk_buff *skb)
+static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        struct btusb_data *data = hci_get_drvdata(hdev);
        struct usb_ctrlrequest *dr;
        struct urb *urb;
@@ -730,6 +729,8 @@ static int btusb_send_frame(struct sk_buff *skb)
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
+       skb->dev = (void *) hdev;
+
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -774,7 +775,7 @@ static int btusb_send_frame(struct sk_buff *skb)
                break;
 
        case HCI_SCODATA_PKT:
-               if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
+               if (!data->isoc_tx_ep || hci_conn_num(hdev, SCO_LINK) < 1)
                        return -ENODEV;
 
                urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
@@ -833,8 +834,8 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
 
        BT_DBG("%s evt %d", hdev->name, evt);
 
-       if (hdev->conn_hash.sco_num != data->sco_num) {
-               data->sco_num = hdev->conn_hash.sco_num;
+       if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
+               data->sco_num = hci_conn_num(hdev, SCO_LINK);
                schedule_work(&data->work);
        }
 }
@@ -889,7 +890,7 @@ static void btusb_work(struct work_struct *work)
        int new_alts;
        int err;
 
-       if (hdev->conn_hash.sco_num > 0) {
+       if (data->sco_num > 0) {
                if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
                        err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
                        if (err < 0) {
@@ -903,9 +904,9 @@ static void btusb_work(struct work_struct *work)
 
                if (hdev->voice_setting & 0x0020) {
                        static const int alts[3] = { 2, 4, 5 };
-                       new_alts = alts[hdev->conn_hash.sco_num - 1];
+                       new_alts = alts[data->sco_num - 1];
                } else {
-                       new_alts = hdev->conn_hash.sco_num;
+                       new_alts = data->sco_num;
                }
 
                if (data->isoc_altsetting != new_alts) {
@@ -1628,7 +1629,6 @@ static struct usb_driver btusb_driver = {
 #ifdef CONFIG_PM
        .suspend        = btusb_suspend,
        .resume         = btusb_resume,
-       .reset_resume   = btusb_resume,
 #endif
        .id_table       = btusb_table,
        .supports_autosuspend = 1,
index 60abf59..f038dba 100644 (file)
@@ -108,10 +108,8 @@ static long st_receive(void *priv_data, struct sk_buff *skb)
                return -EFAULT;
        }
 
-       skb->dev = (void *) lhst->hdev;
-
        /* Forward skb to HCI core layer */
-       err = hci_recv_frame(skb);
+       err = hci_recv_frame(lhst->hdev, skb);
        if (err < 0) {
                BT_ERR("Unable to push skb to HCI core(%d)", err);
                return err;
@@ -253,14 +251,11 @@ static int ti_st_close(struct hci_dev *hdev)
        return err;
 }
 
-static int ti_st_send_frame(struct sk_buff *skb)
+static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev;
        struct ti_st *hst;
        long len;
 
-       hdev = (struct hci_dev *)skb->dev;
-
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
index 33f3a69..52eed1f 100644 (file)
@@ -256,9 +256,8 @@ static void dtl1_receive(dtl1_info_t *info)
                                case 0x83:
                                case 0x84:
                                        /* send frame to the HCI layer */
-                                       info->rx_skb->dev = (void *) info->hdev;
                                        bt_cb(info->rx_skb)->pkt_type &= 0x0f;
-                                       hci_recv_frame(info->rx_skb);
+                                       hci_recv_frame(info->hdev, info->rx_skb);
                                        break;
                                default:
                                        /* unknown packet */
@@ -383,20 +382,12 @@ static int dtl1_hci_close(struct hci_dev *hdev)
 }
 
 
-static int dtl1_hci_send_frame(struct sk_buff *skb)
+static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       dtl1_info_t *info;
-       struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+       dtl1_info_t *info = hci_get_drvdata(hdev);
        struct sk_buff *s;
        nsh_t nsh;
 
-       if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
-       }
-
-       info = hci_get_drvdata(hdev);
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -438,12 +429,6 @@ static int dtl1_hci_send_frame(struct sk_buff *skb)
 }
 
 
-static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,  unsigned long arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-
 
 /* ======================== Card services HCI interaction ======================== */
 
@@ -477,11 +462,10 @@ static int dtl1_open(dtl1_info_t *info)
        hci_set_drvdata(hdev, info);
        SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
 
-       hdev->open     = dtl1_hci_open;
-       hdev->close    = dtl1_hci_close;
-       hdev->flush    = dtl1_hci_flush;
-       hdev->send     = dtl1_hci_send_frame;
-       hdev->ioctl    = dtl1_hci_ioctl;
+       hdev->open  = dtl1_hci_open;
+       hdev->close = dtl1_hci_close;
+       hdev->flush = dtl1_hci_flush;
+       hdev->send  = dtl1_hci_send_frame;
 
        spin_lock_irqsave(&(info->lock), flags);
 
index 57e502e..0bc87f7 100644 (file)
@@ -522,7 +522,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
                                memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
                                bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
 
-                               hci_recv_frame(bcsp->rx_skb);
+                               hci_recv_frame(hu->hdev, bcsp->rx_skb);
                        } else {
                                BT_ERR ("Packet for unknown channel (%u %s)",
                                        bcsp->rx_skb->data[1] & 0x0f,
@@ -536,7 +536,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
                /* Pull out BCSP hdr */
                skb_pull(bcsp->rx_skb, 4);
 
-               hci_recv_frame(bcsp->rx_skb);
+               hci_recv_frame(hu->hdev, bcsp->rx_skb);
        }
 
        bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -655,7 +655,6 @@ static int bcsp_recv(struct hci_uart *hu, void *data, int count)
                                        bcsp->rx_count = 0;
                                        return 0;
                                }
-                               bcsp->rx_skb->dev = (void *) hu->hdev;
                                break;
                        }
                        break;
index 8ae9f1e..7048a58 100644 (file)
@@ -124,30 +124,6 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
        return 0;
 }
 
-static inline int h4_check_data_len(struct h4_struct *h4, int len)
-{
-       int room = skb_tailroom(h4->rx_skb);
-
-       BT_DBG("len %d room %d", len, room);
-
-       if (!len) {
-               hci_recv_frame(h4->rx_skb);
-       } else if (len > room) {
-               BT_ERR("Data length is too large");
-               kfree_skb(h4->rx_skb);
-       } else {
-               h4->rx_state = H4_W4_DATA;
-               h4->rx_count = len;
-               return len;
-       }
-
-       h4->rx_state = H4_W4_PACKET_TYPE;
-       h4->rx_skb   = NULL;
-       h4->rx_count = 0;
-
-       return 0;
-}
-
 /* Recv data */
 static int h4_recv(struct hci_uart *hu, void *data, int count)
 {
index b6154d5..f6f4974 100644 (file)
@@ -340,7 +340,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
                /* Remove Three-wire header */
                skb_pull(h5->rx_skb, 4);
 
-               hci_recv_frame(h5->rx_skb);
+               hci_recv_frame(hu->hdev, h5->rx_skb);
                h5->rx_skb = NULL;
 
                break;
index bc68a44..6e06f6f 100644 (file)
@@ -234,21 +234,13 @@ static int hci_uart_close(struct hci_dev *hdev)
 }
 
 /* Send frames from HCI layer */
-static int hci_uart_send_frame(struct sk_buff *skb)
+static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev* hdev = (struct hci_dev *) skb->dev;
-       struct hci_uart *hu;
-
-       if (!hdev) {
-               BT_ERR("Frame for unknown device (hdev=NULL)");
-               return -ENODEV;
-       }
+       struct hci_uart *hu = hci_get_drvdata(hdev);
 
        if (!test_bit(HCI_RUNNING, &hdev->flags))
                return -EBUSY;
 
-       hu = hci_get_drvdata(hdev);
-
        BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
        hu->proto->enqueue(hu, skb);
index cfc7679..69a90b1 100644 (file)
@@ -110,7 +110,6 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
        /* prepare packet */
        hcill_packet = (struct hcill_cmd *) skb_put(skb, 1);
        hcill_packet->cmd = cmd;
-       skb->dev = (void *) hu->hdev;
 
        /* send packet */
        skb_queue_tail(&ll->txq, skb);
@@ -346,14 +345,14 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
        return 0;
 }
 
-static inline int ll_check_data_len(struct ll_struct *ll, int len)
+static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, int len)
 {
        int room = skb_tailroom(ll->rx_skb);
 
        BT_DBG("len %d room %d", len, room);
 
        if (!len) {
-               hci_recv_frame(ll->rx_skb);
+               hci_recv_frame(hdev, ll->rx_skb);
        } else if (len > room) {
                BT_ERR("Data length is too large");
                kfree_skb(ll->rx_skb);
@@ -395,7 +394,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
                        switch (ll->rx_state) {
                        case HCILL_W4_DATA:
                                BT_DBG("Complete data");
-                               hci_recv_frame(ll->rx_skb);
+                               hci_recv_frame(hu->hdev, ll->rx_skb);
 
                                ll->rx_state = HCILL_W4_PACKET_TYPE;
                                ll->rx_skb = NULL;
@@ -406,7 +405,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
 
                                BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
 
-                               ll_check_data_len(ll, eh->plen);
+                               ll_check_data_len(hu->hdev, ll, eh->plen);
                                continue;
 
                        case HCILL_W4_ACL_HDR:
@@ -415,7 +414,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
 
                                BT_DBG("ACL header: dlen %d", dlen);
 
-                               ll_check_data_len(ll, dlen);
+                               ll_check_data_len(hu->hdev, ll, dlen);
                                continue;
 
                        case HCILL_W4_SCO_HDR:
@@ -423,7 +422,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
 
                                BT_DBG("SCO header: dlen %d", sh->dlen);
 
-                               ll_check_data_len(ll, sh->dlen);
+                               ll_check_data_len(hu->hdev, ll, sh->dlen);
                                continue;
                        }
                }
@@ -494,7 +493,6 @@ static int ll_recv(struct hci_uart *hu, void *data, int count)
                        return -ENOMEM;
                }
 
-               ll->rx_skb->dev = (void *) hu->hdev;
                bt_cb(ll->rx_skb)->pkt_type = type;
        }
 
index d8b7aed..7b16738 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include <linux/module.h>
+#include <asm/unaligned.h>
 
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#define VERSION "1.3"
+#define VERSION "1.4"
 
 static bool amp;
 
 struct vhci_data {
        struct hci_dev *hdev;
 
-       unsigned long flags;
-
        wait_queue_head_t read_wait;
        struct sk_buff_head readq;
+
+       struct delayed_work open_timeout;
 };
 
 static int vhci_open_dev(struct hci_dev *hdev)
@@ -80,35 +81,73 @@ static int vhci_flush(struct hci_dev *hdev)
        return 0;
 }
 
-static int vhci_send_frame(struct sk_buff *skb)
+static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev* hdev = (struct hci_dev *) skb->dev;
-       struct vhci_data *data;
+       struct vhci_data *data = hci_get_drvdata(hdev);
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -EBUSY;
+
+       memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+       skb_queue_tail(&data->readq, skb);
+
+       wake_up_interruptible(&data->read_wait);
+       return 0;
+}
 
+static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
+{
+       struct hci_dev *hdev;
+       struct sk_buff *skb;
+
+       skb = bt_skb_alloc(4, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       hdev = hci_alloc_dev();
        if (!hdev) {
-               BT_ERR("Frame for unknown HCI device (hdev=NULL)");
-               return -ENODEV;
+               kfree_skb(skb);
+               return -ENOMEM;
        }
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
+       data->hdev = hdev;
+
+       hdev->bus = HCI_VIRTUAL;
+       hdev->dev_type = dev_type;
+       hci_set_drvdata(hdev, data);
+
+       hdev->open  = vhci_open_dev;
+       hdev->close = vhci_close_dev;
+       hdev->flush = vhci_flush;
+       hdev->send  = vhci_send_frame;
+
+       if (hci_register_dev(hdev) < 0) {
+               BT_ERR("Can't register HCI device");
+               hci_free_dev(hdev);
+               data->hdev = NULL;
+               kfree_skb(skb);
                return -EBUSY;
+       }
 
-       data = hci_get_drvdata(hdev);
+       bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
 
-       memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+       *skb_put(skb, 1) = 0xff;
+       *skb_put(skb, 1) = dev_type;
+       put_unaligned_le16(hdev->id, skb_put(skb, 2));
        skb_queue_tail(&data->readq, skb);
 
        wake_up_interruptible(&data->read_wait);
-
        return 0;
 }
 
 static inline ssize_t vhci_get_user(struct vhci_data *data,
-                                       const char __user *buf, size_t count)
+                                   const char __user *buf, size_t count)
 {
        struct sk_buff *skb;
+       __u8 pkt_type, dev_type;
+       int ret;
 
-       if (count > HCI_MAX_FRAME_SIZE)
+       if (count < 2 || count > HCI_MAX_FRAME_SIZE)
                return -EINVAL;
 
        skb = bt_skb_alloc(count, GFP_KERNEL);
@@ -120,27 +159,69 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
                return -EFAULT;
        }
 
-       skb->dev = (void *) data->hdev;
-       bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
+       pkt_type = *((__u8 *) skb->data);
        skb_pull(skb, 1);
 
-       hci_recv_frame(skb);
+       switch (pkt_type) {
+       case HCI_EVENT_PKT:
+       case HCI_ACLDATA_PKT:
+       case HCI_SCODATA_PKT:
+               if (!data->hdev) {
+                       kfree_skb(skb);
+                       return -ENODEV;
+               }
+
+               bt_cb(skb)->pkt_type = pkt_type;
+
+               ret = hci_recv_frame(data->hdev, skb);
+               break;
 
-       return count;
+       case HCI_VENDOR_PKT:
+               if (data->hdev) {
+                       kfree_skb(skb);
+                       return -EBADFD;
+               }
+
+               cancel_delayed_work_sync(&data->open_timeout);
+
+               dev_type = *((__u8 *) skb->data);
+               skb_pull(skb, 1);
+
+               if (skb->len > 0) {
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+
+               kfree_skb(skb);
+
+               if (dev_type != HCI_BREDR && dev_type != HCI_AMP)
+                       return -EINVAL;
+
+               ret = vhci_create_device(data, dev_type);
+               break;
+
+       default:
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       return (ret < 0) ? ret : count;
 }
 
 static inline ssize_t vhci_put_user(struct vhci_data *data,
-                       struct sk_buff *skb, char __user *buf, int count)
+                                   struct sk_buff *skb,
+                                   char __user *buf, int count)
 {
        char __user *ptr = buf;
-       int len, total = 0;
+       int len;
 
        len = min_t(unsigned int, skb->len, count);
 
        if (copy_to_user(ptr, skb->data, len))
                return -EFAULT;
 
-       total += len;
+       if (!data->hdev)
+               return len;
 
        data->hdev->stat.byte_tx += len;
 
@@ -148,21 +229,19 @@ static inline ssize_t vhci_put_user(struct vhci_data *data,
        case HCI_COMMAND_PKT:
                data->hdev->stat.cmd_tx++;
                break;
-
        case HCI_ACLDATA_PKT:
                data->hdev->stat.acl_tx++;
                break;
-
        case HCI_SCODATA_PKT:
                data->hdev->stat.sco_tx++;
                break;
        }
 
-       return total;
+       return len;
 }
 
 static ssize_t vhci_read(struct file *file,
-                               char __user *buf, size_t count, loff_t *pos)
+                        char __user *buf, size_t count, loff_t *pos)
 {
        struct vhci_data *data = file->private_data;
        struct sk_buff *skb;
@@ -185,7 +264,7 @@ static ssize_t vhci_read(struct file *file,
                }
 
                ret = wait_event_interruptible(data->read_wait,
-                                       !skb_queue_empty(&data->readq));
+                                              !skb_queue_empty(&data->readq));
                if (ret < 0)
                        break;
        }
@@ -194,7 +273,7 @@ static ssize_t vhci_read(struct file *file,
 }
 
 static ssize_t vhci_write(struct file *file,
-                       const char __user *buf, size_t count, loff_t *pos)
+                         const char __user *buf, size_t count, loff_t *pos)
 {
        struct vhci_data *data = file->private_data;
 
@@ -213,10 +292,17 @@ static unsigned int vhci_poll(struct file *file, poll_table *wait)
        return POLLOUT | POLLWRNORM;
 }
 
+static void vhci_open_timeout(struct work_struct *work)
+{
+       struct vhci_data *data = container_of(work, struct vhci_data,
+                                             open_timeout.work);
+
+       vhci_create_device(data, amp ? HCI_AMP : HCI_BREDR);
+}
+
 static int vhci_open(struct inode *inode, struct file *file)
 {
        struct vhci_data *data;
-       struct hci_dev *hdev;
 
        data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
        if (!data)
@@ -225,35 +311,13 @@ static int vhci_open(struct inode *inode, struct file *file)
        skb_queue_head_init(&data->readq);
        init_waitqueue_head(&data->read_wait);
 
-       hdev = hci_alloc_dev();
-       if (!hdev) {
-               kfree(data);
-               return -ENOMEM;
-       }
-
-       data->hdev = hdev;
-
-       hdev->bus = HCI_VIRTUAL;
-       hci_set_drvdata(hdev, data);
-
-       if (amp)
-               hdev->dev_type = HCI_AMP;
-
-       hdev->open     = vhci_open_dev;
-       hdev->close    = vhci_close_dev;
-       hdev->flush    = vhci_flush;
-       hdev->send     = vhci_send_frame;
-
-       if (hci_register_dev(hdev) < 0) {
-               BT_ERR("Can't register HCI device");
-               kfree(data);
-               hci_free_dev(hdev);
-               return -EBUSY;
-       }
+       INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
 
        file->private_data = data;
        nonseekable_open(inode, file);
 
+       schedule_delayed_work(&data->open_timeout, msecs_to_jiffies(1000));
+
        return 0;
 }
 
@@ -262,8 +326,12 @@ static int vhci_release(struct inode *inode, struct file *file)
        struct vhci_data *data = file->private_data;
        struct hci_dev *hdev = data->hdev;
 
-       hci_unregister_dev(hdev);
-       hci_free_dev(hdev);
+       cancel_delayed_work_sync(&data->open_timeout);
+
+       if (hdev) {
+               hci_unregister_dev(hdev);
+               hci_free_dev(hdev);
+       }
 
        file->private_data = NULL;
        kfree(data);
@@ -309,3 +377,4 @@ MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("devname:vhci");
index 19ab6ff..2394e97 100644 (file)
@@ -700,6 +700,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                                         phys_addr_t sdramwins_phys_base,
                                         size_t sdramwins_size)
 {
+       struct device_node *np;
        int win;
 
        mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
@@ -712,8 +713,11 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                return -ENOMEM;
        }
 
-       if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
+       np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
+       if (np) {
                mbus->hw_io_coherency = 1;
+               of_node_put(np);
+       }
 
        for (win = 0; win < mbus->soc->num_wins; win++)
                mvebu_mbus_disable_window(mbus, win);
@@ -861,11 +865,13 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
        int ret;
 
        /*
-        * These are optional, so we clear them and they'll
-        * be zero if they are missing from the DT.
+        * These are optional, so we make sure that resource_size(x) will
+        * return 0.
         */
        memset(mem, 0, sizeof(struct resource));
+       mem->end = -1;
        memset(io, 0, sizeof(struct resource));
+       io->end = -1;
 
        ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg));
        if (!ret) {
index 7737b5b..7a744d3 100644 (file)
@@ -640,7 +640,7 @@ struct timer_rand_state {
  */
 void add_device_randomness(const void *buf, unsigned int size)
 {
-       unsigned long time = get_cycles() ^ jiffies;
+       unsigned long time = random_get_entropy() ^ jiffies;
 
        mix_pool_bytes(&input_pool, buf, size, NULL);
        mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
@@ -677,7 +677,7 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
                goto out;
 
        sample.jiffies = jiffies;
-       sample.cycles = get_cycles();
+       sample.cycles = random_get_entropy();
        sample.num = num;
        mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL);
 
@@ -744,7 +744,7 @@ void add_interrupt_randomness(int irq, int irq_flags)
        struct fast_pool        *fast_pool = &__get_cpu_var(irq_randomness);
        struct pt_regs          *regs = get_irq_regs();
        unsigned long           now = jiffies;
-       __u32                   input[4], cycles = get_cycles();
+       __u32                   input[4], cycles = random_get_entropy();
 
        input[0] = cycles ^ jiffies;
        input[1] = irq;
@@ -1459,12 +1459,11 @@ struct ctl_table random_table[] = {
 
 static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
 
-static int __init random_int_secret_init(void)
+int random_int_secret_init(void)
 {
        get_random_bytes(random_int_secret, sizeof(random_int_secret));
        return 0;
 }
-late_initcall(random_int_secret_init);
 
 /*
  * Get a random word for internal kernel use only. Similar to urandom but
@@ -1483,7 +1482,7 @@ unsigned int get_random_int(void)
 
        hash = get_cpu_var(get_random_int_hash);
 
-       hash[0] += current->pid + jiffies + get_cycles();
+       hash[0] += current->pid + jiffies + random_get_entropy();
        md5_transform(hash, random_int_secret);
        ret = hash[0];
        put_cpu_var(get_random_int_hash);
index 7a7929b..94c280d 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
+#include <xen/xen.h>
 #include <xen/events.h>
 #include <xen/interface/io/tpmif.h>
 #include <xen/grant_table.h>
@@ -142,32 +143,6 @@ static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        return length;
 }
 
-ssize_t tpm_show_locality(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct tpm_private *priv = TPM_VPRIV(chip);
-       u8 locality = priv->shr->locality;
-
-       return sprintf(buf, "%d\n", locality);
-}
-
-ssize_t tpm_store_locality(struct device *dev, struct device_attribute *attr,
-                       const char *buf, size_t len)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct tpm_private *priv = TPM_VPRIV(chip);
-       u8 val;
-
-       int rv = kstrtou8(buf, 0, &val);
-       if (rv)
-               return rv;
-
-       priv->shr->locality = val;
-
-       return len;
-}
-
 static const struct file_operations vtpm_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -188,8 +163,6 @@ static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
 static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
 static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
 static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
-static DEVICE_ATTR(locality, S_IRUGO | S_IWUSR, tpm_show_locality,
-               tpm_store_locality);
 
 static struct attribute *vtpm_attrs[] = {
        &dev_attr_pubek.attr,
@@ -202,7 +175,6 @@ static struct attribute *vtpm_attrs[] = {
        &dev_attr_cancel.attr,
        &dev_attr_durations.attr,
        &dev_attr_timeouts.attr,
-       &dev_attr_locality.attr,
        NULL,
 };
 
@@ -210,8 +182,6 @@ static struct attribute_group vtpm_attr_grp = {
        .attrs = vtpm_attrs,
 };
 
-#define TPM_LONG_TIMEOUT   (10 * 60 * HZ)
-
 static const struct tpm_vendor_specific tpm_vtpm = {
        .status = vtpm_status,
        .recv = vtpm_recv,
@@ -224,11 +194,6 @@ static const struct tpm_vendor_specific tpm_vtpm = {
        .miscdev = {
                .fops = &vtpm_ops,
        },
-       .duration = {
-               TPM_LONG_TIMEOUT,
-               TPM_LONG_TIMEOUT,
-               TPM_LONG_TIMEOUT,
-       },
 };
 
 static irqreturn_t tpmif_interrupt(int dummy, void *dev_id)
index 51410c2..4d978a3 100644 (file)
  */
 
 #define SRC_CR                 0x00U
+#define SRC_CR_T0_ENSEL                BIT(15)
+#define SRC_CR_T1_ENSEL                BIT(17)
+#define SRC_CR_T2_ENSEL                BIT(19)
+#define SRC_CR_T3_ENSEL                BIT(21)
+#define SRC_CR_T4_ENSEL                BIT(23)
+#define SRC_CR_T5_ENSEL                BIT(25)
+#define SRC_CR_T6_ENSEL                BIT(27)
+#define SRC_CR_T7_ENSEL                BIT(29)
 #define SRC_XTALCR             0x0CU
 #define SRC_XTALCR_XTALTIMEN   BIT(20)
 #define SRC_XTALCR_SXTALDIS    BIT(19)
@@ -543,6 +551,19 @@ void __init nomadik_clk_init(void)
                       __func__, np->name);
                return;
        }
+
+       /* Set all timers to use the 2.4 MHz TIMCLK */
+       val = readl(src_base + SRC_CR);
+       val |= SRC_CR_T0_ENSEL;
+       val |= SRC_CR_T1_ENSEL;
+       val |= SRC_CR_T2_ENSEL;
+       val |= SRC_CR_T3_ENSEL;
+       val |= SRC_CR_T4_ENSEL;
+       val |= SRC_CR_T5_ENSEL;
+       val |= SRC_CR_T6_ENSEL;
+       val |= SRC_CR_T7_ENSEL;
+       writel(val, src_base + SRC_CR);
+
        val = readl(src_base + SRC_XTALCR);
        pr_info("SXTALO is %s\n",
                (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled");
index fc777bd..81a202d 100644 (file)
@@ -39,8 +39,8 @@ static const struct coreclk_ratio a370_coreclk_ratios[] __initconst = {
 };
 
 static const u32 a370_tclk_freqs[] __initconst = {
-       16600000,
-       20000000,
+       166000000,
+       200000000,
 };
 
 static u32 __init a370_get_tclk_freq(void __iomem *sar)
index 5bb848c..81dd31a 100644 (file)
@@ -49,7 +49,7 @@
 #define SOCFPGA_L4_SP_CLK              "l4_sp_clk"
 #define SOCFPGA_NAND_CLK               "nand_clk"
 #define SOCFPGA_NAND_X_CLK             "nand_x_clk"
-#define SOCFPGA_MMC_CLK                        "mmc_clk"
+#define SOCFPGA_MMC_CLK                        "sdmmc_clk"
 #define SOCFPGA_DB_CLK                 "gpio_db_clk"
 
 #define div_mask(width)        ((1 << (width)) - 1)
index 67ccf4a..f5e4c21 100644 (file)
@@ -107,7 +107,7 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
 
        vco = icst_hz_to_vco(icst->params, rate);
        icst->rate = icst_hz(icst->params, vco);
-       vco_set(icst->vcoreg, icst->lockreg, vco);
+       vco_set(icst->lockreg, icst->vcoreg, vco);
        return 0;
 }
 
index 41c6946..971d796 100644 (file)
@@ -26,6 +26,7 @@ config DW_APB_TIMER_OF
 
 config ARMADA_370_XP_TIMER
        bool
+       select CLKSRC_OF
 
 config ORION_TIMER
        select CLKSRC_OF
index 37f5325..b9ddd9e 100644 (file)
@@ -30,6 +30,9 @@ void __init clocksource_of_init(void)
        clocksource_of_init_fn init_func;
 
        for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
+               if (!of_device_is_available(np))
+                       continue;
+
                init_func = match->data;
                init_func(np);
        }
index b9c81b7..3a5909c 100644 (file)
@@ -301,7 +301,7 @@ static void em_sti_register_clockevent(struct em_sti_priv *p)
        ced->name = dev_name(&p->pdev->dev);
        ced->features = CLOCK_EVT_FEAT_ONESHOT;
        ced->rating = 200;
-       ced->cpumask = cpumask_of(0);
+       ced->cpumask = cpu_possible_mask;
        ced->set_next_event = em_sti_clock_event_next;
        ced->set_mode = em_sti_clock_event_mode;
 
index 5b34768..62b0de6 100644 (file)
@@ -428,7 +428,6 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt)
                                evt->irq);
                        return -EIO;
                }
-               irq_set_affinity(evt->irq, cpumask_of(cpu));
        } else {
                enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
        }
@@ -449,6 +448,7 @@ static int exynos4_mct_cpu_notify(struct notifier_block *self,
                                           unsigned long action, void *hcpu)
 {
        struct mct_clock_event_device *mevt;
+       unsigned int cpu;
 
        /*
         * Grab cpu pointer in each case to avoid spurious
@@ -459,6 +459,12 @@ static int exynos4_mct_cpu_notify(struct notifier_block *self,
                mevt = this_cpu_ptr(&percpu_mct_tick);
                exynos4_local_timer_setup(&mevt->evt);
                break;
+       case CPU_ONLINE:
+               cpu = (unsigned long)hcpu;
+               if (mct_int_type == MCT_INT_SPI)
+                       irq_set_affinity(mct_irqs[MCT_L0_IRQ + cpu],
+                                               cpumask_of(cpu));
+               break;
        case CPU_DYING:
                mevt = this_cpu_ptr(&percpu_mct_tick);
                exynos4_local_timer_stop(&mevt->evt);
@@ -500,6 +506,8 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
                                         &percpu_mct_tick);
                WARN(err, "MCT: can't request IRQ %d (%d)\n",
                     mct_irqs[MCT_L0_IRQ], err);
+       } else {
+               irq_set_affinity(mct_irqs[MCT_L0_IRQ], cpumask_of(0));
        }
 
        err = register_cpu_notifier(&exynos4_mct_cpu_nb);
index 08ae128..c73fc2b 100644 (file)
@@ -65,6 +65,7 @@ void proc_fork_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -80,6 +81,7 @@ void proc_fork_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        /*  If cn_netlink_send() failed, the data is not sent */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
@@ -96,6 +98,7 @@ void proc_exec_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -106,6 +109,7 @@ void proc_exec_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -122,6 +126,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        ev->what = which_id;
        ev->event_data.id.process_pid = task->pid;
        ev->event_data.id.process_tgid = task->tgid;
@@ -145,6 +150,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -160,6 +166,7 @@ void proc_sid_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -170,6 +177,7 @@ void proc_sid_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -185,6 +193,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -203,6 +212,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -218,6 +228,7 @@ void proc_comm_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -229,6 +240,7 @@ void proc_comm_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -244,6 +256,7 @@ void proc_coredump_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -254,6 +267,7 @@ void proc_coredump_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -269,6 +283,7 @@ void proc_exit_connector(struct task_struct *task)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -281,6 +296,7 @@ void proc_exit_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -304,6 +320,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
 
        msg = (struct cn_msg *)buffer;
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        msg->seq = rcvd_seq;
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
@@ -313,6 +330,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = rcvd_ack + 1;
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
index 6ecfa75..a36749f 100644 (file)
@@ -109,7 +109,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
 
        data = nlmsg_data(nlh);
 
-       memcpy(data, msg, sizeof(*data) + msg->len);
+       memcpy(data, msg, size);
 
        NETLINK_CB(skb).dst_group = group;
 
@@ -157,17 +157,18 @@ static int cn_call_callback(struct sk_buff *skb)
 static void cn_rx_skb(struct sk_buff *__skb)
 {
        struct nlmsghdr *nlh;
-       int err;
        struct sk_buff *skb;
+       int len, err;
 
        skb = skb_get(__skb);
 
        if (skb->len >= NLMSG_HDRLEN) {
                nlh = nlmsg_hdr(skb);
+               len = nlmsg_len(nlh);
 
-               if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
+               if (len < (int)sizeof(struct cn_msg) ||
                    skb->len < nlh->nlmsg_len ||
-                   nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
+                   len > CONNECTOR_MAX_MSG_SIZE) {
                        kfree_skb(skb);
                        return;
                }
index a1260b4..506fd23 100644 (file)
@@ -987,7 +987,11 @@ static int __init acpi_cpufreq_init(void)
        int ret;
 
        if (acpi_disabled)
-               return 0;
+               return -ENODEV;
+
+       /* don't keep reloading if cpufreq_driver exists */
+       if (cpufreq_get_current_driver())
+               return -EEXIST;
 
        pr_debug("acpi_cpufreq_init\n");
 
index cbfffa9..c522a95 100644 (file)
@@ -12,6 +12,7 @@
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/cpu.h>
 #include <linux/cpufreq.h>
 #include <linux/err.h>
 #include <linux/module.h>
@@ -177,7 +178,11 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
        struct device_node *np;
        int ret;
 
-       cpu_dev = &pdev->dev;
+       cpu_dev = get_cpu_device(0);
+       if (!cpu_dev) {
+               pr_err("failed to get cpu0 device\n");
+               return -ENODEV;
+       }
 
        np = of_node_get(cpu_dev->of_node);
        if (!np) {
@@ -224,7 +229,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
        if (of_property_read_u32(np, "clock-latency", &transition_latency))
                transition_latency = CPUFREQ_ETERNAL;
 
-       if (cpu_reg) {
+       if (!IS_ERR(cpu_reg)) {
                struct opp *opp;
                unsigned long min_uV, max_uV;
                int i;
index 43c24aa..04548f7 100644 (file)
@@ -952,9 +952,20 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
        if (cpu == policy->cpu)
                return;
 
+       /*
+        * Take direct locks as lock_policy_rwsem_write wouldn't work here.
+        * Also lock for last cpu is enough here as contention will happen only
+        * after policy->cpu is changed and after it is changed, other threads
+        * will try to acquire lock for new cpu. And policy is already updated
+        * by then.
+        */
+       down_write(&per_cpu(cpu_policy_rwsem, policy->cpu));
+
        policy->last_cpu = policy->cpu;
        policy->cpu = cpu;
 
+       up_write(&per_cpu(cpu_policy_rwsem, policy->last_cpu));
+
 #ifdef CONFIG_CPU_FREQ_TABLE
        cpufreq_frequency_table_update_policy_cpu(policy);
 #endif
@@ -1125,7 +1136,7 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy,
        int ret;
 
        /* first sibling now owns the new sysfs dir */
-       cpu_dev = get_cpu_device(cpumask_first(policy->cpus));
+       cpu_dev = get_cpu_device(cpumask_any_but(policy->cpus, old_cpu));
 
        /* Don't touch sysfs files during light-weight tear-down */
        if (frozen)
@@ -1189,12 +1200,9 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
                        policy->governor->name, CPUFREQ_NAME_LEN);
 #endif
 
-       WARN_ON(lock_policy_rwsem_write(cpu));
+       lock_policy_rwsem_read(cpu);
        cpus = cpumask_weight(policy->cpus);
-
-       if (cpus > 1)
-               cpumask_clear_cpu(cpu, policy->cpus);
-       unlock_policy_rwsem_write(cpu);
+       unlock_policy_rwsem_read(cpu);
 
        if (cpu != policy->cpu) {
                if (!frozen)
@@ -1203,9 +1211,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
 
                new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu, frozen);
                if (new_cpu >= 0) {
-                       WARN_ON(lock_policy_rwsem_write(cpu));
                        update_policy_cpu(policy, new_cpu);
-                       unlock_policy_rwsem_write(cpu);
 
                        if (!frozen) {
                                pr_debug("%s: policy Kobject moved to cpu: %d "
@@ -1237,9 +1243,12 @@ static int __cpufreq_remove_dev_finish(struct device *dev,
                return -EINVAL;
        }
 
-       lock_policy_rwsem_read(cpu);
+       WARN_ON(lock_policy_rwsem_write(cpu));
        cpus = cpumask_weight(policy->cpus);
-       unlock_policy_rwsem_read(cpu);
+
+       if (cpus > 1)
+               cpumask_clear_cpu(cpu, policy->cpus);
+       unlock_policy_rwsem_write(cpu);
 
        /* If cpu is last user of policy, free policy */
        if (cpus == 1) {
@@ -1451,6 +1460,9 @@ unsigned int cpufreq_get(unsigned int cpu)
 {
        unsigned int ret_freq = 0;
 
+       if (cpufreq_disabled() || !cpufreq_driver)
+               return -ENOENT;
+
        if (!down_read_trylock(&cpufreq_rwsem))
                return 0;
 
@@ -2095,7 +2107,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
        write_lock_irqsave(&cpufreq_driver_lock, flags);
        if (cpufreq_driver) {
                write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-               return -EBUSY;
+               return -EEXIST;
        }
        cpufreq_driver = driver_data;
        write_unlock_irqrestore(&cpufreq_driver_lock, flags);
index d514c15..be5380e 100644 (file)
@@ -457,7 +457,7 @@ err_free_table:
        opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
 err_put_node:
        of_node_put(np);
-       dev_err(dvfs_info->dev, "%s: failed initialization\n", __func__);
+       dev_err(&pdev->dev, "%s: failed initialization\n", __func__);
        return ret;
 }
 
index 3e39654..c3fd2a1 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/cpu.h>
 #include <linux/cpufreq.h>
 #include <linux/delay.h>
 #include <linux/err.h>
@@ -202,7 +203,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
        unsigned long min_volt, max_volt;
        int num, ret;
 
-       cpu_dev = &pdev->dev;
+       cpu_dev = get_cpu_device(0);
+       if (!cpu_dev) {
+               pr_err("failed to get cpu0 device\n");
+               return -ENODEV;
+       }
 
        np = of_node_get(cpu_dev->of_node);
        if (!np) {
index 9733f29..eb3fdc7 100644 (file)
@@ -48,7 +48,7 @@ static inline int32_t div_fp(int32_t x, int32_t y)
 }
 
 struct sample {
-       int core_pct_busy;
+       int32_t core_pct_busy;
        u64 aperf;
        u64 mperf;
        int freq;
@@ -68,7 +68,7 @@ struct _pid {
        int32_t i_gain;
        int32_t d_gain;
        int deadband;
-       int last_err;
+       int32_t last_err;
 };
 
 struct cpudata {
@@ -153,16 +153,15 @@ static inline void pid_d_gain_set(struct _pid *pid, int percent)
        pid->d_gain = div_fp(int_tofp(percent), int_tofp(100));
 }
 
-static signed int pid_calc(struct _pid *pid, int busy)
+static signed int pid_calc(struct _pid *pid, int32_t busy)
 {
-       signed int err, result;
+       signed int result;
        int32_t pterm, dterm, fp_error;
        int32_t integral_limit;
 
-       err = pid->setpoint - busy;
-       fp_error = int_tofp(err);
+       fp_error = int_tofp(pid->setpoint) - busy;
 
-       if (abs(err) <= pid->deadband)
+       if (abs(fp_error) <= int_tofp(pid->deadband))
                return 0;
 
        pterm = mul_fp(pid->p_gain, fp_error);
@@ -176,8 +175,8 @@ static signed int pid_calc(struct _pid *pid, int busy)
        if (pid->integral < -integral_limit)
                pid->integral = -integral_limit;
 
-       dterm = mul_fp(pid->d_gain, (err - pid->last_err));
-       pid->last_err = err;
+       dterm = mul_fp(pid->d_gain, fp_error - pid->last_err);
+       pid->last_err = fp_error;
 
        result = pterm + mul_fp(pid->integral, pid->i_gain) + dterm;
 
@@ -367,12 +366,13 @@ static int intel_pstate_turbo_pstate(void)
 static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
 {
        int max_perf = cpu->pstate.turbo_pstate;
+       int max_perf_adj;
        int min_perf;
        if (limits.no_turbo)
                max_perf = cpu->pstate.max_pstate;
 
-       max_perf = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf));
-       *max = clamp_t(int, max_perf,
+       max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf));
+       *max = clamp_t(int, max_perf_adj,
                        cpu->pstate.min_pstate, cpu->pstate.turbo_pstate);
 
        min_perf = fp_toint(mul_fp(int_tofp(max_perf), limits.min_perf));
@@ -383,6 +383,7 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
 static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
 {
        int max_perf, min_perf;
+       u64 val;
 
        intel_pstate_get_min_max(cpu, &min_perf, &max_perf);
 
@@ -394,8 +395,11 @@ static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
        trace_cpu_frequency(pstate * 100000, cpu->cpu);
 
        cpu->pstate.current_pstate = pstate;
-       wrmsrl(MSR_IA32_PERF_CTL, pstate << 8);
+       val = pstate << 8;
+       if (limits.no_turbo)
+               val |= (u64)1 << 32;
 
+       wrmsrl(MSR_IA32_PERF_CTL, val);
 }
 
 static inline void intel_pstate_pstate_increase(struct cpudata *cpu, int steps)
@@ -432,8 +436,9 @@ static inline void intel_pstate_calc_busy(struct cpudata *cpu,
                                        struct sample *sample)
 {
        u64 core_pct;
-       core_pct = div64_u64(sample->aperf * 100, sample->mperf);
-       sample->freq = cpu->pstate.max_pstate * core_pct * 1000;
+       core_pct = div64_u64(int_tofp(sample->aperf * 100),
+                            sample->mperf);
+       sample->freq = fp_toint(cpu->pstate.max_pstate * core_pct * 1000);
 
        sample->core_pct_busy = core_pct;
 }
@@ -465,22 +470,19 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
        mod_timer_pinned(&cpu->timer, jiffies + delay);
 }
 
-static inline int intel_pstate_get_scaled_busy(struct cpudata *cpu)
+static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
 {
-       int32_t busy_scaled;
        int32_t core_busy, max_pstate, current_pstate;
 
-       core_busy = int_tofp(cpu->samples[cpu->sample_ptr].core_pct_busy);
+       core_busy = cpu->samples[cpu->sample_ptr].core_pct_busy;
        max_pstate = int_tofp(cpu->pstate.max_pstate);
        current_pstate = int_tofp(cpu->pstate.current_pstate);
-       busy_scaled = mul_fp(core_busy, div_fp(max_pstate, current_pstate));
-
-       return fp_toint(busy_scaled);
+       return mul_fp(core_busy, div_fp(max_pstate, current_pstate));
 }
 
 static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
 {
-       int busy_scaled;
+       int32_t busy_scaled;
        struct _pid *pid;
        signed int ctl = 0;
        int steps;
@@ -634,8 +636,8 @@ static int intel_pstate_cpu_exit(struct cpufreq_policy *policy)
 
 static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
 {
-       int rc, min_pstate, max_pstate;
        struct cpudata *cpu;
+       int rc;
 
        rc = intel_pstate_init_cpu(policy->cpu);
        if (rc)
@@ -649,9 +651,8 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
        else
                policy->policy = CPUFREQ_POLICY_POWERSAVE;
 
-       intel_pstate_get_min_max(cpu, &min_pstate, &max_pstate);
-       policy->min = min_pstate * 100000;
-       policy->max = max_pstate * 100000;
+       policy->min = cpu->pstate.min_pstate * 100000;
+       policy->max = cpu->pstate.turbo_pstate * 100000;
 
        /* cpuinfo and default policy values */
        policy->cpuinfo.min_freq = cpu->pstate.min_pstate * 100000;
index 8a72b0c..15631f9 100644 (file)
@@ -166,7 +166,7 @@ static void __init s3c64xx_cpufreq_config_regulator(void)
                if (freq->frequency == CPUFREQ_ENTRY_INVALID)
                        continue;
 
-               dvfs = &s3c64xx_dvfs_table[freq->index];
+               dvfs = &s3c64xx_dvfs_table[freq->driver_data];
                found = 0;
 
                for (i = 0; i < count; i++) {
index 19e364f..3f41816 100644 (file)
@@ -113,7 +113,7 @@ static int spear_cpufreq_target(struct cpufreq_policy *policy,
                unsigned int target_freq, unsigned int relation)
 {
        struct cpufreq_freqs freqs;
-       unsigned long newfreq;
+       long newfreq;
        struct clk *srcclk;
        int index, ret, mult = 1;
 
index 526ec77..f238cfd 100644 (file)
@@ -198,6 +198,7 @@ config TI_EDMA
        depends on ARCH_DAVINCI || ARCH_OMAP
        select DMA_ENGINE
        select DMA_VIRTUAL_CHANNELS
+       select TI_PRIV_EDMA
        default n
        help
          Enable support for the TI EDMA controller. This DMA
index ff50ff4..10b577f 100644 (file)
@@ -305,7 +305,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                                edma_alloc_slot(EDMA_CTLR(echan->ch_num),
                                                EDMA_SLOT_ANY);
                        if (echan->slot[i] < 0) {
+                               kfree(edesc);
                                dev_err(dev, "Failed to allocate slot\n");
+                               kfree(edesc);
                                return NULL;
                        }
                }
@@ -345,6 +347,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
                        ccnt = sg_dma_len(sg) / (acnt * bcnt);
                        if (ccnt > (SZ_64K - 1)) {
                                dev_err(dev, "Exceeded max SG segment size\n");
+                               kfree(edesc);
                                return NULL;
                        }
                        cidx = acnt * bcnt;
@@ -749,6 +752,6 @@ static void __exit edma_exit(void)
 }
 module_exit(edma_exit);
 
-MODULE_AUTHOR("Matt Porter <mporter@ti.com>");
+MODULE_AUTHOR("Matt Porter <matt.porter@linaro.org>");
 MODULE_DESCRIPTION("TI EDMA DMA engine driver");
 MODULE_LICENSE("GPL v2");
index 78f8ca5..55852c0 100644 (file)
@@ -437,17 +437,18 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac)
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        int chno = imxdmac->channel;
        struct imxdma_desc *desc;
+       unsigned long flags;
 
-       spin_lock(&imxdma->lock);
+       spin_lock_irqsave(&imxdma->lock, flags);
        if (list_empty(&imxdmac->ld_active)) {
-               spin_unlock(&imxdma->lock);
+               spin_unlock_irqrestore(&imxdma->lock, flags);
                goto out;
        }
 
        desc = list_first_entry(&imxdmac->ld_active,
                                struct imxdma_desc,
                                node);
-       spin_unlock(&imxdma->lock);
+       spin_unlock_irqrestore(&imxdma->lock, flags);
 
        if (desc->sg) {
                u32 tmp;
@@ -519,7 +520,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
 {
        struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
        struct imxdma_engine *imxdma = imxdmac->imxdma;
-       unsigned long flags;
        int slot = -1;
        int i;
 
@@ -527,7 +527,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
        switch (d->type) {
        case IMXDMA_DESC_INTERLEAVED:
                /* Try to get a free 2D slot */
-               spin_lock_irqsave(&imxdma->lock, flags);
                for (i = 0; i < IMX_DMA_2D_SLOTS; i++) {
                        if ((imxdma->slots_2d[i].count > 0) &&
                        ((imxdma->slots_2d[i].xsr != d->x) ||
@@ -537,10 +536,8 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
                        slot = i;
                        break;
                }
-               if (slot < 0) {
-                       spin_unlock_irqrestore(&imxdma->lock, flags);
+               if (slot < 0)
                        return -EBUSY;
-               }
 
                imxdma->slots_2d[slot].xsr = d->x;
                imxdma->slots_2d[slot].ysr = d->y;
@@ -549,7 +546,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
 
                imxdmac->slot_2d = slot;
                imxdmac->enabled_2d = true;
-               spin_unlock_irqrestore(&imxdma->lock, flags);
 
                if (slot == IMX_DMA_2D_SLOT_A) {
                        d->config_mem &= ~CCR_MSEL_B;
@@ -625,18 +621,17 @@ static void imxdma_tasklet(unsigned long data)
        struct imxdma_channel *imxdmac = (void *)data;
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        struct imxdma_desc *desc;
+       unsigned long flags;
 
-       spin_lock(&imxdma->lock);
+       spin_lock_irqsave(&imxdma->lock, flags);
 
        if (list_empty(&imxdmac->ld_active)) {
                /* Someone might have called terminate all */
-               goto out;
+               spin_unlock_irqrestore(&imxdma->lock, flags);
+               return;
        }
        desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node);
 
-       if (desc->desc.callback)
-               desc->desc.callback(desc->desc.callback_param);
-
        /* If we are dealing with a cyclic descriptor, keep it on ld_active
         * and dont mark the descriptor as complete.
         * Only in non-cyclic cases it would be marked as complete
@@ -663,7 +658,11 @@ static void imxdma_tasklet(unsigned long data)
                                 __func__, imxdmac->channel);
        }
 out:
-       spin_unlock(&imxdma->lock);
+       spin_unlock_irqrestore(&imxdma->lock, flags);
+
+       if (desc->desc.callback)
+               desc->desc.callback(desc->desc.callback_param);
+
 }
 
 static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -883,7 +882,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
        kfree(imxdmac->sg_list);
 
        imxdmac->sg_list = kcalloc(periods + 1,
-                       sizeof(struct scatterlist), GFP_KERNEL);
+                       sizeof(struct scatterlist), GFP_ATOMIC);
        if (!imxdmac->sg_list)
                return NULL;
 
index 45a5202..ebad845 100644 (file)
@@ -93,6 +93,7 @@ struct hpb_dmae_chan {
        void __iomem *base;
        const struct hpb_dmae_slave_config *cfg;
        char dev_id[16];                /* unique name per DMAC of channel */
+       dma_addr_t slave_addr;
 };
 
 struct hpb_dmae_device {
@@ -432,7 +433,6 @@ hpb_dmae_alloc_chan_resources(struct hpb_dmae_chan *hpb_chan,
                hpb_chan->xfer_mode = XFER_DOUBLE;
        } else {
                dev_err(hpb_chan->shdma_chan.dev, "DCR setting error");
-               shdma_free_irq(&hpb_chan->shdma_chan);
                return -EINVAL;
        }
 
@@ -446,7 +446,8 @@ hpb_dmae_alloc_chan_resources(struct hpb_dmae_chan *hpb_chan,
        return 0;
 }
 
-static int hpb_dmae_set_slave(struct shdma_chan *schan, int slave_id, bool try)
+static int hpb_dmae_set_slave(struct shdma_chan *schan, int slave_id,
+                             dma_addr_t slave_addr, bool try)
 {
        struct hpb_dmae_chan *chan = to_chan(schan);
        const struct hpb_dmae_slave_config *sc =
@@ -457,6 +458,7 @@ static int hpb_dmae_set_slave(struct shdma_chan *schan, int slave_id, bool try)
        if (try)
                return 0;
        chan->cfg = sc;
+       chan->slave_addr = slave_addr ? : sc->addr;
        return hpb_dmae_alloc_chan_resources(chan, sc);
 }
 
@@ -468,7 +470,7 @@ static dma_addr_t hpb_dmae_slave_addr(struct shdma_chan *schan)
 {
        struct hpb_dmae_chan *chan = to_chan(schan);
 
-       return chan->cfg->addr;
+       return chan->slave_addr;
 }
 
 static struct shdma_desc *hpb_dmae_embedded_desc(void *buf, int i)
@@ -614,7 +616,6 @@ static void hpb_dmae_chan_remove(struct hpb_dmae_device *hpbdev)
        shdma_for_each_chan(schan, &hpbdev->shdma_dev, i) {
                BUG_ON(!schan);
 
-               shdma_free_irq(schan);
                shdma_chan_remove(schan);
        }
        dma_dev->chancnt = 0;
index 2d9ca60..41b5913 100644 (file)
@@ -248,14 +248,15 @@ static void lp_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
        struct lp_gpio *lg = irq_data_get_irq_handler_data(data);
        struct irq_chip *chip = irq_data_get_irq_chip(data);
        u32 base, pin, mask;
-       unsigned long reg, pending;
+       unsigned long reg, ena, pending;
        unsigned virq;
 
        /* check from GPIO controller which pin triggered the interrupt */
        for (base = 0; base < lg->chip.ngpio; base += 32) {
                reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
+               ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
 
-               while ((pending = inl(reg))) {
+               while ((pending = (inl(reg) & inl(ena)))) {
                        pin = __ffs(pending);
                        mask = BIT(pin);
                        /* Clear before handling so we don't lose an edge */
index 0ff4355..89675f8 100644 (file)
@@ -63,6 +63,7 @@ struct gpio_bank {
        struct gpio_chip chip;
        struct clk *dbck;
        u32 mod_usage;
+       u32 irq_usage;
        u32 dbck_enable_mask;
        bool dbck_enabled;
        struct device *dev;
@@ -86,6 +87,9 @@ struct gpio_bank {
 #define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio))
 #define GPIO_MOD_CTRL_BIT      BIT(0)
 
+#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
+#define LINE_USED(line, offset) (line & (1 << offset))
+
 static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
 {
        return bank->chip.base + gpio_irq;
@@ -420,15 +424,69 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
        return 0;
 }
 
+static void _enable_gpio_module(struct gpio_bank *bank, unsigned offset)
+{
+       if (bank->regs->pinctrl) {
+               void __iomem *reg = bank->base + bank->regs->pinctrl;
+
+               /* Claim the pin for MPU */
+               __raw_writel(__raw_readl(reg) | (1 << offset), reg);
+       }
+
+       if (bank->regs->ctrl && !BANK_USED(bank)) {
+               void __iomem *reg = bank->base + bank->regs->ctrl;
+               u32 ctrl;
+
+               ctrl = __raw_readl(reg);
+               /* Module is enabled, clocks are not gated */
+               ctrl &= ~GPIO_MOD_CTRL_BIT;
+               __raw_writel(ctrl, reg);
+               bank->context.ctrl = ctrl;
+       }
+}
+
+static void _disable_gpio_module(struct gpio_bank *bank, unsigned offset)
+{
+       void __iomem *base = bank->base;
+
+       if (bank->regs->wkup_en &&
+           !LINE_USED(bank->mod_usage, offset) &&
+           !LINE_USED(bank->irq_usage, offset)) {
+               /* Disable wake-up during idle for dynamic tick */
+               _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
+               bank->context.wake_en =
+                       __raw_readl(bank->base + bank->regs->wkup_en);
+       }
+
+       if (bank->regs->ctrl && !BANK_USED(bank)) {
+               void __iomem *reg = bank->base + bank->regs->ctrl;
+               u32 ctrl;
+
+               ctrl = __raw_readl(reg);
+               /* Module is disabled, clocks are gated */
+               ctrl |= GPIO_MOD_CTRL_BIT;
+               __raw_writel(ctrl, reg);
+               bank->context.ctrl = ctrl;
+       }
+}
+
+static int gpio_is_input(struct gpio_bank *bank, int mask)
+{
+       void __iomem *reg = bank->base + bank->regs->direction;
+
+       return __raw_readl(reg) & mask;
+}
+
 static int gpio_irq_type(struct irq_data *d, unsigned type)
 {
        struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
        unsigned gpio = 0;
        int retval;
        unsigned long flags;
+       unsigned offset;
 
-       if (WARN_ON(!bank->mod_usage))
-               return -EINVAL;
+       if (!BANK_USED(bank))
+               pm_runtime_get_sync(bank->dev);
 
 #ifdef CONFIG_ARCH_OMAP1
        if (d->irq > IH_MPUIO_BASE)
@@ -446,7 +504,17 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
                return -EINVAL;
 
        spin_lock_irqsave(&bank->lock, flags);
-       retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type);
+       offset = GPIO_INDEX(bank, gpio);
+       retval = _set_gpio_triggering(bank, offset, type);
+       if (!LINE_USED(bank->mod_usage, offset)) {
+               _enable_gpio_module(bank, offset);
+               _set_gpio_direction(bank, offset, 1);
+       } else if (!gpio_is_input(bank, 1 << offset)) {
+               spin_unlock_irqrestore(&bank->lock, flags);
+               return -EINVAL;
+       }
+
+       bank->irq_usage |= 1 << GPIO_INDEX(bank, gpio);
        spin_unlock_irqrestore(&bank->lock, flags);
 
        if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
@@ -603,35 +671,19 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
         * If this is the first gpio_request for the bank,
         * enable the bank module.
         */
-       if (!bank->mod_usage)
+       if (!BANK_USED(bank))
                pm_runtime_get_sync(bank->dev);
 
        spin_lock_irqsave(&bank->lock, flags);
        /* Set trigger to none. You need to enable the desired trigger with
-        * request_irq() or set_irq_type().
+        * request_irq() or set_irq_type(). Only do this if the IRQ line has
+        * not already been requested.
         */
-       _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
-
-       if (bank->regs->pinctrl) {
-               void __iomem *reg = bank->base + bank->regs->pinctrl;
-
-               /* Claim the pin for MPU */
-               __raw_writel(__raw_readl(reg) | (1 << offset), reg);
-       }
-
-       if (bank->regs->ctrl && !bank->mod_usage) {
-               void __iomem *reg = bank->base + bank->regs->ctrl;
-               u32 ctrl;
-
-               ctrl = __raw_readl(reg);
-               /* Module is enabled, clocks are not gated */
-               ctrl &= ~GPIO_MOD_CTRL_BIT;
-               __raw_writel(ctrl, reg);
-               bank->context.ctrl = ctrl;
+       if (!LINE_USED(bank->irq_usage, offset)) {
+               _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+               _enable_gpio_module(bank, offset);
        }
-
        bank->mod_usage |= 1 << offset;
-
        spin_unlock_irqrestore(&bank->lock, flags);
 
        return 0;
@@ -640,31 +692,11 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
 static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
 {
        struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
-       void __iomem *base = bank->base;
        unsigned long flags;
 
        spin_lock_irqsave(&bank->lock, flags);
-
-       if (bank->regs->wkup_en) {
-               /* Disable wake-up during idle for dynamic tick */
-               _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
-               bank->context.wake_en =
-                       __raw_readl(bank->base + bank->regs->wkup_en);
-       }
-
        bank->mod_usage &= ~(1 << offset);
-
-       if (bank->regs->ctrl && !bank->mod_usage) {
-               void __iomem *reg = bank->base + bank->regs->ctrl;
-               u32 ctrl;
-
-               ctrl = __raw_readl(reg);
-               /* Module is disabled, clocks are gated */
-               ctrl |= GPIO_MOD_CTRL_BIT;
-               __raw_writel(ctrl, reg);
-               bank->context.ctrl = ctrl;
-       }
-
+       _disable_gpio_module(bank, offset);
        _reset_gpio(bank, bank->chip.base + offset);
        spin_unlock_irqrestore(&bank->lock, flags);
 
@@ -672,7 +704,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
         * If this is the last gpio to be freed in the bank,
         * disable the bank module.
         */
-       if (!bank->mod_usage)
+       if (!BANK_USED(bank))
                pm_runtime_put(bank->dev);
 }
 
@@ -762,10 +794,20 @@ static void gpio_irq_shutdown(struct irq_data *d)
        struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
        unsigned int gpio = irq_to_gpio(bank, d->hwirq);
        unsigned long flags;
+       unsigned offset = GPIO_INDEX(bank, gpio);
 
        spin_lock_irqsave(&bank->lock, flags);
+       bank->irq_usage &= ~(1 << offset);
+       _disable_gpio_module(bank, offset);
        _reset_gpio(bank, gpio);
        spin_unlock_irqrestore(&bank->lock, flags);
+
+       /*
+        * If this is the last IRQ to be freed in the bank,
+        * disable the bank module.
+        */
+       if (!BANK_USED(bank))
+               pm_runtime_put(bank->dev);
 }
 
 static void gpio_ack_irq(struct irq_data *d)
@@ -897,13 +939,6 @@ static int gpio_input(struct gpio_chip *chip, unsigned offset)
        return 0;
 }
 
-static int gpio_is_input(struct gpio_bank *bank, int mask)
-{
-       void __iomem *reg = bank->base + bank->regs->direction;
-
-       return __raw_readl(reg) & mask;
-}
-
 static int gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct gpio_bank *bank;
@@ -922,13 +957,22 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
 {
        struct gpio_bank *bank;
        unsigned long flags;
+       int retval = 0;
 
        bank = container_of(chip, struct gpio_bank, chip);
        spin_lock_irqsave(&bank->lock, flags);
+
+       if (LINE_USED(bank->irq_usage, offset)) {
+                       retval = -EINVAL;
+                       goto exit;
+       }
+
        bank->set_dataout(bank, offset, value);
        _set_gpio_direction(bank, offset, 0);
+
+exit:
        spin_unlock_irqrestore(&bank->lock, flags);
-       return 0;
+       return retval;
 }
 
 static int gpio_debounce(struct gpio_chip *chip, unsigned offset,
@@ -1400,7 +1444,7 @@ void omap2_gpio_prepare_for_idle(int pwr_mode)
        struct gpio_bank *bank;
 
        list_for_each_entry(bank, &omap_gpio_list, node) {
-               if (!bank->mod_usage || !bank->loses_context)
+               if (!BANK_USED(bank) || !bank->loses_context)
                        continue;
 
                bank->power_mode = pwr_mode;
@@ -1414,7 +1458,7 @@ void omap2_gpio_resume_after_idle(void)
        struct gpio_bank *bank;
 
        list_for_each_entry(bank, &omap_gpio_list, node) {
-               if (!bank->mod_usage || !bank->loses_context)
+               if (!BANK_USED(bank) || !bank->loses_context)
                        continue;
 
                pm_runtime_get_sync(bank->dev);
index e3745eb..6038966 100644 (file)
@@ -293,10 +293,9 @@ static void gpio_rcar_parse_pdata(struct gpio_rcar_priv *p)
        if (pdata) {
                p->config = *pdata;
        } else if (IS_ENABLED(CONFIG_OF) && np) {
-               ret = of_parse_phandle_with_args(np, "gpio-ranges",
-                               "#gpio-range-cells", 0, &args);
-               p->config.number_of_pins = ret == 0 && args.args_count == 3
-                                        ? args.args[2]
+               ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0,
+                                                      &args);
+               p->config.number_of_pins = ret == 0 ? args.args[2]
                                         : RCAR_MAX_GPIO_PER_BANK;
                p->config.gpio_base = -1;
        }
index 86ef346..0dee0e0 100644 (file)
@@ -136,7 +136,7 @@ static struct gpio_desc *gpio_to_desc(unsigned gpio)
  */
 static int desc_to_gpio(const struct gpio_desc *desc)
 {
-       return desc->chip->base + gpio_chip_hwgpio(desc);
+       return desc - &gpio_desc[0];
 }
 
 
@@ -1398,7 +1398,7 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
        int                     status = -EPROBE_DEFER;
        unsigned long           flags;
 
-       if (!desc || !desc->chip) {
+       if (!desc) {
                pr_warn("%s: invalid GPIO\n", __func__);
                return -EINVAL;
        }
@@ -1406,6 +1406,8 @@ static int gpiod_request(struct gpio_desc *desc, const char *label)
        spin_lock_irqsave(&gpio_lock, flags);
 
        chip = desc->chip;
+       if (chip == NULL)
+               goto done;
 
        if (!try_module_get(chip->owner))
                goto done;
index b4fb86d..224ff96 100644 (file)
 
 #include <drm/drmP.h>
 
+/******************************************************************/
+/** \name Context bitmap support */
+/*@{*/
+
 /**
  * Free a handle from the context bitmap.
  *
  * in drm_device::ctx_idr, while holding the drm_device::struct_mutex
  * lock.
  */
-static void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
+void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
 {
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
        mutex_lock(&dev->struct_mutex);
        idr_remove(&dev->ctx_idr, ctx_handle);
        mutex_unlock(&dev->struct_mutex);
 }
 
-/******************************************************************/
-/** \name Context bitmap support */
-/*@{*/
-
-void drm_legacy_ctxbitmap_release(struct drm_device *dev,
-                                 struct drm_file *file_priv)
-{
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
-       mutex_lock(&dev->ctxlist_mutex);
-       if (!list_empty(&dev->ctxlist)) {
-               struct drm_ctx_list *pos, *n;
-
-               list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
-                       if (pos->tag == file_priv &&
-                           pos->handle != DRM_KERNEL_CONTEXT) {
-                               if (dev->driver->context_dtor)
-                                       dev->driver->context_dtor(dev,
-                                                                 pos->handle);
-
-                               drm_ctxbitmap_free(dev, pos->handle);
-
-                               list_del(&pos->head);
-                               kfree(pos);
-                               --dev->ctx_count;
-                       }
-               }
-       }
-       mutex_unlock(&dev->ctxlist_mutex);
-}
-
 /**
  * Context bitmap allocation.
  *
@@ -121,12 +90,10 @@ static int drm_ctxbitmap_next(struct drm_device * dev)
  *
  * Initialise the drm_device::ctx_idr
  */
-void drm_legacy_ctxbitmap_init(struct drm_device * dev)
+int drm_ctxbitmap_init(struct drm_device * dev)
 {
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return;
-
        idr_init(&dev->ctx_idr);
+       return 0;
 }
 
 /**
@@ -137,7 +104,7 @@ void drm_legacy_ctxbitmap_init(struct drm_device * dev)
  * Free all idr members using drm_ctx_sarea_free helper function
  * while holding the drm_device::struct_mutex lock.
  */
-void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev)
+void drm_ctxbitmap_cleanup(struct drm_device * dev)
 {
        mutex_lock(&dev->struct_mutex);
        idr_destroy(&dev->ctx_idr);
@@ -169,9 +136,6 @@ int drm_getsareactx(struct drm_device *dev, void *data,
        struct drm_local_map *map;
        struct drm_map_list *_entry;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        mutex_lock(&dev->struct_mutex);
 
        map = idr_find(&dev->ctx_idr, request->ctx_id);
@@ -216,9 +180,6 @@ int drm_setsareactx(struct drm_device *dev, void *data,
        struct drm_local_map *map = NULL;
        struct drm_map_list *r_list = NULL;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        mutex_lock(&dev->struct_mutex);
        list_for_each_entry(r_list, &dev->maplist, head) {
                if (r_list->map
@@ -319,9 +280,6 @@ int drm_resctx(struct drm_device *dev, void *data,
        struct drm_ctx ctx;
        int i;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        if (res->count >= DRM_RESERVED_CONTEXTS) {
                memset(&ctx, 0, sizeof(ctx));
                for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
@@ -352,9 +310,6 @@ int drm_addctx(struct drm_device *dev, void *data,
        struct drm_ctx_list *ctx_entry;
        struct drm_ctx *ctx = data;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        ctx->handle = drm_ctxbitmap_next(dev);
        if (ctx->handle == DRM_KERNEL_CONTEXT) {
                /* Skip kernel's context and get a new one. */
@@ -398,9 +353,6 @@ int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
        struct drm_ctx *ctx = data;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        /* This is 0, because we don't handle any context flags */
        ctx->flags = 0;
 
@@ -423,9 +375,6 @@ int drm_switchctx(struct drm_device *dev, void *data,
 {
        struct drm_ctx *ctx = data;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        DRM_DEBUG("%d\n", ctx->handle);
        return drm_context_switch(dev, dev->last_context, ctx->handle);
 }
@@ -446,9 +395,6 @@ int drm_newctx(struct drm_device *dev, void *data,
 {
        struct drm_ctx *ctx = data;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        DRM_DEBUG("%d\n", ctx->handle);
        drm_context_switch_complete(dev, file_priv, ctx->handle);
 
@@ -471,9 +417,6 @@ int drm_rmctx(struct drm_device *dev, void *data,
 {
        struct drm_ctx *ctx = data;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
        DRM_DEBUG("%d\n", ctx->handle);
        if (ctx->handle != DRM_KERNEL_CONTEXT) {
                if (dev->driver->context_dtor)
index e572dd2..fe58d08 100644 (file)
@@ -61,7 +61,7 @@ static int drm_version(struct drm_device *dev, void *data,
 
 /** Ioctl table */
 static const struct drm_ioctl_desc drm_ioctls[] = {
-       DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
        DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
        DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
@@ -402,9 +402,16 @@ long drm_ioctl(struct file *filp,
                cmd = ioctl->cmd_drv;
        }
        else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
+               u32 drv_size;
+
                ioctl = &drm_ioctls[nr];
-               cmd = ioctl->cmd;
+
+               drv_size = _IOC_SIZE(ioctl->cmd);
                usize = asize = _IOC_SIZE(cmd);
+               if (drv_size > asize)
+                       asize = drv_size;
+
+               cmd = ioctl->cmd;
        } else
                goto err_i1;
 
index 1688ff5..830f750 100644 (file)
@@ -2925,6 +2925,8 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
                        /* Speaker Allocation Data Block */
                        if (dbl == 3) {
                                *sadb = kmalloc(dbl, GFP_KERNEL);
+                               if (!*sadb)
+                                       return -ENOMEM;
                                memcpy(*sadb, &db[1], dbl);
                                count = dbl;
                                break;
index f6f6cc7..3d13ca6 100644 (file)
@@ -407,14 +407,6 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
        struct drm_connector *connector;
        int i, j;
 
-       /*
-        * fbdev->blank can be called from irq context in case of a panic.
-        * Since we already have our own special panic handler which will
-        * restore the fbdev console mode completely, just bail out early.
-        */
-       if (oops_in_progress)
-               return;
-
        /*
         * fbdev->blank can be called from irq context in case of a panic.
         * Since we already have our own special panic handler which will
index 4be8e09..3f84277 100644 (file)
@@ -439,7 +439,26 @@ int drm_release(struct inode *inode, struct file *filp)
        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_release(dev, file_priv);
 
-       drm_legacy_ctxbitmap_release(dev, file_priv);
+       mutex_lock(&dev->ctxlist_mutex);
+       if (!list_empty(&dev->ctxlist)) {
+               struct drm_ctx_list *pos, *n;
+
+               list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
+                       if (pos->tag == file_priv &&
+                           pos->handle != DRM_KERNEL_CONTEXT) {
+                               if (dev->driver->context_dtor)
+                                       dev->driver->context_dtor(dev,
+                                                                 pos->handle);
+
+                               drm_ctxbitmap_free(dev, pos->handle);
+
+                               list_del(&pos->head);
+                               kfree(pos);
+                               --dev->ctx_count;
+                       }
+               }
+       }
+       mutex_unlock(&dev->ctxlist_mutex);
 
        mutex_lock(&dev->struct_mutex);
 
index e7eb027..39d8645 100644 (file)
@@ -292,7 +292,13 @@ int drm_fill_in_dev(struct drm_device *dev,
                        goto error_out_unreg;
        }
 
-       drm_legacy_ctxbitmap_init(dev);
+
+
+       retcode = drm_ctxbitmap_init(dev);
+       if (retcode) {
+               DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+               goto error_out_unreg;
+       }
 
        if (driver->driver_features & DRIVER_GEM) {
                retcode = drm_gem_init(dev);
@@ -446,7 +452,7 @@ void drm_put_dev(struct drm_device *dev)
                drm_rmmap(dev, r_list->map);
        drm_ht_remove(&dev->map_hash);
 
-       drm_legacy_ctxbitmap_cleanup(dev);
+       drm_ctxbitmap_cleanup(dev);
 
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                drm_put_minor(&dev->control);
index 4752f22..45b6ef5 100644 (file)
@@ -56,7 +56,7 @@ config DRM_EXYNOS_IPP
 
 config DRM_EXYNOS_FIMC
        bool "Exynos DRM FIMC"
-       depends on DRM_EXYNOS_IPP && MFD_SYSCON && OF
+       depends on DRM_EXYNOS_IPP && MFD_SYSCON
        help
          Choose this option if you want to use Exynos FIMC for DRM.
 
index 3445a0f..9c80884 100644 (file)
@@ -63,7 +63,8 @@ static int lowlevel_buffer_allocate(struct drm_device *dev,
                        return -ENOMEM;
                }
 
-               buf->kvaddr = dma_alloc_attrs(dev->dev, buf->size,
+               buf->kvaddr = (void __iomem *)dma_alloc_attrs(dev->dev,
+                                       buf->size,
                                        &buf->dma_addr, GFP_KERNEL,
                                        &buf->dma_attrs);
                if (!buf->kvaddr) {
@@ -90,9 +91,9 @@ static int lowlevel_buffer_allocate(struct drm_device *dev,
        }
 
        buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages);
-       if (!buf->sgt) {
+       if (IS_ERR(buf->sgt)) {
                DRM_ERROR("failed to get sg table.\n");
-               ret = -ENOMEM;
+               ret = PTR_ERR(buf->sgt);
                goto err_free_attrs;
        }
 
index 78e868b..e7c2f2d 100644 (file)
@@ -99,12 +99,13 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
                if (is_drm_iommu_supported(dev)) {
                        unsigned int nr_pages = buffer->size >> PAGE_SHIFT;
 
-                       buffer->kvaddr = vmap(buffer->pages, nr_pages, VM_MAP,
+                       buffer->kvaddr = (void __iomem *) vmap(buffer->pages,
+                                       nr_pages, VM_MAP,
                                        pgprot_writecombine(PAGE_KERNEL));
                } else {
                        phys_addr_t dma_addr = buffer->dma_addr;
                        if (dma_addr)
-                               buffer->kvaddr = phys_to_virt(dma_addr);
+                               buffer->kvaddr = (void __iomem *)phys_to_virt(dma_addr);
                        else
                                buffer->kvaddr = (void __iomem *)NULL;
                }
index 92babac..2db731f 100644 (file)
@@ -204,6 +204,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt)
        if (IS_ERR(pages))
                return PTR_ERR(pages);
 
+       gt->npage = gt->gem.size / PAGE_SIZE;
        gt->pages = pages;
 
        return 0;
index b1f8fc6..60e8404 100644 (file)
@@ -707,8 +707,7 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
                reg_write(encoder, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
                break;
        case DRM_MODE_DPMS_OFF:
-               /* disable audio and video ports */
-               reg_write(encoder, REG_ENA_AP, 0x00);
+               /* disable video ports */
                reg_write(encoder, REG_ENA_VP_0, 0x00);
                reg_write(encoder, REG_ENA_VP_1, 0x00);
                reg_write(encoder, REG_ENA_VP_2, 0x00);
index c27a210..d5c784d 100644 (file)
@@ -1290,12 +1290,9 @@ static int i915_load_modeset_init(struct drm_device *dev)
         * then we do not take part in VGA arbitration and the
         * vga_client_register() fails with -ENODEV.
         */
-       if (!HAS_PCH_SPLIT(dev)) {
-               ret = vga_client_register(dev->pdev, dev, NULL,
-                                         i915_vga_set_decode);
-               if (ret && ret != -ENODEV)
-                       goto out;
-       }
+       ret = vga_client_register(dev->pdev, dev, NULL, i915_vga_set_decode);
+       if (ret && ret != -ENODEV)
+               goto out;
 
        intel_register_dsm_handler();
 
@@ -1351,12 +1348,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
         */
        intel_fbdev_initial_config(dev);
 
-       /*
-        * Must do this after fbcon init so that
-        * vgacon_save_screen() works during the handover.
-        */
-       i915_disable_vga_mem(dev);
-
        /* Only enable hotplug handling once the fbdev is fully set up. */
        dev_priv->enable_hotplug_processing = true;
 
index 69d8ed5..2ad2788 100644 (file)
@@ -505,6 +505,8 @@ static int i915_drm_freeze(struct drm_device *dev)
                intel_modeset_suspend_hw(dev);
        }
 
+       i915_gem_suspend_gtt_mappings(dev);
+
        i915_save_state(dev);
 
        intel_opregion_fini(dev);
@@ -648,7 +650,8 @@ static int i915_drm_thaw(struct drm_device *dev)
                mutex_lock(&dev->struct_mutex);
                i915_gem_restore_gtt_mappings(dev);
                mutex_unlock(&dev->struct_mutex);
-       }
+       } else if (drm_core_check_feature(dev, DRIVER_MODESET))
+               i915_check_and_clear_faults(dev);
 
        __i915_drm_thaw(dev);
 
index 35874b3..ab0f2c0 100644 (file)
@@ -497,10 +497,12 @@ struct i915_address_space {
 
        /* FIXME: Need a more generic return type */
        gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
-                                    enum i915_cache_level level);
+                                    enum i915_cache_level level,
+                                    bool valid); /* Create a valid PTE */
        void (*clear_range)(struct i915_address_space *vm,
                            unsigned int first_entry,
-                           unsigned int num_entries);
+                           unsigned int num_entries,
+                           bool use_scratch);
        void (*insert_entries)(struct i915_address_space *vm,
                               struct sg_table *st,
                               unsigned int first_entry,
@@ -2065,6 +2067,8 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
 void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
                              struct drm_i915_gem_object *obj);
 
+void i915_check_and_clear_faults(struct drm_device *dev);
+void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
 void i915_gem_restore_gtt_mappings(struct drm_device *dev);
 int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
 void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
index 8507c6d..cdfb9da 100644 (file)
@@ -1392,14 +1392,11 @@ out:
                if (i915_terminally_wedged(&dev_priv->gpu_error))
                        return VM_FAULT_SIGBUS;
        case -EAGAIN:
-               /* Give the error handler a chance to run and move the
-                * objects off the GPU active list. Next time we service the
-                * fault, we should be able to transition the page into the
-                * GTT without touching the GPU (and so avoid further
-                * EIO/EGAIN). If the GPU is wedged, then there is no issue
-                * with coherency, just lost writes.
+               /*
+                * EAGAIN means the gpu is hung and we'll wait for the error
+                * handler to reset everything when re-faulting in
+                * i915_mutex_lock_interruptible.
                 */
-               set_need_resched();
        case 0:
        case -ERESTARTSYS:
        case -EINTR:
@@ -4803,10 +4800,10 @@ i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
 
        if (!mutex_trylock(&dev->struct_mutex)) {
                if (!mutex_is_locked_by(&dev->struct_mutex, current))
-                       return SHRINK_STOP;
+                       return 0;
 
                if (dev_priv->mm.shrinker_no_lock_stealing)
-                       return SHRINK_STOP;
+                       return 0;
 
                unlock = false;
        }
@@ -4904,10 +4901,10 @@ i915_gem_inactive_scan(struct shrinker *shrinker, struct shrink_control *sc)
 
        if (!mutex_trylock(&dev->struct_mutex)) {
                if (!mutex_is_locked_by(&dev->struct_mutex, current))
-                       return 0;
+                       return SHRINK_STOP;
 
                if (dev_priv->mm.shrinker_no_lock_stealing)
-                       return 0;
+                       return SHRINK_STOP;
 
                unlock = false;
        }
index 212f6d8..1f7b4ca 100644 (file)
 #define HSW_WT_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0x6)
 
 static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -79,9 +80,10 @@ static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
 }
 
 static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -105,9 +107,10 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
 #define BYT_PTE_SNOOPED_BY_CPU_CACHES  (1 << 2)
 
 static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        /* Mark the page as writeable.  Other platforms don't have a
@@ -122,9 +125,10 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
 }
 
 static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level)
+                                    enum i915_cache_level level,
+                                    bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= HSW_PTE_ADDR_ENCODE(addr);
 
        if (level != I915_CACHE_NONE)
@@ -134,9 +138,10 @@ static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
 }
 
 static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
-                                     enum i915_cache_level level)
+                                     enum i915_cache_level level,
+                                     bool valid)
 {
-       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= HSW_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -236,7 +241,8 @@ static int gen6_ppgtt_enable(struct drm_device *dev)
 /* PPGTT support for Sandybdrige/Gen6 and later */
 static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
                                   unsigned first_entry,
-                                  unsigned num_entries)
+                                  unsigned num_entries,
+                                  bool use_scratch)
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
@@ -245,7 +251,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
        unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
        unsigned last_pte, i;
 
-       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC);
+       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true);
 
        while (num_entries) {
                last_pte = first_pte + num_entries;
@@ -282,7 +288,7 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
                dma_addr_t page_addr;
 
                page_addr = sg_page_iter_dma_address(&sg_iter);
-               pt_vaddr[act_pte] = vm->pte_encode(page_addr, cache_level);
+               pt_vaddr[act_pte] = vm->pte_encode(page_addr, cache_level, true);
                if (++act_pte == I915_PPGTT_PT_ENTRIES) {
                        kunmap_atomic(pt_vaddr);
                        act_pt++;
@@ -367,7 +373,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        }
 
        ppgtt->base.clear_range(&ppgtt->base, 0,
-                               ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES);
+                               ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES, true);
 
        ppgtt->pd_offset = first_pd_entry_in_global_pt * sizeof(gen6_gtt_pte_t);
 
@@ -444,7 +450,8 @@ void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
 {
        ppgtt->base.clear_range(&ppgtt->base,
                                i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
-                               obj->base.size >> PAGE_SHIFT);
+                               obj->base.size >> PAGE_SHIFT,
+                               true);
 }
 
 extern int intel_iommu_gfx_mapped;
@@ -485,15 +492,65 @@ static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
                dev_priv->mm.interruptible = interruptible;
 }
 
+void i915_check_and_clear_faults(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
+       int i;
+
+       if (INTEL_INFO(dev)->gen < 6)
+               return;
+
+       for_each_ring(ring, dev_priv, i) {
+               u32 fault_reg;
+               fault_reg = I915_READ(RING_FAULT_REG(ring));
+               if (fault_reg & RING_FAULT_VALID) {
+                       DRM_DEBUG_DRIVER("Unexpected fault\n"
+                                        "\tAddr: 0x%08lx\\n"
+                                        "\tAddress space: %s\n"
+                                        "\tSource ID: %d\n"
+                                        "\tType: %d\n",
+                                        fault_reg & PAGE_MASK,
+                                        fault_reg & RING_FAULT_GTTSEL_MASK ? "GGTT" : "PPGTT",
+                                        RING_FAULT_SRCID(fault_reg),
+                                        RING_FAULT_FAULT_TYPE(fault_reg));
+                       I915_WRITE(RING_FAULT_REG(ring),
+                                  fault_reg & ~RING_FAULT_VALID);
+               }
+       }
+       POSTING_READ(RING_FAULT_REG(&dev_priv->ring[RCS]));
+}
+
+void i915_gem_suspend_gtt_mappings(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* Don't bother messing with faults pre GEN6 as we have little
+        * documentation supporting that it's a good idea.
+        */
+       if (INTEL_INFO(dev)->gen < 6)
+               return;
+
+       i915_check_and_clear_faults(dev);
+
+       dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
+                                      dev_priv->gtt.base.start / PAGE_SIZE,
+                                      dev_priv->gtt.base.total / PAGE_SIZE,
+                                      false);
+}
+
 void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
 
+       i915_check_and_clear_faults(dev);
+
        /* First fill our portion of the GTT with scratch pages */
        dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
                                       dev_priv->gtt.base.start / PAGE_SIZE,
-                                      dev_priv->gtt.base.total / PAGE_SIZE);
+                                      dev_priv->gtt.base.total / PAGE_SIZE,
+                                      true);
 
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                i915_gem_clflush_object(obj, obj->pin_display);
@@ -536,7 +593,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
 
        for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
                addr = sg_page_iter_dma_address(&sg_iter);
-               iowrite32(vm->pte_encode(addr, level), &gtt_entries[i]);
+               iowrite32(vm->pte_encode(addr, level, true), &gtt_entries[i]);
                i++;
        }
 
@@ -548,7 +605,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
         */
        if (i != 0)
                WARN_ON(readl(&gtt_entries[i-1]) !=
-                       vm->pte_encode(addr, level));
+                       vm->pte_encode(addr, level, true));
 
        /* This next bit makes the above posting read even more important. We
         * want to flush the TLBs only after we're certain all the PTE updates
@@ -560,7 +617,8 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
 
 static void gen6_ggtt_clear_range(struct i915_address_space *vm,
                                  unsigned int first_entry,
-                                 unsigned int num_entries)
+                                 unsigned int num_entries,
+                                 bool use_scratch)
 {
        struct drm_i915_private *dev_priv = vm->dev->dev_private;
        gen6_gtt_pte_t scratch_pte, __iomem *gtt_base =
@@ -573,7 +631,8 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
                 first_entry, num_entries, max_entries))
                num_entries = max_entries;
 
-       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC);
+       scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, use_scratch);
+
        for (i = 0; i < num_entries; i++)
                iowrite32(scratch_pte, &gtt_base[i]);
        readl(gtt_base);
@@ -594,7 +653,8 @@ static void i915_ggtt_insert_entries(struct i915_address_space *vm,
 
 static void i915_ggtt_clear_range(struct i915_address_space *vm,
                                  unsigned int first_entry,
-                                 unsigned int num_entries)
+                                 unsigned int num_entries,
+                                 bool unused)
 {
        intel_gtt_clear_range(first_entry, num_entries);
 }
@@ -622,7 +682,8 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
 
        dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
                                       entry,
-                                      obj->base.size >> PAGE_SHIFT);
+                                      obj->base.size >> PAGE_SHIFT,
+                                      true);
 
        obj->has_global_gtt_mapping = 0;
 }
@@ -709,11 +770,11 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
                const unsigned long count = (hole_end - hole_start) / PAGE_SIZE;
                DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
                              hole_start, hole_end);
-               ggtt_vm->clear_range(ggtt_vm, hole_start / PAGE_SIZE, count);
+               ggtt_vm->clear_range(ggtt_vm, hole_start / PAGE_SIZE, count, true);
        }
 
        /* And finally clear the reserved guard page */
-       ggtt_vm->clear_range(ggtt_vm, end / PAGE_SIZE - 1, 1);
+       ggtt_vm->clear_range(ggtt_vm, end / PAGE_SIZE - 1, 1, true);
 }
 
 static bool
index aba9d74..dae364f 100644 (file)
@@ -143,8 +143,10 @@ static void i915_error_vprintf(struct drm_i915_error_state_buf *e,
 
        /* Seek the first printf which is hits start position */
        if (e->pos < e->start) {
-               len = vsnprintf(NULL, 0, f, args);
-               if (!__i915_error_seek(e, len))
+               va_list tmp;
+
+               va_copy(tmp, args);
+               if (!__i915_error_seek(e, vsnprintf(NULL, 0, f, tmp)))
                        return;
        }
 
index 83cce0c..4b91228 100644 (file)
@@ -1469,6 +1469,34 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        return ret;
 }
 
+static void i915_error_wake_up(struct drm_i915_private *dev_priv,
+                              bool reset_completed)
+{
+       struct intel_ring_buffer *ring;
+       int i;
+
+       /*
+        * Notify all waiters for GPU completion events that reset state has
+        * been changed, and that they need to restart their wait after
+        * checking for potential errors (and bail out to drop locks if there is
+        * a gpu reset pending so that i915_error_work_func can acquire them).
+        */
+
+       /* Wake up __wait_seqno, potentially holding dev->struct_mutex. */
+       for_each_ring(ring, dev_priv, i)
+               wake_up_all(&ring->irq_queue);
+
+       /* Wake up intel_crtc_wait_for_pending_flips, holding crtc->mutex. */
+       wake_up_all(&dev_priv->pending_flip_queue);
+
+       /*
+        * Signal tasks blocked in i915_gem_wait_for_error that the pending
+        * reset state is cleared.
+        */
+       if (reset_completed)
+               wake_up_all(&dev_priv->gpu_error.reset_queue);
+}
+
 /**
  * i915_error_work_func - do process context error handling work
  * @work: work struct
@@ -1483,11 +1511,10 @@ static void i915_error_work_func(struct work_struct *work)
        drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t,
                                                    gpu_error);
        struct drm_device *dev = dev_priv->dev;
-       struct intel_ring_buffer *ring;
        char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
        char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
        char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
-       int i, ret;
+       int ret;
 
        kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
 
@@ -1506,8 +1533,16 @@ static void i915_error_work_func(struct work_struct *work)
                kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE,
                                   reset_event);
 
+               /*
+                * All state reset _must_ be completed before we update the
+                * reset counter, for otherwise waiters might miss the reset
+                * pending state and not properly drop locks, resulting in
+                * deadlocks with the reset work.
+                */
                ret = i915_reset(dev);
 
+               intel_display_handle_reset(dev);
+
                if (ret == 0) {
                        /*
                         * After all the gem state is reset, increment the reset
@@ -1528,12 +1563,11 @@ static void i915_error_work_func(struct work_struct *work)
                        atomic_set(&error->reset_counter, I915_WEDGED);
                }
 
-               for_each_ring(ring, dev_priv, i)
-                       wake_up_all(&ring->irq_queue);
-
-               intel_display_handle_reset(dev);
-
-               wake_up_all(&dev_priv->gpu_error.reset_queue);
+               /*
+                * Note: The wake_up also serves as a memory barrier so that
+                * waiters see the update value of the reset counter atomic_t.
+                */
+               i915_error_wake_up(dev_priv, true);
        }
 }
 
@@ -1642,8 +1676,6 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
 void i915_handle_error(struct drm_device *dev, bool wedged)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
-       int i;
 
        i915_capture_error_state(dev);
        i915_report_and_clear_eir(dev);
@@ -1653,11 +1685,19 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
                                &dev_priv->gpu_error.reset_counter);
 
                /*
-                * Wakeup waiting processes so that the reset work item
-                * doesn't deadlock trying to grab various locks.
+                * Wakeup waiting processes so that the reset work function
+                * i915_error_work_func doesn't deadlock trying to grab various
+                * locks. By bumping the reset counter first, the woken
+                * processes will see a reset in progress and back off,
+                * releasing their locks and then wait for the reset completion.
+                * We must do this for _all_ gpu waiters that might hold locks
+                * that the reset work needs to acquire.
+                *
+                * Note: The wake_up serves as the required memory barrier to
+                * ensure that the waiters see the updated value of the reset
+                * counter atomic_t.
                 */
-               for_each_ring(ring, dev_priv, i)
-                       wake_up_all(&ring->irq_queue);
+               i915_error_wake_up(dev_priv, false);
        }
 
        /*
index c159e1a..ef9b354 100644 (file)
 #define   ARB_MODE_SWIZZLE_IVB (1<<5)
 #define RENDER_HWS_PGA_GEN7    (0x04080)
 #define RING_FAULT_REG(ring)   (0x4094 + 0x100*(ring)->id)
+#define   RING_FAULT_GTTSEL_MASK (1<<11)
+#define   RING_FAULT_SRCID(x)  ((x >> 3) & 0xff)
+#define   RING_FAULT_FAULT_TYPE(x) ((x >> 1) & 0x3)
+#define   RING_FAULT_VALID     (1<<0)
 #define DONE_REG               0x40b0
 #define BSD_HWS_PGA_GEN7       (0x04180)
 #define BLT_HWS_PGA_GEN7       (0x04280)
 #define GEN7_SQ_CHICKEN_MBCUNIT_CONFIG         0x9030
 #define  GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB      (1<<11)
 
+#define HSW_SCRATCH1                           0xb038
+#define  HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE  (1<<27)
+
 #define HSW_FUSE_STRAP         0x42014
 #define  HSW_CDCLK_LIMIT       (1 << 24)
 
 #define FDI_RX_CHICKEN(pipe) _PIPE(pipe, _FDI_RXA_CHICKEN, _FDI_RXB_CHICKEN)
 
 #define SOUTH_DSPCLK_GATE_D    0xc2020
+#define  PCH_DPLUNIT_CLOCK_GATE_DISABLE (1<<30)
 #define  PCH_DPLSUNIT_CLOCK_GATE_DISABLE (1<<29)
+#define  PCH_CPUNIT_CLOCK_GATE_DISABLE (1<<14)
 #define  PCH_LP_PARTITION_LEVEL_DISABLE  (1<<12)
 
 /* CPU: FDI_TX */
 #define GEN7_ROW_CHICKEN2_GT2          0xf4f4
 #define   DOP_CLOCK_GATING_DISABLE     (1<<0)
 
+#define HSW_ROW_CHICKEN3               0xe49c
+#define  HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE    (1 << 6)
+
 #define G4X_AUD_VID_DID                        (dev_priv->info->display_mmio_offset + 0x62020)
 #define INTEL_AUDIO_DEVCL              0x808629FB
 #define INTEL_AUDIO_DEVBLC             0x80862801
index ea9022e..10d1de5 100644 (file)
@@ -83,8 +83,7 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_crt_get_config(struct intel_encoder *encoder,
-                                struct intel_crtc_config *pipe_config)
+static unsigned int intel_crt_get_flags(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_crt *crt = intel_encoder_to_crt(encoder);
@@ -102,7 +101,25 @@ static void intel_crt_get_config(struct intel_encoder *encoder,
        else
                flags |= DRM_MODE_FLAG_NVSYNC;
 
-       pipe_config->adjusted_mode.flags |= flags;
+       return flags;
+}
+
+static void intel_crt_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config)
+{
+       pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder);
+}
+
+static void hsw_crt_get_config(struct intel_encoder *encoder,
+                              struct intel_crtc_config *pipe_config)
+{
+       intel_ddi_get_config(encoder, pipe_config);
+
+       pipe_config->adjusted_mode.flags &= ~(DRM_MODE_FLAG_PHSYNC |
+                                             DRM_MODE_FLAG_NHSYNC |
+                                             DRM_MODE_FLAG_PVSYNC |
+                                             DRM_MODE_FLAG_NVSYNC);
+       pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder);
 }
 
 /* Note: The caller is required to filter out dpms modes not supported by the
@@ -799,7 +816,10 @@ void intel_crt_init(struct drm_device *dev)
        crt->base.mode_set = intel_crt_mode_set;
        crt->base.disable = intel_disable_crt;
        crt->base.enable = intel_enable_crt;
-       crt->base.get_config = intel_crt_get_config;
+       if (IS_HASWELL(dev))
+               crt->base.get_config = hsw_crt_get_config;
+       else
+               crt->base.get_config = intel_crt_get_config;
        if (I915_HAS_HOTPLUG(dev))
                crt->base.hpd_pin = HPD_CRT;
        if (HAS_DDI(dev))
index 63aca49..b53fff8 100644 (file)
@@ -778,7 +778,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
                        /* Can only use the always-on power well for eDP when
                         * not using the panel fitter, and when not using motion
                          * blur mitigation (which we don't support). */
-                       if (intel_crtc->config.pch_pfit.size)
+                       if (intel_crtc->config.pch_pfit.enabled)
                                temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
                        else
                                temp |= TRANS_DDI_EDP_INPUT_A_ON;
@@ -1249,8 +1249,8 @@ static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
                intel_dp_check_link_status(intel_dp);
 }
 
-static void intel_ddi_get_config(struct intel_encoder *encoder,
-                                struct intel_crtc_config *pipe_config)
+void intel_ddi_get_config(struct intel_encoder *encoder,
+                         struct intel_crtc_config *pipe_config)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
@@ -1268,6 +1268,23 @@ static void intel_ddi_get_config(struct intel_encoder *encoder,
                flags |= DRM_MODE_FLAG_NVSYNC;
 
        pipe_config->adjusted_mode.flags |= flags;
+
+       switch (temp & TRANS_DDI_BPC_MASK) {
+       case TRANS_DDI_BPC_6:
+               pipe_config->pipe_bpp = 18;
+               break;
+       case TRANS_DDI_BPC_8:
+               pipe_config->pipe_bpp = 24;
+               break;
+       case TRANS_DDI_BPC_10:
+               pipe_config->pipe_bpp = 30;
+               break;
+       case TRANS_DDI_BPC_12:
+               pipe_config->pipe_bpp = 36;
+               break;
+       default:
+               break;
+       }
 }
 
 static void intel_ddi_destroy(struct drm_encoder *encoder)
index 2489d0b..d78d33f 100644 (file)
@@ -2249,7 +2249,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                I915_WRITE(PIPESRC(intel_crtc->pipe),
                           ((crtc->mode.hdisplay - 1) << 16) |
                           (crtc->mode.vdisplay - 1));
-               if (!intel_crtc->config.pch_pfit.size &&
+               if (!intel_crtc->config.pch_pfit.enabled &&
                    (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) ||
                     intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) {
                        I915_WRITE(PF_CTL(intel_crtc->pipe), 0);
@@ -2327,9 +2327,10 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
                           FDI_FE_ERRC_ENABLE);
 }
 
-static bool pipe_has_enabled_pch(struct intel_crtc *intel_crtc)
+static bool pipe_has_enabled_pch(struct intel_crtc *crtc)
 {
-       return intel_crtc->base.enabled && intel_crtc->config.has_pch_encoder;
+       return crtc->base.enabled && crtc->active &&
+               crtc->config.has_pch_encoder;
 }
 
 static void ivb_modeset_global_resources(struct drm_device *dev)
@@ -2979,6 +2980,48 @@ static void ironlake_pch_transcoder_set_timings(struct intel_crtc *crtc,
                   I915_READ(VSYNCSHIFT(cpu_transcoder)));
 }
 
+static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t temp;
+
+       temp = I915_READ(SOUTH_CHICKEN1);
+       if (temp & FDI_BC_BIFURCATION_SELECT)
+               return;
+
+       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
+       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
+
+       temp |= FDI_BC_BIFURCATION_SELECT;
+       DRM_DEBUG_KMS("enabling fdi C rx\n");
+       I915_WRITE(SOUTH_CHICKEN1, temp);
+       POSTING_READ(SOUTH_CHICKEN1);
+}
+
+static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc)
+{
+       struct drm_device *dev = intel_crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       switch (intel_crtc->pipe) {
+       case PIPE_A:
+               break;
+       case PIPE_B:
+               if (intel_crtc->config.fdi_lanes > 2)
+                       WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT);
+               else
+                       cpt_enable_fdi_bc_bifurcation(dev);
+
+               break;
+       case PIPE_C:
+               cpt_enable_fdi_bc_bifurcation(dev);
+
+               break;
+       default:
+               BUG();
+       }
+}
+
 /*
  * Enable PCH resources required for PCH ports:
  *   - PCH PLLs
@@ -2997,6 +3040,9 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
 
        assert_pch_transcoder_disabled(dev_priv, pipe);
 
+       if (IS_IVYBRIDGE(dev))
+               ivybridge_update_fdi_bc_bifurcation(intel_crtc);
+
        /* Write the TU size bits before fdi link training, so that error
         * detection works. */
        I915_WRITE(FDI_RX_TUSIZE1(pipe),
@@ -3203,7 +3249,7 @@ static void ironlake_pfit_enable(struct intel_crtc *crtc)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe = crtc->pipe;
 
-       if (crtc->config.pch_pfit.size) {
+       if (crtc->config.pch_pfit.enabled) {
                /* Force use of hard-coded filter coefficients
                 * as some pre-programmed values are broken,
                 * e.g. x201.
@@ -3428,7 +3474,7 @@ static void ironlake_pfit_disable(struct intel_crtc *crtc)
 
        /* To avoid upsetting the power well on haswell only disable the pfit if
         * it's in use. The hw state code will make sure we get this right. */
-       if (crtc->config.pch_pfit.size) {
+       if (crtc->config.pch_pfit.enabled) {
                I915_WRITE(PF_CTL(pipe), 0);
                I915_WRITE(PF_WIN_POS(pipe), 0);
                I915_WRITE(PF_WIN_SZ(pipe), 0);
@@ -3941,8 +3987,6 @@ static void intel_connector_check_state(struct intel_connector *connector)
  * consider. */
 void intel_connector_dpms(struct drm_connector *connector, int mode)
 {
-       struct intel_encoder *encoder = intel_attached_encoder(connector);
-
        /* All the simple cases only support two dpms states. */
        if (mode != DRM_MODE_DPMS_ON)
                mode = DRM_MODE_DPMS_OFF;
@@ -3953,10 +3997,8 @@ void intel_connector_dpms(struct drm_connector *connector, int mode)
        connector->dpms = mode;
 
        /* Only need to change hw state when actually enabled */
-       if (encoder->base.crtc)
-               intel_encoder_dpms(encoder, mode);
-       else
-               WARN_ON(encoder->connectors_active != false);
+       if (connector->encoder)
+               intel_encoder_dpms(to_intel_encoder(connector->encoder), mode);
 
        intel_modeset_check_state(connector->dev);
 }
@@ -4775,6 +4817,10 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
 
        pipeconf = 0;
 
+       if (dev_priv->quirks & QUIRK_PIPEA_FORCE &&
+           I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE)
+               pipeconf |= PIPECONF_ENABLE;
+
        if (intel_crtc->pipe == 0 && INTEL_INFO(dev)->gen < 4) {
                /* Enable pixel doubling when the dot clock is > 90% of the (display)
                 * core speed.
@@ -4877,9 +4923,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                return -EINVAL;
        }
 
-       /* Ensure that the cursor is valid for the new mode before changing... */
-       intel_crtc_update_cursor(crtc, true);
-
        if (is_lvds && dev_priv->lvds_downclock_avail) {
                /*
                 * Ensure we match the reduced clock's P to the target clock.
@@ -4986,6 +5029,22 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
+       if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) {
+               switch (tmp & PIPECONF_BPC_MASK) {
+               case PIPECONF_6BPC:
+                       pipe_config->pipe_bpp = 18;
+                       break;
+               case PIPECONF_8BPC:
+                       pipe_config->pipe_bpp = 24;
+                       break;
+               case PIPECONF_10BPC:
+                       pipe_config->pipe_bpp = 30;
+                       break;
+               default:
+                       break;
+               }
+       }
+
        intel_get_pipe_timings(crtc, pipe_config);
 
        i9xx_get_pfit_config(crtc, pipe_config);
@@ -5579,48 +5638,6 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc,
        return true;
 }
 
-static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t temp;
-
-       temp = I915_READ(SOUTH_CHICKEN1);
-       if (temp & FDI_BC_BIFURCATION_SELECT)
-               return;
-
-       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
-       WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
-
-       temp |= FDI_BC_BIFURCATION_SELECT;
-       DRM_DEBUG_KMS("enabling fdi C rx\n");
-       I915_WRITE(SOUTH_CHICKEN1, temp);
-       POSTING_READ(SOUTH_CHICKEN1);
-}
-
-static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc)
-{
-       struct drm_device *dev = intel_crtc->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       switch (intel_crtc->pipe) {
-       case PIPE_A:
-               break;
-       case PIPE_B:
-               if (intel_crtc->config.fdi_lanes > 2)
-                       WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT);
-               else
-                       cpt_enable_fdi_bc_bifurcation(dev);
-
-               break;
-       case PIPE_C:
-               cpt_enable_fdi_bc_bifurcation(dev);
-
-               break;
-       default:
-               BUG();
-       }
-}
-
 int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp)
 {
        /*
@@ -5768,9 +5785,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                intel_crtc->config.dpll.p2 = clock.p2;
        }
 
-       /* Ensure that the cursor is valid for the new mode before changing... */
-       intel_crtc_update_cursor(crtc, true);
-
        /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */
        if (intel_crtc->config.has_pch_encoder) {
                fp = i9xx_dpll_compute_fp(&intel_crtc->config.dpll);
@@ -5817,9 +5831,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                                             &intel_crtc->config.fdi_m_n);
        }
 
-       if (IS_IVYBRIDGE(dev))
-               ivybridge_update_fdi_bc_bifurcation(intel_crtc);
-
        ironlake_set_pipeconf(crtc);
 
        /* Set up the display plane register */
@@ -5859,6 +5870,7 @@ static void ironlake_get_pfit_config(struct intel_crtc *crtc,
        tmp = I915_READ(PF_CTL(crtc->pipe));
 
        if (tmp & PF_ENABLE) {
+               pipe_config->pch_pfit.enabled = true;
                pipe_config->pch_pfit.pos = I915_READ(PF_WIN_POS(crtc->pipe));
                pipe_config->pch_pfit.size = I915_READ(PF_WIN_SZ(crtc->pipe));
 
@@ -5886,6 +5898,23 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
+       switch (tmp & PIPECONF_BPC_MASK) {
+       case PIPECONF_6BPC:
+               pipe_config->pipe_bpp = 18;
+               break;
+       case PIPECONF_8BPC:
+               pipe_config->pipe_bpp = 24;
+               break;
+       case PIPECONF_10BPC:
+               pipe_config->pipe_bpp = 30;
+               break;
+       case PIPECONF_12BPC:
+               pipe_config->pipe_bpp = 36;
+               break;
+       default:
+               break;
+       }
+
        if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) {
                struct intel_shared_dpll *pll;
 
@@ -6236,7 +6265,7 @@ static void haswell_modeset_global_resources(struct drm_device *dev)
                if (!crtc->base.enabled)
                        continue;
 
-               if (crtc->pipe != PIPE_A || crtc->config.pch_pfit.size ||
+               if (crtc->pipe != PIPE_A || crtc->config.pch_pfit.enabled ||
                    crtc->config.cpu_transcoder != TRANSCODER_EDP)
                        enable = true;
        }
@@ -6259,9 +6288,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
        if (!intel_ddi_pll_mode_set(crtc))
                return -EINVAL;
 
-       /* Ensure that the cursor is valid for the new mode before changing... */
-       intel_crtc_update_cursor(crtc, true);
-
        if (intel_crtc->config.has_dp_encoder)
                intel_dp_set_m_n(intel_crtc);
 
@@ -6494,15 +6520,15 @@ static void haswell_write_eld(struct drm_connector *connector,
 
        /* Set ELD valid state */
        tmp = I915_READ(aud_cntrl_st2);
-       DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%8x\n", tmp);
+       DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%08x\n", tmp);
        tmp |= (AUDIO_ELD_VALID_A << (pipe * 4));
        I915_WRITE(aud_cntrl_st2, tmp);
        tmp = I915_READ(aud_cntrl_st2);
-       DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%8x\n", tmp);
+       DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%08x\n", tmp);
 
        /* Enable HDMI mode */
        tmp = I915_READ(aud_config);
-       DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%8x\n", tmp);
+       DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%08x\n", tmp);
        /* clear N_programing_enable and N_value_index */
        tmp &= ~(AUD_CONFIG_N_VALUE_INDEX | AUD_CONFIG_N_PROG_ENABLE);
        I915_WRITE(aud_config, tmp);
@@ -6937,7 +6963,8 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        intel_crtc->cursor_width = width;
        intel_crtc->cursor_height = height;
 
-       intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
+       if (intel_crtc->active)
+               intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
 
        return 0;
 fail_unpin:
@@ -6956,7 +6983,8 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        intel_crtc->cursor_x = x;
        intel_crtc->cursor_y = y;
 
-       intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
+       if (intel_crtc->active)
+               intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
 
        return 0;
 }
@@ -8205,9 +8233,10 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
                      pipe_config->gmch_pfit.control,
                      pipe_config->gmch_pfit.pgm_ratios,
                      pipe_config->gmch_pfit.lvds_border_bits);
-       DRM_DEBUG_KMS("pch pfit: pos: 0x%08x, size: 0x%08x\n",
+       DRM_DEBUG_KMS("pch pfit: pos: 0x%08x, size: 0x%08x, %s\n",
                      pipe_config->pch_pfit.pos,
-                     pipe_config->pch_pfit.size);
+                     pipe_config->pch_pfit.size,
+                     pipe_config->pch_pfit.enabled ? "enabled" : "disabled");
        DRM_DEBUG_KMS("ips: %i\n", pipe_config->ips_enabled);
 }
 
@@ -8603,8 +8632,11 @@ intel_pipe_config_compare(struct drm_device *dev,
        if (INTEL_INFO(dev)->gen < 4)
                PIPE_CONF_CHECK_I(gmch_pfit.pgm_ratios);
        PIPE_CONF_CHECK_I(gmch_pfit.lvds_border_bits);
-       PIPE_CONF_CHECK_I(pch_pfit.pos);
-       PIPE_CONF_CHECK_I(pch_pfit.size);
+       PIPE_CONF_CHECK_I(pch_pfit.enabled);
+       if (current_config->pch_pfit.enabled) {
+               PIPE_CONF_CHECK_I(pch_pfit.pos);
+               PIPE_CONF_CHECK_I(pch_pfit.size);
+       }
 
        PIPE_CONF_CHECK_I(ips_enabled);
 
@@ -8614,6 +8646,9 @@ intel_pipe_config_compare(struct drm_device *dev,
        PIPE_CONF_CHECK_X(dpll_hw_state.fp0);
        PIPE_CONF_CHECK_X(dpll_hw_state.fp1);
 
+       if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5)
+               PIPE_CONF_CHECK_I(pipe_bpp);
+
 #undef PIPE_CONF_CHECK_X
 #undef PIPE_CONF_CHECK_I
 #undef PIPE_CONF_CHECK_FLAGS
@@ -10047,33 +10082,6 @@ static void i915_disable_vga(struct drm_device *dev)
        POSTING_READ(vga_reg);
 }
 
-static void i915_enable_vga_mem(struct drm_device *dev)
-{
-       /* Enable VGA memory on Intel HD */
-       if (HAS_PCH_SPLIT(dev)) {
-               vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
-               outb(inb(VGA_MSR_READ) | VGA_MSR_MEM_EN, VGA_MSR_WRITE);
-               vga_set_legacy_decoding(dev->pdev, VGA_RSRC_LEGACY_IO |
-                                                  VGA_RSRC_LEGACY_MEM |
-                                                  VGA_RSRC_NORMAL_IO |
-                                                  VGA_RSRC_NORMAL_MEM);
-               vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
-       }
-}
-
-void i915_disable_vga_mem(struct drm_device *dev)
-{
-       /* Disable VGA memory on Intel HD */
-       if (HAS_PCH_SPLIT(dev)) {
-               vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
-               outb(inb(VGA_MSR_READ) & ~VGA_MSR_MEM_EN, VGA_MSR_WRITE);
-               vga_set_legacy_decoding(dev->pdev, VGA_RSRC_LEGACY_IO |
-                                                  VGA_RSRC_NORMAL_IO |
-                                                  VGA_RSRC_NORMAL_MEM);
-               vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
-       }
-}
-
 void intel_modeset_init_hw(struct drm_device *dev)
 {
        intel_init_power_well(dev);
@@ -10352,7 +10360,6 @@ void i915_redisable_vga(struct drm_device *dev)
        if (I915_READ(vga_reg) != VGA_DISP_DISABLE) {
                DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n");
                i915_disable_vga(dev);
-               i915_disable_vga_mem(dev);
        }
 }
 
@@ -10566,8 +10573,6 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
        intel_disable_fbc(dev);
 
-       i915_enable_vga_mem(dev);
-
        intel_disable_gt_powersave(dev);
 
        ironlake_teardown_rc6(dev);
index 2151d13..1a43137 100644 (file)
@@ -588,7 +588,18 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
                        DRM_DEBUG_KMS("aux_ch native nack\n");
                        return -EREMOTEIO;
                case AUX_NATIVE_REPLY_DEFER:
-                       udelay(100);
+                       /*
+                        * For now, just give more slack to branch devices. We
+                        * could check the DPCD for I2C bit rate capabilities,
+                        * and if available, adjust the interval. We could also
+                        * be more careful with DP-to-Legacy adapters where a
+                        * long legacy cable may force very low I2C bit rates.
+                        */
+                       if (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+                           DP_DWN_STRM_PORT_PRESENT)
+                               usleep_range(500, 600);
+                       else
+                               usleep_range(300, 400);
                        continue;
                default:
                        DRM_ERROR("aux_ch invalid native reply 0x%02x\n",
@@ -1390,6 +1401,26 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
                else
                        pipe_config->port_clock = 270000;
        }
+
+       if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp &&
+           pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) {
+               /*
+                * This is a big fat ugly hack.
+                *
+                * Some machines in UEFI boot mode provide us a VBT that has 18
+                * bpp and 1.62 GHz link bandwidth for eDP, which for reasons
+                * unknown we fail to light up. Yet the same BIOS boots up with
+                * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as
+                * max, not what it tells us to use.
+                *
+                * Note: This will still be broken if the eDP panel is not lit
+                * up by the BIOS, and thus we can't get the mode at module
+                * load.
+                */
+               DRM_DEBUG_KMS("pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
+                             pipe_config->pipe_bpp, dev_priv->vbt.edp_bpp);
+               dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp;
+       }
 }
 
 static bool is_edp_psr(struct intel_dp *intel_dp)
@@ -1456,7 +1487,7 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp)
 
        /* Avoid continuous PSR exit by masking memup and hpd */
        I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP |
-                  EDP_PSR_DEBUG_MASK_HPD);
+                  EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
 
        intel_dp->psr_setup_done = true;
 }
index a47799e..7f2b384 100644 (file)
@@ -280,6 +280,7 @@ struct intel_crtc_config {
        struct {
                u32 pos;
                u32 size;
+               bool enabled;
        } pch_pfit;
 
        /* FDI configuration, only valid if has_pch_encoder is set. */
@@ -764,6 +765,8 @@ extern void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
 extern bool
 intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
 extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
+extern void intel_ddi_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config);
 
 extern void intel_display_handle_reset(struct drm_device *dev);
 extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
@@ -792,6 +795,5 @@ extern void hsw_pc8_disable_interrupts(struct drm_device *dev);
 extern void hsw_pc8_restore_interrupts(struct drm_device *dev);
 extern void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
 extern void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
-extern void i915_disable_vga_mem(struct drm_device *dev);
 
 #endif /* __INTEL_DRV_H__ */
index 406303b..7fa7df5 100644 (file)
@@ -263,6 +263,8 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder,
                C(vtotal);
                C(clock);
 #undef C
+
+               drm_mode_set_crtcinfo(adjusted_mode, 0);
        }
 
        if (intel_dvo->dev.dev_ops->mode_fixup)
index 831a5c0..b8af94a 100644 (file)
@@ -698,6 +698,22 @@ static const struct dmi_system_id intel_no_lvds[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
                },
        },
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "Intel D410PT",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
+                       DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
+               },
+       },
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "Intel D425KT",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
+               },
+       },
        {
                .callback = intel_no_lvds_dmi_callback,
                .ident = "Intel D510MO",
index 42114ec..293564a 100644 (file)
@@ -112,6 +112,7 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
 done:
        pipe_config->pch_pfit.pos = (x << 16) | y;
        pipe_config->pch_pfit.size = (width << 16) | height;
+       pipe_config->pch_pfit.enabled = pipe_config->pch_pfit.size != 0;
 }
 
 static void
index 0c115cc..26c2ea3 100644 (file)
@@ -2096,16 +2096,16 @@ static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev,
                                    struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       uint32_t pixel_rate, pfit_size;
+       uint32_t pixel_rate;
 
        pixel_rate = intel_crtc->config.adjusted_mode.clock;
 
        /* We only use IF-ID interlacing. If we ever use PF-ID we'll need to
         * adjust the pixel_rate here. */
 
-       pfit_size = intel_crtc->config.pch_pfit.size;
-       if (pfit_size) {
+       if (intel_crtc->config.pch_pfit.enabled) {
                uint64_t pipe_w, pipe_h, pfit_w, pfit_h;
+               uint32_t pfit_size = intel_crtc->config.pch_pfit.size;
 
                pipe_w = intel_crtc->config.requested_mode.hdisplay;
                pipe_h = intel_crtc->config.requested_mode.vdisplay;
@@ -3864,8 +3864,6 @@ static void valleyview_enable_rps(struct drm_device *dev)
                                      dev_priv->rps.rpe_delay),
                         dev_priv->rps.rpe_delay);
 
-       INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work);
-
        valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay);
 
        gen6_enable_rps_interrupts(dev);
@@ -4761,7 +4759,9 @@ static void cpt_init_clock_gating(struct drm_device *dev)
         * gating for the panel power sequencer or it will fail to
         * start up when no ports are active.
         */
-       I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
+       I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE |
+                  PCH_DPLUNIT_CLOCK_GATE_DISABLE |
+                  PCH_CPUNIT_CLOCK_GATE_DISABLE);
        I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
                   DPLS_EDP_PPS_FIX_DIS);
        /* The below fixes the weird display corruption, a few pixels shifted
@@ -4955,6 +4955,11 @@ static void haswell_init_clock_gating(struct drm_device *dev)
        I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER,
                        GEN7_WA_L3_CHICKEN_MODE);
 
+       /* L3 caching of data atomics doesn't work -- disable it. */
+       I915_WRITE(HSW_SCRATCH1, HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE);
+       I915_WRITE(HSW_ROW_CHICKEN3,
+                  _MASKED_BIT_ENABLE(HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE));
+
        /* This is required by WaCatErrorRejectionIssue:hsw */
        I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
                        I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
@@ -5681,5 +5686,7 @@ void intel_pm_init(struct drm_device *dev)
 
        INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work,
                          intel_gen6_powersave_work);
+
+       INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work);
 }
 
index 85037b9..49482fd 100644 (file)
@@ -788,6 +788,8 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
        uint16_t h_sync_offset, v_sync_offset;
        int mode_clock;
 
+       memset(dtd, 0, sizeof(*dtd));
+
        width = mode->hdisplay;
        height = mode->vdisplay;
 
@@ -830,44 +832,51 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
        if (mode->flags & DRM_MODE_FLAG_PVSYNC)
                dtd->part2.dtd_flags |= DTD_FLAG_VSYNC_POSITIVE;
 
-       dtd->part2.sdvo_flags = 0;
        dtd->part2.v_sync_off_high = v_sync_offset & 0xc0;
-       dtd->part2.reserved = 0;
 }
 
-static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
+static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode *pmode,
                                         const struct intel_sdvo_dtd *dtd)
 {
-       mode->hdisplay = dtd->part1.h_active;
-       mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
-       mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off;
-       mode->hsync_start += (dtd->part2.sync_off_width_high & 0xc0) << 2;
-       mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width;
-       mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
-       mode->htotal = mode->hdisplay + dtd->part1.h_blank;
-       mode->htotal += (dtd->part1.h_high & 0xf) << 8;
-
-       mode->vdisplay = dtd->part1.v_active;
-       mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
-       mode->vsync_start = mode->vdisplay;
-       mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
-       mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0c) << 2;
-       mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0;
-       mode->vsync_end = mode->vsync_start +
+       struct drm_display_mode mode = {};
+
+       mode.hdisplay = dtd->part1.h_active;
+       mode.hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
+       mode.hsync_start = mode.hdisplay + dtd->part2.h_sync_off;
+       mode.hsync_start += (dtd->part2.sync_off_width_high & 0xc0) << 2;
+       mode.hsync_end = mode.hsync_start + dtd->part2.h_sync_width;
+       mode.hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
+       mode.htotal = mode.hdisplay + dtd->part1.h_blank;
+       mode.htotal += (dtd->part1.h_high & 0xf) << 8;
+
+       mode.vdisplay = dtd->part1.v_active;
+       mode.vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
+       mode.vsync_start = mode.vdisplay;
+       mode.vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
+       mode.vsync_start += (dtd->part2.sync_off_width_high & 0x0c) << 2;
+       mode.vsync_start += dtd->part2.v_sync_off_high & 0xc0;
+       mode.vsync_end = mode.vsync_start +
                (dtd->part2.v_sync_off_width & 0xf);
-       mode->vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4;
-       mode->vtotal = mode->vdisplay + dtd->part1.v_blank;
-       mode->vtotal += (dtd->part1.v_high & 0xf) << 8;
+       mode.vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4;
+       mode.vtotal = mode.vdisplay + dtd->part1.v_blank;
+       mode.vtotal += (dtd->part1.v_high & 0xf) << 8;
 
-       mode->clock = dtd->part1.clock * 10;
+       mode.clock = dtd->part1.clock * 10;
 
-       mode->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
        if (dtd->part2.dtd_flags & DTD_FLAG_INTERLACE)
-               mode->flags |= DRM_MODE_FLAG_INTERLACE;
+               mode.flags |= DRM_MODE_FLAG_INTERLACE;
        if (dtd->part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE)
-               mode->flags |= DRM_MODE_FLAG_PHSYNC;
+               mode.flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               mode.flags |= DRM_MODE_FLAG_NHSYNC;
        if (dtd->part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE)
-               mode->flags |= DRM_MODE_FLAG_PVSYNC;
+               mode.flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               mode.flags |= DRM_MODE_FLAG_NVSYNC;
+
+       drm_mode_set_crtcinfo(&mode, 0);
+
+       drm_mode_copy(pmode, &mode);
 }
 
 static bool intel_sdvo_check_supp_encode(struct intel_sdvo *intel_sdvo)
index f2c6d79..dd6f84b 100644 (file)
@@ -916,6 +916,14 @@ intel_tv_compute_config(struct intel_encoder *encoder,
        DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
        pipe_config->pipe_bpp = 8*3;
 
+       /* TV has it's own notion of sync and other mode flags, so clear them. */
+       pipe_config->adjusted_mode.flags = 0;
+
+       /*
+        * FIXME: We don't check whether the input mode is actually what we want
+        * or whether userspace is doing something stupid.
+        */
+
        return true;
 }
 
index a605847..a0b9d8a 100644 (file)
@@ -124,6 +124,8 @@ void adreno_recover(struct msm_gpu *gpu)
 
        /* reset completed fence seqno, just discard anything pending: */
        adreno_gpu->memptrs->fence = gpu->submitted_fence;
+       adreno_gpu->memptrs->rptr  = 0;
+       adreno_gpu->memptrs->wptr  = 0;
 
        gpu->funcs->pm_resume(gpu);
        ret = gpu->funcs->hw_init(gpu);
@@ -229,7 +231,7 @@ void adreno_idle(struct msm_gpu *gpu)
                        return;
        } while(time_before(jiffies, t));
 
-       DRM_ERROR("timeout waiting for %s to drain ringbuffer!\n", gpu->name);
+       DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name);
 
        /* TODO maybe we need to reset GPU here to recover from hang? */
 }
@@ -256,11 +258,17 @@ void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        uint32_t freedwords;
+       unsigned long t = jiffies + ADRENO_IDLE_TIMEOUT;
        do {
                uint32_t size = gpu->rb->size / 4;
                uint32_t wptr = get_wptr(gpu->rb);
                uint32_t rptr = adreno_gpu->memptrs->rptr;
                freedwords = (rptr + (size - 1) - wptr) % size;
+
+               if (time_after(jiffies, t)) {
+                       DRM_ERROR("%s: timeout waiting for ringbuffer space\n", gpu->name);
+                       break;
+               }
        } while(freedwords < ndwords);
 }
 
index 5db5bba..bc7fd11 100644 (file)
@@ -19,8 +19,6 @@
 #include "msm_drv.h"
 #include "mdp4_kms.h"
 
-#include <mach/iommu.h>
-
 static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev);
 
 static int mdp4_hw_init(struct msm_kms *kms)
index 864c977..b3a2f16 100644 (file)
@@ -18,8 +18,6 @@
 #include "msm_drv.h"
 #include "msm_gpu.h"
 
-#include <mach/iommu.h>
-
 static void msm_fb_output_poll_changed(struct drm_device *dev)
 {
        struct msm_drm_private *priv = dev->dev_private;
@@ -62,6 +60,8 @@ int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
        int i, ret;
 
        for (i = 0; i < cnt; i++) {
+               /* TODO maybe some day msm iommu won't require this hack: */
+               struct device *msm_iommu_get_ctx(const char *ctx_name);
                struct device *ctx = msm_iommu_get_ctx(names[i]);
                if (!ctx)
                        continue;
@@ -199,7 +199,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
                 * imx drm driver on iMX5
                 */
                dev_err(dev->dev, "failed to load kms\n");
-               ret = PTR_ERR(priv->kms);
+               ret = PTR_ERR(kms);
                goto fail;
        }
 
@@ -499,25 +499,41 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
                struct timespec *timeout)
 {
        struct msm_drm_private *priv = dev->dev_private;
-       unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
-       unsigned long start_jiffies = jiffies;
-       unsigned long remaining_jiffies;
        int ret;
 
-       if (time_after(start_jiffies, timeout_jiffies))
-               remaining_jiffies = 0;
-       else
-               remaining_jiffies = timeout_jiffies - start_jiffies;
-
-       ret = wait_event_interruptible_timeout(priv->fence_event,
-                       priv->completed_fence >= fence,
-                       remaining_jiffies);
-       if (ret == 0) {
-               DBG("timeout waiting for fence: %u (completed: %u)",
-                               fence, priv->completed_fence);
-               ret = -ETIMEDOUT;
-       } else if (ret != -ERESTARTSYS) {
-               ret = 0;
+       if (!priv->gpu)
+               return 0;
+
+       if (fence > priv->gpu->submitted_fence) {
+               DRM_ERROR("waiting on invalid fence: %u (of %u)\n",
+                               fence, priv->gpu->submitted_fence);
+               return -EINVAL;
+       }
+
+       if (!timeout) {
+               /* no-wait: */
+               ret = fence_completed(dev, fence) ? 0 : -EBUSY;
+       } else {
+               unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
+               unsigned long start_jiffies = jiffies;
+               unsigned long remaining_jiffies;
+
+               if (time_after(start_jiffies, timeout_jiffies))
+                       remaining_jiffies = 0;
+               else
+                       remaining_jiffies = timeout_jiffies - start_jiffies;
+
+               ret = wait_event_interruptible_timeout(priv->fence_event,
+                               fence_completed(dev, fence),
+                               remaining_jiffies);
+
+               if (ret == 0) {
+                       DBG("timeout waiting for fence: %u (completed: %u)",
+                                       fence, priv->completed_fence);
+                       ret = -ETIMEDOUT;
+               } else if (ret != -ERESTARTSYS) {
+                       ret = 0;
+               }
        }
 
        return ret;
@@ -681,7 +697,7 @@ static struct drm_driver msm_driver = {
        .gem_vm_ops         = &vm_ops,
        .dumb_create        = msm_gem_dumb_create,
        .dumb_map_offset    = msm_gem_dumb_map_offset,
-       .dumb_destroy       = msm_gem_dumb_destroy,
+       .dumb_destroy       = drm_gem_dumb_destroy,
 #ifdef CONFIG_DEBUG_FS
        .debugfs_init       = msm_debugfs_init,
        .debugfs_cleanup    = msm_debugfs_cleanup,
index 80d7509..df8f1d0 100644 (file)
@@ -153,7 +153,7 @@ void *msm_gem_vaddr(struct drm_gem_object *obj);
 int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
                struct work_struct *work);
 void msm_gem_move_to_active(struct drm_gem_object *obj,
-               struct msm_gpu *gpu, uint32_t fence);
+               struct msm_gpu *gpu, bool write, uint32_t fence);
 void msm_gem_move_to_inactive(struct drm_gem_object *obj);
 int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
                struct timespec *timeout);
@@ -191,6 +191,12 @@ u32 msm_readl(const void __iomem *addr);
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
+static inline bool fence_completed(struct drm_device *dev, uint32_t fence)
+{
+       struct msm_drm_private *priv = dev->dev_private;
+       return priv->completed_fence >= fence;
+}
+
 static inline int align_pitch(int width, int bpp)
 {
        int bytespp = (bpp + 7) / 8;
index 6b5a6c8..2bae46c 100644 (file)
@@ -40,9 +40,9 @@ static struct page **get_pages(struct drm_gem_object *obj)
                }
 
                msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
-               if (!msm_obj->sgt) {
+               if (IS_ERR(msm_obj->sgt)) {
                        dev_err(dev->dev, "failed to allocate sgt\n");
-                       return ERR_PTR(-ENOMEM);
+                       return ERR_CAST(msm_obj->sgt);
                }
 
                msm_obj->pages = p;
@@ -159,7 +159,6 @@ out_unlock:
 out:
        switch (ret) {
        case -EAGAIN:
-               set_need_resched();
        case 0:
        case -ERESTARTSYS:
        case -EINTR:
@@ -320,13 +319,6 @@ int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
                        MSM_BO_SCANOUT | MSM_BO_WC, &args->handle);
 }
 
-int msm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
-               uint32_t handle)
-{
-       /* No special work needed, drop the reference and see what falls out */
-       return drm_gem_handle_delete(file, handle);
-}
-
 int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
                uint32_t handle, uint64_t *offset)
 {
@@ -393,11 +385,14 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj,
 }
 
 void msm_gem_move_to_active(struct drm_gem_object *obj,
-               struct msm_gpu *gpu, uint32_t fence)
+               struct msm_gpu *gpu, bool write, uint32_t fence)
 {
        struct msm_gem_object *msm_obj = to_msm_bo(obj);
        msm_obj->gpu = gpu;
-       msm_obj->fence = fence;
+       if (write)
+               msm_obj->write_fence = fence;
+       else
+               msm_obj->read_fence = fence;
        list_del_init(&msm_obj->mm_list);
        list_add_tail(&msm_obj->mm_list, &gpu->active_list);
 }
@@ -411,7 +406,8 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj)
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
        msm_obj->gpu = NULL;
-       msm_obj->fence = 0;
+       msm_obj->read_fence = 0;
+       msm_obj->write_fence = 0;
        list_del_init(&msm_obj->mm_list);
        list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
 
@@ -433,8 +429,18 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
        struct msm_gem_object *msm_obj = to_msm_bo(obj);
        int ret = 0;
 
-       if (is_active(msm_obj) && !(op & MSM_PREP_NOSYNC))
-               ret = msm_wait_fence_interruptable(dev, msm_obj->fence, timeout);
+       if (is_active(msm_obj)) {
+               uint32_t fence = 0;
+
+               if (op & MSM_PREP_READ)
+                       fence = msm_obj->write_fence;
+               if (op & MSM_PREP_WRITE)
+                       fence = max(fence, msm_obj->read_fence);
+               if (op & MSM_PREP_NOSYNC)
+                       timeout = NULL;
+
+               ret = msm_wait_fence_interruptable(dev, fence, timeout);
+       }
 
        /* TODO cache maintenance */
 
@@ -455,9 +461,10 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
        uint64_t off = drm_vma_node_start(&obj->vma_node);
 
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-       seq_printf(m, "%08x: %c(%d) %2d (%2d) %08llx %p %d\n",
+       seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %d\n",
                        msm_obj->flags, is_active(msm_obj) ? 'A' : 'I',
-                       msm_obj->fence, obj->name, obj->refcount.refcount.counter,
+                       msm_obj->read_fence, msm_obj->write_fence,
+                       obj->name, obj->refcount.refcount.counter,
                        off, msm_obj->vaddr, obj->size);
 }
 
index d746f13..0676f32 100644 (file)
@@ -36,7 +36,7 @@ struct msm_gem_object {
         */
        struct list_head mm_list;
        struct msm_gpu *gpu;     /* non-null if active */
-       uint32_t fence;
+       uint32_t read_fence, write_fence;
 
        /* Transiently in the process of submit ioctl, objects associated
         * with the submit are on submit->bo_list.. this only lasts for
index 3e1ef3a..5281d4b 100644 (file)
@@ -78,7 +78,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
                }
 
                if (submit_bo.flags & BO_INVALID_FLAGS) {
-                       DBG("invalid flags: %x", submit_bo.flags);
+                       DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
                        ret = -EINVAL;
                        goto out_unlock;
                }
@@ -92,7 +92,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
                 */
                obj = idr_find(&file->object_idr, submit_bo.handle);
                if (!obj) {
-                       DBG("invalid handle %u at index %u", submit_bo.handle, i);
+                       DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);
                        ret = -EINVAL;
                        goto out_unlock;
                }
@@ -100,7 +100,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
                msm_obj = to_msm_bo(obj);
 
                if (!list_empty(&msm_obj->submit_entry)) {
-                       DBG("handle %u at index %u already on submit list",
+                       DRM_ERROR("handle %u at index %u already on submit list\n",
                                        submit_bo.handle, i);
                        ret = -EINVAL;
                        goto out_unlock;
@@ -216,8 +216,9 @@ static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
                struct msm_gem_object **obj, uint32_t *iova, bool *valid)
 {
        if (idx >= submit->nr_bos) {
-               DBG("invalid buffer index: %u (out of %u)", idx, submit->nr_bos);
-               return EINVAL;
+               DRM_ERROR("invalid buffer index: %u (out of %u)\n",
+                               idx, submit->nr_bos);
+               return -EINVAL;
        }
 
        if (obj)
@@ -239,7 +240,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
        int ret;
 
        if (offset % 4) {
-               DBG("non-aligned cmdstream buffer: %u", offset);
+               DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
                return -EINVAL;
        }
 
@@ -266,7 +267,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
                        return -EFAULT;
 
                if (submit_reloc.submit_offset % 4) {
-                       DBG("non-aligned reloc offset: %u",
+                       DRM_ERROR("non-aligned reloc offset: %u\n",
                                        submit_reloc.submit_offset);
                        return -EINVAL;
                }
@@ -276,7 +277,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
 
                if ((off >= (obj->base.size / 4)) ||
                                (off < last_offset)) {
-                       DBG("invalid offset %u at reloc %u", off, i);
+                       DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
                        return -EINVAL;
                }
 
@@ -374,14 +375,15 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
                        goto out;
 
                if (submit_cmd.size % 4) {
-                       DBG("non-aligned cmdstream buffer size: %u",
+                       DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
                                        submit_cmd.size);
                        ret = -EINVAL;
                        goto out;
                }
 
-               if (submit_cmd.size >= msm_obj->base.size) {
-                       DBG("invalid cmdstream size: %u", submit_cmd.size);
+               if ((submit_cmd.size + submit_cmd.submit_offset) >=
+                               msm_obj->base.size) {
+                       DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
                        ret = -EINVAL;
                        goto out;
                }
index e1e1ec9..3bab937 100644 (file)
 static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev)
 {
        struct drm_device *dev = gpu->dev;
-       struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+       struct kgsl_device_platform_data *pdata;
 
        if (!pdev) {
                dev_err(dev->dev, "could not find dtv pdata\n");
                return;
        }
 
+       pdata = pdev->dev.platform_data;
        if (pdata->bus_scale_table) {
                gpu->bsc = msm_bus_scale_register_client(pdata->bus_scale_table);
                DBG("bus scale client: %08x", gpu->bsc);
@@ -230,6 +231,8 @@ static void hangcheck_timer_reset(struct msm_gpu *gpu)
 static void hangcheck_handler(unsigned long data)
 {
        struct msm_gpu *gpu = (struct msm_gpu *)data;
+       struct drm_device *dev = gpu->dev;
+       struct msm_drm_private *priv = dev->dev_private;
        uint32_t fence = gpu->funcs->last_fence(gpu);
 
        if (fence != gpu->hangcheck_fence) {
@@ -237,14 +240,22 @@ static void hangcheck_handler(unsigned long data)
                gpu->hangcheck_fence = fence;
        } else if (fence < gpu->submitted_fence) {
                /* no progress and not done.. hung! */
-               struct msm_drm_private *priv = gpu->dev->dev_private;
                gpu->hangcheck_fence = fence;
+               dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n",
+                               gpu->name);
+               dev_err(dev->dev, "%s:     completed fence: %u\n",
+                               gpu->name, fence);
+               dev_err(dev->dev, "%s:     submitted fence: %u\n",
+                               gpu->name, gpu->submitted_fence);
                queue_work(priv->wq, &gpu->recover_work);
        }
 
        /* if still more pending work, reset the hangcheck timer: */
        if (gpu->submitted_fence > gpu->hangcheck_fence)
                hangcheck_timer_reset(gpu);
+
+       /* workaround for missing irq: */
+       queue_work(priv->wq, &gpu->retire_work);
 }
 
 /*
@@ -265,7 +276,8 @@ static void retire_worker(struct work_struct *work)
                obj = list_first_entry(&gpu->active_list,
                                struct msm_gem_object, mm_list);
 
-               if (obj->fence <= fence) {
+               if ((obj->read_fence <= fence) &&
+                               (obj->write_fence <= fence)) {
                        /* move to inactive: */
                        msm_gem_move_to_inactive(&obj->base);
                        msm_gem_put_iova(&obj->base, gpu->id);
@@ -321,7 +333,11 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
                                        submit->gpu->id, &iova);
                }
 
-               msm_gem_move_to_active(&msm_obj->base, gpu, submit->fence);
+               if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
+                       msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence);
+
+               if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
+                       msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence);
        }
        hangcheck_timer_reset(gpu);
        mutex_unlock(&dev->struct_mutex);
index 37712a6..e290cfa 100644 (file)
@@ -113,7 +113,7 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
                pmc->use_msi = false;
                break;
        default:
-               pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI", true);
+               pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI", false);
                if (pmc->use_msi) {
                        pmc->use_msi = pci_enable_msi(device->pdev) == 0;
                        if (pmc->use_msi) {
index 32923d2..5e891b2 100644 (file)
@@ -707,24 +707,37 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        switch (connector->connector_type) {
        case DRM_MODE_CONNECTOR_DVII:
        case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
-               if ((radeon_connector->audio == RADEON_AUDIO_ENABLE) ||
-                   (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                    (radeon_connector->audio == RADEON_AUDIO_AUTO)))
-                       return ATOM_ENCODER_MODE_HDMI;
-               else if (radeon_connector->use_digital)
+               if (radeon_audio != 0) {
+                       if (radeon_connector->use_digital &&
+                           (radeon_connector->audio == RADEON_AUDIO_ENABLE))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (radeon_connector->use_digital)
+                               return ATOM_ENCODER_MODE_DVI;
+                       else
+                               return ATOM_ENCODER_MODE_CRT;
+               } else if (radeon_connector->use_digital) {
                        return ATOM_ENCODER_MODE_DVI;
-               else
+               } else {
                        return ATOM_ENCODER_MODE_CRT;
+               }
                break;
        case DRM_MODE_CONNECTOR_DVID:
        case DRM_MODE_CONNECTOR_HDMIA:
        default:
-               if ((radeon_connector->audio == RADEON_AUDIO_ENABLE) ||
-                   (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                    (radeon_connector->audio == RADEON_AUDIO_AUTO)))
-                       return ATOM_ENCODER_MODE_HDMI;
-               else
+               if (radeon_audio != 0) {
+                       if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else
+                               return ATOM_ENCODER_MODE_DVI;
+               } else {
                        return ATOM_ENCODER_MODE_DVI;
+               }
                break;
        case DRM_MODE_CONNECTOR_LVDS:
                return ATOM_ENCODER_MODE_LVDS;
@@ -732,14 +745,19 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        case DRM_MODE_CONNECTOR_DisplayPort:
                dig_connector = radeon_connector->con_priv;
                if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
-                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
+                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
                        return ATOM_ENCODER_MODE_DP;
-               else if ((radeon_connector->audio == RADEON_AUDIO_ENABLE) ||
-                        (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                         (radeon_connector->audio == RADEON_AUDIO_AUTO)))
-                       return ATOM_ENCODER_MODE_HDMI;
-               else
+               } else if (radeon_audio != 0) {
+                       if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else
+                               return ATOM_ENCODER_MODE_DVI;
+               } else {
                        return ATOM_ENCODER_MODE_DVI;
+               }
                break;
        case DRM_MODE_CONNECTOR_eDP:
                return ATOM_ENCODER_MODE_DP;
@@ -1655,7 +1673,7 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
                         * does the same thing and more.
                         */
                        if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730) &&
-                           (rdev->family != CHIP_RS880))
+                           (rdev->family != CHIP_RS780) && (rdev->family != CHIP_RS880))
                                atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
                }
                if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
index 05ff315..9b6950d 100644 (file)
@@ -1168,6 +1168,23 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] =
         { 25000, 30000, RADEON_SCLK_UP }
 };
 
+void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table,
+                                                    u32 *max_clock)
+{
+       u32 i, clock = 0;
+
+       if ((table == NULL) || (table->count == 0)) {
+               *max_clock = clock;
+               return;
+       }
+
+       for (i = 0; i < table->count; i++) {
+               if (clock < table->entries[i].clk)
+                       clock = table->entries[i].clk;
+       }
+       *max_clock = clock;
+}
+
 void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
                                        u32 clock, u16 max_voltage, u16 *voltage)
 {
@@ -1913,7 +1930,7 @@ static int btc_set_mc_special_registers(struct radeon_device *rdev,
                        }
                        j++;
 
-                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
 
                        tmp = RREG32(MC_PMG_CMD_MRS);
@@ -1928,7 +1945,7 @@ static int btc_set_mc_special_registers(struct radeon_device *rdev,
                        }
                        j++;
 
-                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
                        break;
                case MC_SEQ_RESERVE_M >> 2:
@@ -1942,7 +1959,7 @@ static int btc_set_mc_special_registers(struct radeon_device *rdev,
                        }
                        j++;
 
-                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
                        break;
                default:
@@ -2080,6 +2097,7 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
        bool disable_mclk_switching;
        u32 mclk, sclk;
        u16 vddc, vddci;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
 
        if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
            btc_dpm_vblank_too_short(rdev))
@@ -2121,6 +2139,39 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
                        ps->low.vddci = max_limits->vddci;
        }
 
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       if (max_sclk_vddc) {
+               if (ps->low.sclk > max_sclk_vddc)
+                       ps->low.sclk = max_sclk_vddc;
+               if (ps->medium.sclk > max_sclk_vddc)
+                       ps->medium.sclk = max_sclk_vddc;
+               if (ps->high.sclk > max_sclk_vddc)
+                       ps->high.sclk = max_sclk_vddc;
+       }
+       if (max_mclk_vddci) {
+               if (ps->low.mclk > max_mclk_vddci)
+                       ps->low.mclk = max_mclk_vddci;
+               if (ps->medium.mclk > max_mclk_vddci)
+                       ps->medium.mclk = max_mclk_vddci;
+               if (ps->high.mclk > max_mclk_vddci)
+                       ps->high.mclk = max_mclk_vddci;
+       }
+       if (max_mclk_vddc) {
+               if (ps->low.mclk > max_mclk_vddc)
+                       ps->low.mclk = max_mclk_vddc;
+               if (ps->medium.mclk > max_mclk_vddc)
+                       ps->medium.mclk = max_mclk_vddc;
+               if (ps->high.mclk > max_mclk_vddc)
+                       ps->high.mclk = max_mclk_vddc;
+       }
+
        /* XXX validate the min clocks required for display */
 
        if (disable_mclk_switching) {
index 1a15e0e..3b6f12b 100644 (file)
@@ -46,6 +46,8 @@ void btc_adjust_clock_combinations(struct radeon_device *rdev,
                                   struct rv7xx_pl *pl);
 void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
                                        u32 clock, u16 max_voltage, u16 *voltage);
+void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table,
+                                                    u32 *max_clock);
 void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
                                   u16 max_vddc, u16 max_vddci,
                                   u16 *vddc, u16 *vddci);
index 8996274..51e947a 100644 (file)
@@ -146,6 +146,8 @@ static const struct ci_pt_config_reg didt_config_ci[] =
 };
 
 extern u8 rv770_get_memory_module_index(struct radeon_device *rdev);
+extern void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table,
+                                                           u32 *max_clock);
 extern int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
                                       u32 arb_freq_src, u32 arb_freq_dest);
 extern u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock);
@@ -712,6 +714,7 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
        struct radeon_clock_and_voltage_limits *max_limits;
        bool disable_mclk_switching;
        u32 sclk, mclk;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
        int i;
 
        if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
@@ -739,6 +742,29 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
                }
        }
 
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (max_sclk_vddc) {
+                       if (ps->performance_levels[i].sclk > max_sclk_vddc)
+                               ps->performance_levels[i].sclk = max_sclk_vddc;
+               }
+               if (max_mclk_vddci) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddci)
+                               ps->performance_levels[i].mclk = max_mclk_vddci;
+               }
+               if (max_mclk_vddc) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddc)
+                               ps->performance_levels[i].mclk = max_mclk_vddc;
+               }
+       }
+
        /* XXX validate the min clocks required for display */
 
        if (disable_mclk_switching) {
index adbdb65..9cd2bc9 100644 (file)
@@ -77,6 +77,8 @@ static void cik_pcie_gen3_enable(struct radeon_device *rdev);
 static void cik_program_aspm(struct radeon_device *rdev);
 static void cik_init_pg(struct radeon_device *rdev);
 static void cik_init_cg(struct radeon_device *rdev);
+static void cik_fini_pg(struct radeon_device *rdev);
+static void cik_fini_cg(struct radeon_device *rdev);
 static void cik_enable_gui_idle_interrupt(struct radeon_device *rdev,
                                          bool enable);
 
@@ -1692,6 +1694,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
                               fw_name);
                        release_firmware(rdev->smc_fw);
                        rdev->smc_fw = NULL;
+                       err = 0;
                } else if (rdev->smc_fw->size != smc_req_size) {
                        printk(KERN_ERR
                               "cik_smc: Bogus length %zu in firmware \"%s\"\n",
@@ -2845,10 +2848,8 @@ static void cik_gpu_init(struct radeon_device *rdev)
                rdev->config.cik.tile_config |= (3 << 0);
                break;
        }
-       if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
-               rdev->config.cik.tile_config |= 1 << 4;
-       else
-               rdev->config.cik.tile_config |= 0 << 4;
+       rdev->config.cik.tile_config |=
+               ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
        rdev->config.cik.tile_config |=
                ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
        rdev->config.cik.tile_config |=
@@ -3182,6 +3183,7 @@ int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
        r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
        if (r) {
                DRM_ERROR("radeon: failed to get ib (%d).\n", r);
+               radeon_scratch_free(rdev, scratch);
                return r;
        }
        ib.ptr[0] = PACKET3(PACKET3_SET_UCONFIG_REG, 1);
@@ -3198,6 +3200,8 @@ int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
        r = radeon_fence_wait(ib.fence, false);
        if (r) {
                DRM_ERROR("radeon: fence wait failed (%d).\n", r);
+               radeon_scratch_free(rdev, scratch);
+               radeon_ib_free(rdev, &ib);
                return r;
        }
        for (i = 0; i < rdev->usec_timeout; i++) {
@@ -4187,6 +4191,10 @@ static void cik_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask)
        dev_info(rdev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
                 RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS));
 
+       /* disable CG/PG */
+       cik_fini_pg(rdev);
+       cik_fini_cg(rdev);
+
        /* stop the rlc */
        cik_rlc_stop(rdev);
 
@@ -4456,8 +4464,8 @@ static int cik_mc_init(struct radeon_device *rdev)
        rdev->mc.aper_base = pci_resource_start(rdev->pdev, 0);
        rdev->mc.aper_size = pci_resource_len(rdev->pdev, 0);
        /* size in MB on si */
-       rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
-       rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+       rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024ULL * 1024ULL;
+       rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024ULL * 1024ULL;
        rdev->mc.visible_vram_size = rdev->mc.aper_size;
        si_vram_gtt_location(rdev, &rdev->mc);
        radeon_update_bandwidth_info(rdev);
@@ -4735,12 +4743,13 @@ static void cik_vm_decode_fault(struct radeon_device *rdev,
        u32 mc_id = (status & MEMORY_CLIENT_ID_MASK) >> MEMORY_CLIENT_ID_SHIFT;
        u32 vmid = (status & FAULT_VMID_MASK) >> FAULT_VMID_SHIFT;
        u32 protections = (status & PROTECTIONS_MASK) >> PROTECTIONS_SHIFT;
-       char *block = (char *)&mc_client;
+       char block[5] = { mc_client >> 24, (mc_client >> 16) & 0xff,
+               (mc_client >> 8) & 0xff, mc_client & 0xff, 0 };
 
-       printk("VM fault (0x%02x, vmid %d) at page %u, %s from %s (%d)\n",
+       printk("VM fault (0x%02x, vmid %d) at page %u, %s from '%s' (0x%08x) (%d)\n",
               protections, vmid, addr,
               (status & MEMORY_CLIENT_RW_MASK) ? "write" : "read",
-              block, mc_id);
+              block, mc_client, mc_id);
 }
 
 /**
index 85a69d2..9fcd338 100644 (file)
@@ -113,6 +113,9 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
+       /* XXX: setting this register causes hangs on some asics */
+       return;
+
        if (!dig->afmt->pin)
                return;
 
index 555164e..b5c67a9 100644 (file)
@@ -3131,7 +3131,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
                rdev->config.evergreen.sx_max_export_size = 256;
                rdev->config.evergreen.sx_max_export_pos_size = 64;
                rdev->config.evergreen.sx_max_export_smx_size = 192;
-               rdev->config.evergreen.max_hw_contexts = 8;
+               rdev->config.evergreen.max_hw_contexts = 4;
                rdev->config.evergreen.sq_num_cf_insts = 2;
 
                rdev->config.evergreen.sc_prim_fifo_size = 0x40;
index f71ce39..57fcc4b 100644 (file)
@@ -67,6 +67,9 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
+       /* XXX: setting this register causes hangs on some asics */
+       return;
+
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder)
                        radeon_connector = to_radeon_connector(connector);
@@ -288,8 +291,8 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
        /* fglrx clears sth in AFMT_AUDIO_PACKET_CONTROL2 here */
 
        WREG32(HDMI_ACR_PACKET_CONTROL + offset,
-              HDMI_ACR_AUTO_SEND | /* allow hw to sent ACR packets when required */
-              HDMI_ACR_SOURCE); /* select SW CTS value */
+              HDMI_ACR_SOURCE | /* select SW CTS value */
+              HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
 
        evergreen_hdmi_update_ACR(encoder, mode->clock);
 
index 8768fd6..4f6d296 100644 (file)
  * 6. COMMAND [29:22] | BYTE_COUNT [20:0]
  */
 #              define PACKET3_CP_DMA_DST_SEL(x)    ((x) << 20)
-                /* 0 - SRC_ADDR
+                /* 0 - DST_ADDR
                 * 1 - GDS
                 */
 #              define PACKET3_CP_DMA_ENGINE(x)     ((x) << 27)
 #              define PACKET3_CP_DMA_CP_SYNC       (1 << 31)
 /* COMMAND */
 #              define PACKET3_CP_DMA_DIS_WC        (1 << 21)
-#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23)
+#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
                 /* 0 - none
                 * 1 - 8 in 16
                 * 2 - 8 in 32
index 7139906..b419055 100644 (file)
@@ -2635,7 +2635,7 @@ int kv_dpm_init(struct radeon_device *rdev)
        pi->caps_sclk_ds = true;
        pi->enable_auto_thermal_throttling = true;
        pi->disable_nb_ps3_in_battery = false;
-       pi->bapm_enable = true;
+       pi->bapm_enable = false;
        pi->voltage_drop_t = 0;
        pi->caps_sclk_throttle_low_notification = false;
        pi->caps_fps = false; /* true? */
index 93c1f9e..cac2866 100644 (file)
@@ -804,6 +804,7 @@ int ni_init_microcode(struct radeon_device *rdev)
                               fw_name);
                        release_firmware(rdev->smc_fw);
                        rdev->smc_fw = NULL;
+                       err = 0;
                } else if (rdev->smc_fw->size != smc_req_size) {
                        printk(KERN_ERR
                               "ni_mc: Bogus length %zu in firmware \"%s\"\n",
index 6c398a4..f263390 100644 (file)
@@ -787,6 +787,7 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
        bool disable_mclk_switching;
        u32 mclk, sclk;
        u16 vddc, vddci;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
        int i;
 
        if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
@@ -813,6 +814,29 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
                }
        }
 
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (max_sclk_vddc) {
+                       if (ps->performance_levels[i].sclk > max_sclk_vddc)
+                               ps->performance_levels[i].sclk = max_sclk_vddc;
+               }
+               if (max_mclk_vddci) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddci)
+                               ps->performance_levels[i].mclk = max_mclk_vddci;
+               }
+               if (max_mclk_vddc) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddc)
+                               ps->performance_levels[i].mclk = max_mclk_vddc;
+               }
+       }
+
        /* XXX validate the min clocks required for display */
 
        if (disable_mclk_switching) {
index 2417571..d713330 100644 (file)
@@ -2933,9 +2933,11 @@ static int r100_debugfs_cp_ring_info(struct seq_file *m, void *data)
        seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp);
        seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw);
        seq_printf(m, "%u dwords in ring\n", count);
-       for (j = 0; j <= count; j++) {
-               i = (rdp + j) & ring->ptr_mask;
-               seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]);
+       if (ring->ready) {
+               for (j = 0; j <= count; j++) {
+                       i = (rdp + j) & ring->ptr_mask;
+                       seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]);
+               }
        }
        return 0;
 }
index 2a1b187..f9be220 100644 (file)
@@ -2302,6 +2302,7 @@ int r600_init_microcode(struct radeon_device *rdev)
                               fw_name);
                        release_firmware(rdev->smc_fw);
                        rdev->smc_fw = NULL;
+                       err = 0;
                } else if (rdev->smc_fw->size != smc_req_size) {
                        printk(KERN_ERR
                               "smc: Bogus length %zu in firmware \"%s\"\n",
index e65f211..5513d8f 100644 (file)
@@ -1084,7 +1084,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
                                rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].dclk =
                                        le16_to_cpu(uvd_clk->usDClkLow) | (uvd_clk->ucDClkHigh << 16);
                                rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].v =
-                                       le16_to_cpu(limits->entries[i].usVoltage);
+                                       le16_to_cpu(entry->usVoltage);
                                entry = (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *)
                                        ((u8 *)entry + sizeof(ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record));
                        }
index f443010..06022e3 100644 (file)
@@ -57,15 +57,15 @@ enum r600_hdmi_iec_status_bits {
 static const struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = {
     /*      32kHz        44.1kHz       48kHz    */
     /* Clock      N     CTS      N     CTS      N     CTS */
-    {  25174,  4576,  28125,  7007,  31250,  6864,  28125 }, /*  25,20/1.001 MHz */
+    {  25175,  4576,  28125,  7007,  31250,  6864,  28125 }, /*  25,20/1.001 MHz */
     {  25200,  4096,  25200,  6272,  28000,  6144,  25200 }, /*  25.20       MHz */
     {  27000,  4096,  27000,  6272,  30000,  6144,  27000 }, /*  27.00       MHz */
     {  27027,  4096,  27027,  6272,  30030,  6144,  27027 }, /*  27.00*1.001 MHz */
     {  54000,  4096,  54000,  6272,  60000,  6144,  54000 }, /*  54.00       MHz */
     {  54054,  4096,  54054,  6272,  60060,  6144,  54054 }, /*  54.00*1.001 MHz */
-    {  74175, 11648, 210937, 17836, 234375, 11648, 140625 }, /*  74.25/1.001 MHz */
+    {  74176, 11648, 210937, 17836, 234375, 11648, 140625 }, /*  74.25/1.001 MHz */
     {  74250,  4096,  74250,  6272,  82500,  6144,  74250 }, /*  74.25       MHz */
-    { 148351, 11648, 421875,  8918, 234375,  5824, 140625 }, /* 148.50/1.001 MHz */
+    { 148352, 11648, 421875,  8918, 234375,  5824, 140625 }, /* 148.50/1.001 MHz */
     { 148500,  4096, 148500,  6272, 165000,  6144, 148500 }, /* 148.50       MHz */
     {      0,  4096,      0,  6272,      0,  6144,      0 }  /* Other */
 };
@@ -75,8 +75,15 @@ static const struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = {
  */
 static void r600_hdmi_calc_cts(uint32_t clock, int *CTS, int N, int freq)
 {
-       if (*CTS == 0)
-               *CTS = clock * N / (128 * freq) * 1000;
+       u64 n;
+       u32 d;
+
+       if (*CTS == 0) {
+               n = (u64)clock * (u64)N * 1000ULL;
+               d = 128 * freq;
+               do_div(n, d);
+               *CTS = n;
+       }
        DRM_DEBUG("Using ACR timing N=%d CTS=%d for frequency %d\n",
                  N, *CTS, freq);
 }
@@ -257,10 +264,7 @@ void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
         * number (coefficient of two integer numbers.  DCCG_AUDIO_DTOx_PHASE
         * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
         */
-       if (ASIC_IS_DCE3(rdev)) {
-               /* according to the reg specs, this should DCE3.2 only, but in
-                * practice it seems to cover DCE3.0 as well.
-                */
+       if (ASIC_IS_DCE32(rdev)) {
                if (dig->dig_encoder == 0) {
                        dto_cntl = RREG32(DCCG_AUDIO_DTO0_CNTL) & ~DCCG_AUDIO_DTO_WALLCLOCK_RATIO_MASK;
                        dto_cntl |= DCCG_AUDIO_DTO_WALLCLOCK_RATIO(wallclock_ratio);
@@ -276,8 +280,21 @@ void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
                        WREG32(DCCG_AUDIO_DTO1_MODULE, dto_modulo);
                        WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */
                }
+       } else if (ASIC_IS_DCE3(rdev)) {
+               /* according to the reg specs, this should DCE3.2 only, but in
+                * practice it seems to cover DCE3.0/3.1 as well.
+                */
+               if (dig->dig_encoder == 0) {
+                       WREG32(DCCG_AUDIO_DTO0_PHASE, base_rate * 100);
+                       WREG32(DCCG_AUDIO_DTO0_MODULE, clock * 100);
+                       WREG32(DCCG_AUDIO_DTO_SELECT, 0); /* select DTO0 */
+               } else {
+                       WREG32(DCCG_AUDIO_DTO1_PHASE, base_rate * 100);
+                       WREG32(DCCG_AUDIO_DTO1_MODULE, clock * 100);
+                       WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */
+               }
        } else {
-               /* according to the reg specs, this should be DCE2.0 and DCE3.0 */
+               /* according to the reg specs, this should be DCE2.0 and DCE3.0/3.1 */
                WREG32(AUDIO_DTO, AUDIO_DTO_PHASE(base_rate / 10) |
                       AUDIO_DTO_MODULE(clock / 10));
        }
@@ -292,6 +309,9 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
        u8 *sadb;
        int sad_count;
 
+       /* XXX: setting this register causes hangs on some asics */
+       return;
+
        list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder)
                        radeon_connector = to_radeon_connector(connector);
@@ -434,8 +454,8 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
        }
 
        WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
-              HDMI0_ACR_AUTO_SEND | /* allow hw to sent ACR packets when required */
-              HDMI0_ACR_SOURCE); /* select SW CTS value */
+              HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
+              HDMI0_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
 
        WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
               HDMI0_NULL_SEND | /* send null packets when required */
index e673fe2..7b3c7b5 100644 (file)
  */
 #              define PACKET3_CP_DMA_CP_SYNC       (1 << 31)
 /* COMMAND */
-#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23)
+#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
                 /* 0 - none
                 * 1 - 8 in 16
                 * 2 - 8 in 32
index a400ac1..24f4960 100644 (file)
@@ -1272,8 +1272,8 @@ struct radeon_blacklist_clocks
 struct radeon_clock_and_voltage_limits {
        u32 sclk;
        u32 mclk;
-       u32 vddc;
-       u32 vddci;
+       u16 vddc;
+       u16 vddci;
 };
 
 struct radeon_clock_array {
index 5003385..8f7e045 100644 (file)
@@ -1004,6 +1004,8 @@ static struct radeon_asic rv6xx_asic = {
                .wait_for_vblank = &avivo_wait_for_vblank,
                .set_backlight_level = &atombios_set_backlight_level,
                .get_backlight_level = &atombios_get_backlight_level,
+               .hdmi_enable = &r600_hdmi_enable,
+               .hdmi_setmode = &r600_hdmi_setmode,
        },
        .copy = {
                .blit = &r600_copy_cpdma,
index 404e25d..f79ee18 100644 (file)
@@ -1367,6 +1367,7 @@ bool radeon_atombios_get_ppll_ss_info(struct radeon_device *rdev,
        int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info);
        uint16_t data_offset, size;
        struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info;
+       struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT *ss_assign;
        uint8_t frev, crev;
        int i, num_indices;
 
@@ -1378,18 +1379,21 @@ bool radeon_atombios_get_ppll_ss_info(struct radeon_device *rdev,
 
                num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
                        sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT);
-
+               ss_assign = (struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT*)
+                       ((u8 *)&ss_info->asSS_Info[0]);
                for (i = 0; i < num_indices; i++) {
-                       if (ss_info->asSS_Info[i].ucSS_Id == id) {
+                       if (ss_assign->ucSS_Id == id) {
                                ss->percentage =
-                                       le16_to_cpu(ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
-                               ss->type = ss_info->asSS_Info[i].ucSpreadSpectrumType;
-                               ss->step = ss_info->asSS_Info[i].ucSS_Step;
-                               ss->delay = ss_info->asSS_Info[i].ucSS_Delay;
-                               ss->range = ss_info->asSS_Info[i].ucSS_Range;
-                               ss->refdiv = ss_info->asSS_Info[i].ucRecommendedRef_Div;
+                                       le16_to_cpu(ss_assign->usSpreadSpectrumPercentage);
+                               ss->type = ss_assign->ucSpreadSpectrumType;
+                               ss->step = ss_assign->ucSS_Step;
+                               ss->delay = ss_assign->ucSS_Delay;
+                               ss->range = ss_assign->ucSS_Range;
+                               ss->refdiv = ss_assign->ucRecommendedRef_Div;
                                return true;
                        }
+                       ss_assign = (struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT*)
+                               ((u8 *)ss_assign + sizeof(struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT));
                }
        }
        return false;
@@ -1477,6 +1481,12 @@ union asic_ss_info {
        struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3;
 };
 
+union asic_ss_assignment {
+       struct _ATOM_ASIC_SS_ASSIGNMENT v1;
+       struct _ATOM_ASIC_SS_ASSIGNMENT_V2 v2;
+       struct _ATOM_ASIC_SS_ASSIGNMENT_V3 v3;
+};
+
 bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
                                      struct radeon_atom_ss *ss,
                                      int id, u32 clock)
@@ -1485,6 +1495,7 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
        int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
        uint16_t data_offset, size;
        union asic_ss_info *ss_info;
+       union asic_ss_assignment *ss_assign;
        uint8_t frev, crev;
        int i, num_indices;
 
@@ -1509,45 +1520,52 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
                        num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
                                sizeof(ATOM_ASIC_SS_ASSIGNMENT);
 
+                       ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info.asSpreadSpectrum[0]);
                        for (i = 0; i < num_indices; i++) {
-                               if ((ss_info->info.asSpreadSpectrum[i].ucClockIndication == id) &&
-                                   (clock <= le32_to_cpu(ss_info->info.asSpreadSpectrum[i].ulTargetClockRange))) {
+                               if ((ss_assign->v1.ucClockIndication == id) &&
+                                   (clock <= le32_to_cpu(ss_assign->v1.ulTargetClockRange))) {
                                        ss->percentage =
-                                               le16_to_cpu(ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
-                                       ss->type = ss_info->info.asSpreadSpectrum[i].ucSpreadSpectrumMode;
-                                       ss->rate = le16_to_cpu(ss_info->info.asSpreadSpectrum[i].usSpreadRateInKhz);
+                                               le16_to_cpu(ss_assign->v1.usSpreadSpectrumPercentage);
+                                       ss->type = ss_assign->v1.ucSpreadSpectrumMode;
+                                       ss->rate = le16_to_cpu(ss_assign->v1.usSpreadRateInKhz);
                                        return true;
                                }
+                               ss_assign = (union asic_ss_assignment *)
+                                       ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT));
                        }
                        break;
                case 2:
                        num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
                                sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);
+                       ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info_2.asSpreadSpectrum[0]);
                        for (i = 0; i < num_indices; i++) {
-                               if ((ss_info->info_2.asSpreadSpectrum[i].ucClockIndication == id) &&
-                                   (clock <= le32_to_cpu(ss_info->info_2.asSpreadSpectrum[i].ulTargetClockRange))) {
+                               if ((ss_assign->v2.ucClockIndication == id) &&
+                                   (clock <= le32_to_cpu(ss_assign->v2.ulTargetClockRange))) {
                                        ss->percentage =
-                                               le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
-                                       ss->type = ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode;
-                                       ss->rate = le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz);
+                                               le16_to_cpu(ss_assign->v2.usSpreadSpectrumPercentage);
+                                       ss->type = ss_assign->v2.ucSpreadSpectrumMode;
+                                       ss->rate = le16_to_cpu(ss_assign->v2.usSpreadRateIn10Hz);
                                        if ((crev == 2) &&
                                            ((id == ASIC_INTERNAL_ENGINE_SS) ||
                                             (id == ASIC_INTERNAL_MEMORY_SS)))
                                                ss->rate /= 100;
                                        return true;
                                }
+                               ss_assign = (union asic_ss_assignment *)
+                                       ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2));
                        }
                        break;
                case 3:
                        num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
                                sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3);
+                       ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info_3.asSpreadSpectrum[0]);
                        for (i = 0; i < num_indices; i++) {
-                               if ((ss_info->info_3.asSpreadSpectrum[i].ucClockIndication == id) &&
-                                   (clock <= le32_to_cpu(ss_info->info_3.asSpreadSpectrum[i].ulTargetClockRange))) {
+                               if ((ss_assign->v3.ucClockIndication == id) &&
+                                   (clock <= le32_to_cpu(ss_assign->v3.ulTargetClockRange))) {
                                        ss->percentage =
-                                               le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
-                                       ss->type = ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode;
-                                       ss->rate = le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz);
+                                               le16_to_cpu(ss_assign->v3.usSpreadSpectrumPercentage);
+                                       ss->type = ss_assign->v3.ucSpreadSpectrumMode;
+                                       ss->rate = le16_to_cpu(ss_assign->v3.usSpreadRateIn10Hz);
                                        if ((id == ASIC_INTERNAL_ENGINE_SS) ||
                                            (id == ASIC_INTERNAL_MEMORY_SS))
                                                ss->rate /= 100;
@@ -1555,6 +1573,8 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
                                                radeon_atombios_get_igp_ss_overrides(rdev, ss, id);
                                        return true;
                                }
+                               ss_assign = (union asic_ss_assignment *)
+                                       ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3));
                        }
                        break;
                default:
index 79159b5..6456573 100644 (file)
@@ -1658,9 +1658,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                        drm_object_attach_property(&radeon_connector->base.base,
                                                      rdev->mode_info.underscan_vborder_property,
                                                      0);
-                       drm_object_attach_property(&radeon_connector->base.base,
-                                                  rdev->mode_info.audio_property,
-                                                  RADEON_AUDIO_DISABLE);
+                       if (radeon_audio != 0)
+                               drm_object_attach_property(&radeon_connector->base.base,
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        subpixel_order = SubPixelHorizontalRGB;
                        connector->interlace_allowed = true;
                        if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
@@ -1754,10 +1757,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                                                              rdev->mode_info.underscan_vborder_property,
                                                              0);
                        }
-                       if (ASIC_IS_DCE2(rdev)) {
+                       if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
-                                                             rdev->mode_info.audio_property,
-                                                             RADEON_AUDIO_DISABLE);
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        }
                        if (connector_type == DRM_MODE_CONNECTOR_DVII) {
                                radeon_connector->dac_load_detect = true;
@@ -1799,10 +1804,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                                                              rdev->mode_info.underscan_vborder_property,
                                                              0);
                        }
-                       if (ASIC_IS_DCE2(rdev)) {
+                       if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
-                                                             rdev->mode_info.audio_property,
-                                                             RADEON_AUDIO_DISABLE);
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        }
                        subpixel_order = SubPixelHorizontalRGB;
                        connector->interlace_allowed = true;
@@ -1843,10 +1850,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                                                              rdev->mode_info.underscan_vborder_property,
                                                              0);
                        }
-                       if (ASIC_IS_DCE2(rdev)) {
+                       if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
                                drm_object_attach_property(&radeon_connector->base.base,
-                                                             rdev->mode_info.audio_property,
-                                                             RADEON_AUDIO_DISABLE);
+                                                          rdev->mode_info.audio_property,
+                                                          (radeon_audio == 1) ?
+                                                          RADEON_AUDIO_AUTO :
+                                                          RADEON_AUDIO_DISABLE);
                        }
                        connector->interlace_allowed = true;
                        /* in theory with a DP to VGA converter... */
index ac6ece6..80285e3 100644 (file)
@@ -85,7 +85,7 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
                   VRAM, also but everything into VRAM on AGP cards to avoid
                   image corruptions */
                if (p->ring == R600_RING_TYPE_UVD_INDEX &&
-                   (i == 0 || p->rdev->flags & RADEON_IS_AGP)) {
+                   (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
                        /* TODO: is this still needed for NI+ ? */
                        p->relocs[i].lobj.domain =
                                RADEON_GEM_DOMAIN_VRAM;
index e29faa7..841d0e0 100644 (file)
@@ -1320,13 +1320,22 @@ int radeon_device_init(struct radeon_device *rdev,
                        return r;
        }
        if ((radeon_testing & 1)) {
-               radeon_test_moves(rdev);
+               if (rdev->accel_working)
+                       radeon_test_moves(rdev);
+               else
+                       DRM_INFO("radeon: acceleration disabled, skipping move tests\n");
        }
        if ((radeon_testing & 2)) {
-               radeon_test_syncing(rdev);
+               if (rdev->accel_working)
+                       radeon_test_syncing(rdev);
+               else
+                       DRM_INFO("radeon: acceleration disabled, skipping sync tests\n");
        }
        if (radeon_benchmarking) {
-               radeon_benchmark(rdev, radeon_benchmarking);
+               if (rdev->accel_working)
+                       radeon_benchmark(rdev, radeon_benchmarking);
+               else
+                       DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n");
        }
        return 0;
 }
index cdd12dc..9c14a1b 100644 (file)
@@ -153,7 +153,7 @@ int radeon_benchmarking = 0;
 int radeon_testing = 0;
 int radeon_connector_table = 0;
 int radeon_tv = 1;
-int radeon_audio = 1;
+int radeon_audio = -1;
 int radeon_disp_priority = 0;
 int radeon_hw_i2c = 0;
 int radeon_pcie_gen2 = -1;
@@ -196,7 +196,7 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
 MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
 module_param_named(tv, radeon_tv, int, 0444);
 
-MODULE_PARM_DESC(audio, "Audio enable (1 = enable)");
+MODULE_PARM_DESC(audio, "Audio enable (-1 = auto, 0 = disable, 1 = enable)");
 module_param_named(audio, radeon_audio, int, 0444);
 
 MODULE_PARM_DESC(disp_priority, "Display Priority (0 = auto, 1 = normal, 2 = high)");
index 87e1d69..4f6b7fc 100644 (file)
@@ -945,6 +945,8 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
                if (enable) {
                        mutex_lock(&rdev->pm.mutex);
                        rdev->pm.dpm.uvd_active = true;
+                       /* disable this for now */
+#if 0
                        if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
                                dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
                        else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
@@ -954,6 +956,7 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
                        else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
                                dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
                        else
+#endif
                                dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
                        rdev->pm.dpm.state = dpm_state;
                        mutex_unlock(&rdev->pm.mutex);
@@ -1002,7 +1005,7 @@ static void radeon_pm_resume_old(struct radeon_device *rdev)
 {
        /* set up the default clocks if the MC ucode is loaded */
        if ((rdev->family >= CHIP_BARTS) &&
-           (rdev->family <= CHIP_HAINAN) &&
+           (rdev->family <= CHIP_CAYMAN) &&
            rdev->mc_fw) {
                if (rdev->pm.default_vddc)
                        radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -1046,7 +1049,7 @@ static void radeon_pm_resume_dpm(struct radeon_device *rdev)
        if (ret) {
                DRM_ERROR("radeon: dpm resume failed\n");
                if ((rdev->family >= CHIP_BARTS) &&
-                   (rdev->family <= CHIP_HAINAN) &&
+                   (rdev->family <= CHIP_CAYMAN) &&
                    rdev->mc_fw) {
                        if (rdev->pm.default_vddc)
                                radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -1097,7 +1100,7 @@ static int radeon_pm_init_old(struct radeon_device *rdev)
                radeon_pm_init_profile(rdev);
                /* set up the default clocks if the MC ucode is loaded */
                if ((rdev->family >= CHIP_BARTS) &&
-                   (rdev->family <= CHIP_HAINAN) &&
+                   (rdev->family <= CHIP_CAYMAN) &&
                    rdev->mc_fw) {
                        if (rdev->pm.default_vddc)
                                radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -1183,7 +1186,7 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev)
        if (ret) {
                rdev->pm.dpm_enabled = false;
                if ((rdev->family >= CHIP_BARTS) &&
-                   (rdev->family <= CHIP_HAINAN) &&
+                   (rdev->family <= CHIP_CAYMAN) &&
                    rdev->mc_fw) {
                        if (rdev->pm.default_vddc)
                                radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
index 46a25f0..18254e1 100644 (file)
@@ -839,9 +839,11 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data)
         * packet that is the root issue
         */
        i = (ring->rptr + ring->ptr_mask + 1 - 32) & ring->ptr_mask;
-       for (j = 0; j <= (count + 32); j++) {
-               seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]);
-               i = (i + 1) & ring->ptr_mask;
+       if (ring->ready) {
+               for (j = 0; j <= (count + 32); j++) {
+                       seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]);
+                       i = (i + 1) & ring->ptr_mask;
+               }
        }
        return 0;
 }
index f4d6bce..12e8099 100644 (file)
@@ -36,8 +36,8 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
        struct radeon_bo *vram_obj = NULL;
        struct radeon_bo **gtt_obj = NULL;
        uint64_t gtt_addr, vram_addr;
-       unsigned i, n, size;
-       int r, ring;
+       unsigned n, size;
+       int i, r, ring;
 
        switch (flag) {
        case RADEON_TEST_COPY_DMA:
index 1a01bbf..308eff5 100644 (file)
@@ -799,7 +799,8 @@ void radeon_uvd_note_usage(struct radeon_device *rdev)
                    (rdev->pm.dpm.hd != hd)) {
                        rdev->pm.dpm.sd = sd;
                        rdev->pm.dpm.hd = hd;
-                       streams_changed = true;
+                       /* disable this for now */
+                       /*streams_changed = true;*/
                }
        }
 
index c354c10..d96f7cb 100644 (file)
@@ -85,6 +85,9 @@ extern void si_dma_vm_set_page(struct radeon_device *rdev,
                               uint32_t incr, uint32_t flags);
 static void si_enable_gui_idle_interrupt(struct radeon_device *rdev,
                                         bool enable);
+static void si_fini_pg(struct radeon_device *rdev);
+static void si_fini_cg(struct radeon_device *rdev);
+static void si_rlc_stop(struct radeon_device *rdev);
 
 static const u32 verde_rlc_save_restore_register_list[] =
 {
@@ -1678,6 +1681,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                       fw_name);
                release_firmware(rdev->smc_fw);
                rdev->smc_fw = NULL;
+               err = 0;
        } else if (rdev->smc_fw->size != smc_req_size) {
                printk(KERN_ERR
                       "si_smc: Bogus length %zu in firmware \"%s\"\n",
@@ -3608,6 +3612,13 @@ static void si_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask)
        dev_info(rdev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
                 RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS));
 
+       /* disable PG/CG */
+       si_fini_pg(rdev);
+       si_fini_cg(rdev);
+
+       /* stop the rlc */
+       si_rlc_stop(rdev);
+
        /* Disable CP parsing/prefetching */
        WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT);
 
index cfe5d4d..2332aa1 100644 (file)
@@ -2910,6 +2910,7 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
        bool disable_sclk_switching = false;
        u32 mclk, sclk;
        u16 vddc, vddci;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
        int i;
 
        if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
@@ -2943,6 +2944,29 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
                }
        }
 
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (max_sclk_vddc) {
+                       if (ps->performance_levels[i].sclk > max_sclk_vddc)
+                               ps->performance_levels[i].sclk = max_sclk_vddc;
+               }
+               if (max_mclk_vddci) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddci)
+                               ps->performance_levels[i].mclk = max_mclk_vddci;
+               }
+               if (max_mclk_vddc) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddc)
+                               ps->performance_levels[i].mclk = max_mclk_vddc;
+               }
+       }
+
        /* XXX validate the min clocks required for display */
 
        if (disable_mclk_switching) {
@@ -5184,7 +5208,7 @@ static int si_set_mc_special_registers(struct radeon_device *rdev,
                                        table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
                        }
                        j++;
-                       if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
 
                        if (!pi->mem_gddr5) {
@@ -5194,7 +5218,7 @@ static int si_set_mc_special_registers(struct radeon_device *rdev,
                                        table->mc_reg_table_entry[k].mc_data[j] =
                                                (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
                                j++;
-                               if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
                                        return -EINVAL;
                        }
                        break;
@@ -5207,7 +5231,7 @@ static int si_set_mc_special_registers(struct radeon_device *rdev,
                                        (temp_reg & 0xffff0000) |
                                        (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
                        j++;
-                       if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
                        break;
                default:
index 52d2ab6..7e2e0ea 100644 (file)
  * 6. COMMAND [30:21] | BYTE_COUNT [20:0]
  */
 #              define PACKET3_CP_DMA_DST_SEL(x)    ((x) << 20)
-                /* 0 - SRC_ADDR
+                /* 0 - DST_ADDR
                 * 1 - GDS
                 */
 #              define PACKET3_CP_DMA_ENGINE(x)     ((x) << 27)
 #              define PACKET3_CP_DMA_CP_SYNC       (1 << 31)
 /* COMMAND */
 #              define PACKET3_CP_DMA_DIS_WC        (1 << 21)
-#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23)
+#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
                 /* 0 - none
                 * 1 - 8 in 16
                 * 2 - 8 in 32
index 7f998bf..9364129 100644 (file)
@@ -1868,7 +1868,7 @@ int trinity_dpm_init(struct radeon_device *rdev)
        for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++)
                pi->at[i] = TRINITY_AT_DFLT;
 
-       pi->enable_bapm = true;
+       pi->enable_bapm = false;
        pi->enable_nbps_policy = true;
        pi->enable_sclk_ds = true;
        pi->enable_gfx_power_gating = true;
index 1a90f0a..0508f93 100644 (file)
@@ -740,9 +740,17 @@ static void vmw_postclose(struct drm_device *dev,
        struct vmw_fpriv *vmw_fp;
 
        vmw_fp = vmw_fpriv(file_priv);
-       ttm_object_file_release(&vmw_fp->tfile);
-       if (vmw_fp->locked_master)
+
+       if (vmw_fp->locked_master) {
+               struct vmw_master *vmaster =
+                       vmw_master(vmw_fp->locked_master);
+
+               ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
+               ttm_vt_unlock(&vmaster->lock);
                drm_master_put(&vmw_fp->locked_master);
+       }
+
+       ttm_object_file_release(&vmw_fp->tfile);
        kfree(vmw_fp);
 }
 
@@ -925,14 +933,13 @@ static void vmw_master_drop(struct drm_device *dev,
 
        vmw_fp->locked_master = drm_master_get(file_priv->master);
        ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
-       vmw_execbuf_release_pinned_bo(dev_priv);
-
        if (unlikely((ret != 0))) {
                DRM_ERROR("Unable to lock TTM at VT switch.\n");
                drm_master_put(&vmw_fp->locked_master);
        }
 
-       ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
+       ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
+       vmw_execbuf_release_pinned_bo(dev_priv);
 
        if (!dev_priv->enable_fb) {
                ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
index 0e67cf4..37fb4be 100644 (file)
@@ -970,7 +970,7 @@ void vmw_resource_unreserve(struct vmw_resource *res,
        if (new_backup)
                res->backup_offset = new_backup_offset;
 
-       if (!res->func->may_evict)
+       if (!res->func->may_evict || res->id == -1)
                return;
 
        write_lock(&dev_priv->resource_lock);
index 71b70e3..c91d547 100644 (file)
@@ -241,6 +241,7 @@ config HID_HOLTEK
          - Sharkoon Drakonia / Perixx MX-2000 gaming mice
          - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 /
            Zalman ZM-GM1
+         - SHARKOON DarkGlider Gaming mouse
 
 config HOLTEK_FF
        bool "Holtek On Line Grip force feedback support"
index b8470b1..e80da62 100644 (file)
@@ -319,7 +319,7 @@ static s32 item_sdata(struct hid_item *item)
 
 static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 {
-       __u32 raw_value;
+       __s32 raw_value;
        switch (item->tag) {
        case HID_GLOBAL_ITEM_TAG_PUSH:
 
@@ -370,10 +370,11 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
                return 0;
 
        case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
-               /* Units exponent negative numbers are given through a
-                * two's complement.
-                * See "6.2.2.7 Global Items" for more information. */
-               raw_value = item_udata(item);
+               /* Many devices provide unit exponent as a two's complement
+                * nibble due to the common misunderstanding of HID
+                * specification 1.11, 6.2.2.7 Global Items. Attempt to handle
+                * both this and the standard encoding. */
+               raw_value = item_sdata(item);
                if (!(raw_value & 0xfffffff0))
                        parser->global.unit_exponent = hid_snto32(raw_value, 4);
                else
@@ -1715,6 +1716,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) },
        { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
@@ -1869,6 +1871,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
 };
index 7e6db3c..e696566 100644 (file)
@@ -27,6 +27,7 @@
  * - USB ID 04d9:a067, sold as Sharkoon Drakonia and Perixx MX-2000
  * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200
  *   and Zalman ZM-GM1
+ * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse
  */
 
 static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -46,6 +47,7 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                        }
                        break;
                case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A:
+               case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081:
                        if (*rsize >= 113 && rdesc[106] == 0xff && rdesc[107] == 0x7f
                                        && rdesc[111] == 0xff && rdesc[112] == 0x7f) {
                                hid_info(hdev, "Fixing up report descriptor\n");
@@ -63,6 +65,8 @@ static const struct hid_device_id holtek_mouse_devices[] = {
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
+                       USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, holtek_mouse_devices);
index e60e8d5..f0296a5 100644 (file)
 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD      0xa055
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067    0xa067
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A    0xa04a
+#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081    0xa081
 
 #define USB_VENDOR_ID_IMATION          0x0718
 #define USB_DEVICE_ID_DISC_STAKKA      0xd000
 #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN   0x0003
 
 #define USB_VENDOR_ID_NINTENDO         0x057e
+#define USB_VENDOR_ID_NINTENDO2                0x054c
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE2        0x0330
 
 #define USB_DEVICE_ID_SYNAPTICS_COMP_TP        0x0009
 #define USB_DEVICE_ID_SYNAPTICS_WTP    0x0010
 #define USB_DEVICE_ID_SYNAPTICS_DPAD   0x0013
+#define USB_DEVICE_ID_SYNAPTICS_LTS1   0x0af8
+#define USB_DEVICE_ID_SYNAPTICS_LTS2   0x1d10
 
 #define USB_VENDOR_ID_THINGM           0x27b8
 #define USB_DEVICE_ID_BLINK1           0x01ed
 #define USB_VENDOR_ID_PRIMAX   0x0461
 #define USB_DEVICE_ID_PRIMAX_KEYBOARD  0x4e05
 
+#define USB_VENDOR_ID_SIS      0x0457
+#define USB_DEVICE_ID_SIS_TS   0x1013
+
 #endif
index 8741d95..d97f232 100644 (file)
@@ -192,6 +192,7 @@ static int hidinput_setkeycode(struct input_dev *dev,
        return -EINVAL;
 }
 
+
 /**
  * hidinput_calc_abs_res - calculate an absolute axis resolution
  * @field: the HID report field to calculate resolution for
@@ -234,23 +235,17 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
        case ABS_MT_TOOL_Y:
        case ABS_MT_TOUCH_MAJOR:
        case ABS_MT_TOUCH_MINOR:
-               if (field->unit & 0xffffff00)           /* Not a length */
-                       return 0;
-               unit_exponent += hid_snto32(field->unit >> 4, 4) - 1;
-               switch (field->unit & 0xf) {
-               case 0x1:                               /* If centimeters */
+               if (field->unit == 0x11) {              /* If centimeters */
                        /* Convert to millimeters */
                        unit_exponent += 1;
-                       break;
-               case 0x3:                               /* If inches */
+               } else if (field->unit == 0x13) {       /* If inches */
                        /* Convert to millimeters */
                        prev = physical_extents;
                        physical_extents *= 254;
                        if (physical_extents < prev)
                                return 0;
                        unit_exponent -= 1;
-                       break;
-               default:
+               } else {
                        return 0;
                }
                break;
index 602c188..6101816 100644 (file)
@@ -382,7 +382,7 @@ static ssize_t kone_sysfs_write_profilex(struct file *fp,
 }
 #define PROFILE_ATTR(number)                                   \
 static struct bin_attribute bin_attr_profile##number = {       \
-       .attr = { .name = "profile##number", .mode = 0660 },    \
+       .attr = { .name = "profile" #number, .mode = 0660 },    \
        .size = sizeof(struct kone_profile),                    \
        .read = kone_sysfs_read_profilex,                       \
        .write = kone_sysfs_write_profilex,                     \
index 5ddf605..5e99fcd 100644 (file)
@@ -229,13 +229,13 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
 
 #define PROFILE_ATTR(number)                                           \
 static struct bin_attribute bin_attr_profile##number##_settings = {    \
-       .attr = { .name = "profile##number##_settings", .mode = 0440 }, \
+       .attr = { .name = "profile" #number "_settings", .mode = 0440 },        \
        .size = KONEPLUS_SIZE_PROFILE_SETTINGS,                         \
        .read = koneplus_sysfs_read_profilex_settings,                  \
        .private = &profile_numbers[number-1],                          \
 };                                                                     \
 static struct bin_attribute bin_attr_profile##number##_buttons = {     \
-       .attr = { .name = "profile##number##_buttons", .mode = 0440 },  \
+       .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
        .size = KONEPLUS_SIZE_PROFILE_BUTTONS,                          \
        .read = koneplus_sysfs_read_profilex_buttons,                   \
        .private = &profile_numbers[number-1],                          \
index 515bc03..0c8e1ef 100644 (file)
@@ -257,13 +257,13 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
 
 #define PROFILE_ATTR(number)                                           \
 static struct bin_attribute bin_attr_profile##number##_settings = {    \
-       .attr = { .name = "profile##number##_settings", .mode = 0440 }, \
+       .attr = { .name = "profile" #number "_settings", .mode = 0440 },        \
        .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,                         \
        .read = kovaplus_sysfs_read_profilex_settings,                  \
        .private = &profile_numbers[number-1],                          \
 };                                                                     \
 static struct bin_attribute bin_attr_profile##number##_buttons = {     \
-       .attr = { .name = "profile##number##_buttons", .mode = 0440 },  \
+       .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
        .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,                          \
        .read = kovaplus_sysfs_read_profilex_buttons,                   \
        .private = &profile_numbers[number-1],                          \
index 5a6dbbe..1a07e07 100644 (file)
@@ -225,13 +225,13 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
 
 #define PROFILE_ATTR(number)                                           \
 static struct bin_attribute bin_attr_profile##number##_settings = {    \
-       .attr = { .name = "profile##number##_settings", .mode = 0440 }, \
+       .attr = { .name = "profile" #number "_settings", .mode = 0440 },        \
        .size = PYRA_SIZE_PROFILE_SETTINGS,                             \
        .read = pyra_sysfs_read_profilex_settings,                      \
        .private = &profile_numbers[number-1],                          \
 };                                                                     \
 static struct bin_attribute bin_attr_profile##number##_buttons = {     \
-       .attr = { .name = "profile##number##_buttons", .mode = 0440 },  \
+       .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
        .size = PYRA_SIZE_PROFILE_BUTTONS,                              \
        .read = pyra_sysfs_read_profilex_buttons,                       \
        .private = &profile_numbers[number-1],                          \
index abb20db..1446f52 100644 (file)
@@ -834,7 +834,8 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
                goto done;
        }
 
-       if (vendor == USB_VENDOR_ID_NINTENDO) {
+       if (vendor == USB_VENDOR_ID_NINTENDO ||
+           vendor == USB_VENDOR_ID_NINTENDO2) {
                if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) {
                        devtype = WIIMOTE_DEV_GEN10;
                        goto done;
@@ -1855,6 +1856,8 @@ static void wiimote_hid_remove(struct hid_device *hdev)
 static const struct hid_device_id wiimote_hid_devices[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
                                USB_DEVICE_ID_NINTENDO_WIIMOTE) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2,
+                               USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
                                USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
index 2e7d644..71adf9e 100644 (file)
@@ -119,12 +119,22 @@ static const struct wiimod_ops wiimod_keys = {
  * the rumble motor, this flag shouldn't be set.
  */
 
+/* used by wiimod_rumble and wiipro_rumble */
+static void wiimod_rumble_worker(struct work_struct *work)
+{
+       struct wiimote_data *wdata = container_of(work, struct wiimote_data,
+                                                 rumble_worker);
+
+       spin_lock_irq(&wdata->state.lock);
+       wiiproto_req_rumble(wdata, wdata->state.cache_rumble);
+       spin_unlock_irq(&wdata->state.lock);
+}
+
 static int wiimod_rumble_play(struct input_dev *dev, void *data,
                              struct ff_effect *eff)
 {
        struct wiimote_data *wdata = input_get_drvdata(dev);
        __u8 value;
-       unsigned long flags;
 
        /*
         * The wiimote supports only a single rumble motor so if any magnitude
@@ -137,9 +147,10 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data,
        else
                value = 0;
 
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiiproto_req_rumble(wdata, value);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
+       /* Locking state.lock here might deadlock with input_event() calls.
+        * schedule_work acts as barrier. Merging multiple changes is fine. */
+       wdata->state.cache_rumble = value;
+       schedule_work(&wdata->rumble_worker);
 
        return 0;
 }
@@ -147,6 +158,8 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data,
 static int wiimod_rumble_probe(const struct wiimod_ops *ops,
                               struct wiimote_data *wdata)
 {
+       INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
+
        set_bit(FF_RUMBLE, wdata->input->ffbit);
        if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play))
                return -ENOMEM;
@@ -159,6 +172,8 @@ static void wiimod_rumble_remove(const struct wiimod_ops *ops,
 {
        unsigned long flags;
 
+       cancel_work_sync(&wdata->rumble_worker);
+
        spin_lock_irqsave(&wdata->state.lock, flags);
        wiiproto_req_rumble(wdata, 0);
        spin_unlock_irqrestore(&wdata->state.lock, flags);
@@ -1731,7 +1746,6 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,
 {
        struct wiimote_data *wdata = input_get_drvdata(dev);
        __u8 value;
-       unsigned long flags;
 
        /*
         * The wiimote supports only a single rumble motor so if any magnitude
@@ -1744,9 +1758,10 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,
        else
                value = 0;
 
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiiproto_req_rumble(wdata, value);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
+       /* Locking state.lock here might deadlock with input_event() calls.
+        * schedule_work acts as barrier. Merging multiple changes is fine. */
+       wdata->state.cache_rumble = value;
+       schedule_work(&wdata->rumble_worker);
 
        return 0;
 }
@@ -1756,6 +1771,8 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
 {
        int ret, i;
 
+       INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
+
        wdata->extension.input = input_allocate_device();
        if (!wdata->extension.input)
                return -ENOMEM;
@@ -1817,12 +1834,13 @@ static void wiimod_pro_remove(const struct wiimod_ops *ops,
        if (!wdata->extension.input)
                return;
 
+       input_unregister_device(wdata->extension.input);
+       wdata->extension.input = NULL;
+       cancel_work_sync(&wdata->rumble_worker);
+
        spin_lock_irqsave(&wdata->state.lock, flags);
        wiiproto_req_rumble(wdata, 0);
        spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       input_unregister_device(wdata->extension.input);
-       wdata->extension.input = NULL;
 }
 
 static const struct wiimod_ops wiimod_pro = {
index f1474f3..75db0c4 100644 (file)
@@ -133,13 +133,15 @@ struct wiimote_state {
        __u8 *cmd_read_buf;
        __u8 cmd_read_size;
 
-       /* calibration data */
+       /* calibration/cache data */
        __u16 calib_bboard[4][3];
+       __u8 cache_rumble;
 };
 
 struct wiimote_data {
        struct hid_device *hdev;
        struct input_dev *input;
+       struct work_struct rumble_worker;
        struct led_classdev *leds[4];
        struct input_dev *accel;
        struct input_dev *ir;
index 8918dd1..6a6dd5c 100644 (file)
@@ -308,18 +308,25 @@ static int hidraw_fasync(int fd, struct file *file, int on)
 static void drop_ref(struct hidraw *hidraw, int exists_bit)
 {
        if (exists_bit) {
-               hid_hw_close(hidraw->hid);
                hidraw->exist = 0;
-               if (hidraw->open)
+               if (hidraw->open) {
+                       hid_hw_close(hidraw->hid);
                        wake_up_interruptible(&hidraw->wait);
+               }
        } else {
                --hidraw->open;
        }
-
-       if (!hidraw->open && !hidraw->exist) {
-               device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
-               hidraw_table[hidraw->minor] = NULL;
-               kfree(hidraw);
+       if (!hidraw->open) {
+               if (!hidraw->exist) {
+                       device_destroy(hidraw_class,
+                                       MKDEV(hidraw_major, hidraw->minor));
+                       hidraw_table[hidraw->minor] = NULL;
+                       kfree(hidraw);
+               } else {
+                       /* close device for last reader */
+                       hid_hw_power(hidraw->hid, PM_HINT_NORMAL);
+                       hid_hw_close(hidraw->hid);
+               }
        }
 }
 
index 5bf2fb7..93b00d7 100644 (file)
@@ -615,7 +615,7 @@ static const struct file_operations uhid_fops = {
 
 static struct miscdevice uhid_misc = {
        .fops           = &uhid_fops,
-       .minor          = MISC_DYNAMIC_MINOR,
+       .minor          = UHID_MINOR,
        .name           = UHID_NAME,
 };
 
@@ -634,4 +634,5 @@ module_exit(uhid_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
 MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
+MODULE_ALIAS_MISCDEV(UHID_MINOR);
 MODULE_ALIAS("devname:" UHID_NAME);
index 0734552..3fca3be 100644 (file)
@@ -110,6 +110,9 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SIS, USB_DEVICE_ID_SIS_TS, HID_QUIRK_NO_INIT_REPORTS },
 
        { 0, 0 }
 };
index 8f4743a..936093e 100644 (file)
@@ -195,7 +195,7 @@ int vmbus_connect(void)
 
        do {
                ret = vmbus_negotiate_version(msginfo, version);
-               if (ret)
+               if (ret == -ETIMEDOUT)
                        goto cleanup;
 
                if (vmbus_connection.conn_state == CONNECTED)
index 28b0332..09988b2 100644 (file)
 /*
  * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
  */
+#define WS2008_SRV_MAJOR       1
+#define WS2008_SRV_MINOR       0
+#define WS2008_SRV_VERSION     (WS2008_SRV_MAJOR << 16 | WS2008_SRV_MINOR)
+
 #define WIN7_SRV_MAJOR   3
 #define WIN7_SRV_MINOR   0
-#define WIN7_SRV_MAJOR_MINOR     (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)
+#define WIN7_SRV_VERSION     (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)
 
 #define WIN8_SRV_MAJOR   4
 #define WIN8_SRV_MINOR   0
-#define WIN8_SRV_MAJOR_MINOR     (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
+#define WIN8_SRV_VERSION     (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
 
 /*
  * Global state maintained for transaction that is being processed.
@@ -587,6 +591,8 @@ void hv_kvp_onchannelcallback(void *context)
 
        struct icmsg_hdr *icmsghdrp;
        struct icmsg_negotiate *negop = NULL;
+       int util_fw_version;
+       int kvp_srv_version;
 
        if (kvp_transaction.active) {
                /*
@@ -606,17 +612,26 @@ void hv_kvp_onchannelcallback(void *context)
 
                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
                        /*
-                        * We start with win8 version and if the host cannot
-                        * support that we use the previous version.
+                        * Based on the host, select appropriate
+                        * framework and service versions we will
+                        * negotiate.
                         */
-                       if (vmbus_prep_negotiate_resp(icmsghdrp, negop,
-                                recv_buffer, UTIL_FW_MAJOR_MINOR,
-                                WIN8_SRV_MAJOR_MINOR))
-                               goto done;
-
+                       switch (vmbus_proto_version) {
+                       case (VERSION_WS2008):
+                               util_fw_version = UTIL_WS2K8_FW_VERSION;
+                               kvp_srv_version = WS2008_SRV_VERSION;
+                               break;
+                       case (VERSION_WIN7):
+                               util_fw_version = UTIL_FW_VERSION;
+                               kvp_srv_version = WIN7_SRV_VERSION;
+                               break;
+                       default:
+                               util_fw_version = UTIL_FW_VERSION;
+                               kvp_srv_version = WIN8_SRV_VERSION;
+                       }
                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
-                                recv_buffer, UTIL_FW_MAJOR_MINOR,
-                                WIN7_SRV_MAJOR_MINOR);
+                                recv_buffer, util_fw_version,
+                                kvp_srv_version);
 
                } else {
                        kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
@@ -649,7 +664,6 @@ void hv_kvp_onchannelcallback(void *context)
                        return;
 
                }
-done:
 
                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
                        | ICMSGHDRFLAG_RESPONSE;
index e4572f3..0c35462 100644 (file)
@@ -26,7 +26,7 @@
 
 #define VSS_MAJOR  5
 #define VSS_MINOR  0
-#define VSS_MAJOR_MINOR    (VSS_MAJOR << 16 | VSS_MINOR)
+#define VSS_VERSION    (VSS_MAJOR << 16 | VSS_MINOR)
 
 
 
@@ -190,8 +190,8 @@ void hv_vss_onchannelcallback(void *context)
 
                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
-                                recv_buffer, UTIL_FW_MAJOR_MINOR,
-                                VSS_MAJOR_MINOR);
+                                recv_buffer, UTIL_FW_VERSION,
+                                VSS_VERSION);
                } else {
                        vss_msg = (struct hv_vss_msg *)&recv_buffer[
                                sizeof(struct vmbuspipe_hdr) +
index cb82233..273e3dd 100644 (file)
 #include <linux/reboot.h>
 #include <linux/hyperv.h>
 
-#define SHUTDOWN_MAJOR 3
-#define SHUTDOWN_MINOR  0
-#define SHUTDOWN_MAJOR_MINOR   (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR)
 
-#define TIMESYNCH_MAJOR        3
-#define TIMESYNCH_MINOR 0
-#define TIMESYNCH_MAJOR_MINOR  (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR)
+#define SD_MAJOR       3
+#define SD_MINOR       0
+#define SD_VERSION     (SD_MAJOR << 16 | SD_MINOR)
 
-#define HEARTBEAT_MAJOR        3
-#define HEARTBEAT_MINOR 0
-#define HEARTBEAT_MAJOR_MINOR  (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR)
+#define SD_WS2008_MAJOR                1
+#define SD_WS2008_VERSION      (SD_WS2008_MAJOR << 16 | SD_MINOR)
+
+#define TS_MAJOR       3
+#define TS_MINOR       0
+#define TS_VERSION     (TS_MAJOR << 16 | TS_MINOR)
+
+#define TS_WS2008_MAJOR                1
+#define TS_WS2008_VERSION      (TS_WS2008_MAJOR << 16 | TS_MINOR)
+
+#define HB_MAJOR       3
+#define HB_MINOR 0
+#define HB_VERSION     (HB_MAJOR << 16 | HB_MINOR)
+
+#define HB_WS2008_MAJOR        1
+#define HB_WS2008_VERSION      (HB_WS2008_MAJOR << 16 | HB_MINOR)
+
+static int sd_srv_version;
+static int ts_srv_version;
+static int hb_srv_version;
+static int util_fw_version;
 
 static void shutdown_onchannelcallback(void *context);
 static struct hv_util_service util_shutdown = {
@@ -99,8 +114,8 @@ static void shutdown_onchannelcallback(void *context)
 
                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
                        vmbus_prep_negotiate_resp(icmsghdrp, negop,
-                                       shut_txf_buf, UTIL_FW_MAJOR_MINOR,
-                                       SHUTDOWN_MAJOR_MINOR);
+                                       shut_txf_buf, util_fw_version,
+                                       sd_srv_version);
                } else {
                        shutdown_msg =
                                (struct shutdown_msg_data *)&shut_txf_buf[
@@ -216,6 +231,7 @@ static void timesync_onchannelcallback(void *context)
        struct icmsg_hdr *icmsghdrp;
        struct ictimesync_data *timedatap;
        u8 *time_txf_buf = util_timesynch.recv_buffer;
+       struct icmsg_negotiate *negop = NULL;
 
        vmbus_recvpacket(channel, time_txf_buf,
                         PAGE_SIZE, &recvlen, &requestid);
@@ -225,9 +241,10 @@ static void timesync_onchannelcallback(void *context)
                                sizeof(struct vmbuspipe_hdr)];
 
                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
-                       vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf,
-                                               UTIL_FW_MAJOR_MINOR,
-                                               TIMESYNCH_MAJOR_MINOR);
+                       vmbus_prep_negotiate_resp(icmsghdrp, negop,
+                                               time_txf_buf,
+                                               util_fw_version,
+                                               ts_srv_version);
                } else {
                        timedatap = (struct ictimesync_data *)&time_txf_buf[
                                sizeof(struct vmbuspipe_hdr) +
@@ -257,6 +274,7 @@ static void heartbeat_onchannelcallback(void *context)
        struct icmsg_hdr *icmsghdrp;
        struct heartbeat_msg_data *heartbeat_msg;
        u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
+       struct icmsg_negotiate *negop = NULL;
 
        vmbus_recvpacket(channel, hbeat_txf_buf,
                         PAGE_SIZE, &recvlen, &requestid);
@@ -266,9 +284,9 @@ static void heartbeat_onchannelcallback(void *context)
                                sizeof(struct vmbuspipe_hdr)];
 
                if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
-                       vmbus_prep_negotiate_resp(icmsghdrp, NULL,
-                               hbeat_txf_buf, UTIL_FW_MAJOR_MINOR,
-                               HEARTBEAT_MAJOR_MINOR);
+                       vmbus_prep_negotiate_resp(icmsghdrp, negop,
+                               hbeat_txf_buf, util_fw_version,
+                               hb_srv_version);
                } else {
                        heartbeat_msg =
                                (struct heartbeat_msg_data *)&hbeat_txf_buf[
@@ -321,6 +339,25 @@ static int util_probe(struct hv_device *dev,
                goto error;
 
        hv_set_drvdata(dev, srv);
+       /*
+        * Based on the host; initialize the framework and
+        * service version numbers we will negotiate.
+        */
+       switch (vmbus_proto_version) {
+       case (VERSION_WS2008):
+               util_fw_version = UTIL_WS2K8_FW_VERSION;
+               sd_srv_version = SD_WS2008_VERSION;
+               ts_srv_version = TS_WS2008_VERSION;
+               hb_srv_version = HB_WS2008_VERSION;
+               break;
+
+       default:
+               util_fw_version = UTIL_FW_VERSION;
+               sd_srv_version = SD_VERSION;
+               ts_srv_version = TS_VERSION;
+               hb_srv_version = HB_VERSION;
+       }
+
        return 0;
 
 error:
index 62c2e32..3288f13 100644 (file)
@@ -230,6 +230,7 @@ static int send_argument(const char *key)
 
 static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
 {
+       u8 status, data = 0;
        int i;
 
        if (send_command(cmd) || send_argument(key)) {
@@ -237,6 +238,7 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
                return -EIO;
        }
 
+       /* This has no effect on newer (2012) SMCs */
        if (send_byte(len, APPLESMC_DATA_PORT)) {
                pr_warn("%.4s: read len fail\n", key);
                return -EIO;
@@ -250,6 +252,17 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
                buffer[i] = inb(APPLESMC_DATA_PORT);
        }
 
+       /* Read the data port until bit0 is cleared */
+       for (i = 0; i < 16; i++) {
+               udelay(APPLESMC_MIN_WAIT);
+               status = inb(APPLESMC_CMD_PORT);
+               if (!(status & 0x01))
+                       break;
+               data = inb(APPLESMC_DATA_PORT);
+       }
+       if (i)
+               pr_warn("flushed %d bytes, last value is: %d\n", i, data);
+
        return 0;
 }
 
@@ -525,16 +538,25 @@ static int applesmc_init_smcreg_try(void)
 {
        struct applesmc_registers *s = &smcreg;
        bool left_light_sensor, right_light_sensor;
+       unsigned int count;
        u8 tmp[1];
        int ret;
 
        if (s->init_complete)
                return 0;
 
-       ret = read_register_count(&s->key_count);
+       ret = read_register_count(&count);
        if (ret)
                return ret;
 
+       if (s->cache && s->key_count != count) {
+               pr_warn("key count changed from %d to %d\n",
+                       s->key_count, count);
+               kfree(s->cache);
+               s->cache = NULL;
+       }
+       s->key_count = count;
+
        if (!s->cache)
                s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL);
        if (!s->cache)
index dbecf08..5888fee 100644 (file)
@@ -98,6 +98,8 @@
 
 #define DW_IC_ERR_TX_ABRT      0x1
 
+#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
+
 /*
  * status codes
  */
@@ -388,22 +390,34 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
 static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
 {
        struct i2c_msg *msgs = dev->msgs;
-       u32 ic_con;
+       u32 ic_con, ic_tar = 0;
 
        /* Disable the adapter */
        __i2c_dw_enable(dev, false);
 
-       /* set the slave (target) address */
-       dw_writel(dev, msgs[dev->msg_write_idx].addr, DW_IC_TAR);
-
        /* if the slave address is ten bit address, enable 10BITADDR */
        ic_con = dw_readl(dev, DW_IC_CON);
-       if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
+       if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
                ic_con |= DW_IC_CON_10BITADDR_MASTER;
-       else
+               /*
+                * If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing
+                * mode has to be enabled via bit 12 of IC_TAR register.
+                * We set it always as I2C_DYNAMIC_TAR_UPDATE can't be
+                * detected from registers.
+                */
+               ic_tar = DW_IC_TAR_10BITADDR_MASTER;
+       } else {
                ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
+       }
+
        dw_writel(dev, ic_con, DW_IC_CON);
 
+       /*
+        * Set the slave (target) address and enable 10-bit addressing mode
+        * if applicable.
+        */
+       dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);
+
        /* Enable the adapter */
        __i2c_dw_enable(dev, true);
 
index 4c1b605..0aa0113 100644 (file)
@@ -270,7 +270,8 @@ static SIMPLE_DEV_PM_OPS(dw_i2c_dev_pm_ops, dw_i2c_suspend, dw_i2c_resume);
 MODULE_ALIAS("platform:i2c_designware");
 
 static struct platform_driver dw_i2c_driver = {
-       .remove         = dw_i2c_remove,
+       .probe = dw_i2c_probe,
+       .remove = dw_i2c_remove,
        .driver         = {
                .name   = "i2c_designware",
                .owner  = THIS_MODULE,
@@ -282,7 +283,7 @@ static struct platform_driver dw_i2c_driver = {
 
 static int __init dw_i2c_init_driver(void)
 {
-       return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+       return platform_driver_register(&dw_i2c_driver);
 }
 subsys_initcall(dw_i2c_init_driver);
 
index ccf4665..1d7efa3 100644 (file)
@@ -365,7 +365,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
        clk_disable_unprepare(i2c_imx->clk);
 }
 
-static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
                                                        unsigned int rate)
 {
        struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
@@ -589,7 +589,7 @@ static struct i2c_algorithm i2c_imx_algo = {
        .functionality  = i2c_imx_func,
 };
 
-static int __init i2c_imx_probe(struct platform_device *pdev)
+static int i2c_imx_probe(struct platform_device *pdev)
 {
        const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
                                                           &pdev->dev);
@@ -697,7 +697,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
        return 0;   /* Return OK */
 }
 
-static int __exit i2c_imx_remove(struct platform_device *pdev)
+static int i2c_imx_remove(struct platform_device *pdev)
 {
        struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
 
@@ -715,7 +715,8 @@ static int __exit i2c_imx_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver i2c_imx_driver = {
-       .remove         = __exit_p(i2c_imx_remove),
+       .probe = i2c_imx_probe,
+       .remove = i2c_imx_remove,
        .driver = {
                .name   = DRIVER_NAME,
                .owner  = THIS_MODULE,
@@ -726,7 +727,7 @@ static struct platform_driver i2c_imx_driver = {
 
 static int __init i2c_adap_imx_init(void)
 {
-       return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);
+       return platform_driver_register(&i2c_imx_driver);
 }
 subsys_initcall(i2c_adap_imx_init);
 
index 8ed79a0..1672eff 100644 (file)
@@ -393,6 +393,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
 
        desc = &priv->hw[priv->head];
 
+       /* Initialize the DMA buffer */
+       memset(priv->dma_buffer, 0, sizeof(priv->dma_buffer));
+
        /* Initialize the descriptor */
        memset(desc, 0, sizeof(struct ismt_desc));
        desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
index 7f3a474..d3e9cc3 100644 (file)
@@ -234,9 +234,9 @@ static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data)
                ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR |
                    (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT;
 
-               writel_relaxed(data_reg_lo,
+               writel(data_reg_lo,
                        drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_LO);
-               writel_relaxed(data_reg_hi,
+               writel(data_reg_hi,
                        drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_HI);
 
        } else {
@@ -697,6 +697,7 @@ static const struct of_device_id mv64xxx_i2c_of_match_table[] = {
 MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
 
 #ifdef CONFIG_OF
+#ifdef CONFIG_HAVE_CLK
 static int
 mv64xxx_calc_freq(const int tclk, const int n, const int m)
 {
@@ -726,16 +727,12 @@ mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
                return false;
        return true;
 }
+#endif /* CONFIG_HAVE_CLK */
 
 static int
 mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
                  struct device *dev)
 {
-       const struct of_device_id *device;
-       struct device_node *np = dev->of_node;
-       u32 bus_freq, tclk;
-       int rc = 0;
-
        /* CLK is mandatory when using DT to describe the i2c bus. We
         * need to know tclk in order to calculate bus clock
         * factors.
@@ -744,6 +741,11 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
        /* Have OF but no CLK */
        return -ENODEV;
 #else
+       const struct of_device_id *device;
+       struct device_node *np = dev->of_node;
+       u32 bus_freq, tclk;
+       int rc = 0;
+
        if (IS_ERR(drv_data->clk)) {
                rc = -ENODEV;
                goto out;
index f4a0167..b7c8577 100644 (file)
@@ -780,12 +780,13 @@ static struct platform_driver mxs_i2c_driver = {
                   .owner = THIS_MODULE,
                   .of_match_table = mxs_i2c_dt_ids,
                   },
+       .probe = mxs_i2c_probe,
        .remove = mxs_i2c_remove,
 };
 
 static int __init mxs_i2c_init(void)
 {
-       return platform_driver_probe(&mxs_i2c_driver, mxs_i2c_probe);
+       return platform_driver_register(&mxs_i2c_driver);
 }
 subsys_initcall(mxs_i2c_init);
 
index 6d8308d..9967a6f 100644 (file)
@@ -939,6 +939,9 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
                /*
                 * ProDB0017052: Clear ARDY bit twice
                 */
+               if (stat & OMAP_I2C_STAT_ARDY)
+                       omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ARDY);
+
                if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
                                        OMAP_I2C_STAT_AL)) {
                        omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY |
index 3535f3c..3747b9b 100644 (file)
@@ -1178,8 +1178,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
 
        i2c_del_adapter(&i2c->adap);
 
-       clk_disable_unprepare(i2c->clk);
-
        if (pdev->dev.of_node && IS_ERR(i2c->pctrl))
                s3c24xx_i2c_dt_gpio_free(i2c);
 
index f8f6f2e..04a17b9 100644 (file)
@@ -859,8 +859,7 @@ static const struct i2c_algorithm stu300_algo = {
        .functionality  = stu300_func,
 };
 
-static int __init
-stu300_probe(struct platform_device *pdev)
+static int stu300_probe(struct platform_device *pdev)
 {
        struct stu300_dev *dev;
        struct i2c_adapter *adap;
@@ -966,8 +965,7 @@ static SIMPLE_DEV_PM_OPS(stu300_pm, stu300_suspend, stu300_resume);
 #define STU300_I2C_PM  NULL
 #endif
 
-static int __exit
-stu300_remove(struct platform_device *pdev)
+static int stu300_remove(struct platform_device *pdev)
 {
        struct stu300_dev *dev = platform_get_drvdata(pdev);
 
@@ -989,13 +987,14 @@ static struct platform_driver stu300_i2c_driver = {
                .pm     = STU300_I2C_PM,
                .of_match_table = stu300_dt_match,
        },
-       .remove         = __exit_p(stu300_remove),
+       .probe = stu300_probe,
+       .remove = stu300_remove,
 
 };
 
 static int __init stu300_init(void)
 {
-       return platform_driver_probe(&stu300_i2c_driver, stu300_probe);
+       return platform_driver_register(&stu300_i2c_driver);
 }
 
 static void __exit stu300_exit(void)
index 29d3f04..3be58f8 100644 (file)
@@ -1134,6 +1134,9 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)
        acpi_handle handle;
        acpi_status status;
 
+       if (!adap->dev.parent)
+               return;
+
        handle = ACPI_HANDLE(adap->dev.parent);
        if (!handle)
                return;
index 74b41ae..928656e 100644 (file)
@@ -200,7 +200,7 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
        arb->parent = of_find_i2c_adapter_by_node(parent_np);
        if (!arb->parent) {
                dev_err(dev, "Cannot find parent bus\n");
-               return -EINVAL;
+               return -EPROBE_DEFER;
        }
 
        /* Actually add the mux adapter */
index 5d4a99b..a764da7 100644 (file)
@@ -66,7 +66,7 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
        struct device_node *adapter_np, *child;
        struct i2c_adapter *adapter;
        unsigned *values, *gpios;
-       int i = 0;
+       int i = 0, ret;
 
        if (!np)
                return -ENODEV;
@@ -79,7 +79,7 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
        adapter = of_find_i2c_adapter_by_node(adapter_np);
        if (!adapter) {
                dev_err(&pdev->dev, "Cannot find parent bus\n");
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
        mux->data.parent = i2c_adapter_id(adapter);
        put_device(&adapter->dev);
@@ -116,8 +116,12 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
                return -ENOMEM;
        }
 
-       for (i = 0; i < mux->data.n_gpios; i++)
-               gpios[i] = of_get_named_gpio(np, "mux-gpios", i);
+       for (i = 0; i < mux->data.n_gpios; i++) {
+               ret = of_get_named_gpio(np, "mux-gpios", i);
+               if (ret < 0)
+                       return ret;
+               gpios[i] = ret;
+       }
 
        mux->data.gpios = gpios;
 
@@ -177,7 +181,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
        if (!parent) {
                dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
                        mux->data.parent);
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
 
        mux->parent = parent;
index 69a9173..68a3715 100644 (file)
@@ -113,7 +113,7 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
        adapter = of_find_i2c_adapter_by_node(adapter_np);
        if (!adapter) {
                dev_err(mux->dev, "Cannot find parent bus\n");
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
        mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
        put_device(&adapter->dev);
@@ -211,7 +211,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
        if (!mux->parent) {
                dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
                        mux->pdata->parent_bus_num);
-               ret = -ENODEV;
+               ret = -EPROBE_DEFER;
                goto err;
        }
 
index 12e32e6..81e3dc2 100644 (file)
@@ -620,7 +620,7 @@ static int bma180_remove(struct i2c_client *client)
 #ifdef CONFIG_PM_SLEEP
 static int bma180_suspend(struct device *dev)
 {
-       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct bma180_data *data = iio_priv(indio_dev);
        int ret;
 
@@ -633,7 +633,7 @@ static int bma180_suspend(struct device *dev)
 
 static int bma180_resume(struct device *dev)
 {
-       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct bma180_data *data = iio_priv(indio_dev);
        int ret;
 
index 84be63b..0f16b55 100644 (file)
@@ -556,7 +556,7 @@ static const struct iio_info at91_adc_info = {
 
 static int at91_adc_probe(struct platform_device *pdev)
 {
-       unsigned int prsc, mstrclk, ticks, adc_clk, shtim;
+       unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
        int ret;
        struct iio_dev *idev;
        struct at91_adc_state *st;
@@ -649,6 +649,7 @@ static int at91_adc_probe(struct platform_device *pdev)
         */
        mstrclk = clk_get_rate(st->clk);
        adc_clk = clk_get_rate(st->adc_clk);
+       adc_clk_khz = adc_clk / 1000;
        prsc = (mstrclk / (2 * adc_clk)) - 1;
 
        if (!st->startup_time) {
@@ -662,15 +663,15 @@ static int at91_adc_probe(struct platform_device *pdev)
         * defined in the electrical characteristics of the board, divided by 8.
         * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
         */
-       ticks = round_up((st->startup_time * adc_clk /
-                         1000000) - 1, 8) / 8;
+       ticks = round_up((st->startup_time * adc_clk_khz /
+                         1000) - 1, 8) / 8;
        /*
         * a minimal Sample and Hold Time is necessary for the ADC to guarantee
         * the best converted final value between two channels selection
         * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
         */
-       shtim = round_up((st->sample_hold_time * adc_clk /
-                         1000000) - 1, 1);
+       shtim = round_up((st->sample_hold_time * adc_clk_khz /
+                         1000) - 1, 1);
 
        reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask;
        reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask;
index d0a79a4..ba6f6a9 100644 (file)
@@ -185,10 +185,8 @@ static int ad8366_remove(struct spi_device *spi)
 
        iio_device_unregister(indio_dev);
 
-       if (!IS_ERR(reg)) {
+       if (!IS_ERR(reg))
                regulator_disable(reg);
-               regulator_put(reg);
-       }
 
        return 0;
 }
index 9d19ba7..415f3c6 100644 (file)
@@ -41,6 +41,8 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
                goto error_ret;
        }
 
+       iio_buffer_init(&cb_buff->buffer);
+
        cb_buff->private = private;
        cb_buff->cb = cb;
        cb_buff->buffer.access = &iio_cb_access;
index 1f4a48e..1397b6e 100644 (file)
@@ -37,21 +37,21 @@ struct mcp4725_data {
 
 static int mcp4725_suspend(struct device *dev)
 {
-       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-       struct mcp4725_data *data = iio_priv(indio_dev);
+       struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
+               to_i2c_client(dev)));
        u8 outbuf[2];
 
        outbuf[0] = (data->powerdown_mode + 1) << 4;
        outbuf[1] = 0;
        data->powerdown = true;
 
-       return i2c_master_send(to_i2c_client(dev), outbuf, 2);
+       return i2c_master_send(data->client, outbuf, 2);
 }
 
 static int mcp4725_resume(struct device *dev)
 {
-       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
-       struct mcp4725_data *data = iio_priv(indio_dev);
+       struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
+               to_i2c_client(dev)));
        u8 outbuf[2];
 
        /* restore previous DAC value */
@@ -59,7 +59,7 @@ static int mcp4725_resume(struct device *dev)
        outbuf[1] = data->dac_value & 0xff;
        data->powerdown = false;
 
-       return i2c_master_send(to_i2c_client(dev), outbuf, 2);
+       return i2c_master_send(data->client, outbuf, 2);
 }
 
 #ifdef CONFIG_PM_SLEEP
index a7b30be..52605c0 100644 (file)
@@ -525,8 +525,10 @@ static int adf4350_probe(struct spi_device *spi)
        }
 
        indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
-       if (indio_dev == NULL)
-               return -ENOMEM;
+       if (indio_dev == NULL) {
+               ret =  -ENOMEM;
+               goto error_disable_clk;
+       }
 
        st = iio_priv(indio_dev);
 
index 05c1b74..9b32253 100644 (file)
@@ -49,11 +49,15 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
 #define iio_buffer_poll_addr (&iio_buffer_poll)
 #define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
 
+void iio_disable_all_buffers(struct iio_dev *indio_dev);
+
 #else
 
 #define iio_buffer_poll_addr NULL
 #define iio_buffer_read_first_n_outer_addr NULL
 
+static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
+
 #endif
 
 int iio_device_register_eventset(struct iio_dev *indio_dev);
index e73033f..2db7dcd 100644 (file)
@@ -460,6 +460,28 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,
        return bytes;
 }
 
+void iio_disable_all_buffers(struct iio_dev *indio_dev)
+{
+       struct iio_buffer *buffer, *_buffer;
+
+       if (list_empty(&indio_dev->buffer_list))
+               return;
+
+       if (indio_dev->setup_ops->predisable)
+               indio_dev->setup_ops->predisable(indio_dev);
+
+       list_for_each_entry_safe(buffer, _buffer,
+                       &indio_dev->buffer_list, buffer_list)
+               list_del_init(&buffer->buffer_list);
+
+       indio_dev->currentmode = INDIO_DIRECT_MODE;
+       if (indio_dev->setup_ops->postdisable)
+               indio_dev->setup_ops->postdisable(indio_dev);
+
+       if (indio_dev->available_scan_masks == NULL)
+               kfree(indio_dev->active_scan_mask);
+}
+
 int iio_update_buffers(struct iio_dev *indio_dev,
                       struct iio_buffer *insert_buffer,
                       struct iio_buffer *remove_buffer)
@@ -528,8 +550,15 @@ int iio_update_buffers(struct iio_dev *indio_dev,
                         * Note can only occur when adding a buffer.
                         */
                        list_del(&insert_buffer->buffer_list);
-                       indio_dev->active_scan_mask = old_mask;
-                       success = -EINVAL;
+                       if (old_mask) {
+                               indio_dev->active_scan_mask = old_mask;
+                               success = -EINVAL;
+                       }
+                       else {
+                               kfree(compound_mask);
+                               ret = -EINVAL;
+                               goto error_ret;
+                       }
                }
        } else {
                indio_dev->active_scan_mask = compound_mask;
index 97f0297..f95c697 100644 (file)
@@ -848,13 +848,10 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
 static void iio_dev_release(struct device *device)
 {
        struct iio_dev *indio_dev = dev_to_iio_dev(device);
-       if (indio_dev->chrdev.dev)
-               cdev_del(&indio_dev->chrdev);
        if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
                iio_device_unregister_trigger_consumer(indio_dev);
        iio_device_unregister_eventset(indio_dev);
        iio_device_unregister_sysfs(indio_dev);
-       iio_device_unregister_debugfs(indio_dev);
 
        ida_simple_remove(&iio_ida, indio_dev->id);
        kfree(indio_dev);
@@ -970,6 +967,8 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
        if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags))
                return -EBUSY;
 
+       iio_device_get(indio_dev);
+
        filp->private_data = indio_dev;
 
        return 0;
@@ -983,6 +982,8 @@ static int iio_chrdev_release(struct inode *inode, struct file *filp)
        struct iio_dev *indio_dev = container_of(inode->i_cdev,
                                                struct iio_dev, chrdev);
        clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
+       iio_device_put(indio_dev);
+
        return 0;
 }
 
@@ -1052,18 +1053,20 @@ int iio_device_register(struct iio_dev *indio_dev)
                indio_dev->setup_ops == NULL)
                indio_dev->setup_ops = &noop_ring_setup_ops;
 
-       ret = device_add(&indio_dev->dev);
-       if (ret < 0)
-               goto error_unreg_eventset;
        cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
        indio_dev->chrdev.owner = indio_dev->info->driver_module;
+       indio_dev->chrdev.kobj.parent = &indio_dev->dev.kobj;
        ret = cdev_add(&indio_dev->chrdev, indio_dev->dev.devt, 1);
        if (ret < 0)
-               goto error_del_device;
-       return 0;
+               goto error_unreg_eventset;
 
-error_del_device:
-       device_del(&indio_dev->dev);
+       ret = device_add(&indio_dev->dev);
+       if (ret < 0)
+               goto error_cdev_del;
+
+       return 0;
+error_cdev_del:
+       cdev_del(&indio_dev->chrdev);
 error_unreg_eventset:
        iio_device_unregister_eventset(indio_dev);
 error_free_sysfs:
@@ -1078,9 +1081,17 @@ EXPORT_SYMBOL(iio_device_register);
 void iio_device_unregister(struct iio_dev *indio_dev)
 {
        mutex_lock(&indio_dev->info_exist_lock);
+
+       device_del(&indio_dev->dev);
+
+       if (indio_dev->chrdev.dev)
+               cdev_del(&indio_dev->chrdev);
+       iio_device_unregister_debugfs(indio_dev);
+
+       iio_disable_all_buffers(indio_dev);
+
        indio_dev->info = NULL;
        mutex_unlock(&indio_dev->info_exist_lock);
-       device_del(&indio_dev->dev);
 }
 EXPORT_SYMBOL(iio_device_unregister);
 subsys_initcall(iio_init);
index 10aa9ef..6be65ef 100644 (file)
@@ -72,7 +72,8 @@ EXPORT_SYMBOL(iio_push_event);
 static unsigned int iio_event_poll(struct file *filep,
                             struct poll_table_struct *wait)
 {
-       struct iio_event_interface *ev_int = filep->private_data;
+       struct iio_dev *indio_dev = filep->private_data;
+       struct iio_event_interface *ev_int = indio_dev->event_interface;
        unsigned int events = 0;
 
        poll_wait(filep, &ev_int->wait, wait);
@@ -90,7 +91,8 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
                                     size_t count,
                                     loff_t *f_ps)
 {
-       struct iio_event_interface *ev_int = filep->private_data;
+       struct iio_dev *indio_dev = filep->private_data;
+       struct iio_event_interface *ev_int = indio_dev->event_interface;
        unsigned int copied;
        int ret;
 
@@ -121,7 +123,8 @@ error_unlock:
 
 static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
 {
-       struct iio_event_interface *ev_int = filep->private_data;
+       struct iio_dev *indio_dev = filep->private_data;
+       struct iio_event_interface *ev_int = indio_dev->event_interface;
 
        spin_lock_irq(&ev_int->wait.lock);
        __clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
@@ -133,6 +136,8 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
        kfifo_reset_out(&ev_int->det_events);
        spin_unlock_irq(&ev_int->wait.lock);
 
+       iio_device_put(indio_dev);
+
        return 0;
 }
 
@@ -158,12 +163,15 @@ int iio_event_getfd(struct iio_dev *indio_dev)
                return -EBUSY;
        }
        spin_unlock_irq(&ev_int->wait.lock);
-       fd = anon_inode_getfd("iio:event",
-                               &iio_event_chrdev_fileops, ev_int, O_RDONLY);
+       iio_device_get(indio_dev);
+
+       fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,
+                               indio_dev, O_RDONLY);
        if (fd < 0) {
                spin_lock_irq(&ev_int->wait.lock);
                __clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
                spin_unlock_irq(&ev_int->wait.lock);
+               iio_device_put(indio_dev);
        }
        return fd;
 }
@@ -276,7 +284,7 @@ static int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
                        goto error_ret;
                }
                if (chan->modified)
-                       mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel,
+                       mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel2,
                                                  i/IIO_EV_DIR_MAX,
                                                  i%IIO_EV_DIR_MAX);
                else if (chan->differential)
index e8d2849..cab3bc7 100644 (file)
@@ -29,9 +29,9 @@
 #define ST_MAGN_NUMBER_DATA_CHANNELS           3
 
 /* DEFAULT VALUE FOR SENSORS */
-#define ST_MAGN_DEFAULT_OUT_X_L_ADDR           0X04
-#define ST_MAGN_DEFAULT_OUT_Y_L_ADDR           0X08
-#define ST_MAGN_DEFAULT_OUT_Z_L_ADDR           0X06
+#define ST_MAGN_DEFAULT_OUT_X_H_ADDR           0X03
+#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR           0X07
+#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR           0X05
 
 /* FULLSCALE */
 #define ST_MAGN_FS_AVL_1300MG                  1300
 static const struct iio_chan_spec st_magn_16bit_channels[] = {
        ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
                        BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
-                       ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
-                       ST_MAGN_DEFAULT_OUT_X_L_ADDR),
+                       ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_BE, 16, 16,
+                       ST_MAGN_DEFAULT_OUT_X_H_ADDR),
        ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
                        BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
-                       ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
-                       ST_MAGN_DEFAULT_OUT_Y_L_ADDR),
+                       ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_BE, 16, 16,
+                       ST_MAGN_DEFAULT_OUT_Y_H_ADDR),
        ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
                        BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
-                       ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
-                       ST_MAGN_DEFAULT_OUT_Z_L_ADDR),
+                       ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_BE, 16, 16,
+                       ST_MAGN_DEFAULT_OUT_Z_H_ADDR),
        IIO_CHAN_SOFT_TIMESTAMP(3)
 };
 
index 64ccde3..6d63883 100644 (file)
@@ -255,12 +255,14 @@ static int tmp006_remove(struct i2c_client *client)
 #ifdef CONFIG_PM_SLEEP
 static int tmp006_suspend(struct device *dev)
 {
-       return tmp006_powerdown(iio_priv(dev_to_iio_dev(dev)));
+       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+       return tmp006_powerdown(iio_priv(indio_dev));
 }
 
 static int tmp006_resume(struct device *dev)
 {
-       struct tmp006_data *data = iio_priv(dev_to_iio_dev(dev));
+       struct tmp006_data *data = iio_priv(i2c_get_clientdata(
+               to_i2c_client(dev)));
        return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
                data->config | TMP006_CONFIG_MOD_MASK);
 }
index 5ceda71..b84791f 100644 (file)
@@ -31,6 +31,17 @@ config INFINIBAND_USER_ACCESS
          libibverbs, libibcm and a hardware driver library from
          <http://www.openfabrics.org/git/>.
 
+config INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
+       bool "Experimental and unstable ABI for userspace access to flow steering verbs"
+       depends on INFINIBAND_USER_ACCESS
+       depends on STAGING
+       ---help---
+         The final ABI for userspace access to flow steering verbs
+         has not been defined.  To use the current ABI, *WHICH WILL
+         CHANGE IN THE FUTURE*, say Y here.
+
+         If unsure, say N.
+
 config INFINIBAND_USER_MEM
        bool
        depends on INFINIBAND_USER_ACCESS != n
index dab4b41..d2172e7 100644 (file)
@@ -1848,6 +1848,26 @@ static int cma_resolve_iw_route(struct rdma_id_private *id_priv, int timeout_ms)
        return 0;
 }
 
+static int iboe_tos_to_sl(struct net_device *ndev, int tos)
+{
+       int prio;
+       struct net_device *dev;
+
+       prio = rt_tos2priority(tos);
+       dev = ndev->priv_flags & IFF_802_1Q_VLAN ?
+               vlan_dev_real_dev(ndev) : ndev;
+
+       if (dev->num_tc)
+               return netdev_get_prio_tc_map(dev, prio);
+
+#if IS_ENABLED(CONFIG_VLAN_8021Q)
+       if (ndev->priv_flags & IFF_802_1Q_VLAN)
+               return (vlan_dev_get_egress_qos_mask(ndev, prio) &
+                       VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+#endif
+       return 0;
+}
+
 static int cma_resolve_iboe_route(struct rdma_id_private *id_priv)
 {
        struct rdma_route *route = &id_priv->id.route;
@@ -1888,11 +1908,7 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv)
        route->path_rec->reversible = 1;
        route->path_rec->pkey = cpu_to_be16(0xffff);
        route->path_rec->mtu_selector = IB_SA_EQ;
-       route->path_rec->sl = netdev_get_prio_tc_map(
-                       ndev->priv_flags & IFF_802_1Q_VLAN ?
-                               vlan_dev_real_dev(ndev) : ndev,
-                       rt_tos2priority(id_priv->tos));
-
+       route->path_rec->sl = iboe_tos_to_sl(ndev, id_priv->tos);
        route->path_rec->mtu = iboe_get_mtu(ndev->mtu);
        route->path_rec->rate_selector = IB_SA_EQ;
        route->path_rec->rate = iboe_get_rate(ndev);
@@ -2294,7 +2310,7 @@ static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv)
        int low, high, remaining;
        unsigned int rover;
 
-       inet_get_local_port_range(&low, &high);
+       inet_get_local_port_range(&init_net, &low, &high);
        remaining = (high - low) + 1;
        rover = net_random() % remaining + low;
 retry:
index d040b87..d8f9c6c 100644 (file)
@@ -217,7 +217,9 @@ IB_UVERBS_DECLARE_CMD(destroy_srq);
 IB_UVERBS_DECLARE_CMD(create_xsrq);
 IB_UVERBS_DECLARE_CMD(open_xrcd);
 IB_UVERBS_DECLARE_CMD(close_xrcd);
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 IB_UVERBS_DECLARE_CMD(create_flow);
 IB_UVERBS_DECLARE_CMD(destroy_flow);
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 #endif /* UVERBS_H */
index f2b81b9..2f0f01b 100644 (file)
@@ -54,7 +54,9 @@ static struct uverbs_lock_class qp_lock_class = { .name = "QP-uobj" };
 static struct uverbs_lock_class ah_lock_class  = { .name = "AH-uobj" };
 static struct uverbs_lock_class srq_lock_class = { .name = "SRQ-uobj" };
 static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" };
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" };
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 #define INIT_UDATA(udata, ibuf, obuf, ilen, olen)                      \
        do {                                                            \
@@ -2599,6 +2601,7 @@ out_put:
        return ret ? ret : in_len;
 }
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 static int kern_spec_to_ib_spec(struct ib_kern_spec *kern_spec,
                                union ib_flow_spec *ib_spec)
 {
@@ -2824,6 +2827,7 @@ ssize_t ib_uverbs_destroy_flow(struct ib_uverbs_file *file,
 
        return ret ? ret : in_len;
 }
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
                                struct ib_uverbs_create_xsrq *cmd,
index 75ad86c..2df31f6 100644 (file)
@@ -115,8 +115,10 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file,
        [IB_USER_VERBS_CMD_CLOSE_XRCD]          = ib_uverbs_close_xrcd,
        [IB_USER_VERBS_CMD_CREATE_XSRQ]         = ib_uverbs_create_xsrq,
        [IB_USER_VERBS_CMD_OPEN_QP]             = ib_uverbs_open_qp,
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        [IB_USER_VERBS_CMD_CREATE_FLOW]         = ib_uverbs_create_flow,
        [IB_USER_VERBS_CMD_DESTROY_FLOW]        = ib_uverbs_destroy_flow
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 };
 
 static void ib_uverbs_add_one(struct ib_device *device);
@@ -605,6 +607,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
        if (!(file->device->ib_dev->uverbs_cmd_mask & (1ull << hdr.command)))
                return -ENOSYS;
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        if (hdr.command >= IB_USER_VERBS_CMD_THRESHOLD) {
                struct ib_uverbs_cmd_hdr_ex hdr_ex;
 
@@ -621,6 +624,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
                                                     (hdr_ex.out_words +
                                                      hdr_ex.provider_out_words) * 4);
        } else {
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
                if (hdr.in_words * 4 != count)
                        return -EINVAL;
 
@@ -628,7 +632,9 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
                                                     buf + sizeof(hdr),
                                                     hdr.in_words * 4,
                                                     hdr.out_words * 4);
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        }
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 }
 
 static int ib_uverbs_mmap(struct file *filp, struct vm_area_struct *vma)
index d5d1929..cedda25 100644 (file)
@@ -141,7 +141,7 @@ static const char *to_qp_state_str(int state)
                return "C2_QP_STATE_ERROR";
        default:
                return "<invalid QP state>";
-       };
+       }
 }
 
 void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
index d6c5a73..6a0a0d2 100644 (file)
@@ -177,18 +177,18 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
 
        props->max_mr_size         = ~0ull;
        props->page_size_cap       = dev->dev->caps.page_size_cap;
-       props->max_qp              = dev->dev->caps.num_qps - dev->dev->caps.reserved_qps;
+       props->max_qp              = dev->dev->quotas.qp;
        props->max_qp_wr           = dev->dev->caps.max_wqes - MLX4_IB_SQ_MAX_SPARE;
        props->max_sge             = min(dev->dev->caps.max_sq_sg,
                                         dev->dev->caps.max_rq_sg);
-       props->max_cq              = dev->dev->caps.num_cqs - dev->dev->caps.reserved_cqs;
+       props->max_cq              = dev->dev->quotas.cq;
        props->max_cqe             = dev->dev->caps.max_cqes;
-       props->max_mr              = dev->dev->caps.num_mpts - dev->dev->caps.reserved_mrws;
+       props->max_mr              = dev->dev->quotas.mpt;
        props->max_pd              = dev->dev->caps.num_pds - dev->dev->caps.reserved_pds;
        props->max_qp_rd_atom      = dev->dev->caps.max_qp_dest_rdma;
        props->max_qp_init_rd_atom = dev->dev->caps.max_qp_init_rdma;
        props->max_res_rd_atom     = props->max_qp_rd_atom * props->max_qp;
-       props->max_srq             = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs;
+       props->max_srq             = dev->dev->quotas.srq;
        props->max_srq_wr          = dev->dev->caps.max_srq_wqes - 1;
        props->max_srq_sge         = dev->dev->caps.max_srq_sge;
        props->max_fast_reg_page_list_len = MLX4_MAX_FAST_REG_PAGES;
@@ -526,7 +526,6 @@ static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask,
        if (IS_ERR(mailbox))
                return 0;
 
-       memset(mailbox->buf, 0, 256);
        memcpy(mailbox->buf, props->node_desc, 64);
        mlx4_cmd(to_mdev(ibdev)->dev, mailbox->dma, 1, 0,
                 MLX4_CMD_SET_NODE, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
@@ -547,8 +546,6 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols,
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
 
-       memset(mailbox->buf, 0, 256);
-
        if (dev->dev->flags & MLX4_FLAG_OLD_PORT_CMDS) {
                *(u8 *) mailbox->buf         = !!reset_qkey_viols << 6;
                ((__be32 *) mailbox->buf)[2] = cpu_to_be32(cap_mask);
@@ -879,8 +876,6 @@ static int __mlx4_ib_create_flow(struct ib_qp *qp, struct ib_flow_attr *flow_att
        struct mlx4_ib_dev *mdev = to_mdev(qp->device);
        struct mlx4_cmd_mailbox *mailbox;
        struct mlx4_net_trans_rule_hw_ctrl *ctrl;
-       size_t rule_size = sizeof(struct mlx4_net_trans_rule_hw_ctrl) +
-                          (sizeof(struct _rule_hw) * flow_attr->num_of_specs);
 
        static const u16 __mlx4_domain[] = {
                [IB_FLOW_DOMAIN_USER] = MLX4_DOMAIN_UVERBS,
@@ -905,7 +900,6 @@ static int __mlx4_ib_create_flow(struct ib_qp *qp, struct ib_flow_attr *flow_att
        mailbox = mlx4_alloc_cmd_mailbox(mdev->dev);
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
-       memset(mailbox->buf, 0, rule_size);
        ctrl = mailbox->buf;
 
        ctrl->prio = cpu_to_be16(__mlx4_domain[domain] |
@@ -1691,9 +1685,11 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
                ibdev->ib_dev.create_flow       = mlx4_ib_create_flow;
                ibdev->ib_dev.destroy_flow      = mlx4_ib_destroy_flow;
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
                ibdev->ib_dev.uverbs_cmd_mask   |=
                        (1ull << IB_USER_VERBS_CMD_CREATE_FLOW) |
                        (1ull << IB_USER_VERBS_CMD_DESTROY_FLOW);
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
        }
 
        mlx4_ib_alloc_eqs(dev, ibdev);
index 3f831de..b1a6cb3 100644 (file)
@@ -164,6 +164,7 @@ int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn)
 static int alloc_comp_eqs(struct mlx5_ib_dev *dev)
 {
        struct mlx5_eq_table *table = &dev->mdev.priv.eq_table;
+       char name[MLX5_MAX_EQ_NAME];
        struct mlx5_eq *eq, *n;
        int ncomp_vec;
        int nent;
@@ -180,11 +181,10 @@ static int alloc_comp_eqs(struct mlx5_ib_dev *dev)
                        goto clean;
                }
 
-               snprintf(eq->name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i);
+               snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i);
                err = mlx5_create_map_eq(&dev->mdev, eq,
                                         i + MLX5_EQ_VEC_COMP_BASE, nent, 0,
-                                        eq->name,
-                                        &dev->mdev.priv.uuari.uars[0]);
+                                        name, &dev->mdev.priv.uuari.uars[0]);
                if (err) {
                        kfree(eq);
                        goto clean;
@@ -301,9 +301,8 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
        props->max_srq_sge         = max_rq_sg - 1;
        props->max_fast_reg_page_list_len = (unsigned int)-1;
        props->local_ca_ack_delay  = dev->mdev.caps.local_ca_ack_delay;
-       props->atomic_cap          = dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_ATOMIC ?
-               IB_ATOMIC_HCA : IB_ATOMIC_NONE;
-       props->masked_atomic_cap   = IB_ATOMIC_HCA;
+       props->atomic_cap          = IB_ATOMIC_NONE;
+       props->masked_atomic_cap   = IB_ATOMIC_NONE;
        props->max_pkeys           = be16_to_cpup((__be16 *)(out_mad->data + 28));
        props->max_mcast_grp       = 1 << dev->mdev.caps.log_max_mcg;
        props->max_mcast_qp_attach = dev->mdev.caps.max_qp_mcg;
@@ -1006,6 +1005,11 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
        ibev.device           = &ibdev->ib_dev;
        ibev.element.port_num = port;
 
+       if (port < 1 || port > ibdev->num_ports) {
+               mlx5_ib_warn(ibdev, "warning: event on port %d\n", port);
+               return;
+       }
+
        if (ibdev->ib_active)
                ib_dispatch_event(&ibev);
 }
index bd41df9..3453580 100644 (file)
@@ -42,6 +42,10 @@ enum {
        DEF_CACHE_SIZE  = 10,
 };
 
+enum {
+       MLX5_UMR_ALIGN  = 2048
+};
+
 static __be64 *mr_align(__be64 *ptr, int align)
 {
        unsigned long mask = align - 1;
@@ -61,13 +65,11 @@ static int order2idx(struct mlx5_ib_dev *dev, int order)
 
 static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
 {
-       struct device *ddev = dev->ib_dev.dma_device;
        struct mlx5_mr_cache *cache = &dev->cache;
        struct mlx5_cache_ent *ent = &cache->ent[c];
        struct mlx5_create_mkey_mbox_in *in;
        struct mlx5_ib_mr *mr;
        int npages = 1 << ent->order;
-       int size = sizeof(u64) * npages;
        int err = 0;
        int i;
 
@@ -83,21 +85,6 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
                }
                mr->order = ent->order;
                mr->umred = 1;
-               mr->pas = kmalloc(size + 0x3f, GFP_KERNEL);
-               if (!mr->pas) {
-                       kfree(mr);
-                       err = -ENOMEM;
-                       goto out;
-               }
-               mr->dma = dma_map_single(ddev, mr_align(mr->pas, 0x40), size,
-                                        DMA_TO_DEVICE);
-               if (dma_mapping_error(ddev, mr->dma)) {
-                       kfree(mr->pas);
-                       kfree(mr);
-                       err = -ENOMEM;
-                       goto out;
-               }
-
                in->seg.status = 1 << 6;
                in->seg.xlt_oct_size = cpu_to_be32((npages + 1) / 2);
                in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
@@ -108,8 +95,6 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
                                            sizeof(*in));
                if (err) {
                        mlx5_ib_warn(dev, "create mkey failed %d\n", err);
-                       dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
-                       kfree(mr->pas);
                        kfree(mr);
                        goto out;
                }
@@ -129,11 +114,9 @@ out:
 
 static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
 {
-       struct device *ddev = dev->ib_dev.dma_device;
        struct mlx5_mr_cache *cache = &dev->cache;
        struct mlx5_cache_ent *ent = &cache->ent[c];
        struct mlx5_ib_mr *mr;
-       int size;
        int err;
        int i;
 
@@ -149,14 +132,10 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
                ent->size--;
                spin_unlock(&ent->lock);
                err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
-               if (err) {
+               if (err)
                        mlx5_ib_warn(dev, "failed destroy mkey\n");
-               } else {
-                       size = ALIGN(sizeof(u64) * (1 << mr->order), 0x40);
-                       dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
-                       kfree(mr->pas);
+               else
                        kfree(mr);
-               }
        }
 }
 
@@ -408,13 +387,12 @@ static void free_cached_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
 
 static void clean_keys(struct mlx5_ib_dev *dev, int c)
 {
-       struct device *ddev = dev->ib_dev.dma_device;
        struct mlx5_mr_cache *cache = &dev->cache;
        struct mlx5_cache_ent *ent = &cache->ent[c];
        struct mlx5_ib_mr *mr;
-       int size;
        int err;
 
+       cancel_delayed_work(&ent->dwork);
        while (1) {
                spin_lock(&ent->lock);
                if (list_empty(&ent->head)) {
@@ -427,14 +405,10 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c)
                ent->size--;
                spin_unlock(&ent->lock);
                err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
-               if (err) {
+               if (err)
                        mlx5_ib_warn(dev, "failed destroy mkey\n");
-               } else {
-                       size = ALIGN(sizeof(u64) * (1 << mr->order), 0x40);
-                       dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
-                       kfree(mr->pas);
+               else
                        kfree(mr);
-               }
        }
 }
 
@@ -540,13 +514,15 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
        int i;
 
        dev->cache.stopped = 1;
-       destroy_workqueue(dev->cache.wq);
+       flush_workqueue(dev->cache.wq);
 
        mlx5_mr_cache_debugfs_cleanup(dev);
 
        for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++)
                clean_keys(dev, i);
 
+       destroy_workqueue(dev->cache.wq);
+
        return 0;
 }
 
@@ -675,10 +651,12 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
                                  int page_shift, int order, int access_flags)
 {
        struct mlx5_ib_dev *dev = to_mdev(pd->device);
+       struct device *ddev = dev->ib_dev.dma_device;
        struct umr_common *umrc = &dev->umrc;
        struct ib_send_wr wr, *bad;
        struct mlx5_ib_mr *mr;
        struct ib_sge sg;
+       int size = sizeof(u64) * npages;
        int err;
        int i;
 
@@ -697,7 +675,22 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
        if (!mr)
                return ERR_PTR(-EAGAIN);
 
-       mlx5_ib_populate_pas(dev, umem, page_shift, mr_align(mr->pas, 0x40), 1);
+       mr->pas = kmalloc(size + MLX5_UMR_ALIGN - 1, GFP_KERNEL);
+       if (!mr->pas) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       mlx5_ib_populate_pas(dev, umem, page_shift,
+                            mr_align(mr->pas, MLX5_UMR_ALIGN), 1);
+
+       mr->dma = dma_map_single(ddev, mr_align(mr->pas, MLX5_UMR_ALIGN), size,
+                                DMA_TO_DEVICE);
+       if (dma_mapping_error(ddev, mr->dma)) {
+               kfree(mr->pas);
+               err = -ENOMEM;
+               goto error;
+       }
 
        memset(&wr, 0, sizeof(wr));
        wr.wr_id = (u64)(unsigned long)mr;
@@ -718,6 +711,9 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
        wait_for_completion(&mr->done);
        up(&umrc->sem);
 
+       dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
+       kfree(mr->pas);
+
        if (mr->status != IB_WC_SUCCESS) {
                mlx5_ib_warn(dev, "reg umr failed\n");
                err = -EFAULT;
index 045f8cd..5659ea8 100644 (file)
@@ -203,7 +203,7 @@ static int sq_overhead(enum ib_qp_type qp_type)
 
        switch (qp_type) {
        case IB_QPT_XRC_INI:
-               size = sizeof(struct mlx5_wqe_xrc_seg);
+               size += sizeof(struct mlx5_wqe_xrc_seg);
                /* fall through */
        case IB_QPT_RC:
                size += sizeof(struct mlx5_wqe_ctrl_seg) +
@@ -211,20 +211,23 @@ static int sq_overhead(enum ib_qp_type qp_type)
                        sizeof(struct mlx5_wqe_raddr_seg);
                break;
 
+       case IB_QPT_XRC_TGT:
+               return 0;
+
        case IB_QPT_UC:
-               size = sizeof(struct mlx5_wqe_ctrl_seg) +
+               size += sizeof(struct mlx5_wqe_ctrl_seg) +
                        sizeof(struct mlx5_wqe_raddr_seg);
                break;
 
        case IB_QPT_UD:
        case IB_QPT_SMI:
        case IB_QPT_GSI:
-               size = sizeof(struct mlx5_wqe_ctrl_seg) +
+               size += sizeof(struct mlx5_wqe_ctrl_seg) +
                        sizeof(struct mlx5_wqe_datagram_seg);
                break;
 
        case MLX5_IB_QPT_REG_UMR:
-               size = sizeof(struct mlx5_wqe_ctrl_seg) +
+               size += sizeof(struct mlx5_wqe_ctrl_seg) +
                        sizeof(struct mlx5_wqe_umr_ctrl_seg) +
                        sizeof(struct mlx5_mkey_seg);
                break;
@@ -270,7 +273,8 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
                return wqe_size;
 
        if (wqe_size > dev->mdev.caps.max_sq_desc_sz) {
-               mlx5_ib_dbg(dev, "\n");
+               mlx5_ib_dbg(dev, "wqe_size(%d) > max_sq_desc_sz(%d)\n",
+                           wqe_size, dev->mdev.caps.max_sq_desc_sz);
                return -EINVAL;
        }
 
@@ -280,9 +284,15 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
 
        wq_size = roundup_pow_of_two(attr->cap.max_send_wr * wqe_size);
        qp->sq.wqe_cnt = wq_size / MLX5_SEND_WQE_BB;
+       if (qp->sq.wqe_cnt > dev->mdev.caps.max_wqes) {
+               mlx5_ib_dbg(dev, "wqe count(%d) exceeds limits(%d)\n",
+                           qp->sq.wqe_cnt, dev->mdev.caps.max_wqes);
+               return -ENOMEM;
+       }
        qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
        qp->sq.max_gs = attr->cap.max_send_sge;
-       qp->sq.max_post = 1 << ilog2(wq_size / wqe_size);
+       qp->sq.max_post = wq_size / wqe_size;
+       attr->cap.max_send_wr = qp->sq.max_post;
 
        return wq_size;
 }
@@ -1280,6 +1290,11 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q
                                          MLX5_QP_OPTPAR_Q_KEY,
                        [MLX5_QP_ST_MLX] = MLX5_QP_OPTPAR_PKEY_INDEX    |
                                           MLX5_QP_OPTPAR_Q_KEY,
+                       [MLX5_QP_ST_XRC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH |
+                                         MLX5_QP_OPTPAR_RRE            |
+                                         MLX5_QP_OPTPAR_RAE            |
+                                         MLX5_QP_OPTPAR_RWE            |
+                                         MLX5_QP_OPTPAR_PKEY_INDEX,
                },
        },
        [MLX5_QP_STATE_RTR] = {
@@ -1314,6 +1329,11 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q
                [MLX5_QP_STATE_RTS] = {
                        [MLX5_QP_ST_UD]  = MLX5_QP_OPTPAR_Q_KEY,
                        [MLX5_QP_ST_MLX] = MLX5_QP_OPTPAR_Q_KEY,
+                       [MLX5_QP_ST_UC]  = MLX5_QP_OPTPAR_RWE,
+                       [MLX5_QP_ST_RC]  = MLX5_QP_OPTPAR_RNR_TIMEOUT   |
+                                          MLX5_QP_OPTPAR_RWE           |
+                                          MLX5_QP_OPTPAR_RAE           |
+                                          MLX5_QP_OPTPAR_RRE,
                },
        },
 };
@@ -1651,29 +1671,6 @@ static __always_inline void set_raddr_seg(struct mlx5_wqe_raddr_seg *rseg,
        rseg->reserved = 0;
 }
 
-static void set_atomic_seg(struct mlx5_wqe_atomic_seg *aseg, struct ib_send_wr *wr)
-{
-       if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
-               aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap);
-               aseg->compare  = cpu_to_be64(wr->wr.atomic.compare_add);
-       } else if (wr->opcode == IB_WR_MASKED_ATOMIC_FETCH_AND_ADD) {
-               aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add);
-               aseg->compare  = cpu_to_be64(wr->wr.atomic.compare_add_mask);
-       } else {
-               aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add);
-               aseg->compare  = 0;
-       }
-}
-
-static void set_masked_atomic_seg(struct mlx5_wqe_masked_atomic_seg *aseg,
-                                 struct ib_send_wr *wr)
-{
-       aseg->swap_add          = cpu_to_be64(wr->wr.atomic.swap);
-       aseg->swap_add_mask     = cpu_to_be64(wr->wr.atomic.swap_mask);
-       aseg->compare           = cpu_to_be64(wr->wr.atomic.compare_add);
-       aseg->compare_mask      = cpu_to_be64(wr->wr.atomic.compare_add_mask);
-}
-
 static void set_datagram_seg(struct mlx5_wqe_datagram_seg *dseg,
                             struct ib_send_wr *wr)
 {
@@ -2063,28 +2060,11 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
 
                        case IB_WR_ATOMIC_CMP_AND_SWP:
                        case IB_WR_ATOMIC_FETCH_AND_ADD:
-                               set_raddr_seg(seg, wr->wr.atomic.remote_addr,
-                                             wr->wr.atomic.rkey);
-                               seg  += sizeof(struct mlx5_wqe_raddr_seg);
-
-                               set_atomic_seg(seg, wr);
-                               seg  += sizeof(struct mlx5_wqe_atomic_seg);
-
-                               size += (sizeof(struct mlx5_wqe_raddr_seg) +
-                                        sizeof(struct mlx5_wqe_atomic_seg)) / 16;
-                               break;
-
                        case IB_WR_MASKED_ATOMIC_CMP_AND_SWP:
-                               set_raddr_seg(seg, wr->wr.atomic.remote_addr,
-                                             wr->wr.atomic.rkey);
-                               seg  += sizeof(struct mlx5_wqe_raddr_seg);
-
-                               set_masked_atomic_seg(seg, wr);
-                               seg  += sizeof(struct mlx5_wqe_masked_atomic_seg);
-
-                               size += (sizeof(struct mlx5_wqe_raddr_seg) +
-                                        sizeof(struct mlx5_wqe_masked_atomic_seg)) / 16;
-                               break;
+                               mlx5_ib_warn(dev, "Atomic operations are not supported yet\n");
+                               err = -ENOSYS;
+                               *bad_wr = wr;
+                               goto out;
 
                        case IB_WR_LOCAL_INV:
                                next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
index 84d297a..0aa478b 100644 (file)
@@ -295,7 +295,7 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
        mlx5_vfree(in);
        if (err) {
                mlx5_ib_dbg(dev, "create SRQ failed, err %d\n", err);
-               goto err_srq;
+               goto err_usr_kern_srq;
        }
 
        mlx5_ib_dbg(dev, "create SRQ with srqn 0x%x\n", srq->msrq.srqn);
@@ -316,6 +316,8 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
 
 err_core:
        mlx5_core_destroy_srq(&dev->mdev, &srq->msrq);
+
+err_usr_kern_srq:
        if (pd->uobject)
                destroy_srq_user(pd, srq);
        else
index 7c9d35f..6902017 100644 (file)
@@ -357,7 +357,7 @@ static int mthca_eq_int(struct mthca_dev *dev, struct mthca_eq *eq)
                        mthca_warn(dev, "Unhandled event %02x(%02x) on EQ %d\n",
                                   eqe->type, eqe->subtype, eq->eqn);
                        break;
-               };
+               }
 
                set_eqe_hw(eqe);
                ++eq->cons_index;
index 4ed8235..50219ab 100644 (file)
@@ -150,7 +150,7 @@ enum ib_qp_state get_ibqp_state(enum ocrdma_qp_state qps)
                return IB_QPS_SQE;
        case OCRDMA_QPS_ERR:
                return IB_QPS_ERR;
-       };
+       }
        return IB_QPS_ERR;
 }
 
@@ -171,7 +171,7 @@ static enum ocrdma_qp_state get_ocrdma_qp_state(enum ib_qp_state qps)
                return OCRDMA_QPS_SQE;
        case IB_QPS_ERR:
                return OCRDMA_QPS_ERR;
-       };
+       }
        return OCRDMA_QPS_ERR;
 }
 
@@ -1982,7 +1982,7 @@ int ocrdma_mbx_create_qp(struct ocrdma_qp *qp, struct ib_qp_init_attr *attrs,
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_CREATE_QP, sizeof(*cmd));
        if (!cmd)
index 56e0049..0ce7674 100644 (file)
@@ -531,7 +531,7 @@ static void ocrdma_event_handler(struct ocrdma_dev *dev, u32 event)
        case BE_DEV_DOWN:
                ocrdma_close(dev);
                break;
-       };
+       }
 }
 
 static struct ocrdma_driver ocrdma_drv = {
index 6e982bb..69f1d12 100644 (file)
@@ -141,7 +141,7 @@ static inline void get_link_speed_and_width(struct ocrdma_dev *dev,
                /* Unsupported */
                *ib_speed = IB_SPEED_SDR;
                *ib_width = IB_WIDTH_1X;
-       };
+       }
 }
 
 
@@ -2331,7 +2331,7 @@ static enum ib_wc_status ocrdma_to_ibwc_err(u16 status)
        default:
                ibwc_status = IB_WC_GENERAL_ERR;
                break;
-       };
+       }
        return ibwc_status;
 }
 
@@ -2370,7 +2370,7 @@ static void ocrdma_update_wc(struct ocrdma_qp *qp, struct ib_wc *ibwc,
                pr_err("%s() invalid opcode received = 0x%x\n",
                       __func__, hdr->cw & OCRDMA_WQE_OPCODE_MASK);
                break;
-       };
+       }
 }
 
 static void ocrdma_set_cqe_status_flushed(struct ocrdma_qp *qp,
index 3591855..6df2350 100644 (file)
@@ -594,7 +594,7 @@ isert_connect_release(struct isert_conn *isert_conn)
 
        pr_debug("Entering isert_connect_release(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
 
-       if (device->use_frwr)
+       if (device && device->use_frwr)
                isert_conn_free_frwr_pool(isert_conn);
 
        if (isert_conn->conn_qp) {
index 653ac6b..6c923c7 100644 (file)
@@ -1588,7 +1588,7 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
        int resp_data_len;
        int resp_len;
 
-       resp_data_len = (rsp_code == SRP_TSK_MGMT_SUCCESS) ? 0 : 4;
+       resp_data_len = 4;
        resp_len = sizeof(*srp_rsp) + resp_data_len;
 
        srp_rsp = ioctx->ioctx.buf;
@@ -1600,11 +1600,9 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
                                    + atomic_xchg(&ch->req_lim_delta, 0));
        srp_rsp->tag = tag;
 
-       if (rsp_code != SRP_TSK_MGMT_SUCCESS) {
-               srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID;
-               srp_rsp->resp_data_len = cpu_to_be32(resp_data_len);
-               srp_rsp->data[3] = rsp_code;
-       }
+       srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID;
+       srp_rsp->resp_data_len = cpu_to_be32(resp_data_len);
+       srp_rsp->data[3] = rsp_code;
 
        return resp_len;
 }
@@ -2358,6 +2356,8 @@ static void srpt_release_channel_work(struct work_struct *w)
        transport_deregister_session(se_sess);
        ch->sess = NULL;
 
+       ib_destroy_cm_id(ch->cm_id);
+
        srpt_destroy_ch_ib(ch);
 
        srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring,
@@ -2368,8 +2368,6 @@ static void srpt_release_channel_work(struct work_struct *w)
        list_del(&ch->list);
        spin_unlock_irq(&sdev->spinlock);
 
-       ib_destroy_cm_id(ch->cm_id);
-
        if (ch->release_done)
                complete(ch->release_done);
 
index c044699..e75d015 100644 (file)
@@ -1734,6 +1734,7 @@ EXPORT_SYMBOL_GPL(input_class);
  */
 struct input_dev *input_allocate_device(void)
 {
+       static atomic_t input_no = ATOMIC_INIT(0);
        struct input_dev *dev;
 
        dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
@@ -1743,9 +1744,13 @@ struct input_dev *input_allocate_device(void)
                device_initialize(&dev->dev);
                mutex_init(&dev->mutex);
                spin_lock_init(&dev->event_lock);
+               init_timer(&dev->timer);
                INIT_LIST_HEAD(&dev->h_list);
                INIT_LIST_HEAD(&dev->node);
 
+               dev_set_name(&dev->dev, "input%ld",
+                            (unsigned long) atomic_inc_return(&input_no) - 1);
+
                __module_get(THIS_MODULE);
        }
 
@@ -2019,7 +2024,6 @@ static void devm_input_device_unregister(struct device *dev, void *res)
  */
 int input_register_device(struct input_dev *dev)
 {
-       static atomic_t input_no = ATOMIC_INIT(0);
        struct input_devres *devres = NULL;
        struct input_handler *handler;
        unsigned int packet_size;
@@ -2059,7 +2063,6 @@ int input_register_device(struct input_dev *dev)
         * If delay and period are pre-set by the driver, then autorepeating
         * is handled by the driver itself and we don't do it in input.c.
         */
-       init_timer(&dev->timer);
        if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
                dev->timer.data = (long) dev;
                dev->timer.function = input_repeat_key;
@@ -2073,9 +2076,6 @@ int input_register_device(struct input_dev *dev)
        if (!dev->setkeycode)
                dev->setkeycode = input_default_setkeycode;
 
-       dev_set_name(&dev->dev, "input%ld",
-                    (unsigned long) atomic_inc_return(&input_no) - 1);
-
        error = device_add(&dev->dev);
        if (error)
                goto err_free_vals;
index 134c3b4..a2e758d 100644 (file)
@@ -786,10 +786,17 @@ static int pxa27x_keypad_probe(struct platform_device *pdev)
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
        input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 
-       if (pdata)
+       if (pdata) {
                error = pxa27x_keypad_build_keycode(keypad);
-       else
+       } else {
                error = pxa27x_keypad_build_keycode_from_dt(keypad);
+               /*
+                * Data that we get from DT resides in dynamically
+                * allocated memory so we need to update our pdata
+                * pointer.
+                */
+               pdata = keypad->pdata;
+       }
        if (error) {
                dev_err(&pdev->dev, "failed to build keycode\n");
                goto failed_put_clk;
index 082684e..9365535 100644 (file)
@@ -351,7 +351,9 @@ static void cm109_urb_irq_callback(struct urb *urb)
        if (status) {
                if (status == -ESHUTDOWN)
                        return;
-               dev_err(&dev->intf->dev, "%s: urb status %d\n", __func__, status);
+               dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
+                                   __func__, status);
+               goto out;
        }
 
        /* Special keys */
@@ -418,8 +420,12 @@ static void cm109_urb_ctl_callback(struct urb *urb)
             dev->ctl_data->byte[2],
             dev->ctl_data->byte[3]);
 
-       if (status)
-               dev_err(&dev->intf->dev, "%s: urb status %d\n", __func__, status);
+       if (status) {
+               if (status == -ESHUTDOWN)
+                       return;
+               dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
+                                   __func__, status);
+       }
 
        spin_lock(&dev->ctl_submit_lock);
 
@@ -427,7 +433,7 @@ static void cm109_urb_ctl_callback(struct urb *urb)
 
        if (likely(!dev->shutdown)) {
 
-               if (dev->buzzer_pending) {
+               if (dev->buzzer_pending || status) {
                        dev->buzzer_pending = 0;
                        dev->ctl_urb_pending = 1;
                        cm109_submit_buzz_toggle(dev);
index 7c5d72a..8365847 100644 (file)
@@ -103,6 +103,7 @@ static const struct alps_model_info alps_model_data[] = {
        /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
        { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
+       { { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_DUALPOINT },              /* Dell XT2 */
        { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },           /* Dell Vostro 1400 */
        { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },                            /* Toshiba Tecra A11-11L */
index 78e4de4..52c9ebf 100644 (file)
@@ -223,21 +223,26 @@ static int i8042_flush(void)
 {
        unsigned long flags;
        unsigned char data, str;
-       int i = 0;
+       int count = 0;
+       int retval = 0;
 
        spin_lock_irqsave(&i8042_lock, flags);
 
-       while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) {
-               udelay(50);
-               data = i8042_read_data();
-               i++;
-               dbg("%02x <- i8042 (flush, %s)\n",
-                   data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+       while ((str = i8042_read_status()) & I8042_STR_OBF) {
+               if (count++ < I8042_BUFFER_SIZE) {
+                       udelay(50);
+                       data = i8042_read_data();
+                       dbg("%02x <- i8042 (flush, %s)\n",
+                           data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+               } else {
+                       retval = -EIO;
+                       break;
+               }
        }
 
        spin_unlock_irqrestore(&i8042_lock, flags);
 
-       return i;
+       return retval;
 }
 
 /*
@@ -849,7 +854,7 @@ static int __init i8042_check_aux(void)
 
 static int i8042_controller_check(void)
 {
-       if (i8042_flush() == I8042_BUFFER_SIZE) {
+       if (i8042_flush()) {
                pr_err("No controller found\n");
                return -ENODEV;
        }
index 79b69ea..e53416a 100644 (file)
@@ -1031,6 +1031,7 @@ static void wacom_destroy_leds(struct wacom *wacom)
 }
 
 static enum power_supply_property wacom_battery_props[] = {
+       POWER_SUPPLY_PROP_SCOPE,
        POWER_SUPPLY_PROP_CAPACITY
 };
 
@@ -1042,6 +1043,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
        int ret = 0;
 
        switch (psp) {
+               case POWER_SUPPLY_PROP_SCOPE:
+                       val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+                       break;
                case POWER_SUPPLY_PROP_CAPACITY:
                        val->intval =
                                wacom->wacom_wac.battery_capacity * 100 / 31;
index b2aa503..c59b797 100644 (file)
@@ -2054,6 +2054,12 @@ static const struct wacom_features wacom_features_0x101 =
 static const struct wacom_features wacom_features_0x10D =
        { "Wacom ISDv4 10D",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
          0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10E =
+       { "Wacom ISDv4 10E",      WACOM_PKGLEN_MTTPC,     27760, 15694,  255,
+         0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10F =
+       { "Wacom ISDv4 10F",      WACOM_PKGLEN_MTTPC,     27760, 15694,  255,
+         0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x4001 =
        { "Wacom ISDv4 4001",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
          0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -2248,6 +2254,8 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x100) },
        { USB_DEVICE_WACOM(0x101) },
        { USB_DEVICE_WACOM(0x10D) },
+       { USB_DEVICE_WACOM(0x10E) },
+       { USB_DEVICE_WACOM(0x10F) },
        { USB_DEVICE_WACOM(0x300) },
        { USB_DEVICE_WACOM(0x301) },
        { USB_DEVICE_WACOM(0x304) },
index fe302e3..c880eba 100644 (file)
@@ -52,7 +52,7 @@ config AMD_IOMMU
        select PCI_PRI
        select PCI_PASID
        select IOMMU_API
-       depends on X86_64 && PCI && ACPI && X86_IO_APIC
+       depends on X86_64 && PCI && ACPI
        ---help---
          With this option you can enable support for AMD IOMMU hardware in
          your system. An IOMMU is a hardware component which provides
index f417e89..181c9ba 100644 (file)
@@ -377,6 +377,7 @@ struct arm_smmu_cfg {
        u32                             cbar;
        pgd_t                           *pgd;
 };
+#define INVALID_IRPTNDX                        0xff
 
 #define ARM_SMMU_CB_ASID(cfg)          ((cfg)->cbndx)
 #define ARM_SMMU_CB_VMID(cfg)          ((cfg)->cbndx + 1)
@@ -840,7 +841,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
        if (IS_ERR_VALUE(ret)) {
                dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
                        root_cfg->irptndx, irq);
-               root_cfg->irptndx = -1;
+               root_cfg->irptndx = INVALID_IRPTNDX;
                goto out_free_context;
        }
 
@@ -869,7 +870,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
        writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
        arm_smmu_tlb_inv_context(root_cfg);
 
-       if (root_cfg->irptndx != -1) {
+       if (root_cfg->irptndx != INVALID_IRPTNDX) {
                irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
                free_irq(irq, domain);
        }
@@ -1857,8 +1858,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
                goto out_put_parent;
        }
 
-       arm_smmu_device_reset(smmu);
-
        for (i = 0; i < smmu->num_global_irqs; ++i) {
                err = request_irq(smmu->irqs[i],
                                  arm_smmu_global_fault,
@@ -1876,6 +1875,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
        spin_lock(&arm_smmu_devices_lock);
        list_add(&smmu->list, &arm_smmu_devices);
        spin_unlock(&arm_smmu_devices_lock);
+
+       arm_smmu_device_reset(smmu);
        return 0;
 
 out_free_irqs:
@@ -1966,10 +1967,10 @@ static int __init arm_smmu_init(void)
                return ret;
 
        /* Oh, for a proper bus abstraction */
-       if (!iommu_present(&platform_bus_type));
+       if (!iommu_present(&platform_bus_type))
                bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
 
-       if (!iommu_present(&amba_bustype));
+       if (!iommu_present(&amba_bustype))
                bus_set_iommu(&amba_bustype, &arm_smmu_ops);
 
        return 0;
index 52377b4..a2e0ed6 100644 (file)
@@ -481,7 +481,7 @@ void __inline__ outpp(void __iomem *addr, word p)
 int diva_os_register_irq(void *context, byte irq, const char *name)
 {
        int result = request_irq(irq, diva_os_irq_wrapper,
-                                IRQF_DISABLED | IRQF_SHARED, name, context);
+                                IRQF_SHARED, name, context);
        return (result);
 }
 
index 7cab5c3..e151971 100644 (file)
@@ -288,9 +288,9 @@ int divas_um_idi_delete_entity(int adapter_nr, void *entity)
        cleanup_entity(e);
        diva_os_free(0, e->os_context);
        memset(e, 0x00, sizeof(*e));
-       diva_os_free(0, e);
 
        DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e));
+       diva_os_free(0, e);
 
        return (0);
 }
index e74df7c..53d487f 100644 (file)
@@ -1580,8 +1580,7 @@ icn_addcard(int port, char *id1, char *id2)
        }
        if (!(card2 = icn_initcard(port, id2))) {
                printk(KERN_INFO
-                      "icn: (%s) half ICN-4B, port 0x%x added\n",
-                      card2->interface.id, port);
+                      "icn: (%s) half ICN-4B, port 0x%x added\n", id2, port);
                return 0;
        }
        card->doubleS0 = 1;
index ca997bd..92acc81 100644 (file)
@@ -336,7 +336,7 @@ static int __init sc_init(void)
                 */
                sc_adapter[cinst]->interrupt = irq[b];
                if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
-                               IRQF_DISABLED, interface->id,
+                               0, interface->id,
                                (void *)(unsigned long) cinst))
                {
                        kfree(sc_adapter[cinst]->channel);
index eba380d..42d2b89 100644 (file)
@@ -325,7 +325,6 @@ static int omap2_mbox_remove(struct platform_device *pdev)
        kfree(privblk);
        kfree(mboxblk);
        kfree(list);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index b39f6f0..0f12382 100644 (file)
@@ -498,7 +498,7 @@ struct cached_dev {
         */
        atomic_t                has_dirty;
 
-       struct ratelimit        writeback_rate;
+       struct bch_ratelimit    writeback_rate;
        struct delayed_work     writeback_rate_update;
 
        /*
@@ -507,10 +507,9 @@ struct cached_dev {
         */
        sector_t                last_read;
 
-       /* Number of writeback bios in flight */
-       atomic_t                in_flight;
+       /* Limit number of writeback bios in flight */
+       struct semaphore        in_flight;
        struct closure_with_timer writeback;
-       struct closure_waitlist writeback_wait;
 
        struct keybuf           writeback_keys;
 
index 8010eed..22d1ae7 100644 (file)
@@ -926,28 +926,45 @@ struct bkey *bch_next_recurse_key(struct btree *b, struct bkey *search)
 
 /* Mergesort */
 
+static void sort_key_next(struct btree_iter *iter,
+                         struct btree_iter_set *i)
+{
+       i->k = bkey_next(i->k);
+
+       if (i->k == i->end)
+               *i = iter->data[--iter->used];
+}
+
 static void btree_sort_fixup(struct btree_iter *iter)
 {
        while (iter->used > 1) {
                struct btree_iter_set *top = iter->data, *i = top + 1;
-               struct bkey *k;
 
                if (iter->used > 2 &&
                    btree_iter_cmp(i[0], i[1]))
                        i++;
 
-               for (k = i->k;
-                    k != i->end && bkey_cmp(top->k, &START_KEY(k)) > 0;
-                    k = bkey_next(k))
-                       if (top->k > i->k)
-                               __bch_cut_front(top->k, k);
-                       else if (KEY_SIZE(k))
-                               bch_cut_back(&START_KEY(k), top->k);
-
-               if (top->k < i->k || k == i->k)
+               if (bkey_cmp(top->k, &START_KEY(i->k)) <= 0)
                        break;
 
-               heap_sift(iter, i - top, btree_iter_cmp);
+               if (!KEY_SIZE(i->k)) {
+                       sort_key_next(iter, i);
+                       heap_sift(iter, i - top, btree_iter_cmp);
+                       continue;
+               }
+
+               if (top->k > i->k) {
+                       if (bkey_cmp(top->k, i->k) >= 0)
+                               sort_key_next(iter, i);
+                       else
+                               bch_cut_front(top->k, i->k);
+
+                       heap_sift(iter, i - top, btree_iter_cmp);
+               } else {
+                       /* can't happen because of comparison func */
+                       BUG_ON(!bkey_cmp(&START_KEY(top->k), &START_KEY(i->k)));
+                       bch_cut_back(&START_KEY(i->k), top->k);
+               }
        }
 }
 
index f9764e6..f42fc7e 100644 (file)
@@ -255,7 +255,7 @@ void bch_btree_node_read(struct btree *b)
 
        return;
 err:
-       bch_cache_set_error(b->c, "io error reading bucket %lu",
+       bch_cache_set_error(b->c, "io error reading bucket %zu",
                            PTR_BUCKET_NR(b->c, &b->key, 0));
 }
 
@@ -612,7 +612,7 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
                return SHRINK_STOP;
 
        /* Return -1 if we can't do anything right now */
-       if (sc->gfp_mask & __GFP_WAIT)
+       if (sc->gfp_mask & __GFP_IO)
                mutex_lock(&c->bucket_lock);
        else if (!mutex_trylock(&c->bucket_lock))
                return -1;
index ba95ab8..8435f81 100644 (file)
@@ -153,7 +153,8 @@ int bch_journal_read(struct cache_set *c, struct list_head *list,
                bitmap_zero(bitmap, SB_JOURNAL_BUCKETS);
                pr_debug("%u journal buckets", ca->sb.njournal_buckets);
 
-               /* Read journal buckets ordered by golden ratio hash to quickly
+               /*
+                * Read journal buckets ordered by golden ratio hash to quickly
                 * find a sequence of buckets with valid journal entries
                 */
                for (i = 0; i < ca->sb.njournal_buckets; i++) {
@@ -166,18 +167,20 @@ int bch_journal_read(struct cache_set *c, struct list_head *list,
                                goto bsearch;
                }
 
-               /* If that fails, check all the buckets we haven't checked
+               /*
+                * If that fails, check all the buckets we haven't checked
                 * already
                 */
                pr_debug("falling back to linear search");
 
-               for (l = 0; l < ca->sb.njournal_buckets; l++) {
-                       if (test_bit(l, bitmap))
-                               continue;
-
+               for (l = find_first_zero_bit(bitmap, ca->sb.njournal_buckets);
+                    l < ca->sb.njournal_buckets;
+                    l = find_next_zero_bit(bitmap, ca->sb.njournal_buckets, l + 1))
                        if (read_bucket(l))
                                goto bsearch;
-               }
+
+               if (list_empty(list))
+                       continue;
 bsearch:
                /* Binary search */
                m = r = find_next_bit(bitmap, ca->sb.njournal_buckets, l + 1);
@@ -197,10 +200,12 @@ bsearch:
                                r = m;
                }
 
-               /* Read buckets in reverse order until we stop finding more
+               /*
+                * Read buckets in reverse order until we stop finding more
                 * journal entries
                 */
-               pr_debug("finishing up");
+               pr_debug("finishing up: m %u njournal_buckets %u",
+                        m, ca->sb.njournal_buckets);
                l = m;
 
                while (1) {
@@ -228,9 +233,10 @@ bsearch:
                        }
        }
 
-       c->journal.seq = list_entry(list->prev,
-                                   struct journal_replay,
-                                   list)->j.seq;
+       if (!list_empty(list))
+               c->journal.seq = list_entry(list->prev,
+                                           struct journal_replay,
+                                           list)->j.seq;
 
        return 0;
 #undef read_bucket
@@ -428,7 +434,7 @@ static void do_journal_discard(struct cache *ca)
                return;
        }
 
-       switch (atomic_read(&ja->discard_in_flight) == DISCARD_IN_FLIGHT) {
+       switch (atomic_read(&ja->discard_in_flight)) {
        case DISCARD_IN_FLIGHT:
                return;
 
@@ -689,6 +695,7 @@ void bch_journal_meta(struct cache_set *c, struct closure *cl)
                if (cl)
                        BUG_ON(!closure_wait(&w->wait, cl));
 
+               closure_flush(&c->journal.io);
                __journal_try_write(c, true);
        }
 }
index 786a1a4..2a7f0dd 100644 (file)
@@ -996,17 +996,19 @@ static void request_write(struct cached_dev *dc, struct search *s)
                closure_bio_submit(bio, cl, s->d);
        } else {
                bch_writeback_add(dc);
+               s->op.cache_bio = bio;
 
-               if (s->op.flush_journal) {
+               if (bio->bi_rw & REQ_FLUSH) {
                        /* Also need to send a flush to the backing device */
-                       s->op.cache_bio = bio_clone_bioset(bio, GFP_NOIO,
-                                                          dc->disk.bio_split);
-
-                       bio->bi_size = 0;
-                       bio->bi_vcnt = 0;
-                       closure_bio_submit(bio, cl, s->d);
-               } else {
-                       s->op.cache_bio = bio;
+                       struct bio *flush = bio_alloc_bioset(GFP_NOIO, 0,
+                                                            dc->disk.bio_split);
+
+                       flush->bi_rw    = WRITE_FLUSH;
+                       flush->bi_bdev  = bio->bi_bdev;
+                       flush->bi_end_io = request_endio;
+                       flush->bi_private = cl;
+
+                       closure_bio_submit(flush, cl, s->d);
                }
        }
 out:
index 4fe6ab2..924dcfd 100644 (file)
@@ -223,8 +223,13 @@ STORE(__cached_dev)
        }
 
        if (attr == &sysfs_label) {
-               /* note: endlines are preserved */
-               memcpy(dc->sb.label, buf, SB_LABEL_SIZE);
+               if (size > SB_LABEL_SIZE)
+                       return -EINVAL;
+               memcpy(dc->sb.label, buf, size);
+               if (size < SB_LABEL_SIZE)
+                       dc->sb.label[size] = '\0';
+               if (size && dc->sb.label[size - 1] == '\n')
+                       dc->sb.label[size - 1] = '\0';
                bch_write_bdev_super(dc, NULL);
                if (dc->disk.c) {
                        memcpy(dc->disk.c->uuids[dc->disk.id].label,
index 98eb811..420dad5 100644 (file)
@@ -190,7 +190,16 @@ void bch_time_stats_update(struct time_stats *stats, uint64_t start_time)
        stats->last = now ?: 1;
 }
 
-unsigned bch_next_delay(struct ratelimit *d, uint64_t done)
+/**
+ * bch_next_delay() - increment @d by the amount of work done, and return how
+ * long to delay until the next time to do some work.
+ *
+ * @d - the struct bch_ratelimit to update
+ * @done - the amount of work done, in arbitrary units
+ *
+ * Returns the amount of time to delay by, in jiffies
+ */
+uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done)
 {
        uint64_t now = local_clock();
 
index 1ae2a73..ea345c6 100644 (file)
@@ -450,17 +450,23 @@ read_attribute(name ## _last_ ## frequency_units)
        (ewma) >> factor;                                               \
 })
 
-struct ratelimit {
+struct bch_ratelimit {
+       /* Next time we want to do some work, in nanoseconds */
        uint64_t                next;
+
+       /*
+        * Rate at which we want to do work, in units per nanosecond
+        * The units here correspond to the units passed to bch_next_delay()
+        */
        unsigned                rate;
 };
 
-static inline void ratelimit_reset(struct ratelimit *d)
+static inline void bch_ratelimit_reset(struct bch_ratelimit *d)
 {
        d->next = local_clock();
 }
 
-unsigned bch_next_delay(struct ratelimit *d, uint64_t done);
+uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done);
 
 #define __DIV_SAFE(n, d, zero)                                         \
 ({                                                                     \
index 22cbff5..ba3ee48 100644 (file)
@@ -94,11 +94,15 @@ static void update_writeback_rate(struct work_struct *work)
 
 static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
 {
+       uint64_t ret;
+
        if (atomic_read(&dc->disk.detaching) ||
            !dc->writeback_percent)
                return 0;
 
-       return bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL);
+       ret = bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL);
+
+       return min_t(uint64_t, ret, HZ);
 }
 
 /* Background writeback */
@@ -208,7 +212,7 @@ normal_refill:
 
        up_write(&dc->writeback_lock);
 
-       ratelimit_reset(&dc->writeback_rate);
+       bch_ratelimit_reset(&dc->writeback_rate);
 
        /* Punt to workqueue only so we don't recurse and blow the stack */
        continue_at(cl, read_dirty, dirty_wq);
@@ -318,9 +322,7 @@ static void write_dirty_finish(struct closure *cl)
        }
 
        bch_keybuf_del(&dc->writeback_keys, w);
-       atomic_dec_bug(&dc->in_flight);
-
-       closure_wake_up(&dc->writeback_wait);
+       up(&dc->in_flight);
 
        closure_return_with_destructor(cl, dirty_io_destructor);
 }
@@ -349,7 +351,7 @@ static void write_dirty(struct closure *cl)
 
        closure_bio_submit(&io->bio, cl, &io->dc->disk);
 
-       continue_at(cl, write_dirty_finish, dirty_wq);
+       continue_at(cl, write_dirty_finish, system_wq);
 }
 
 static void read_dirty_endio(struct bio *bio, int error)
@@ -369,7 +371,7 @@ static void read_dirty_submit(struct closure *cl)
 
        closure_bio_submit(&io->bio, cl, &io->dc->disk);
 
-       continue_at(cl, write_dirty, dirty_wq);
+       continue_at(cl, write_dirty, system_wq);
 }
 
 static void read_dirty(struct closure *cl)
@@ -394,12 +396,8 @@ static void read_dirty(struct closure *cl)
 
                if (delay > 0 &&
                    (KEY_START(&w->key) != dc->last_read ||
-                    jiffies_to_msecs(delay) > 50)) {
-                       w->private = NULL;
-
-                       closure_delay(&dc->writeback, delay);
-                       continue_at(cl, read_dirty, dirty_wq);
-               }
+                    jiffies_to_msecs(delay) > 50))
+                       delay = schedule_timeout_uninterruptible(delay);
 
                dc->last_read   = KEY_OFFSET(&w->key);
 
@@ -424,15 +422,10 @@ static void read_dirty(struct closure *cl)
 
                trace_bcache_writeback(&w->key);
 
-               closure_call(&io->cl, read_dirty_submit, NULL, &dc->disk.cl);
+               down(&dc->in_flight);
+               closure_call(&io->cl, read_dirty_submit, NULL, cl);
 
                delay = writeback_delay(dc, KEY_SIZE(&w->key));
-
-               atomic_inc(&dc->in_flight);
-
-               if (!closure_wait_event(&dc->writeback_wait, cl,
-                                       atomic_read(&dc->in_flight) < 64))
-                       continue_at(cl, read_dirty, dirty_wq);
        }
 
        if (0) {
@@ -442,7 +435,11 @@ err:
                bch_keybuf_del(&dc->writeback_keys, w);
        }
 
-       refill_dirty(cl);
+       /*
+        * Wait for outstanding writeback IOs to finish (and keybuf slots to be
+        * freed) before refilling again
+        */
+       continue_at(cl, refill_dirty, dirty_wq);
 }
 
 /* Init */
@@ -484,6 +481,7 @@ void bch_sectors_dirty_init(struct cached_dev *dc)
 
 void bch_cached_dev_writeback_init(struct cached_dev *dc)
 {
+       sema_init(&dc->in_flight, 64);
        closure_init_unlocked(&dc->writeback);
        init_rwsem(&dc->writeback_lock);
 
@@ -513,7 +511,7 @@ void bch_writeback_exit(void)
 
 int __init bch_writeback_init(void)
 {
-       dirty_wq = create_singlethread_workqueue("bcache_writeback");
+       dirty_wq = create_workqueue("bcache_writeback");
        if (!dirty_wq)
                return -ENOMEM;
 
index ea49834..2a20986 100644 (file)
@@ -19,8 +19,6 @@
 #define DM_MSG_PREFIX "io"
 
 #define DM_IO_MAX_REGIONS      BITS_PER_LONG
-#define MIN_IOS                16
-#define MIN_BIOS       16
 
 struct dm_io_client {
        mempool_t *pool;
@@ -50,16 +48,17 @@ static struct kmem_cache *_dm_io_cache;
 struct dm_io_client *dm_io_client_create(void)
 {
        struct dm_io_client *client;
+       unsigned min_ios = dm_get_reserved_bio_based_ios();
 
        client = kmalloc(sizeof(*client), GFP_KERNEL);
        if (!client)
                return ERR_PTR(-ENOMEM);
 
-       client->pool = mempool_create_slab_pool(MIN_IOS, _dm_io_cache);
+       client->pool = mempool_create_slab_pool(min_ios, _dm_io_cache);
        if (!client->pool)
                goto bad;
 
-       client->bios = bioset_create(MIN_BIOS, 0);
+       client->bios = bioset_create(min_ios, 0);
        if (!client->bios)
                goto bad;
 
index b759a12..de570a5 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/device-mapper.h>
 
+#include "dm.h"
 #include "dm-path-selector.h"
 #include "dm-uevent.h"
 
@@ -116,8 +117,6 @@ struct dm_mpath_io {
 
 typedef int (*action_fn) (struct pgpath *pgpath);
 
-#define MIN_IOS 256    /* Mempool size */
-
 static struct kmem_cache *_mpio_cache;
 
 static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
@@ -190,6 +189,7 @@ static void free_priority_group(struct priority_group *pg,
 static struct multipath *alloc_multipath(struct dm_target *ti)
 {
        struct multipath *m;
+       unsigned min_ios = dm_get_reserved_rq_based_ios();
 
        m = kzalloc(sizeof(*m), GFP_KERNEL);
        if (m) {
@@ -202,7 +202,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
                INIT_WORK(&m->trigger_event, trigger_event);
                init_waitqueue_head(&m->pg_init_wait);
                mutex_init(&m->work_mutex);
-               m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
+               m->mpio_pool = mempool_create_slab_pool(min_ios, _mpio_cache);
                if (!m->mpio_pool) {
                        kfree(m);
                        return NULL;
@@ -1268,6 +1268,7 @@ static int noretry_error(int error)
        case -EREMOTEIO:
        case -EILSEQ:
        case -ENODATA:
+       case -ENOSPC:
                return 1;
        }
 
@@ -1298,8 +1299,17 @@ static int do_end_io(struct multipath *m, struct request *clone,
        if (!error && !clone->errors)
                return 0;       /* I/O complete */
 
-       if (noretry_error(error))
+       if (noretry_error(error)) {
+               if ((clone->cmd_flags & REQ_WRITE_SAME) &&
+                   !clone->q->limits.max_write_same_sectors) {
+                       struct queue_limits *limits;
+
+                       /* device doesn't really support WRITE SAME, disable it */
+                       limits = dm_get_queue_limits(dm_table_get_md(m->ti->table));
+                       limits->max_write_same_sectors = 0;
+               }
                return error;
+       }
 
        if (mpio->pgpath)
                fail_path(mpio->pgpath);
index 3ac4156..2d2b1b7 100644 (file)
@@ -256,7 +256,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
         */
        INIT_WORK_ONSTACK(&req.work, do_metadata);
        queue_work(ps->metadata_wq, &req.work);
-       flush_work(&req.work);
+       flush_workqueue(ps->metadata_wq);
 
        return req.result;
 }
@@ -269,6 +269,14 @@ static chunk_t area_location(struct pstore *ps, chunk_t area)
        return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);
 }
 
+static void skip_metadata(struct pstore *ps)
+{
+       uint32_t stride = ps->exceptions_per_area + 1;
+       chunk_t next_free = ps->next_free;
+       if (sector_div(next_free, stride) == NUM_SNAPSHOT_HDR_CHUNKS)
+               ps->next_free++;
+}
+
 /*
  * Read or write a metadata area.  Remembering to skip the first
  * chunk which holds the header.
@@ -502,6 +510,8 @@ static int read_exceptions(struct pstore *ps,
 
        ps->current_area--;
 
+       skip_metadata(ps);
+
        return 0;
 }
 
@@ -616,8 +626,6 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
                                        struct dm_exception *e)
 {
        struct pstore *ps = get_info(store);
-       uint32_t stride;
-       chunk_t next_free;
        sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
 
        /* Is there enough room ? */
@@ -630,10 +638,8 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
         * Move onto the next free pending, making sure to take
         * into account the location of the metadata chunks.
         */
-       stride = (ps->exceptions_per_area + 1);
-       next_free = ++ps->next_free;
-       if (sector_div(next_free, stride) == 1)
-               ps->next_free++;
+       ps->next_free++;
+       skip_metadata(ps);
 
        atomic_inc(&ps->pending_count);
        return 0;
index c434e5a..aec57d7 100644 (file)
@@ -725,17 +725,16 @@ static int calc_max_buckets(void)
  */
 static int init_hash_tables(struct dm_snapshot *s)
 {
-       sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets;
+       sector_t hash_size, cow_dev_size, max_buckets;
 
        /*
         * Calculate based on the size of the original volume or
         * the COW volume...
         */
        cow_dev_size = get_dev_size(s->cow->bdev);
-       origin_dev_size = get_dev_size(s->origin->bdev);
        max_buckets = calc_max_buckets();
 
-       hash_size = min(origin_dev_size, cow_dev_size) >> s->store->chunk_shift;
+       hash_size = cow_dev_size >> s->store->chunk_shift;
        hash_size = min(hash_size, max_buckets);
 
        if (hash_size < 64)
index 8ae31e8..3d404c1 100644 (file)
@@ -451,19 +451,26 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
        struct dm_stat_percpu *p;
 
        /*
-        * For strict correctness we should use local_irq_disable/enable
+        * For strict correctness we should use local_irq_save/restore
         * instead of preempt_disable/enable.
         *
-        * This is racy if the driver finishes bios from non-interrupt
-        * context as well as from interrupt context or from more different
-        * interrupts.
+        * preempt_disable/enable is racy if the driver finishes bios
+        * from non-interrupt context as well as from interrupt context
+        * or from more different interrupts.
         *
-        * However, the race only results in not counting some events,
-        * so it is acceptable.
+        * On 64-bit architectures the race only results in not counting some
+        * events, so it is acceptable.  On 32-bit architectures the race could
+        * cause the counter going off by 2^32, so we need to do proper locking
+        * there.
         *
         * part_stat_lock()/part_stat_unlock() have this race too.
         */
+#if BITS_PER_LONG == 32
+       unsigned long flags;
+       local_irq_save(flags);
+#else
        preempt_disable();
+#endif
        p = &s->stat_percpu[smp_processor_id()][entry];
 
        if (!end) {
@@ -478,7 +485,11 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
                p->ticks[idx] += duration;
        }
 
+#if BITS_PER_LONG == 32
+       local_irq_restore(flags);
+#else
        preempt_enable();
+#endif
 }
 
 static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
index ed06342..2c0cf51 100644 (file)
@@ -2095,6 +2095,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
         * them down to the data device.  The thin device's discard
         * processing will cause mappings to be removed from the btree.
         */
+       ti->discard_zeroes_data_unsupported = true;
        if (pf.discard_enabled && pf.discard_passdown) {
                ti->num_discard_bios = 1;
 
@@ -2104,7 +2105,6 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
                 * thin devices' discard limits consistent).
                 */
                ti->discards_supported = true;
-               ti->discard_zeroes_data_unsupported = true;
        }
        ti->private = pt;
 
@@ -2689,8 +2689,16 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
         * They get transferred to the live pool in bind_control_target()
         * called from pool_preresume().
         */
-       if (!pt->adjusted_pf.discard_enabled)
+       if (!pt->adjusted_pf.discard_enabled) {
+               /*
+                * Must explicitly disallow stacking discard limits otherwise the
+                * block layer will stack them if pool's data device has support.
+                * QUEUE_FLAG_DISCARD wouldn't be set but there is no way for the
+                * user to see that, so make sure to set all discard limits to 0.
+                */
+               limits->discard_granularity = 0;
                return;
+       }
 
        disable_passdown_if_not_supported(pt);
 
@@ -2826,10 +2834,10 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
        ti->per_bio_data_size = sizeof(struct dm_thin_endio_hook);
 
        /* In case the pool supports discards, pass them on. */
+       ti->discard_zeroes_data_unsupported = true;
        if (tc->pool->pf.discard_enabled) {
                ti->discards_supported = true;
                ti->num_discard_bios = 1;
-               ti->discard_zeroes_data_unsupported = true;
                /* Discard bios must be split on a block boundary */
                ti->split_discard_bios = true;
        }
index 6a5e9ed..b3e26c7 100644 (file)
@@ -211,10 +211,55 @@ struct dm_md_mempools {
        struct bio_set *bs;
 };
 
-#define MIN_IOS 256
+#define RESERVED_BIO_BASED_IOS         16
+#define RESERVED_REQUEST_BASED_IOS     256
+#define RESERVED_MAX_IOS               1024
 static struct kmem_cache *_io_cache;
 static struct kmem_cache *_rq_tio_cache;
 
+/*
+ * Bio-based DM's mempools' reserved IOs set by the user.
+ */
+static unsigned reserved_bio_based_ios = RESERVED_BIO_BASED_IOS;
+
+/*
+ * Request-based DM's mempools' reserved IOs set by the user.
+ */
+static unsigned reserved_rq_based_ios = RESERVED_REQUEST_BASED_IOS;
+
+static unsigned __dm_get_reserved_ios(unsigned *reserved_ios,
+                                     unsigned def, unsigned max)
+{
+       unsigned ios = ACCESS_ONCE(*reserved_ios);
+       unsigned modified_ios = 0;
+
+       if (!ios)
+               modified_ios = def;
+       else if (ios > max)
+               modified_ios = max;
+
+       if (modified_ios) {
+               (void)cmpxchg(reserved_ios, ios, modified_ios);
+               ios = modified_ios;
+       }
+
+       return ios;
+}
+
+unsigned dm_get_reserved_bio_based_ios(void)
+{
+       return __dm_get_reserved_ios(&reserved_bio_based_ios,
+                                    RESERVED_BIO_BASED_IOS, RESERVED_MAX_IOS);
+}
+EXPORT_SYMBOL_GPL(dm_get_reserved_bio_based_ios);
+
+unsigned dm_get_reserved_rq_based_ios(void)
+{
+       return __dm_get_reserved_ios(&reserved_rq_based_ios,
+                                    RESERVED_REQUEST_BASED_IOS, RESERVED_MAX_IOS);
+}
+EXPORT_SYMBOL_GPL(dm_get_reserved_rq_based_ios);
+
 static int __init local_init(void)
 {
        int r = -ENOMEM;
@@ -2277,6 +2322,17 @@ struct target_type *dm_get_immutable_target_type(struct mapped_device *md)
        return md->immutable_target_type;
 }
 
+/*
+ * The queue_limits are only valid as long as you have a reference
+ * count on 'md'.
+ */
+struct queue_limits *dm_get_queue_limits(struct mapped_device *md)
+{
+       BUG_ON(!atomic_read(&md->holders));
+       return &md->queue->limits;
+}
+EXPORT_SYMBOL_GPL(dm_get_queue_limits);
+
 /*
  * Fully initialize a request-based queue (->elevator, ->request_fn, etc).
  */
@@ -2862,18 +2918,18 @@ struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, u
 
        if (type == DM_TYPE_BIO_BASED) {
                cachep = _io_cache;
-               pool_size = 16;
+               pool_size = dm_get_reserved_bio_based_ios();
                front_pad = roundup(per_bio_data_size, __alignof__(struct dm_target_io)) + offsetof(struct dm_target_io, clone);
        } else if (type == DM_TYPE_REQUEST_BASED) {
                cachep = _rq_tio_cache;
-               pool_size = MIN_IOS;
+               pool_size = dm_get_reserved_rq_based_ios();
                front_pad = offsetof(struct dm_rq_clone_bio_info, clone);
                /* per_bio_data_size is not used. See __bind_mempools(). */
                WARN_ON(per_bio_data_size != 0);
        } else
                goto out;
 
-       pools->io_pool = mempool_create_slab_pool(MIN_IOS, cachep);
+       pools->io_pool = mempool_create_slab_pool(pool_size, cachep);
        if (!pools->io_pool)
                goto out;
 
@@ -2924,6 +2980,13 @@ module_exit(dm_exit);
 
 module_param(major, uint, 0);
 MODULE_PARM_DESC(major, "The major number of the device mapper");
+
+module_param(reserved_bio_based_ios, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(reserved_bio_based_ios, "Reserved IOs in bio-based mempools");
+
+module_param(reserved_rq_based_ios, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(reserved_rq_based_ios, "Reserved IOs in request-based mempools");
+
 MODULE_DESCRIPTION(DM_NAME " driver");
 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
 MODULE_LICENSE("GPL");
index 5e604cc..1d1ad7b 100644 (file)
@@ -184,6 +184,9 @@ void dm_free_md_mempools(struct dm_md_mempools *pools);
 /*
  * Helpers that are used by DM core
  */
+unsigned dm_get_reserved_bio_based_ios(void);
+unsigned dm_get_reserved_rq_based_ios(void);
+
 static inline bool dm_message_test_buffer_overflow(char *result, unsigned maxlen)
 {
        return !maxlen || strlen(result) + 1 >= maxlen;
index adf4d7e..561a65f 100644 (file)
@@ -8111,6 +8111,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
        u64 *p;
        int lo, hi;
        int rv = 1;
+       unsigned long flags;
 
        if (bb->shift < 0)
                /* badblocks are disabled */
@@ -8125,7 +8126,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
                sectors = next - s;
        }
 
-       write_seqlock_irq(&bb->lock);
+       write_seqlock_irqsave(&bb->lock, flags);
 
        p = bb->page;
        lo = 0;
@@ -8241,7 +8242,7 @@ static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
        bb->changed = 1;
        if (!acknowledged)
                bb->unacked_exist = 1;
-       write_sequnlock_irq(&bb->lock);
+       write_sequnlock_irqrestore(&bb->lock, flags);
 
        return rv;
 }
index d60412c..aacf6bf 100644 (file)
@@ -1479,6 +1479,7 @@ static int raid1_spare_active(struct mddev *mddev)
                        }
                }
                if (rdev
+                   && rdev->recovery_offset == MaxSector
                    && !test_bit(Faulty, &rdev->flags)
                    && !test_and_set_bit(In_sync, &rdev->flags)) {
                        count++;
index df7b0a0..73dc8a3 100644 (file)
@@ -1782,6 +1782,7 @@ static int raid10_spare_active(struct mddev *mddev)
                        }
                        sysfs_notify_dirent_safe(tmp->replacement->sysfs_state);
                } else if (tmp->rdev
+                          && tmp->rdev->recovery_offset == MaxSector
                           && !test_bit(Faulty, &tmp->rdev->flags)
                           && !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
                        count++;
index 7ff4f25..f8b9068 100644 (file)
@@ -778,6 +778,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
                        bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
                        bi->bi_io_vec[0].bv_offset = 0;
                        bi->bi_size = STRIPE_SIZE;
+                       /*
+                        * If this is discard request, set bi_vcnt 0. We don't
+                        * want to confuse SCSI because SCSI will replace payload
+                        */
+                       if (rw & REQ_DISCARD)
+                               bi->bi_vcnt = 0;
                        if (rrdev)
                                set_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags);
 
@@ -816,6 +822,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
                        rbi->bi_io_vec[0].bv_len = STRIPE_SIZE;
                        rbi->bi_io_vec[0].bv_offset = 0;
                        rbi->bi_size = STRIPE_SIZE;
+                       /*
+                        * If this is discard request, set bi_vcnt 0. We don't
+                        * want to confuse SCSI because SCSI will replace payload
+                        */
+                       if (rw & REQ_DISCARD)
+                               rbi->bi_vcnt = 0;
                        if (conf->mddev->gendisk)
                                trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev),
                                                      rbi, disk_devt(conf->mddev->gendisk),
@@ -2910,6 +2922,14 @@ static void handle_stripe_clean_event(struct r5conf *conf,
                }
                /* now that discard is done we can proceed with any sync */
                clear_bit(STRIPE_DISCARD, &sh->state);
+               /*
+                * SCSI discard will change some bio fields and the stripe has
+                * no updated data, so remove it from hash list and the stripe
+                * will be reinitialized
+                */
+               spin_lock_irq(&conf->device_lock);
+               remove_hash(sh);
+               spin_unlock_irq(&conf->device_lock);
                if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state))
                        set_bit(STRIPE_HANDLE, &sh->state);
 
index 2521f7e..e79749c 100644 (file)
@@ -912,14 +912,8 @@ static int tda10071_init(struct dvb_frontend *fe)
                { 0xd5, 0x03, 0x03 },
        };
 
-       /* firmware status */
-       ret = tda10071_rd_reg(priv, 0x51, &tmp);
-       if (ret)
-               goto error;
-
-       if (!tmp) {
+       if (priv->warm) {
                /* warm state - wake up device from sleep */
-               priv->warm = 1;
 
                for (i = 0; i < ARRAY_SIZE(tab); i++) {
                        ret = tda10071_wr_reg_mask(priv, tab[i].reg,
@@ -937,7 +931,6 @@ static int tda10071_init(struct dvb_frontend *fe)
                        goto error;
        } else {
                /* cold state - try to download firmware */
-               priv->warm = 0;
 
                /* request the firmware, this will block and timeout */
                ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
index bb0c99d..b06a7e5 100644 (file)
@@ -628,16 +628,13 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)
 
 static const struct v4l2_dv_timings_cap ad9389b_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1200,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 170000000,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+               V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static int ad9389b_s_dv_timings(struct v4l2_subdev *sd,
index 7a57609..7c8d971 100644 (file)
@@ -119,16 +119,14 @@ static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
 
 static const struct v4l2_dv_timings_cap adv7511_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = ADV7511_MAX_WIDTH,
-               .max_height = ADV7511_MAX_HEIGHT,
-               .min_pixelclock = ADV7511_MIN_PIXELCLOCK,
-               .max_pixelclock = ADV7511_MAX_PIXELCLOCK,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, ADV7511_MAX_WIDTH, 0, ADV7511_MAX_HEIGHT,
+               ADV7511_MIN_PIXELCLOCK, ADV7511_MAX_PIXELCLOCK,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+                       V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static inline struct adv7511_state *get_adv7511_state(struct v4l2_subdev *sd)
@@ -1126,6 +1124,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
        state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);
        if (state->i2c_edid == NULL) {
                v4l2_err(sd, "failed to register edid i2c client\n");
+               err = -ENOMEM;
                goto err_entity;
        }
 
@@ -1133,6 +1132,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
        state->work_queue = create_singlethread_workqueue(sd->name);
        if (state->work_queue == NULL) {
                v4l2_err(sd, "could not create workqueue\n");
+               err = -ENOMEM;
                goto err_unreg_cec;
        }
 
index d174890..22f729d 100644 (file)
@@ -546,30 +546,24 @@ static inline bool is_digital_input(struct v4l2_subdev *sd)
 
 static const struct v4l2_dv_timings_cap adv7842_timings_cap_analog = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1200,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 170000000,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+                       V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static const struct v4l2_dv_timings_cap adv7842_timings_cap_digital = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1200,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 225000000,
-               .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000,
+               V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
                        V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
-                       V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM,
-       },
+               V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
+                       V4L2_DV_BT_CAP_CUSTOM)
 };
 
 static inline const struct v4l2_dv_timings_cap *
index a58a8f6..d9f65d7 100644 (file)
@@ -46,14 +46,10 @@ struct ths8200_state {
 
 static const struct v4l2_dv_timings_cap ths8200_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
-       .bt = {
-               .max_width = 1920,
-               .max_height = 1080,
-               .min_pixelclock = 25000000,
-               .max_pixelclock = 148500000,
-               .standards = V4L2_DV_BT_STD_CEA861,
-               .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE,
-       },
+       /* keep this initialization for compatibility with GCC < 4.4.6 */
+       .reserved = { 0 },
+       V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1080, 25000000, 148500000,
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_BT_CAP_PROGRESSIVE)
 };
 
 static inline struct ths8200_state *to_state(struct v4l2_subdev *sd)
index e12bbd8..fb60da8 100644 (file)
@@ -1455,6 +1455,7 @@ static int video_release(struct file *file)
 
        /* stop video capture */
        if (res_check(fh, RESOURCE_VIDEO)) {
+               pm_qos_remove_request(&dev->qos_request);
                videobuf_streamoff(&fh->cap);
                res_free(dev,fh,RESOURCE_VIDEO);
        }
index 15d2396..9b88a46 100644 (file)
@@ -1423,6 +1423,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
        jpeg->vfd_decoder->release      = video_device_release;
        jpeg->vfd_decoder->lock         = &jpeg->lock;
        jpeg->vfd_decoder->v4l2_dev     = &jpeg->v4l2_dev;
+       jpeg->vfd_decoder->vfl_dir      = VFL_DIR_M2M;
 
        ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
        if (ret) {
index 7a9c5e9..4f30341 100644 (file)
@@ -776,7 +776,7 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
        v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
                              &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
 
-       for (i = 0; ARRAY_SIZE(vou_fmt); i++)
+       for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)
                if (vou_fmt[i].pfmt == pix->pixelformat)
                        return 0;
 
index 8f9f621..f975b70 100644 (file)
@@ -266,7 +266,6 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
        struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
        struct idmac_video_param *video = &ichan->params.video;
        const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
-       unsigned long flags;
        dma_cookie_t cookie;
        size_t new_size;
 
@@ -328,7 +327,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
                memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
 #endif
 
-       spin_lock_irqsave(&mx3_cam->lock, flags);
+       spin_lock_irq(&mx3_cam->lock);
        list_add_tail(&buf->queue, &mx3_cam->capture);
 
        if (!mx3_cam->active)
@@ -351,7 +350,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
        if (mx3_cam->active == buf)
                mx3_cam->active = NULL;
 
-       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+       spin_unlock_irq(&mx3_cam->lock);
 error:
        vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
 }
index ad9309d..6c96e48 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include "e4000_priv.h"
+#include <linux/math64.h>
 
 /* write multiple registers */
 static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
@@ -233,7 +234,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
         * or more.
         */
        f_vco = c->frequency * e4000_pll_lut[i].mul;
-       sigma_delta = 0x10000UL * (f_vco % priv->cfg->clock) / priv->cfg->clock;
+       sigma_delta = div_u64(0x10000ULL * (f_vco % priv->cfg->clock), priv->cfg->clock);
        buf[0] = f_vco / priv->cfg->clock;
        buf[1] = (sigma_delta >> 0) & 0xff;
        buf[2] = (sigma_delta >> 8) & 0xff;
index c43c8d3..be77482 100644 (file)
@@ -111,6 +111,13 @@ static const struct dmi_system_id stk_upside_down_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "F3JC")
                }
        },
+       {
+               .ident = "T12Rg-H",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "HCL Infosystems Limited"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "T12Rg-H")
+               }
+       },
        {}
 };
 
index 81695d4..c3bb250 100644 (file)
@@ -2090,6 +2090,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* Microsoft Lifecam NX-3000 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x045e,
+         .idProduct            = 0x0721,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_DEF },
        /* Microsoft Lifecam VX-7000 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2174,6 +2183,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_DEF },
+       /* Dell SP2008WFP Monitor */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x05a9,
+         .idProduct            = 0x2641,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_DEF },
        /* Dell Alienware X51 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
index 594c75e..de0e87f 100644 (file)
@@ -353,7 +353,9 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
                        if (b->m.planes[plane].bytesused > length)
                                return -EINVAL;
-                       if (b->m.planes[plane].data_offset >=
+
+                       if (b->m.planes[plane].data_offset > 0 &&
+                           b->m.planes[plane].data_offset >=
                            b->m.planes[plane].bytesused)
                                return -EINVAL;
                }
index fd56f25..646f08f 100644 (file)
@@ -423,6 +423,39 @@ static inline int vma_is_io(struct vm_area_struct *vma)
        return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
 }
 
+static int vb2_dc_get_user_pfn(unsigned long start, int n_pages,
+       struct vm_area_struct *vma, unsigned long *res)
+{
+       unsigned long pfn, start_pfn, prev_pfn;
+       unsigned int i;
+       int ret;
+
+       if (!vma_is_io(vma))
+               return -EFAULT;
+
+       ret = follow_pfn(vma, start, &pfn);
+       if (ret)
+               return ret;
+
+       start_pfn = pfn;
+       start += PAGE_SIZE;
+
+       for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) {
+               prev_pfn = pfn;
+               ret = follow_pfn(vma, start, &pfn);
+
+               if (ret) {
+                       pr_err("no page for address %lu\n", start);
+                       return ret;
+               }
+               if (pfn != prev_pfn + 1)
+                       return -EINVAL;
+       }
+
+       *res = start_pfn;
+       return 0;
+}
+
 static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
        int n_pages, struct vm_area_struct *vma, int write)
 {
@@ -433,6 +466,9 @@ static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
                        unsigned long pfn;
                        int ret = follow_pfn(vma, start, &pfn);
 
+                       if (!pfn_valid(pfn))
+                               return -EINVAL;
+
                        if (ret) {
                                pr_err("no page for address %lu\n", start);
                                return ret;
@@ -468,16 +504,49 @@ static void vb2_dc_put_userptr(void *buf_priv)
        struct vb2_dc_buf *buf = buf_priv;
        struct sg_table *sgt = buf->dma_sgt;
 
-       dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
-       if (!vma_is_io(buf->vma))
-               vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
+       if (sgt) {
+               dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+               if (!vma_is_io(buf->vma))
+                       vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
 
-       sg_free_table(sgt);
-       kfree(sgt);
+               sg_free_table(sgt);
+               kfree(sgt);
+       }
        vb2_put_vma(buf->vma);
        kfree(buf);
 }
 
+/*
+ * For some kind of reserved memory there might be no struct page available,
+ * so all that can be done to support such 'pages' is to try to convert
+ * pfn to dma address or at the last resort just assume that
+ * dma address == physical address (like it has been assumed in earlier version
+ * of videobuf2-dma-contig
+ */
+
+#ifdef __arch_pfn_to_dma
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
+}
+#elif defined(__pfn_to_bus)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       return (dma_addr_t)__pfn_to_bus(pfn);
+}
+#elif defined(__pfn_to_phys)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       return (dma_addr_t)__pfn_to_phys(pfn);
+}
+#else
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+       /* really, we cannot do anything better at this point */
+       return (dma_addr_t)(pfn) << PAGE_SHIFT;
+}
+#endif
+
 static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        unsigned long size, int write)
 {
@@ -548,6 +617,14 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        /* extract page list from userspace mapping */
        ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write);
        if (ret) {
+               unsigned long pfn;
+               if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
+                       buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn);
+                       buf->size = size;
+                       kfree(pages);
+                       return buf;
+               }
+
                pr_err("failed to get user pages\n");
                goto fail_vma;
        }
index d0fdc13..f6ff711 100644 (file)
@@ -57,6 +57,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
        dev->iamthif_ioctl = false;
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
+       dev->iamthif_stall_timer = 0;
 }
 
 /**
index 6d0282c..cd2033c 100644 (file)
@@ -297,10 +297,13 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
 
        if (cl->reading_state != MEI_READ_COMPLETE &&
            !waitqueue_active(&cl->rx_wait)) {
+
                mutex_unlock(&dev->device_lock);
 
                if (wait_event_interruptible(cl->rx_wait,
-                               (MEI_READ_COMPLETE == cl->reading_state))) {
+                               cl->reading_state == MEI_READ_COMPLETE  ||
+                               mei_cl_is_transitioning(cl))) {
+
                        if (signal_pending(current))
                                return -EINTR;
                        return -ERESTARTSYS;
index 9eb031e..892cc42 100644 (file)
@@ -90,6 +90,12 @@ static inline bool mei_cl_is_connected(struct mei_cl *cl)
                cl->dev->dev_state == MEI_DEV_ENABLED &&
                cl->state == MEI_FILE_CONNECTED);
 }
+static inline bool mei_cl_is_transitioning(struct mei_cl *cl)
+{
+       return (MEI_FILE_INITIALIZING == cl->state ||
+               MEI_FILE_DISCONNECTED == cl->state ||
+               MEI_FILE_DISCONNECTING == cl->state);
+}
 
 bool mei_cl_is_other_connecting(struct mei_cl *cl);
 int mei_cl_disconnect(struct mei_cl *cl);
index 6127ab6..0a04483 100644 (file)
@@ -35,11 +35,15 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
        struct mei_me_client *clients;
        int b;
 
+       dev->me_clients_num = 0;
+       dev->me_client_presentation_num = 0;
+       dev->me_client_index = 0;
+
        /* count how many ME clients we have */
        for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
                dev->me_clients_num++;
 
-       if (dev->me_clients_num <= 0)
+       if (dev->me_clients_num == 0)
                return;
 
        kfree(dev->me_clients);
@@ -221,7 +225,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
        struct hbm_props_request *prop_req;
        const size_t len = sizeof(struct hbm_props_request);
        unsigned long next_client_index;
-       u8 client_num;
+       unsigned long client_num;
 
 
        client_num = dev->me_client_presentation_num;
@@ -677,8 +681,6 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
                if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
                    dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
                                dev->init_clients_timer = 0;
-                               dev->me_client_presentation_num = 0;
-                               dev->me_client_index = 0;
                                mei_hbm_me_cl_allocate(dev);
                                dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
 
index 92c7311..6197018 100644 (file)
@@ -175,6 +175,9 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
                memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
        }
 
+       /* we're already in reset, cancel the init timer */
+       dev->init_clients_timer = 0;
+
        dev->me_clients_num = 0;
        dev->rd_msg_hdr = 0;
        dev->wd_pending = false;
index 173ff09..cabeddd 100644 (file)
@@ -249,19 +249,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                mutex_unlock(&dev->device_lock);
 
                if (wait_event_interruptible(cl->rx_wait,
-                       (MEI_READ_COMPLETE == cl->reading_state ||
-                        MEI_FILE_INITIALIZING == cl->state ||
-                        MEI_FILE_DISCONNECTED == cl->state ||
-                        MEI_FILE_DISCONNECTING == cl->state))) {
+                               MEI_READ_COMPLETE == cl->reading_state ||
+                               mei_cl_is_transitioning(cl))) {
+
                        if (signal_pending(current))
                                return -EINTR;
                        return -ERESTARTSYS;
                }
 
                mutex_lock(&dev->device_lock);
-               if (MEI_FILE_INITIALIZING == cl->state ||
-                   MEI_FILE_DISCONNECTED == cl->state ||
-                   MEI_FILE_DISCONNECTING == cl->state) {
+               if (mei_cl_is_transitioning(cl)) {
                        rets = -EBUSY;
                        goto out;
                }
index 7b918b2..456b322 100644 (file)
@@ -396,9 +396,9 @@ struct mei_device {
        struct mei_me_client *me_clients; /* Note: memory has to be allocated */
        DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX);
        DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
-       u8 me_clients_num;
-       u8 me_client_presentation_num;
-       u8 me_client_index;
+       unsigned long me_clients_num;
+       unsigned long me_client_presentation_num;
+       unsigned long me_client_index;
 
        struct mei_cl wd_cl;
        enum mei_wd_states wd_state;
index 87ed3fb..f344659 100644 (file)
@@ -113,14 +113,14 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {
 };
 
 static const struct of_device_id sh_mobile_sdhi_of_match[] = {
-       { .compatible = "renesas,shmobile-sdhi" },
-       { .compatible = "renesas,sh7372-sdhi" },
-       { .compatible = "renesas,sh73a0-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
-       { .compatible = "renesas,r8a73a4-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
-       { .compatible = "renesas,r8a7740-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
-       { .compatible = "renesas,r8a7778-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
-       { .compatible = "renesas,r8a7779-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
-       { .compatible = "renesas,r8a7790-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
+       { .compatible = "renesas,sdhi-shmobile" },
+       { .compatible = "renesas,sdhi-sh7372" },
+       { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
+       { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
+       { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
+       { .compatible = "renesas,sdhi-r8a7778", .data = &sh_mobile_sdhi_of_cfg[0], },
+       { .compatible = "renesas,sdhi-r8a7779", .data = &sh_mobile_sdhi_of_cfg[0], },
+       { .compatible = "renesas,sdhi-r8a7790", .data = &sh_mobile_sdhi_of_cfg[0], },
        {},
 };
 MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
index 26b14f9..6bc9618 100644 (file)
@@ -168,12 +168,25 @@ static inline int write_disable(struct m25p *flash)
  */
 static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
 {
+       int status;
+       bool need_wren = false;
+
        switch (JEDEC_MFR(jedec_id)) {
-       case CFI_MFR_MACRONIX:
        case CFI_MFR_ST: /* Micron, actually */
+               /* Some Micron need WREN command; all will accept it */
+               need_wren = true;
+       case CFI_MFR_MACRONIX:
        case 0xEF /* winbond */:
+               if (need_wren)
+                       write_enable(flash);
+
                flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
-               return spi_write(flash->spi, flash->command, 1);
+               status = spi_write(flash->spi, flash->command, 1);
+
+               if (need_wren)
+                       write_disable(flash);
+
+               return status;
        default:
                /* Spansion style */
                flash->command[0] = OPCODE_BRWR;
index 59ab069..a9830ff 100644 (file)
@@ -349,7 +349,7 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
 
 int common_nfc_set_geometry(struct gpmi_nand_data *this)
 {
-       return set_geometry_by_ecc_info(this) ? 0 : legacy_set_geometry(this);
+       return legacy_set_geometry(this);
 }
 
 struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
index 7ed4841..d340b2f 100644 (file)
@@ -2869,10 +2869,8 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
 
        len = le16_to_cpu(p->ext_param_page_length) * 16;
        ep = kmalloc(len, GFP_KERNEL);
-       if (!ep) {
-               ret = -ENOMEM;
-               goto ext_out;
-       }
+       if (!ep)
+               return -ENOMEM;
 
        /* Send our own NAND_CMD_PARAM. */
        chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
@@ -2920,7 +2918,7 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
        }
 
        pr_info("ONFI extended param page detected.\n");
-       return 0;
+       ret = 0;
 
 ext_out:
        kfree(ep);
index 5db900d..c28d4e2 100644 (file)
@@ -1236,7 +1236,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_OF
 static struct of_device_id pxa3xx_nand_dt_ids[] = {
        {
                .compatible = "marvell,pxa3xx-nand",
@@ -1284,12 +1283,6 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
 
        return 0;
 }
-#else
-static inline int pxa3xx_nand_probe_dt(struct platform_device *pdev)
-{
-       return 0;
-}
-#endif
 
 static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
@@ -1327,7 +1320,12 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
        for (cs = 0; cs < pdata->num_cs; cs++) {
                struct mtd_info *mtd = info->host[cs]->mtd;
 
-               mtd->name = pdev->name;
+               /*
+                * The mtd name matches the one used in 'mtdparts' kernel
+                * parameter. This name cannot be changed or otherwise
+                * user's mtd partitions configuration would get broken.
+                */
+               mtd->name = "pxa3xx_nand-0";
                info->cs = cs;
                ret = pxa3xx_nand_scan(mtd);
                if (ret) {
index 4c21bf6..5a5d720 100644 (file)
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_BONDING) += bonding.o
 
-bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o
+bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o
 
 proc-$(CONFIG_PROC_FS) += bond_procfs.o
 bonding-objs += $(proc-y)
index 0d8f427..187b1b7 100644 (file)
@@ -135,41 +135,6 @@ static inline struct bonding *__get_bond_by_port(struct port *port)
        return bond_get_bond_by_slave(port->slave);
 }
 
-/**
- * __get_first_port - get the first port in the bond
- * @bond: the bond we're looking at
- *
- * Return the port of the first slave in @bond, or %NULL if it can't be found.
- */
-static inline struct port *__get_first_port(struct bonding *bond)
-{
-       struct slave *first_slave = bond_first_slave(bond);
-
-       return first_slave ? &(SLAVE_AD_INFO(first_slave).port) : NULL;
-}
-
-/**
- * __get_next_port - get the next port in the bond
- * @port: the port we're looking at
- *
- * Return the port of the slave that is next in line of @port's slave in the
- * bond, or %NULL if it can't be found.
- */
-static inline struct port *__get_next_port(struct port *port)
-{
-       struct bonding *bond = __get_bond_by_port(port);
-       struct slave *slave = port->slave, *slave_next;
-
-       // If there's no bond for this port, or this is the last slave
-       if (bond == NULL)
-               return NULL;
-       slave_next = bond_next_slave(bond, slave);
-       if (!slave_next || bond_is_first_slave(bond, slave_next))
-               return NULL;
-
-       return &(SLAVE_AD_INFO(slave_next).port);
-}
-
 /**
  * __get_first_agg - get the first aggregator in the bond
  * @bond: the bond we're looking at
@@ -190,28 +155,6 @@ static inline struct aggregator *__get_first_agg(struct port *port)
        return first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL;
 }
 
-/**
- * __get_next_agg - get the next aggregator in the bond
- * @aggregator: the aggregator we're looking at
- *
- * Return the aggregator of the slave that is next in line of @aggregator's
- * slave in the bond, or %NULL if it can't be found.
- */
-static inline struct aggregator *__get_next_agg(struct aggregator *aggregator)
-{
-       struct slave *slave = aggregator->slave, *slave_next;
-       struct bonding *bond = bond_get_bond_by_slave(slave);
-
-       // If there's no bond for this aggregator, or this is the last slave
-       if (bond == NULL)
-               return NULL;
-       slave_next = bond_next_slave(bond, slave);
-       if (!slave_next || bond_is_first_slave(bond, slave_next))
-               return NULL;
-
-       return &(SLAVE_AD_INFO(slave_next).aggregator);
-}
-
 /*
  * __agg_has_partner
  *
@@ -755,16 +698,15 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
  */
 static struct aggregator *__get_active_agg(struct aggregator *aggregator)
 {
-       struct aggregator *retval = NULL;
+       struct bonding *bond = aggregator->slave->bond;
+       struct list_head *iter;
+       struct slave *slave;
 
-       for (; aggregator; aggregator = __get_next_agg(aggregator)) {
-               if (aggregator->is_active) {
-                       retval = aggregator;
-                       break;
-               }
-       }
+       bond_for_each_slave(bond, slave, iter)
+               if (SLAVE_AD_INFO(slave).aggregator.is_active)
+                       return &(SLAVE_AD_INFO(slave).aggregator);
 
-       return retval;
+       return NULL;
 }
 
 /**
@@ -1274,12 +1216,17 @@ static void ad_port_selection_logic(struct port *port)
 {
        struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator;
        struct port *last_port = NULL, *curr_port;
+       struct list_head *iter;
+       struct bonding *bond;
+       struct slave *slave;
        int found = 0;
 
        // if the port is already Selected, do nothing
        if (port->sm_vars & AD_PORT_SELECTED)
                return;
 
+       bond = __get_bond_by_port(port);
+
        // if the port is connected to other aggregator, detach it
        if (port->aggregator) {
                // detach the port from its former aggregator
@@ -1320,8 +1267,8 @@ static void ad_port_selection_logic(struct port *port)
                }
        }
        // search on all aggregators for a suitable aggregator for this port
-       for (aggregator = __get_first_agg(port); aggregator;
-            aggregator = __get_next_agg(aggregator)) {
+       bond_for_each_slave(bond, slave, iter) {
+               aggregator = &(SLAVE_AD_INFO(slave).aggregator);
 
                // keep a free aggregator for later use(if needed)
                if (!aggregator->lag_ports) {
@@ -1515,19 +1462,23 @@ static int agg_device_up(const struct aggregator *agg)
 static void ad_agg_selection_logic(struct aggregator *agg)
 {
        struct aggregator *best, *active, *origin;
+       struct bonding *bond = agg->slave->bond;
+       struct list_head *iter;
+       struct slave *slave;
        struct port *port;
 
        origin = agg;
        active = __get_active_agg(agg);
        best = (active && agg_device_up(active)) ? active : NULL;
 
-       do {
+       bond_for_each_slave(bond, slave, iter) {
+               agg = &(SLAVE_AD_INFO(slave).aggregator);
+
                agg->is_active = 0;
 
                if (agg->num_of_ports && agg_device_up(agg))
                        best = ad_agg_selection_test(best, agg);
-
-       } while ((agg = __get_next_agg(agg)));
+       }
 
        if (best &&
            __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) {
@@ -1565,8 +1516,8 @@ static void ad_agg_selection_logic(struct aggregator *agg)
                         best->lag_ports, best->slave,
                         best->slave ? best->slave->dev->name : "NULL");
 
-               for (agg = __get_first_agg(best->lag_ports); agg;
-                    agg = __get_next_agg(agg)) {
+               bond_for_each_slave(bond, slave, iter) {
+                       agg = &(SLAVE_AD_INFO(slave).aggregator);
 
                        pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
                                 agg->aggregator_identifier, agg->num_of_ports,
@@ -1614,13 +1565,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
                }
        }
 
-       if (origin->slave) {
-               struct bonding *bond;
-
-               bond = bond_get_bond_by_slave(origin->slave);
-               if (bond)
-                       bond_3ad_set_carrier(bond);
-       }
+       bond_3ad_set_carrier(bond);
 }
 
 /**
@@ -1969,6 +1914,9 @@ void bond_3ad_unbind_slave(struct slave *slave)
        struct port *port, *prev_port, *temp_port;
        struct aggregator *aggregator, *new_aggregator, *temp_aggregator;
        int select_new_active_agg = 0;
+       struct bonding *bond = slave->bond;
+       struct slave *slave_iter;
+       struct list_head *iter;
 
        // find the aggregator related to this slave
        aggregator = &(SLAVE_AD_INFO(slave).aggregator);
@@ -1998,14 +1946,16 @@ void bond_3ad_unbind_slave(struct slave *slave)
                // reason to search for new aggregator, and that we will find one
                if ((aggregator->lag_ports != port) || (aggregator->lag_ports->next_port_in_aggregator)) {
                        // find new aggregator for the related port(s)
-                       new_aggregator = __get_first_agg(port);
-                       for (; new_aggregator; new_aggregator = __get_next_agg(new_aggregator)) {
+                       bond_for_each_slave(bond, slave_iter, iter) {
+                               new_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
                                // if the new aggregator is empty, or it is connected to our port only
                                if (!new_aggregator->lag_ports
                                    || ((new_aggregator->lag_ports == port)
                                        && !new_aggregator->lag_ports->next_port_in_aggregator))
                                        break;
                        }
+                       if (!slave_iter)
+                               new_aggregator = NULL;
                        // if new aggregator found, copy the aggregator's parameters
                        // and connect the related lag_ports to the new aggregator
                        if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) {
@@ -2056,15 +2006,17 @@ void bond_3ad_unbind_slave(struct slave *slave)
                                pr_info("%s: Removing an active aggregator\n",
                                        slave->bond->dev->name);
                                // select new active aggregator
-                               ad_agg_selection_logic(__get_first_agg(port));
+                               temp_aggregator = __get_first_agg(port);
+                               if (temp_aggregator)
+                                       ad_agg_selection_logic(temp_aggregator);
                        }
                }
        }
 
        pr_debug("Unbinding port %d\n", port->actor_port_number);
        // find the aggregator that this port is connected to
-       temp_aggregator = __get_first_agg(port);
-       for (; temp_aggregator; temp_aggregator = __get_next_agg(temp_aggregator)) {
+       bond_for_each_slave(bond, slave_iter, iter) {
+               temp_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
                prev_port = NULL;
                // search the port in the aggregator's related ports
                for (temp_port = temp_aggregator->lag_ports; temp_port;
@@ -2111,19 +2063,24 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
 {
        struct bonding *bond = container_of(work, struct bonding,
                                            ad_work.work);
-       struct port *port;
        struct aggregator *aggregator;
+       struct list_head *iter;
+       struct slave *slave;
+       struct port *port;
 
        read_lock(&bond->lock);
 
        //check if there are any slaves
-       if (list_empty(&bond->slave_list))
+       if (!bond_has_slaves(bond))
                goto re_arm;
 
        // check if agg_select_timer timer after initialize is timed out
        if (BOND_AD_INFO(bond).agg_select_timer && !(--BOND_AD_INFO(bond).agg_select_timer)) {
+               slave = bond_first_slave(bond);
+               port = slave ? &(SLAVE_AD_INFO(slave).port) : NULL;
+
                // select the active aggregator for the bond
-               if ((port = __get_first_port(bond))) {
+               if (port) {
                        if (!port->slave) {
                                pr_warning("%s: Warning: bond's first port is uninitialized\n",
                                           bond->dev->name);
@@ -2137,7 +2094,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
        }
 
        // for each port run the state machines
-       for (port = __get_first_port(bond); port; port = __get_next_port(port)) {
+       bond_for_each_slave(bond, slave, iter) {
+               port = &(SLAVE_AD_INFO(slave).port);
                if (!port->slave) {
                        pr_warning("%s: Warning: Found an uninitialized port\n",
                                   bond->dev->name);
@@ -2382,9 +2340,12 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
                                   struct ad_info *ad_info)
 {
        struct aggregator *aggregator = NULL;
+       struct list_head *iter;
+       struct slave *slave;
        struct port *port;
 
-       for (port = __get_first_port(bond); port; port = __get_next_port(port)) {
+       bond_for_each_slave_rcu(bond, slave, iter) {
+               port = &(SLAVE_AD_INFO(slave).port);
                if (port->aggregator && port->aggregator->is_active) {
                        aggregator = port->aggregator;
                        break;
@@ -2408,25 +2369,25 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
 {
        int ret;
 
-       read_lock(&bond->lock);
+       rcu_read_lock();
        ret = __bond_3ad_get_active_agg_info(bond, ad_info);
-       read_unlock(&bond->lock);
+       rcu_read_unlock();
 
        return ret;
 }
 
 int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 {
-       struct slave *slave, *start_at;
        struct bonding *bond = netdev_priv(dev);
-       int slave_agg_no;
-       int slaves_in_agg;
-       int agg_id;
-       int i;
+       struct slave *slave, *first_ok_slave;
+       struct aggregator *agg;
        struct ad_info ad_info;
+       struct list_head *iter;
+       int slaves_in_agg;
+       int slave_agg_no;
        int res = 1;
+       int agg_id;
 
-       read_lock(&bond->lock);
        if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
                pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n",
                         dev->name);
@@ -2437,20 +2398,28 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
        agg_id = ad_info.aggregator_id;
 
        if (slaves_in_agg == 0) {
-               /*the aggregator is empty*/
                pr_debug("%s: Error: active aggregator is empty\n", dev->name);
                goto out;
        }
 
-       slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg);
+       slave_agg_no = bond_xmit_hash(bond, skb, slaves_in_agg);
+       first_ok_slave = NULL;
 
-       bond_for_each_slave(bond, slave) {
-               struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
+       bond_for_each_slave_rcu(bond, slave, iter) {
+               agg = SLAVE_AD_INFO(slave).port.aggregator;
+               if (!agg || agg->aggregator_identifier != agg_id)
+                       continue;
 
-               if (agg && (agg->aggregator_identifier == agg_id)) {
+               if (slave_agg_no >= 0) {
+                       if (!first_ok_slave && SLAVE_IS_OK(slave))
+                               first_ok_slave = slave;
                        slave_agg_no--;
-                       if (slave_agg_no < 0)
-                               break;
+                       continue;
+               }
+
+               if (SLAVE_IS_OK(slave)) {
+                       res = bond_dev_queue_xmit(bond, skb, slave->dev);
+                       goto out;
                }
        }
 
@@ -2460,23 +2429,12 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
                goto out;
        }
 
-       start_at = slave;
-
-       bond_for_each_slave_from(bond, slave, i, start_at) {
-               int slave_agg_id = 0;
-               struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
-
-               if (agg)
-                       slave_agg_id = agg->aggregator_identifier;
-
-               if (SLAVE_IS_OK(slave) && agg && (slave_agg_id == agg_id)) {
-                       res = bond_dev_queue_xmit(bond, skb, slave->dev);
-                       break;
-               }
-       }
+       /* we couldn't find any suitable slave after the agg_no, so use the
+        * first suitable found, if found. */
+       if (first_ok_slave)
+               res = bond_dev_queue_xmit(bond, skb, first_ok_slave->dev);
 
 out:
-       read_unlock(&bond->lock);
        if (res) {
                /* no suitable interface, frame not sent */
                kfree_skb(skb);
@@ -2515,11 +2473,12 @@ int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
 void bond_3ad_update_lacp_rate(struct bonding *bond)
 {
        struct port *port = NULL;
+       struct list_head *iter;
        struct slave *slave;
        int lacp_fast;
 
        lacp_fast = bond->params.lacp_fast;
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                port = &(SLAVE_AD_INFO(slave).port);
                __get_state_machine_lock(port);
                if (lacp_fast)
index f428ef5..0287240 100644 (file)
@@ -223,13 +223,14 @@ static long long compute_gap(struct slave *slave)
 static struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
 {
        struct slave *slave, *least_loaded;
+       struct list_head *iter;
        long long max_gap;
 
        least_loaded = NULL;
        max_gap = LLONG_MIN;
 
        /* Find the slave with the largest gap */
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave_rcu(bond, slave, iter) {
                if (SLAVE_IS_OK(slave)) {
                        long long gap = compute_gap(slave);
 
@@ -382,30 +383,64 @@ out:
 static struct slave *rlb_next_rx_slave(struct bonding *bond)
 {
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
-       struct slave *rx_slave, *slave, *start_at;
-       int i = 0;
+       struct slave *before = NULL, *rx_slave = NULL, *slave;
+       struct list_head *iter;
+       bool found = false;
 
-       if (bond_info->next_rx_slave)
-               start_at = bond_info->next_rx_slave;
-       else
-               start_at = bond_first_slave(bond);
+       bond_for_each_slave(bond, slave, iter) {
+               if (!SLAVE_IS_OK(slave))
+                       continue;
+               if (!found) {
+                       if (!before || before->speed < slave->speed)
+                               before = slave;
+               } else {
+                       if (!rx_slave || rx_slave->speed < slave->speed)
+                               rx_slave = slave;
+               }
+               if (slave == bond_info->rx_slave)
+                       found = true;
+       }
+       /* we didn't find anything after the current or we have something
+        * better before and up to the current slave
+        */
+       if (!rx_slave || (before && rx_slave->speed < before->speed))
+               rx_slave = before;
 
-       rx_slave = NULL;
+       if (rx_slave)
+               bond_info->rx_slave = rx_slave;
 
-       bond_for_each_slave_from(bond, slave, i, start_at) {
-               if (SLAVE_IS_OK(slave)) {
-                       if (!rx_slave) {
-                               rx_slave = slave;
-                       } else if (slave->speed > rx_slave->speed) {
+       return rx_slave;
+}
+
+/* Caller must hold rcu_read_lock() for read */
+static struct slave *__rlb_next_rx_slave(struct bonding *bond)
+{
+       struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+       struct slave *before = NULL, *rx_slave = NULL, *slave;
+       struct list_head *iter;
+       bool found = false;
+
+       bond_for_each_slave_rcu(bond, slave, iter) {
+               if (!SLAVE_IS_OK(slave))
+                       continue;
+               if (!found) {
+                       if (!before || before->speed < slave->speed)
+                               before = slave;
+               } else {
+                       if (!rx_slave || rx_slave->speed < slave->speed)
                                rx_slave = slave;
-                       }
                }
+               if (slave == bond_info->rx_slave)
+                       found = true;
        }
+       /* we didn't find anything after the current or we have something
+        * better before and up to the current slave
+        */
+       if (!rx_slave || (before && rx_slave->speed < before->speed))
+               rx_slave = before;
 
-       if (rx_slave) {
-               slave = bond_next_slave(bond, rx_slave);
-               bond_info->next_rx_slave = slave;
-       }
+       if (rx_slave)
+               bond_info->rx_slave = rx_slave;
 
        return rx_slave;
 }
@@ -626,12 +661,14 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
 {
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
        struct arp_pkt *arp = arp_pkt(skb);
-       struct slave *assigned_slave;
+       struct slave *assigned_slave, *curr_active_slave;
        struct rlb_client_info *client_info;
        u32 hash_index = 0;
 
        _lock_rx_hashtbl(bond);
 
+       curr_active_slave = rcu_dereference(bond->curr_active_slave);
+
        hash_index = _simple_hash((u8 *)&arp->ip_dst, sizeof(arp->ip_dst));
        client_info = &(bond_info->rx_hashtbl[hash_index]);
 
@@ -656,14 +693,14 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
                         * that the new client can be assigned to this entry.
                         */
                        if (bond->curr_active_slave &&
-                           client_info->slave != bond->curr_active_slave) {
-                               client_info->slave = bond->curr_active_slave;
+                           client_info->slave != curr_active_slave) {
+                               client_info->slave = curr_active_slave;
                                rlb_update_client(client_info);
                        }
                }
        }
        /* assign a new slave */
-       assigned_slave = rlb_next_rx_slave(bond);
+       assigned_slave = __rlb_next_rx_slave(bond);
 
        if (assigned_slave) {
                if (!(client_info->assigned &&
@@ -726,7 +763,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
        /* Don't modify or load balance ARPs that do not originate locally
         * (e.g.,arrive via a bridge).
         */
-       if (!bond_slave_has_mac(bond, arp->mac_src))
+       if (!bond_slave_has_mac_rcu(bond, arp->mac_src))
                return NULL;
 
        if (arp->op_code == htons(ARPOP_REPLY)) {
@@ -1019,7 +1056,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
 
        /* loop through vlans and send one packet for each */
        rcu_read_lock();
-       netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) {
+       netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
                if (upper->priv_flags & IFF_802_1Q_VLAN)
                        alb_send_lp_vid(slave, mac_addr,
                                        vlan_dev_vlan_id(upper));
@@ -1172,10 +1209,11 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
  */
 static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave)
 {
-       struct slave *tmp_slave1, *free_mac_slave = NULL;
        struct slave *has_bond_addr = bond->curr_active_slave;
+       struct slave *tmp_slave1, *free_mac_slave = NULL;
+       struct list_head *iter;
 
-       if (list_empty(&bond->slave_list)) {
+       if (!bond_has_slaves(bond)) {
                /* this is the first slave */
                return 0;
        }
@@ -1196,7 +1234,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
        /* The slave's address is equal to the address of the bond.
         * Search for a spare address in the bond for this slave.
         */
-       bond_for_each_slave(bond, tmp_slave1) {
+       bond_for_each_slave(bond, tmp_slave1, iter) {
                if (!bond_slave_has_mac(bond, tmp_slave1->perm_hwaddr)) {
                        /* no slave has tmp_slave1's perm addr
                         * as its curr addr
@@ -1246,15 +1284,16 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
  */
 static int alb_set_mac_address(struct bonding *bond, void *addr)
 {
-       char tmp_addr[ETH_ALEN];
-       struct slave *slave;
+       struct slave *slave, *rollback_slave;
+       struct list_head *iter;
        struct sockaddr sa;
+       char tmp_addr[ETH_ALEN];
        int res;
 
        if (bond->alb_info.rlb_enabled)
                return 0;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                /* save net_device's current hw address */
                memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN);
 
@@ -1274,10 +1313,12 @@ unwind:
        sa.sa_family = bond->dev->type;
 
        /* unwind from head to the slave that failed */
-       bond_for_each_slave_continue_reverse(bond, slave) {
-               memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN);
-               dev_set_mac_address(slave->dev, &sa);
-               memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN);
+       bond_for_each_slave(bond, rollback_slave, iter) {
+               if (rollback_slave == slave)
+                       break;
+               memcpy(tmp_addr, rollback_slave->dev->dev_addr, ETH_ALEN);
+               dev_set_mac_address(rollback_slave->dev, &sa);
+               memcpy(rollback_slave->dev->dev_addr, tmp_addr, ETH_ALEN);
        }
 
        return res;
@@ -1337,11 +1378,6 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        skb_reset_mac_header(skb);
        eth_data = eth_hdr(skb);
 
-       /* make sure that the curr_active_slave do not change during tx
-        */
-       read_lock(&bond->lock);
-       read_lock(&bond->curr_slave_lock);
-
        switch (ntohs(skb->protocol)) {
        case ETH_P_IP: {
                const struct iphdr *iph = ip_hdr(skb);
@@ -1423,12 +1459,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
 
        if (!tx_slave) {
                /* unbalanced or unassigned, send through primary */
-               tx_slave = bond->curr_active_slave;
+               tx_slave = rcu_dereference(bond->curr_active_slave);
                bond_info->unbalanced_load += skb->len;
        }
 
        if (tx_slave && SLAVE_IS_OK(tx_slave)) {
-               if (tx_slave != bond->curr_active_slave) {
+               if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
                        memcpy(eth_data->h_source,
                               tx_slave->dev->dev_addr,
                               ETH_ALEN);
@@ -1443,8 +1479,6 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                }
        }
 
-       read_unlock(&bond->curr_slave_lock);
-       read_unlock(&bond->lock);
        if (res) {
                /* no suitable interface, frame not sent */
                kfree_skb(skb);
@@ -1458,11 +1492,12 @@ void bond_alb_monitor(struct work_struct *work)
        struct bonding *bond = container_of(work, struct bonding,
                                            alb_work.work);
        struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+       struct list_head *iter;
        struct slave *slave;
 
        read_lock(&bond->lock);
 
-       if (list_empty(&bond->slave_list)) {
+       if (!bond_has_slaves(bond)) {
                bond_info->tx_rebalance_counter = 0;
                bond_info->lp_counter = 0;
                goto re_arm;
@@ -1480,7 +1515,7 @@ void bond_alb_monitor(struct work_struct *work)
                 */
                read_lock(&bond->curr_slave_lock);
 
-               bond_for_each_slave(bond, slave)
+               bond_for_each_slave(bond, slave, iter)
                        alb_send_learning_packets(slave, slave->dev->dev_addr);
 
                read_unlock(&bond->curr_slave_lock);
@@ -1493,7 +1528,7 @@ void bond_alb_monitor(struct work_struct *work)
 
                read_lock(&bond->curr_slave_lock);
 
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave(bond, slave, iter) {
                        tlb_clear_slave(bond, slave, 1);
                        if (slave == bond->curr_active_slave) {
                                SLAVE_TLB_INFO(slave).load =
@@ -1599,13 +1634,13 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
  */
 void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
 {
-       if (!list_empty(&bond->slave_list))
+       if (bond_has_slaves(bond))
                alb_change_hw_addr_on_detach(bond, slave);
 
        tlb_clear_slave(bond, slave, 0);
 
        if (bond->alb_info.rlb_enabled) {
-               bond->alb_info.next_rx_slave = NULL;
+               bond->alb_info.rx_slave = NULL;
                rlb_clear_slave(bond, slave);
        }
 }
@@ -1669,7 +1704,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        swap_slave = bond->curr_active_slave;
        rcu_assign_pointer(bond->curr_active_slave, new_slave);
 
-       if (!new_slave || list_empty(&bond->slave_list))
+       if (!new_slave || !bond_has_slaves(bond))
                return;
 
        /* set the new curr_active_slave to the bonds mac address
@@ -1692,6 +1727,23 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
 
        ASSERT_RTNL();
 
+       /* in TLB mode, the slave might flip down/up with the old dev_addr,
+        * and thus filter bond->dev_addr's packets, so force bond's mac
+        */
+       if (bond->params.mode == BOND_MODE_TLB) {
+               struct sockaddr sa;
+               u8 tmp_addr[ETH_ALEN];
+
+               memcpy(tmp_addr, new_slave->dev->dev_addr, ETH_ALEN);
+
+               memcpy(sa.sa_data, bond->dev->dev_addr, bond->dev->addr_len);
+               sa.sa_family = bond->dev->type;
+               /* we don't care if it can't change its mac, best effort */
+               dev_set_mac_address(new_slave->dev, &sa);
+
+               memcpy(new_slave->dev->dev_addr, tmp_addr, ETH_ALEN);
+       }
+
        /* curr_active_slave must be set before calling alb_swap_mac_addr */
        if (swap_slave) {
                /* swap mac address */
index c5eff5d..4226044 100644 (file)
@@ -154,9 +154,7 @@ struct alb_bond_info {
        u8                      rx_ntt; /* flag - need to transmit
                                         * to all rx clients
                                         */
-       struct slave            *next_rx_slave;/* next slave to be assigned
-                                               * to a new rx client for
-                                               */
+       struct slave            *rx_slave;/* last slave to xmit from */
        u8                      primary_is_promisc;        /* boolean */
        u32                     rlb_promisc_timeout_counter;/* counts primary
                                                             * promiscuity time
index 55bbb8b..4dd5ee2 100644 (file)
@@ -78,6 +78,8 @@
 #include <net/netns/generic.h>
 #include <net/pkt_sched.h>
 #include <linux/rculist.h>
+#include <net/flow_keys.h>
+#include <linux/reciprocal_div.h>
 #include "bonding.h"
 #include "bond_3ad.h"
 #include "bond_alb.h"
@@ -110,6 +112,7 @@ static char *fail_over_mac;
 static int all_slaves_active;
 static struct bond_params bonding_defaults;
 static int resend_igmp = BOND_DEFAULT_RESEND_IGMP;
+static int packets_per_slave = 1;
 
 module_param(max_bonds, int, 0);
 MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
@@ -159,7 +162,8 @@ MODULE_PARM_DESC(min_links, "Minimum number of available links before turning on
 module_param(xmit_hash_policy, charp, 0);
 MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; "
                                   "0 for layer 2 (default), 1 for layer 3+4, "
-                                  "2 for layer 2+3");
+                                  "2 for layer 2+3, 3 for encap layer 2+3, "
+                                  "4 for encap layer 3+4");
 module_param(arp_interval, int, 0);
 MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
 module_param_array(arp_ip_target, charp, NULL, 0);
@@ -181,6 +185,10 @@ MODULE_PARM_DESC(all_slaves_active, "Keep all frames received on an interface"
 module_param(resend_igmp, int, 0);
 MODULE_PARM_DESC(resend_igmp, "Number of IGMP membership reports to send on "
                              "link failure");
+module_param(packets_per_slave, int, 0);
+MODULE_PARM_DESC(packets_per_slave, "Packets to send per slave in balance-rr "
+                                   "mode; 0 for a random slave, 1 packet per "
+                                   "slave (default), >1 packets per slave.");
 
 /*----------------------------- Global variables ----------------------------*/
 
@@ -217,6 +225,8 @@ const struct bond_parm_tbl xmit_hashtype_tbl[] = {
 {      "layer2",               BOND_XMIT_POLICY_LAYER2},
 {      "layer3+4",             BOND_XMIT_POLICY_LAYER34},
 {      "layer2+3",             BOND_XMIT_POLICY_LAYER23},
+{      "encap2+3",             BOND_XMIT_POLICY_ENCAP23},
+{      "encap3+4",             BOND_XMIT_POLICY_ENCAP34},
 {      NULL,                   -1},
 };
 
@@ -332,10 +342,11 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev,
                                __be16 proto, u16 vid)
 {
        struct bonding *bond = netdev_priv(bond_dev);
-       struct slave *slave;
+       struct slave *slave, *rollback_slave;
+       struct list_head *iter;
        int res;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                res = vlan_vid_add(slave->dev, proto, vid);
                if (res)
                        goto unwind;
@@ -344,9 +355,13 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev,
        return 0;
 
 unwind:
-       /* unwind from the slave that failed */
-       bond_for_each_slave_continue_reverse(bond, slave)
-               vlan_vid_del(slave->dev, proto, vid);
+       /* unwind to the slave that failed */
+       bond_for_each_slave(bond, rollback_slave, iter) {
+               if (rollback_slave == slave)
+                       break;
+
+               vlan_vid_del(rollback_slave->dev, proto, vid);
+       }
 
        return res;
 }
@@ -360,9 +375,10 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
                                 __be16 proto, u16 vid)
 {
        struct bonding *bond = netdev_priv(bond_dev);
+       struct list_head *iter;
        struct slave *slave;
 
-       bond_for_each_slave(bond, slave)
+       bond_for_each_slave(bond, slave, iter)
                vlan_vid_del(slave->dev, proto, vid);
 
        if (bond_is_lb(bond))
@@ -382,15 +398,16 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
  */
 static int bond_set_carrier(struct bonding *bond)
 {
+       struct list_head *iter;
        struct slave *slave;
 
-       if (list_empty(&bond->slave_list))
+       if (!bond_has_slaves(bond))
                goto down;
 
        if (bond->params.mode == BOND_MODE_8023AD)
                return bond_3ad_set_carrier(bond);
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (slave->link == BOND_LINK_UP) {
                        if (!netif_carrier_ok(bond->dev)) {
                                netif_carrier_on(bond->dev);
@@ -522,7 +539,9 @@ static int bond_check_dev_link(struct bonding *bond,
  */
 static int bond_set_promiscuity(struct bonding *bond, int inc)
 {
+       struct list_head *iter;
        int err = 0;
+
        if (USES_PRIMARY(bond->params.mode)) {
                /* write lock already acquired */
                if (bond->curr_active_slave) {
@@ -532,7 +551,7 @@ static int bond_set_promiscuity(struct bonding *bond, int inc)
        } else {
                struct slave *slave;
 
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave(bond, slave, iter) {
                        err = dev_set_promiscuity(slave->dev, inc);
                        if (err)
                                return err;
@@ -546,7 +565,9 @@ static int bond_set_promiscuity(struct bonding *bond, int inc)
  */
 static int bond_set_allmulti(struct bonding *bond, int inc)
 {
+       struct list_head *iter;
        int err = 0;
+
        if (USES_PRIMARY(bond->params.mode)) {
                /* write lock already acquired */
                if (bond->curr_active_slave) {
@@ -556,7 +577,7 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
        } else {
                struct slave *slave;
 
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave(bond, slave, iter) {
                        err = dev_set_allmulti(slave->dev, inc);
                        if (err)
                                return err;
@@ -774,43 +795,24 @@ static bool bond_should_change_active(struct bonding *bond)
 /**
  * find_best_interface - select the best available slave to be the active one
  * @bond: our bonding struct
- *
- * Warning: Caller must hold curr_slave_lock for writing.
  */
 static struct slave *bond_find_best_slave(struct bonding *bond)
 {
-       struct slave *new_active, *old_active;
-       struct slave *bestslave = NULL;
+       struct slave *slave, *bestslave = NULL;
+       struct list_head *iter;
        int mintime = bond->params.updelay;
-       int i;
 
-       new_active = bond->curr_active_slave;
-
-       if (!new_active) { /* there were no active slaves left */
-               new_active = bond_first_slave(bond);
-               if (!new_active)
-                       return NULL; /* still no slave, return NULL */
-       }
-
-       if ((bond->primary_slave) &&
-           bond->primary_slave->link == BOND_LINK_UP &&
-           bond_should_change_active(bond)) {
-               new_active = bond->primary_slave;
-       }
-
-       /* remember where to stop iterating over the slaves */
-       old_active = new_active;
-
-       bond_for_each_slave_from(bond, new_active, i, old_active) {
-               if (new_active->link == BOND_LINK_UP) {
-                       return new_active;
-               } else if (new_active->link == BOND_LINK_BACK &&
-                          IS_UP(new_active->dev)) {
-                       /* link up, but waiting for stabilization */
-                       if (new_active->delay < mintime) {
-                               mintime = new_active->delay;
-                               bestslave = new_active;
-                       }
+       if (bond->primary_slave && bond->primary_slave->link == BOND_LINK_UP &&
+           bond_should_change_active(bond))
+               return bond->primary_slave;
+
+       bond_for_each_slave(bond, slave, iter) {
+               if (slave->link == BOND_LINK_UP)
+                       return slave;
+               if (slave->link == BOND_LINK_BACK && IS_UP(slave->dev) &&
+                   slave->delay < mintime) {
+                       mintime = slave->delay;
+                       bestslave = slave;
                }
        }
 
@@ -971,35 +973,6 @@ void bond_select_active_slave(struct bonding *bond)
        }
 }
 
-/*--------------------------- slave list handling ---------------------------*/
-
-/*
- * This function attaches the slave to the end of list.
- *
- * bond->lock held for writing by caller.
- */
-static void bond_attach_slave(struct bonding *bond, struct slave *new_slave)
-{
-       list_add_tail_rcu(&new_slave->list, &bond->slave_list);
-       bond->slave_cnt++;
-}
-
-/*
- * This function detaches the slave from the list.
- * WARNING: no check is made to verify if the slave effectively
- * belongs to <bond>.
- * Nothing is freed on return, structures are just unchained.
- * If any slave pointer in bond was pointing to <slave>,
- * it should be changed by the calling function.
- *
- * bond->lock held for writing by caller.
- */
-static void bond_detach_slave(struct bonding *bond, struct slave *slave)
-{
-       list_del_rcu(&slave->list);
-       bond->slave_cnt--;
-}
-
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static inline int slave_enable_netpoll(struct slave *slave)
 {
@@ -1046,9 +1019,10 @@ static void bond_poll_controller(struct net_device *bond_dev)
 static void bond_netpoll_cleanup(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
+       struct list_head *iter;
        struct slave *slave;
 
-       bond_for_each_slave(bond, slave)
+       bond_for_each_slave(bond, slave, iter)
                if (IS_UP(slave->dev))
                        slave_disable_netpoll(slave);
 }
@@ -1056,10 +1030,11 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
 static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp)
 {
        struct bonding *bond = netdev_priv(dev);
+       struct list_head *iter;
        struct slave *slave;
        int err = 0;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                err = slave_enable_netpoll(slave);
                if (err) {
                        bond_netpoll_cleanup(dev);
@@ -1087,10 +1062,11 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
                                           netdev_features_t features)
 {
        struct bonding *bond = netdev_priv(dev);
+       struct list_head *iter;
        netdev_features_t mask;
        struct slave *slave;
 
-       if (list_empty(&bond->slave_list)) {
+       if (!bond_has_slaves(bond)) {
                /* Disable adding VLANs to empty bond. But why? --mq */
                features |= NETIF_F_VLAN_CHALLENGED;
                return features;
@@ -1100,7 +1076,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
        features &= ~NETIF_F_ONE_FOR_ALL;
        features |= NETIF_F_ALL_FOR_ALL;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                features = netdev_increment_features(features,
                                                     slave->dev->features,
                                                     mask);
@@ -1118,16 +1094,17 @@ static void bond_compute_features(struct bonding *bond)
 {
        unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE;
        netdev_features_t vlan_features = BOND_VLAN_FEATURES;
+       struct net_device *bond_dev = bond->dev;
+       struct list_head *iter;
+       struct slave *slave;
        unsigned short max_hard_header_len = ETH_HLEN;
        unsigned int gso_max_size = GSO_MAX_SIZE;
-       struct net_device *bond_dev = bond->dev;
        u16 gso_max_segs = GSO_MAX_SEGS;
-       struct slave *slave;
 
-       if (list_empty(&bond->slave_list))
+       if (!bond_has_slaves(bond))
                goto done;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                vlan_features = netdev_increment_features(vlan_features,
                        slave->dev->vlan_features, BOND_VLAN_FEATURES);
 
@@ -1233,15 +1210,16 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
 }
 
 static int bond_master_upper_dev_link(struct net_device *bond_dev,
-                                     struct net_device *slave_dev)
+                                     struct net_device *slave_dev,
+                                     struct slave *slave)
 {
        int err;
 
-       err = netdev_master_upper_dev_link(slave_dev, bond_dev);
+       err = netdev_master_upper_dev_link_private(slave_dev, bond_dev, slave);
        if (err)
                return err;
        slave_dev->flags |= IFF_SLAVE;
-       rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE);
+       rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
        return 0;
 }
 
@@ -1250,7 +1228,7 @@ static void bond_upper_dev_unlink(struct net_device *bond_dev,
 {
        netdev_upper_dev_unlink(slave_dev, bond_dev);
        slave_dev->flags &= ~IFF_SLAVE;
-       rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE);
+       rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
 }
 
 /* enslave device <slave> to bond device <master> */
@@ -1258,7 +1236,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
-       struct slave *new_slave = NULL;
+       struct slave *new_slave = NULL, *prev_slave;
        struct sockaddr addr;
        int link_reporting;
        int res = 0, i;
@@ -1313,7 +1291,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
         * bond ether type mutual exclusion - don't allow slaves of dissimilar
         * ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond
         */
-       if (list_empty(&bond->slave_list)) {
+       if (!bond_has_slaves(bond)) {
                if (bond_dev->type != slave_dev->type) {
                        pr_debug("%s: change device type from %d to %d\n",
                                 bond_dev->name,
@@ -1352,7 +1330,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        }
 
        if (slave_ops->ndo_set_mac_address == NULL) {
-               if (list_empty(&bond->slave_list)) {
+               if (!bond_has_slaves(bond)) {
                        pr_warning("%s: Warning: The first slave device specified does not support setting the MAC address. Setting fail_over_mac to active.",
                                   bond_dev->name);
                        bond->params.fail_over_mac = BOND_FOM_ACTIVE;
@@ -1368,7 +1346,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 
        /* If this is the first slave, then we need to set the master's hardware
         * address to be the same as the slave's. */
-       if (list_empty(&bond->slave_list) &&
+       if (!bond_has_slaves(bond) &&
            bond->dev->addr_assign_type == NET_ADDR_RANDOM)
                bond_set_dev_addr(bond->dev, slave_dev);
 
@@ -1377,7 +1355,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                res = -ENOMEM;
                goto err_undo_flags;
        }
-       INIT_LIST_HEAD(&new_slave->list);
        /*
         * Set the new_slave's queue_id to be zero.  Queue ID mapping
         * is set via sysfs or module option if desired.
@@ -1413,17 +1390,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                }
        }
 
-       res = bond_master_upper_dev_link(bond_dev, slave_dev);
-       if (res) {
-               pr_debug("Error %d calling bond_master_upper_dev_link\n", res);
-               goto err_restore_mac;
-       }
-
        /* open the slave since the application closed it */
        res = dev_open(slave_dev);
        if (res) {
                pr_debug("Opening slave %s failed\n", slave_dev->name);
-               goto err_unset_master;
+               goto err_restore_mac;
        }
 
        new_slave->bond = bond;
@@ -1479,21 +1450,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                goto err_close;
        }
 
-       write_lock_bh(&bond->lock);
-
-       bond_attach_slave(bond, new_slave);
+       prev_slave = bond_last_slave(bond);
 
        new_slave->delay = 0;
        new_slave->link_failure_count = 0;
 
-       write_unlock_bh(&bond->lock);
-
-       bond_compute_features(bond);
-
        bond_update_speed_duplex(new_slave);
 
-       read_lock(&bond->lock);
-
        new_slave->last_arp_rx = jiffies -
                (msecs_to_jiffies(bond->params.arp_interval) + 1);
        for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
@@ -1554,12 +1517,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                }
        }
 
-       write_lock_bh(&bond->curr_slave_lock);
-
        switch (bond->params.mode) {
        case BOND_MODE_ACTIVEBACKUP:
                bond_set_slave_inactive_flags(new_slave);
-               bond_select_active_slave(bond);
                break;
        case BOND_MODE_8023AD:
                /* in 802.3ad mode, the internal mechanism
@@ -1568,16 +1528,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                 */
                bond_set_slave_inactive_flags(new_slave);
                /* if this is the first slave */
-               if (bond_first_slave(bond) == new_slave) {
+               if (!prev_slave) {
                        SLAVE_AD_INFO(new_slave).id = 1;
                        /* Initialize AD with the number of times that the AD timer is called in 1 second
                         * can be called only after the mac address of the bond is set
                         */
                        bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL);
                } else {
-                       struct slave *prev_slave;
-
-                       prev_slave = bond_prev_slave(bond, new_slave);
                        SLAVE_AD_INFO(new_slave).id =
                                SLAVE_AD_INFO(prev_slave).id + 1;
                }
@@ -1588,7 +1545,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        case BOND_MODE_ALB:
                bond_set_active_slave(new_slave);
                bond_set_slave_inactive_flags(new_slave);
-               bond_select_active_slave(bond);
                break;
        default:
                pr_debug("This slave is always active in trunk mode\n");
@@ -1606,10 +1562,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                break;
        } /* switch(bond_mode) */
 
-       write_unlock_bh(&bond->curr_slave_lock);
-
-       bond_set_carrier(bond);
-
 #ifdef CONFIG_NET_POLL_CONTROLLER
        slave_dev->npinfo = bond->dev->npinfo;
        if (slave_dev->npinfo) {
@@ -1624,17 +1576,29 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        }
 #endif
 
-       read_unlock(&bond->lock);
-
-       res = bond_create_slave_symlinks(bond_dev, slave_dev);
-       if (res)
-               goto err_detach;
-
        res = netdev_rx_handler_register(slave_dev, bond_handle_frame,
                                         new_slave);
        if (res) {
                pr_debug("Error %d calling netdev_rx_handler_register\n", res);
-               goto err_dest_symlinks;
+               goto err_detach;
+       }
+
+       res = bond_master_upper_dev_link(bond_dev, slave_dev, new_slave);
+       if (res) {
+               pr_debug("Error %d calling bond_master_upper_dev_link\n", res);
+               goto err_unregister;
+       }
+
+       bond->slave_cnt++;
+       bond_compute_features(bond);
+       bond_set_carrier(bond);
+
+       if (USES_PRIMARY(bond->params.mode)) {
+               read_lock(&bond->lock);
+               write_lock_bh(&bond->curr_slave_lock);
+               bond_select_active_slave(bond);
+               write_unlock_bh(&bond->curr_slave_lock);
+               read_unlock(&bond->lock);
        }
 
        pr_info("%s: enslaving %s as a%s interface with a%s link.\n",
@@ -1646,8 +1610,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        return 0;
 
 /* Undo stages on error */
-err_dest_symlinks:
-       bond_destroy_slave_symlinks(bond_dev, slave_dev);
+err_unregister:
+       netdev_rx_handler_unregister(slave_dev);
 
 err_detach:
        if (!USES_PRIMARY(bond->params.mode))
@@ -1655,7 +1619,6 @@ err_detach:
 
        vlan_vids_del_by_dev(slave_dev, bond_dev);
        write_lock_bh(&bond->lock);
-       bond_detach_slave(bond, new_slave);
        if (bond->primary_slave == new_slave)
                bond->primary_slave = NULL;
        if (bond->curr_active_slave == new_slave) {
@@ -1675,9 +1638,6 @@ err_close:
        slave_dev->priv_flags &= ~IFF_BONDING;
        dev_close(slave_dev);
 
-err_unset_master:
-       bond_upper_dev_unlink(bond_dev, slave_dev);
-
 err_restore_mac:
        if (!bond->params.fail_over_mac) {
                /* XXX TODO - fom follow mode needs to change master's
@@ -1696,9 +1656,8 @@ err_free:
        kfree(new_slave);
 
 err_undo_flags:
-       bond_compute_features(bond);
        /* Enslave of first slave has failed and we need to fix master's mac */
-       if (list_empty(&bond->slave_list) &&
+       if (!bond_has_slaves(bond) &&
            ether_addr_equal(bond_dev->dev_addr, slave_dev->dev_addr))
                eth_hw_addr_random(bond_dev);
 
@@ -1724,6 +1683,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        struct bonding *bond = netdev_priv(bond_dev);
        struct slave *slave, *oldcurrent;
        struct sockaddr addr;
+       int old_flags = bond_dev->flags;
        netdev_features_t old_features = bond_dev->features;
 
        /* slave is not a slave or master is not master of this slave */
@@ -1748,6 +1708,11 @@ static int __bond_release_one(struct net_device *bond_dev,
        }
 
        write_unlock_bh(&bond->lock);
+
+       /* release the slave from its bond */
+       bond->slave_cnt--;
+
+       bond_upper_dev_unlink(bond_dev, slave_dev);
        /* unregister rx_handler early so bond_handle_frame wouldn't be called
         * for this slave anymore.
         */
@@ -1771,12 +1736,9 @@ static int __bond_release_one(struct net_device *bond_dev,
 
        bond->current_arp_slave = NULL;
 
-       /* release the slave from its bond */
-       bond_detach_slave(bond, slave);
-
        if (!all && !bond->params.fail_over_mac) {
                if (ether_addr_equal(bond_dev->dev_addr, slave->perm_hwaddr) &&
-                   !list_empty(&bond->slave_list))
+                   bond_has_slaves(bond))
                        pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s. Set the HWaddr of %s to a different address to avoid conflicts.\n",
                                   bond_dev->name, slave_dev->name,
                                   slave->perm_hwaddr,
@@ -1819,7 +1781,7 @@ static int __bond_release_one(struct net_device *bond_dev,
                write_lock_bh(&bond->lock);
        }
 
-       if (list_empty(&bond->slave_list)) {
+       if (!bond_has_slaves(bond)) {
                bond_set_carrier(bond);
                eth_hw_addr_random(bond_dev);
 
@@ -1835,7 +1797,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        unblock_netpoll_tx();
        synchronize_rcu();
 
-       if (list_empty(&bond->slave_list)) {
+       if (!bond_has_slaves(bond)) {
                call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
                call_netdevice_notifiers(NETDEV_RELEASE, bond->dev);
        }
@@ -1847,27 +1809,29 @@ static int __bond_release_one(struct net_device *bond_dev,
                        bond_dev->name, slave_dev->name, bond_dev->name);
 
        /* must do this from outside any spinlocks */
-       bond_destroy_slave_symlinks(bond_dev, slave_dev);
-
        vlan_vids_del_by_dev(slave_dev, bond_dev);
 
        /* If the mode USES_PRIMARY, then this cases was handled above by
         * bond_change_active_slave(..., NULL)
         */
        if (!USES_PRIMARY(bond->params.mode)) {
-               /* unset promiscuity level from slave */
-               if (bond_dev->flags & IFF_PROMISC)
+               /* unset promiscuity level from slave
+                * NOTE: The NETDEV_CHANGEADDR call above may change the value
+                * of the IFF_PROMISC flag in the bond_dev, but we need the
+                * value of that flag before that change, as that was the value
+                * when this slave was attached, so we cache at the start of the
+                * function and use it here. Same goes for ALLMULTI below
+                */
+               if (old_flags & IFF_PROMISC)
                        dev_set_promiscuity(slave_dev, -1);
 
                /* unset allmulti level from slave */
-               if (bond_dev->flags & IFF_ALLMULTI)
+               if (old_flags & IFF_ALLMULTI)
                        dev_set_allmulti(slave_dev, -1);
 
                bond_hw_addr_flush(bond_dev, slave_dev);
        }
 
-       bond_upper_dev_unlink(bond_dev, slave_dev);
-
        slave_disable_netpoll(slave);
 
        /* close slave before restoring its mac address */
@@ -1906,7 +1870,7 @@ static int  bond_release_and_destroy(struct net_device *bond_dev,
        int ret;
 
        ret = bond_release(bond_dev, slave_dev);
-       if (ret == 0 && list_empty(&bond->slave_list)) {
+       if (ret == 0 && !bond_has_slaves(bond)) {
                bond_dev->priv_flags |= IFF_DISABLE_NETPOLL;
                pr_info("%s: destroying bond %s.\n",
                        bond_dev->name, bond_dev->name);
@@ -1915,61 +1879,6 @@ static int  bond_release_and_destroy(struct net_device *bond_dev,
        return ret;
 }
 
-/*
- * This function changes the active slave to slave <slave_dev>.
- * It returns -EINVAL in the following cases.
- *  - <slave_dev> is not found in the list.
- *  - There is not active slave now.
- *  - <slave_dev> is already active.
- *  - The link state of <slave_dev> is not BOND_LINK_UP.
- *  - <slave_dev> is not running.
- * In these cases, this function does nothing.
- * In the other cases, current_slave pointer is changed and 0 is returned.
- */
-static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev)
-{
-       struct bonding *bond = netdev_priv(bond_dev);
-       struct slave *old_active = NULL;
-       struct slave *new_active = NULL;
-       int res = 0;
-
-       if (!USES_PRIMARY(bond->params.mode))
-               return -EINVAL;
-
-       /* Verify that bond_dev is indeed the master of slave_dev */
-       if (!(slave_dev->flags & IFF_SLAVE) ||
-           !netdev_has_upper_dev(slave_dev, bond_dev))
-               return -EINVAL;
-
-       read_lock(&bond->lock);
-
-       old_active = bond->curr_active_slave;
-       new_active = bond_get_slave_by_dev(bond, slave_dev);
-       /*
-        * Changing to the current active: do nothing; return success.
-        */
-       if (new_active && new_active == old_active) {
-               read_unlock(&bond->lock);
-               return 0;
-       }
-
-       if (new_active &&
-           old_active &&
-           new_active->link == BOND_LINK_UP &&
-           IS_UP(new_active->dev)) {
-               block_netpoll_tx();
-               write_lock_bh(&bond->curr_slave_lock);
-               bond_change_active_slave(bond, new_active);
-               write_unlock_bh(&bond->curr_slave_lock);
-               unblock_netpoll_tx();
-       } else
-               res = -EINVAL;
-
-       read_unlock(&bond->lock);
-
-       return res;
-}
-
 static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
        struct bonding *bond = netdev_priv(bond_dev);
@@ -1987,11 +1896,12 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info)
 {
        struct bonding *bond = netdev_priv(bond_dev);
+       struct list_head *iter;
        int i = 0, res = -ENODEV;
        struct slave *slave;
 
        read_lock(&bond->lock);
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (i++ == (int)info->slave_id) {
                        res = 0;
                        strcpy(info->slave_name, slave->dev->name);
@@ -2012,12 +1922,13 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
 static int bond_miimon_inspect(struct bonding *bond)
 {
        int link_state, commit = 0;
+       struct list_head *iter;
        struct slave *slave;
        bool ignore_updelay;
 
        ignore_updelay = !bond->curr_active_slave ? true : false;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                slave->new_link = BOND_LINK_NOCHANGE;
 
                link_state = bond_check_dev_link(bond, slave->dev, 0);
@@ -2111,9 +2022,10 @@ static int bond_miimon_inspect(struct bonding *bond)
 
 static void bond_miimon_commit(struct bonding *bond)
 {
+       struct list_head *iter;
        struct slave *slave;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                switch (slave->new_link) {
                case BOND_LINK_NOCHANGE:
                        continue;
@@ -2218,7 +2130,7 @@ void bond_mii_monitor(struct work_struct *work)
 
        delay = msecs_to_jiffies(bond->params.miimon);
 
-       if (list_empty(&bond->slave_list))
+       if (!bond_has_slaves(bond))
                goto re_arm;
 
        should_notify_peers = bond_should_notify_peers(bond);
@@ -2267,7 +2179,7 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip)
                return true;
 
        rcu_read_lock();
-       netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) {
+       netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
                if (ip == bond_confirm_addr(upper, 0, ip)) {
                        ret = true;
                        break;
@@ -2342,10 +2254,12 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                 *
                 * TODO: QinQ?
                 */
-               netdev_for_each_upper_dev_rcu(bond->dev, vlan_upper, vlan_iter) {
+               netdev_for_each_all_upper_dev_rcu(bond->dev, vlan_upper,
+                                                 vlan_iter) {
                        if (!is_vlan_dev(vlan_upper))
                                continue;
-                       netdev_for_each_upper_dev_rcu(vlan_upper, upper, iter) {
+                       netdev_for_each_all_upper_dev_rcu(vlan_upper, upper,
+                                                         iter) {
                                if (upper == rt->dst.dev) {
                                        vlan_id = vlan_dev_vlan_id(vlan_upper);
                                        rcu_read_unlock();
@@ -2358,7 +2272,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                 * our upper vlans, then just search for any dev that
                 * matches, and in case it's a vlan - save the id
                 */
-               netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) {
+               netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
                        if (upper == rt->dst.dev) {
                                /* if it's a vlan - get its VID */
                                if (is_vlan_dev(upper))
@@ -2505,11 +2419,12 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
        struct bonding *bond = container_of(work, struct bonding,
                                            arp_work.work);
        struct slave *slave, *oldcurrent;
+       struct list_head *iter;
        int do_failover = 0;
 
        read_lock(&bond->lock);
 
-       if (list_empty(&bond->slave_list))
+       if (!bond_has_slaves(bond))
                goto re_arm;
 
        oldcurrent = bond->curr_active_slave;
@@ -2521,7 +2436,7 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
         * TODO: what about up/down delay in arp mode? it wasn't here before
         *       so it can wait
         */
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                unsigned long trans_start = dev_trans_start(slave->dev);
 
                if (slave->link != BOND_LINK_UP) {
@@ -2612,10 +2527,11 @@ re_arm:
 static int bond_ab_arp_inspect(struct bonding *bond)
 {
        unsigned long trans_start, last_rx;
+       struct list_head *iter;
        struct slave *slave;
        int commit = 0;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                slave->new_link = BOND_LINK_NOCHANGE;
                last_rx = slave_last_rx(bond, slave);
 
@@ -2682,9 +2598,10 @@ static int bond_ab_arp_inspect(struct bonding *bond)
 static void bond_ab_arp_commit(struct bonding *bond)
 {
        unsigned long trans_start;
+       struct list_head *iter;
        struct slave *slave;
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                switch (slave->new_link) {
                case BOND_LINK_NOCHANGE:
                        continue;
@@ -2755,8 +2672,9 @@ do_failover:
  */
 static void bond_ab_arp_probe(struct bonding *bond)
 {
-       struct slave *slave, *next_slave;
-       int i;
+       struct slave *slave, *before = NULL, *new_slave = NULL;
+       struct list_head *iter;
+       bool found = false;
 
        read_lock(&bond->curr_slave_lock);
 
@@ -2786,18 +2704,12 @@ static void bond_ab_arp_probe(struct bonding *bond)
 
        bond_set_slave_inactive_flags(bond->current_arp_slave);
 
-       /* search for next candidate */
-       next_slave = bond_next_slave(bond, bond->current_arp_slave);
-       bond_for_each_slave_from(bond, slave, i, next_slave) {
-               if (IS_UP(slave->dev)) {
-                       slave->link = BOND_LINK_BACK;
-                       bond_set_slave_active_flags(slave);
-                       bond_arp_send_all(bond, slave);
-                       slave->jiffies = jiffies;
-                       bond->current_arp_slave = slave;
-                       break;
-               }
+       bond_for_each_slave(bond, slave, iter) {
+               if (!found && !before && IS_UP(slave->dev))
+                       before = slave;
 
+               if (found && !new_slave && IS_UP(slave->dev))
+                       new_slave = slave;
                /* if the link state is up at this point, we
                 * mark it down - this can happen if we have
                 * simultaneous link failures and
@@ -2805,7 +2717,7 @@ static void bond_ab_arp_probe(struct bonding *bond)
                 * one the current slave so it is still marked
                 * up when it is actually down
                 */
-               if (slave->link == BOND_LINK_UP) {
+               if (!IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
                        slave->link = BOND_LINK_DOWN;
                        if (slave->link_failure_count < UINT_MAX)
                                slave->link_failure_count++;
@@ -2815,7 +2727,22 @@ static void bond_ab_arp_probe(struct bonding *bond)
                        pr_info("%s: backup interface %s is now down.\n",
                                bond->dev->name, slave->dev->name);
                }
+               if (slave == bond->current_arp_slave)
+                       found = true;
        }
+
+       if (!new_slave && before)
+               new_slave = before;
+
+       if (!new_slave)
+               return;
+
+       new_slave->link = BOND_LINK_BACK;
+       bond_set_slave_active_flags(new_slave);
+       bond_arp_send_all(bond, new_slave);
+       new_slave->jiffies = jiffies;
+       bond->current_arp_slave = new_slave;
+
 }
 
 void bond_activebackup_arp_mon(struct work_struct *work)
@@ -2829,7 +2756,7 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 
        delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval);
 
-       if (list_empty(&bond->slave_list))
+       if (!bond_has_slaves(bond))
                goto re_arm;
 
        should_notify_peers = bond_should_notify_peers(bond);
@@ -3026,99 +2953,85 @@ static struct notifier_block bond_netdev_notifier = {
 
 /*---------------------------- Hashing Policies -----------------------------*/
 
-/*
- * Hash for the output device based upon layer 2 data
- */
-static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count)
+/* L2 hash helper */
+static inline u32 bond_eth_hash(struct sk_buff *skb)
 {
        struct ethhdr *data = (struct ethhdr *)skb->data;
 
        if (skb_headlen(skb) >= offsetof(struct ethhdr, h_proto))
-               return (data->h_dest[5] ^ data->h_source[5]) % count;
+               return data->h_dest[5] ^ data->h_source[5];
 
        return 0;
 }
 
-/*
- * Hash for the output device based upon layer 2 and layer 3 data. If
- * the packet is not IP, fall back on bond_xmit_hash_policy_l2()
- */
-static int bond_xmit_hash_policy_l23(struct sk_buff *skb, int count)
+/* Extract the appropriate headers based on bond's xmit policy */
+static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
+                             struct flow_keys *fk)
 {
-       const struct ethhdr *data;
+       const struct ipv6hdr *iph6;
        const struct iphdr *iph;
-       const struct ipv6hdr *ipv6h;
-       u32 v6hash;
-       const __be32 *s, *d;
+       int noff, proto = -1;
+
+       if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23)
+               return skb_flow_dissect(skb, fk);
 
-       if (skb->protocol == htons(ETH_P_IP) &&
-           pskb_network_may_pull(skb, sizeof(*iph))) {
+       fk->ports = 0;
+       noff = skb_network_offset(skb);
+       if (skb->protocol == htons(ETH_P_IP)) {
+               if (!pskb_may_pull(skb, noff + sizeof(*iph)))
+                       return false;
                iph = ip_hdr(skb);
-               data = (struct ethhdr *)skb->data;
-               return ((ntohl(iph->saddr ^ iph->daddr) & 0xffff) ^
-                       (data->h_dest[5] ^ data->h_source[5])) % count;
-       } else if (skb->protocol == htons(ETH_P_IPV6) &&
-                  pskb_network_may_pull(skb, sizeof(*ipv6h))) {
-               ipv6h = ipv6_hdr(skb);
-               data = (struct ethhdr *)skb->data;
-               s = &ipv6h->saddr.s6_addr32[0];
-               d = &ipv6h->daddr.s6_addr32[0];
-               v6hash = (s[1] ^ d[1]) ^ (s[2] ^ d[2]) ^ (s[3] ^ d[3]);
-               v6hash ^= (v6hash >> 24) ^ (v6hash >> 16) ^ (v6hash >> 8);
-               return (v6hash ^ data->h_dest[5] ^ data->h_source[5]) % count;
-       }
-
-       return bond_xmit_hash_policy_l2(skb, count);
+               fk->src = iph->saddr;
+               fk->dst = iph->daddr;
+               noff += iph->ihl << 2;
+               if (!ip_is_fragment(iph))
+                       proto = iph->protocol;
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+               if (!pskb_may_pull(skb, noff + sizeof(*iph6)))
+                       return false;
+               iph6 = ipv6_hdr(skb);
+               fk->src = (__force __be32)ipv6_addr_hash(&iph6->saddr);
+               fk->dst = (__force __be32)ipv6_addr_hash(&iph6->daddr);
+               noff += sizeof(*iph6);
+               proto = iph6->nexthdr;
+       } else {
+               return false;
+       }
+       if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34 && proto >= 0)
+               fk->ports = skb_flow_get_ports(skb, noff, proto);
+
+       return true;
 }
 
-/*
- * Hash for the output device based upon layer 3 and layer 4 data. If
- * the packet is a frag or not TCP or UDP, just use layer 3 data.  If it is
- * altogether not IP, fall back on bond_xmit_hash_policy_l2()
+/**
+ * bond_xmit_hash - generate a hash value based on the xmit policy
+ * @bond: bonding device
+ * @skb: buffer to use for headers
+ * @count: modulo value
+ *
+ * This function will extract the necessary headers from the skb buffer and use
+ * them to generate a hash based on the xmit_policy set in the bonding device
+ * which will be reduced modulo count before returning.
  */
-static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count)
+int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
 {
-       u32 layer4_xor = 0;
-       const struct iphdr *iph;
-       const struct ipv6hdr *ipv6h;
-       const __be32 *s, *d;
-       const __be16 *l4 = NULL;
-       __be16 _l4[2];
-       int noff = skb_network_offset(skb);
-       int poff;
-
-       if (skb->protocol == htons(ETH_P_IP) &&
-           pskb_may_pull(skb, noff + sizeof(*iph))) {
-               iph = ip_hdr(skb);
-               poff = proto_ports_offset(iph->protocol);
+       struct flow_keys flow;
+       u32 hash;
 
-               if (!ip_is_fragment(iph) && poff >= 0) {
-                       l4 = skb_header_pointer(skb, noff + (iph->ihl << 2) + poff,
-                                               sizeof(_l4), &_l4);
-                       if (l4)
-                               layer4_xor = ntohs(l4[0] ^ l4[1]);
-               }
-               return (layer4_xor ^
-                       ((ntohl(iph->saddr ^ iph->daddr)) & 0xffff)) % count;
-       } else if (skb->protocol == htons(ETH_P_IPV6) &&
-                  pskb_may_pull(skb, noff + sizeof(*ipv6h))) {
-               ipv6h = ipv6_hdr(skb);
-               poff = proto_ports_offset(ipv6h->nexthdr);
-               if (poff >= 0) {
-                       l4 = skb_header_pointer(skb, noff + sizeof(*ipv6h) + poff,
-                                               sizeof(_l4), &_l4);
-                       if (l4)
-                               layer4_xor = ntohs(l4[0] ^ l4[1]);
-               }
-               s = &ipv6h->saddr.s6_addr32[0];
-               d = &ipv6h->daddr.s6_addr32[0];
-               layer4_xor ^= (s[1] ^ d[1]) ^ (s[2] ^ d[2]) ^ (s[3] ^ d[3]);
-               layer4_xor ^= (layer4_xor >> 24) ^ (layer4_xor >> 16) ^
-                              (layer4_xor >> 8);
-               return layer4_xor % count;
-       }
+       if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
+           !bond_flow_dissect(bond, skb, &flow))
+               return bond_eth_hash(skb) % count;
+
+       if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 ||
+           bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23)
+               hash = bond_eth_hash(skb);
+       else
+               hash = (__force u32)flow.ports;
+       hash ^= (__force u32)flow.dst ^ (__force u32)flow.src;
+       hash ^= (hash >> 16);
+       hash ^= (hash >> 8);
 
-       return bond_xmit_hash_policy_l2(skb, count);
+       return hash % count;
 }
 
 /*-------------------------- Device entry points ----------------------------*/
@@ -3148,13 +3061,14 @@ static void bond_work_cancel_all(struct bonding *bond)
 static int bond_open(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
+       struct list_head *iter;
        struct slave *slave;
 
        /* reset slave->backup and slave->inactive */
        read_lock(&bond->lock);
-       if (!list_empty(&bond->slave_list)) {
+       if (bond_has_slaves(bond)) {
                read_lock(&bond->curr_slave_lock);
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave(bond, slave, iter) {
                        if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP)
                                && (slave != bond->curr_active_slave)) {
                                bond_set_slave_inactive_flags(slave);
@@ -3214,12 +3128,13 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct rtnl_link_stats64 temp;
+       struct list_head *iter;
        struct slave *slave;
 
        memset(stats, 0, sizeof(*stats));
 
        read_lock_bh(&bond->lock);
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                const struct rtnl_link_stats64 *sstats =
                        dev_get_stats(slave->dev, &temp);
 
@@ -3256,6 +3171,7 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
 
 static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)
 {
+       struct bonding *bond = netdev_priv(bond_dev);
        struct net_device *slave_dev = NULL;
        struct ifbond k_binfo;
        struct ifbond __user *u_binfo = NULL;
@@ -3286,7 +3202,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
 
 
                if (mii->reg_num == 1) {
-                       struct bonding *bond = netdev_priv(bond_dev);
                        mii->val_out = 0;
                        read_lock(&bond->lock);
                        read_lock(&bond->curr_slave_lock);
@@ -3358,7 +3273,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
                        break;
                case BOND_CHANGE_ACTIVE_OLD:
                case SIOCBONDCHANGEACTIVE:
-                       res = bond_ioctl_change_active(bond_dev, slave_dev);
+                       res = bond_option_active_slave_set(bond, slave_dev);
                        break;
                default:
                        res = -EOPNOTSUPP;
@@ -3386,22 +3301,24 @@ static void bond_change_rx_flags(struct net_device *bond_dev, int change)
 static void bond_set_rx_mode(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
+       struct list_head *iter;
        struct slave *slave;
 
-       ASSERT_RTNL();
 
+       rcu_read_lock();
        if (USES_PRIMARY(bond->params.mode)) {
-               slave = rtnl_dereference(bond->curr_active_slave);
+               slave = rcu_dereference(bond->curr_active_slave);
                if (slave) {
                        dev_uc_sync(slave->dev, bond_dev);
                        dev_mc_sync(slave->dev, bond_dev);
                }
        } else {
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave_rcu(bond, slave, iter) {
                        dev_uc_sync_multiple(slave->dev, bond_dev);
                        dev_mc_sync_multiple(slave->dev, bond_dev);
                }
        }
+       rcu_read_unlock();
 }
 
 static int bond_neigh_init(struct neighbour *n)
@@ -3464,7 +3381,8 @@ static int bond_neigh_setup(struct net_device *dev,
 static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
 {
        struct bonding *bond = netdev_priv(bond_dev);
-       struct slave *slave;
+       struct slave *slave, *rollback_slave;
+       struct list_head *iter;
        int res = 0;
 
        pr_debug("bond=%p, name=%s, new_mtu=%d\n", bond,
@@ -3485,10 +3403,9 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
         * call to the base driver.
         */
 
-       bond_for_each_slave(bond, slave) {
-               pr_debug("s %p s->p %p c_m %p\n",
+       bond_for_each_slave(bond, slave, iter) {
+               pr_debug("s %p c_m %p\n",
                         slave,
-                        bond_prev_slave(bond, slave),
                         slave->dev->netdev_ops->ndo_change_mtu);
 
                res = dev_set_mtu(slave->dev, new_mtu);
@@ -3513,13 +3430,16 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
 
 unwind:
        /* unwind from head to the slave that failed */
-       bond_for_each_slave_continue_reverse(bond, slave) {
+       bond_for_each_slave(bond, rollback_slave, iter) {
                int tmp_res;
 
-               tmp_res = dev_set_mtu(slave->dev, bond_dev->mtu);
+               if (rollback_slave == slave)
+                       break;
+
+               tmp_res = dev_set_mtu(rollback_slave->dev, bond_dev->mtu);
                if (tmp_res) {
                        pr_debug("unwind err %d dev %s\n",
-                                tmp_res, slave->dev->name);
+                                tmp_res, rollback_slave->dev->name);
                }
        }
 
@@ -3536,8 +3456,9 @@ unwind:
 static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
 {
        struct bonding *bond = netdev_priv(bond_dev);
+       struct slave *slave, *rollback_slave;
        struct sockaddr *sa = addr, tmp_sa;
-       struct slave *slave;
+       struct list_head *iter;
        int res = 0;
 
        if (bond->params.mode == BOND_MODE_ALB)
@@ -3571,7 +3492,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
         * call to the base driver.
         */
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                const struct net_device_ops *slave_ops = slave->dev->netdev_ops;
                pr_debug("slave %p %s\n", slave, slave->dev->name);
 
@@ -3603,13 +3524,16 @@ unwind:
        tmp_sa.sa_family = bond_dev->type;
 
        /* unwind from head to the slave that failed */
-       bond_for_each_slave_continue_reverse(bond, slave) {
+       bond_for_each_slave(bond, rollback_slave, iter) {
                int tmp_res;
 
-               tmp_res = dev_set_mac_address(slave->dev, &tmp_sa);
+               if (rollback_slave == slave)
+                       break;
+
+               tmp_res = dev_set_mac_address(rollback_slave->dev, &tmp_sa);
                if (tmp_res) {
                        pr_debug("unwind err %d dev %s\n",
-                                tmp_res, slave->dev->name);
+                                tmp_res, rollback_slave->dev->name);
                }
        }
 
@@ -3628,11 +3552,12 @@ unwind:
  */
 void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id)
 {
+       struct list_head *iter;
        struct slave *slave;
        int i = slave_id;
 
        /* Here we start from the slave with slave_id */
-       bond_for_each_slave_rcu(bond, slave) {
+       bond_for_each_slave_rcu(bond, slave, iter) {
                if (--i < 0) {
                        if (slave_can_tx(slave)) {
                                bond_dev_queue_xmit(bond, skb, slave->dev);
@@ -3643,7 +3568,7 @@ void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id)
 
        /* Here we start from the first slave up to slave_id */
        i = slave_id;
-       bond_for_each_slave_rcu(bond, slave) {
+       bond_for_each_slave_rcu(bond, slave, iter) {
                if (--i < 0)
                        break;
                if (slave_can_tx(slave)) {
@@ -3655,14 +3580,44 @@ void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id)
        kfree_skb(skb);
 }
 
+/**
+ * bond_rr_gen_slave_id - generate slave id based on packets_per_slave
+ * @bond: bonding device to use
+ *
+ * Based on the value of the bonding device's packets_per_slave parameter
+ * this function generates a slave id, which is usually used as the next
+ * slave to transmit through.
+ */
+static u32 bond_rr_gen_slave_id(struct bonding *bond)
+{
+       int packets_per_slave = bond->params.packets_per_slave;
+       u32 slave_id;
+
+       switch (packets_per_slave) {
+       case 0:
+               slave_id = prandom_u32();
+               break;
+       case 1:
+               slave_id = bond->rr_tx_counter;
+               break;
+       default:
+               slave_id = reciprocal_divide(bond->rr_tx_counter,
+                                            packets_per_slave);
+               break;
+       }
+       bond->rr_tx_counter++;
+
+       return slave_id;
+}
+
 static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct iphdr *iph = ip_hdr(skb);
        struct slave *slave;
+       u32 slave_id;
 
-       /*
-        * Start with the curr_active_slave that joined the bond as the
+       /* Start with the curr_active_slave that joined the bond as the
         * default for sending IGMP traffic.  For failover purposes one
         * needs to maintain some consistency for the interface that will
         * send the join/membership reports.  The curr_active_slave found
@@ -3675,8 +3630,8 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
                else
                        bond_xmit_slave_id(bond, skb, 0);
        } else {
-               bond_xmit_slave_id(bond, skb,
-                                  bond->rr_tx_counter++ % bond->slave_cnt);
+               slave_id = bond_rr_gen_slave_id(bond);
+               bond_xmit_slave_id(bond, skb, slave_id % bond->slave_cnt);
        }
 
        return NETDEV_TX_OK;
@@ -3700,8 +3655,7 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
        return NETDEV_TX_OK;
 }
 
-/*
- * In bond_xmit_xor() , we determine the output device by using a pre-
+/* In bond_xmit_xor() , we determine the output device by using a pre-
  * determined xmit_hash_policy(), If the selected device is not enabled,
  * find the next active slave.
  */
@@ -3709,8 +3663,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
 
-       bond_xmit_slave_id(bond, skb,
-                          bond->xmit_hash_policy(skb, bond->slave_cnt));
+       bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb, bond->slave_cnt));
 
        return NETDEV_TX_OK;
 }
@@ -3720,8 +3673,9 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct slave *slave = NULL;
+       struct list_head *iter;
 
-       bond_for_each_slave_rcu(bond, slave) {
+       bond_for_each_slave_rcu(bond, slave, iter) {
                if (bond_is_last_slave(bond, slave))
                        break;
                if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
@@ -3746,22 +3700,6 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
 
 /*------------------------- Device initialization ---------------------------*/
 
-static void bond_set_xmit_hash_policy(struct bonding *bond)
-{
-       switch (bond->params.xmit_policy) {
-       case BOND_XMIT_POLICY_LAYER23:
-               bond->xmit_hash_policy = bond_xmit_hash_policy_l23;
-               break;
-       case BOND_XMIT_POLICY_LAYER34:
-               bond->xmit_hash_policy = bond_xmit_hash_policy_l34;
-               break;
-       case BOND_XMIT_POLICY_LAYER2:
-       default:
-               bond->xmit_hash_policy = bond_xmit_hash_policy_l2;
-               break;
-       }
-}
-
 /*
  * Lookup the slave that corresponds to a qid
  */
@@ -3770,13 +3708,14 @@ static inline int bond_slave_override(struct bonding *bond,
 {
        struct slave *slave = NULL;
        struct slave *check_slave;
+       struct list_head *iter;
        int res = 1;
 
        if (!skb->queue_mapping)
                return 1;
 
        /* Find out if any slaves have the same mapping as this skb. */
-       bond_for_each_slave_rcu(bond, check_slave) {
+       bond_for_each_slave_rcu(bond, check_slave, iter) {
                if (check_slave->queue_id == skb->queue_mapping) {
                        slave = check_slave;
                        break;
@@ -3862,7 +3801,7 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_BUSY;
 
        rcu_read_lock();
-       if (!list_empty(&bond->slave_list))
+       if (bond_has_slaves(bond))
                ret = __bond_start_xmit(skb, dev);
        else
                kfree_skb(skb);
@@ -3871,43 +3810,12 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return ret;
 }
 
-/*
- * set bond mode specific net device operations
- */
-void bond_set_mode_ops(struct bonding *bond, int mode)
-{
-       struct net_device *bond_dev = bond->dev;
-
-       switch (mode) {
-       case BOND_MODE_ROUNDROBIN:
-               break;
-       case BOND_MODE_ACTIVEBACKUP:
-               break;
-       case BOND_MODE_XOR:
-               bond_set_xmit_hash_policy(bond);
-               break;
-       case BOND_MODE_BROADCAST:
-               break;
-       case BOND_MODE_8023AD:
-               bond_set_xmit_hash_policy(bond);
-               break;
-       case BOND_MODE_ALB:
-               /* FALLTHRU */
-       case BOND_MODE_TLB:
-               break;
-       default:
-               /* Should never happen, mode already checked */
-               pr_err("%s: Error: Unknown bonding mode %d\n",
-                      bond_dev->name, mode);
-               break;
-       }
-}
-
 static int bond_ethtool_get_settings(struct net_device *bond_dev,
                                     struct ethtool_cmd *ecmd)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        unsigned long speed = 0;
+       struct list_head *iter;
        struct slave *slave;
 
        ecmd->duplex = DUPLEX_UNKNOWN;
@@ -3919,7 +3827,7 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
         * this is an accurate maximum.
         */
        read_lock(&bond->lock);
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (SLAVE_IS_OK(slave)) {
                        if (slave->speed != SPEED_UNKNOWN)
                                speed += slave->speed;
@@ -3987,14 +3895,13 @@ static void bond_destructor(struct net_device *bond_dev)
        free_netdev(bond_dev);
 }
 
-static void bond_setup(struct net_device *bond_dev)
+void bond_setup(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
 
        /* initialize rwlocks */
        rwlock_init(&bond->lock);
        rwlock_init(&bond->curr_slave_lock);
-       INIT_LIST_HEAD(&bond->slave_list);
        bond->params = bonding_defaults;
 
        /* Initialize pointers */
@@ -4004,7 +3911,6 @@ static void bond_setup(struct net_device *bond_dev)
        ether_setup(bond_dev);
        bond_dev->netdev_ops = &bond_netdev_ops;
        bond_dev->ethtool_ops = &bond_ethtool_ops;
-       bond_set_mode_ops(bond, bond->params.mode);
 
        bond_dev->destructor = bond_destructor;
 
@@ -4050,12 +3956,13 @@ static void bond_setup(struct net_device *bond_dev)
 static void bond_uninit(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
-       struct slave *slave, *tmp_slave;
+       struct list_head *iter;
+       struct slave *slave;
 
        bond_netpoll_cleanup(bond_dev);
 
        /* Release the bonded slaves */
-       list_for_each_entry_safe(slave, tmp_slave, &bond->slave_list, list)
+       bond_for_each_slave(bond, slave, iter)
                __bond_release_one(bond_dev, slave->dev, true);
        pr_info("%s: released all slaves\n", bond_dev->name);
 
@@ -4228,6 +4135,12 @@ static int bond_check_params(struct bond_params *params)
                resend_igmp = BOND_DEFAULT_RESEND_IGMP;
        }
 
+       if (packets_per_slave < 0 || packets_per_slave > USHRT_MAX) {
+               pr_warn("Warning: packets_per_slave (%d) should be between 0 and %u resetting to 1\n",
+                       packets_per_slave, USHRT_MAX);
+               packets_per_slave = 1;
+       }
+
        /* reset values for TLB/ALB */
        if ((bond_mode == BOND_MODE_TLB) ||
            (bond_mode == BOND_MODE_ALB)) {
@@ -4417,7 +4330,10 @@ static int bond_check_params(struct bond_params *params)
        params->resend_igmp = resend_igmp;
        params->min_links = min_links;
        params->lp_interval = BOND_ALB_DEFAULT_LP_INTERVAL;
-
+       if (packets_per_slave > 1)
+               params->packets_per_slave = reciprocal_value(packets_per_slave);
+       else
+               params->packets_per_slave = packets_per_slave;
        if (primary) {
                strncpy(params->primary, primary, IFNAMSIZ);
                params->primary[IFNAMSIZ - 1] = 0;
@@ -4488,32 +4404,11 @@ static int bond_init(struct net_device *bond_dev)
        return 0;
 }
 
-static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
-{
-       if (tb[IFLA_ADDRESS]) {
-               if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
-                       return -EINVAL;
-               if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
-                       return -EADDRNOTAVAIL;
-       }
-       return 0;
-}
-
-static unsigned int bond_get_num_tx_queues(void)
+unsigned int bond_get_num_tx_queues(void)
 {
        return tx_queues;
 }
 
-static struct rtnl_link_ops bond_link_ops __read_mostly = {
-       .kind                   = "bond",
-       .priv_size              = sizeof(struct bonding),
-       .setup                  = bond_setup,
-       .validate               = bond_validate,
-       .get_num_tx_queues      = bond_get_num_tx_queues,
-       .get_num_rx_queues      = bond_get_num_tx_queues, /* Use the same number
-                                                            as for TX queues */
-};
-
 /* Create a new bond based on the specified name and bonding parameters.
  * If name is NULL, obtain a suitable "bond%d" name for us.
  * Caller must NOT hold rtnl_lock; we need to release it here before we
@@ -4600,7 +4495,7 @@ static int __init bonding_init(void)
        if (res)
                goto out;
 
-       res = rtnl_link_register(&bond_link_ops);
+       res = bond_netlink_init();
        if (res)
                goto err_link;
 
@@ -4616,7 +4511,7 @@ static int __init bonding_init(void)
 out:
        return res;
 err:
-       rtnl_link_unregister(&bond_link_ops);
+       bond_netlink_fini();
 err_link:
        unregister_pernet_subsys(&bond_net_ops);
        goto out;
@@ -4629,7 +4524,7 @@ static void __exit bonding_exit(void)
 
        bond_destroy_debugfs();
 
-       rtnl_link_unregister(&bond_link_ops);
+       bond_netlink_fini();
        unregister_pernet_subsys(&bond_net_ops);
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -4646,4 +4541,3 @@ MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION);
 MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others");
-MODULE_ALIAS_RTNL_LINK("bond");
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
new file mode 100644 (file)
index 0000000..40e7b1c
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * drivers/net/bond/bond_netlink.c - Netlink interface for bonding
+ * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_link.h>
+#include <linux/if_ether.h>
+#include <net/netlink.h>
+#include <net/rtnetlink.h>
+#include "bonding.h"
+
+static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
+       [IFLA_BOND_MODE]                = { .type = NLA_U8 },
+       [IFLA_BOND_ACTIVE_SLAVE]        = { .type = NLA_U32 },
+};
+
+static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       if (tb[IFLA_ADDRESS]) {
+               if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+                       return -EINVAL;
+               if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+                       return -EADDRNOTAVAIL;
+       }
+       return 0;
+}
+
+static int bond_changelink(struct net_device *bond_dev,
+                          struct nlattr *tb[], struct nlattr *data[])
+{
+       struct bonding *bond = netdev_priv(bond_dev);
+       int err;
+
+       if (data && data[IFLA_BOND_MODE]) {
+               int mode = nla_get_u8(data[IFLA_BOND_MODE]);
+
+               err = bond_option_mode_set(bond, mode);
+               if (err)
+                       return err;
+       }
+       if (data && data[IFLA_BOND_ACTIVE_SLAVE]) {
+               int ifindex = nla_get_u32(data[IFLA_BOND_ACTIVE_SLAVE]);
+               struct net_device *slave_dev;
+
+               if (ifindex == 0) {
+                       slave_dev = NULL;
+               } else {
+                       slave_dev = __dev_get_by_index(dev_net(bond_dev),
+                                                      ifindex);
+                       if (!slave_dev)
+                               return -ENODEV;
+               }
+               err = bond_option_active_slave_set(bond, slave_dev);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int bond_newlink(struct net *src_net, struct net_device *bond_dev,
+                       struct nlattr *tb[], struct nlattr *data[])
+{
+       int err;
+
+       err = bond_changelink(bond_dev, tb, data);
+       if (err < 0)
+               return err;
+
+       return register_netdevice(bond_dev);
+}
+
+static size_t bond_get_size(const struct net_device *bond_dev)
+{
+       return nla_total_size(sizeof(u8)) +     /* IFLA_BOND_MODE */
+               nla_total_size(sizeof(u32));    /* IFLA_BOND_ACTIVE_SLAVE */
+}
+
+static int bond_fill_info(struct sk_buff *skb,
+                         const struct net_device *bond_dev)
+{
+       struct bonding *bond = netdev_priv(bond_dev);
+       struct net_device *slave_dev = bond_option_active_slave_get(bond);
+
+       if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode) ||
+           (slave_dev &&
+            nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, slave_dev->ifindex)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+struct rtnl_link_ops bond_link_ops __read_mostly = {
+       .kind                   = "bond",
+       .priv_size              = sizeof(struct bonding),
+       .setup                  = bond_setup,
+       .maxtype                = IFLA_BOND_MAX,
+       .policy                 = bond_policy,
+       .validate               = bond_validate,
+       .newlink                = bond_newlink,
+       .changelink             = bond_changelink,
+       .get_size               = bond_get_size,
+       .fill_info              = bond_fill_info,
+       .get_num_tx_queues      = bond_get_num_tx_queues,
+       .get_num_rx_queues      = bond_get_num_tx_queues, /* Use the same number
+                                                            as for TX queues */
+};
+
+int __init bond_netlink_init(void)
+{
+       return rtnl_link_register(&bond_link_ops);
+}
+
+void bond_netlink_fini(void)
+{
+       rtnl_link_unregister(&bond_link_ops);
+}
+
+MODULE_ALIAS_RTNL_LINK("bond");
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
new file mode 100644 (file)
index 0000000..9a5223c
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * drivers/net/bond/bond_options.c - bonding options
+ * Copyright (c) 2013 Jiri Pirko <jiri@resnulli.us>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/rwlock.h>
+#include <linux/rcupdate.h>
+#include "bonding.h"
+
+static bool bond_mode_is_valid(int mode)
+{
+       int i;
+
+       for (i = 0; bond_mode_tbl[i].modename; i++);
+
+       return mode >= 0 && mode < i;
+}
+
+int bond_option_mode_set(struct bonding *bond, int mode)
+{
+       if (!bond_mode_is_valid(mode)) {
+               pr_err("invalid mode value %d.\n", mode);
+               return -EINVAL;
+       }
+
+       if (bond->dev->flags & IFF_UP) {
+               pr_err("%s: unable to update mode because interface is up.\n",
+                      bond->dev->name);
+               return -EPERM;
+       }
+
+       if (bond_has_slaves(bond)) {
+               pr_err("%s: unable to update mode because bond has slaves.\n",
+                       bond->dev->name);
+               return -EPERM;
+       }
+
+       if (BOND_MODE_IS_LB(mode) && bond->params.arp_interval) {
+               pr_err("%s: %s mode is incompatible with arp monitoring.\n",
+                      bond->dev->name, bond_mode_tbl[mode].modename);
+               return -EINVAL;
+       }
+
+       /* don't cache arp_validate between modes */
+       bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
+       bond->params.mode = mode;
+       return 0;
+}
+
+static struct net_device *__bond_option_active_slave_get(struct bonding *bond,
+                                                        struct slave *slave)
+{
+       return USES_PRIMARY(bond->params.mode) && slave ? slave->dev : NULL;
+}
+
+struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
+{
+       struct slave *slave = rcu_dereference(bond->curr_active_slave);
+
+       return __bond_option_active_slave_get(bond, slave);
+}
+
+struct net_device *bond_option_active_slave_get(struct bonding *bond)
+{
+       return __bond_option_active_slave_get(bond, bond->curr_active_slave);
+}
+
+int bond_option_active_slave_set(struct bonding *bond,
+                                struct net_device *slave_dev)
+{
+       int ret = 0;
+
+       if (slave_dev) {
+               if (!netif_is_bond_slave(slave_dev)) {
+                       pr_err("Device %s is not bonding slave.\n",
+                              slave_dev->name);
+                       return -EINVAL;
+               }
+
+               if (bond->dev != netdev_master_upper_dev_get(slave_dev)) {
+                       pr_err("%s: Device %s is not our slave.\n",
+                              bond->dev->name, slave_dev->name);
+                       return -EINVAL;
+               }
+       }
+
+       if (!USES_PRIMARY(bond->params.mode)) {
+               pr_err("%s: Unable to change active slave; %s is in mode %d\n",
+                      bond->dev->name, bond->dev->name, bond->params.mode);
+               return -EINVAL;
+       }
+
+       block_netpoll_tx();
+       read_lock(&bond->lock);
+       write_lock_bh(&bond->curr_slave_lock);
+
+       /* check to see if we are clearing active */
+       if (!slave_dev) {
+               pr_info("%s: Clearing current active slave.\n",
+               bond->dev->name);
+               rcu_assign_pointer(bond->curr_active_slave, NULL);
+               bond_select_active_slave(bond);
+       } else {
+               struct slave *old_active = bond->curr_active_slave;
+               struct slave *new_active = bond_slave_get_rtnl(slave_dev);
+
+               BUG_ON(!new_active);
+
+               if (new_active == old_active) {
+                       /* do nothing */
+                       pr_info("%s: %s is already the current active slave.\n",
+                               bond->dev->name, new_active->dev->name);
+               } else {
+                       if (old_active && (new_active->link == BOND_LINK_UP) &&
+                           IS_UP(new_active->dev)) {
+                               pr_info("%s: Setting %s as active slave.\n",
+                                       bond->dev->name, new_active->dev->name);
+                               bond_change_active_slave(bond, new_active);
+                       } else {
+                               pr_err("%s: Could not set %s as active slave; either %s is down or the link is down.\n",
+                                      bond->dev->name, new_active->dev->name,
+                                      new_active->dev->name);
+                               ret = -EINVAL;
+                       }
+               }
+       }
+
+       write_unlock_bh(&bond->curr_slave_lock);
+       read_unlock(&bond->lock);
+       unblock_netpoll_tx();
+       return ret;
+}
index 20a6ee2..fb868d6 100644 (file)
@@ -10,8 +10,9 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
        __acquires(&bond->lock)
 {
        struct bonding *bond = seq->private;
-       loff_t off = 0;
+       struct list_head *iter;
        struct slave *slave;
+       loff_t off = 0;
 
        /* make sure the bond won't be taken away */
        rcu_read_lock();
@@ -20,7 +21,7 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
        if (*pos == 0)
                return SEQ_START_TOKEN;
 
-       bond_for_each_slave(bond, slave)
+       bond_for_each_slave(bond, slave, iter)
                if (++off == *pos)
                        return slave;
 
@@ -30,17 +31,25 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
 static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        struct bonding *bond = seq->private;
-       struct slave *slave = v;
+       struct list_head *iter;
+       struct slave *slave;
+       bool found = false;
 
        ++*pos;
        if (v == SEQ_START_TOKEN)
                return bond_first_slave(bond);
 
-       if (bond_is_last_slave(bond, slave))
+       if (bond_is_last_slave(bond, v))
                return NULL;
-       slave = bond_next_slave(bond, slave);
 
-       return slave;
+       bond_for_each_slave(bond, slave, iter) {
+               if (found)
+                       return slave;
+               if (slave == v)
+                       found = true;
+       }
+
+       return NULL;
 }
 
 static void bond_info_seq_stop(struct seq_file *seq, void *v)
index c29b836..75dc4d0 100644 (file)
@@ -40,6 +40,7 @@
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 #include <linux/nsproxy.h>
+#include <linux/reciprocal_div.h>
 
 #include "bonding.h"
 
@@ -168,41 +169,6 @@ static const struct class_attribute class_attr_bonding_masters = {
        .namespace = bonding_namespace,
 };
 
-int bond_create_slave_symlinks(struct net_device *master,
-                              struct net_device *slave)
-{
-       char linkname[IFNAMSIZ+7];
-       int ret = 0;
-
-       /* first, create a link from the slave back to the master */
-       ret = sysfs_create_link(&(slave->dev.kobj), &(master->dev.kobj),
-                               "master");
-       if (ret)
-               return ret;
-       /* next, create a link from the master to the slave */
-       sprintf(linkname, "slave_%s", slave->name);
-       ret = sysfs_create_link(&(master->dev.kobj), &(slave->dev.kobj),
-                               linkname);
-
-       /* free the master link created earlier in case of error */
-       if (ret)
-               sysfs_remove_link(&(slave->dev.kobj), "master");
-
-       return ret;
-
-}
-
-void bond_destroy_slave_symlinks(struct net_device *master,
-                                struct net_device *slave)
-{
-       char linkname[IFNAMSIZ+7];
-
-       sysfs_remove_link(&(slave->dev.kobj), "master");
-       sprintf(linkname, "slave_%s", slave->name);
-       sysfs_remove_link(&(master->dev.kobj), linkname);
-}
-
-
 /*
  * Show the slaves in the current bond.
  */
@@ -210,11 +176,14 @@ static ssize_t bonding_show_slaves(struct device *d,
                                   struct device_attribute *attr, char *buf)
 {
        struct bonding *bond = to_bond(d);
+       struct list_head *iter;
        struct slave *slave;
        int res = 0;
 
-       read_lock(&bond->lock);
-       bond_for_each_slave(bond, slave) {
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       bond_for_each_slave(bond, slave, iter) {
                if (res > (PAGE_SIZE - IFNAMSIZ)) {
                        /* not enough space for another interface name */
                        if ((PAGE_SIZE - res) > 10)
@@ -224,7 +193,9 @@ static ssize_t bonding_show_slaves(struct device *d,
                }
                res += sprintf(buf + res, "%s ", slave->dev->name);
        }
-       read_unlock(&bond->lock);
+
+       rtnl_unlock();
+
        if (res)
                buf[res-1] = '\n'; /* eat the leftover space */
 
@@ -313,50 +284,26 @@ static ssize_t bonding_store_mode(struct device *d,
                                  struct device_attribute *attr,
                                  const char *buf, size_t count)
 {
-       int new_value, ret = count;
+       int new_value, ret;
        struct bonding *bond = to_bond(d);
 
-       if (!rtnl_trylock())
-               return restart_syscall();
-
-       if (bond->dev->flags & IFF_UP) {
-               pr_err("unable to update mode of %s because interface is up.\n",
-                      bond->dev->name);
-               ret = -EPERM;
-               goto out;
-       }
-
-       if (!list_empty(&bond->slave_list)) {
-               pr_err("unable to update mode of %s because it has slaves.\n",
-                       bond->dev->name);
-               ret = -EPERM;
-               goto out;
-       }
-
        new_value = bond_parse_parm(buf, bond_mode_tbl);
        if (new_value < 0)  {
                pr_err("%s: Ignoring invalid mode value %.*s.\n",
                       bond->dev->name, (int)strlen(buf) - 1, buf);
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
-       if ((new_value == BOND_MODE_ALB ||
-            new_value == BOND_MODE_TLB) &&
-           bond->params.arp_interval) {
-               pr_err("%s: %s mode is incompatible with arp monitoring.\n",
-                      bond->dev->name, bond_mode_tbl[new_value].modename);
-               ret = -EINVAL;
-               goto out;
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       ret = bond_option_mode_set(bond, new_value);
+       if (!ret) {
+               pr_info("%s: setting mode to %s (%d).\n",
+                       bond->dev->name, bond_mode_tbl[new_value].modename,
+                       new_value);
+               ret = count;
        }
 
-       /* don't cache arp_validate between modes */
-       bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
-       bond->params.mode = new_value;
-       bond_set_mode_ops(bond, bond->params.mode);
-       pr_info("%s: setting mode to %s (%d).\n",
-               bond->dev->name, bond_mode_tbl[new_value].modename,
-               new_value);
-out:
        rtnl_unlock();
        return ret;
 }
@@ -392,7 +339,6 @@ static ssize_t bonding_store_xmit_hash(struct device *d,
                ret = -EINVAL;
        } else {
                bond->params.xmit_policy = new_value;
-               bond_set_mode_ops(bond, bond->params.mode);
                pr_info("%s: setting xmit hash policy to %s (%d).\n",
                        bond->dev->name,
                        xmit_hashtype_tbl[new_value].modename, new_value);
@@ -522,7 +468,7 @@ static ssize_t bonding_store_fail_over_mac(struct device *d,
        if (!rtnl_trylock())
                return restart_syscall();
 
-       if (!list_empty(&bond->slave_list)) {
+       if (bond_has_slaves(bond)) {
                pr_err("%s: Can't alter fail_over_mac with slaves in bond.\n",
                       bond->dev->name);
                ret = -EPERM;
@@ -656,11 +602,15 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                                         const char *buf, size_t count)
 {
        struct bonding *bond = to_bond(d);
+       struct list_head *iter;
        struct slave *slave;
        __be32 newtarget, *targets;
        unsigned long *targets_rx;
        int ind, i, j, ret = -EINVAL;
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        targets = bond->params.arp_targets;
        newtarget = in_aton(buf + 1);
        /* look for adds */
@@ -688,7 +638,7 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                         &newtarget);
                /* not to race with bond_arp_rcv */
                write_lock_bh(&bond->lock);
-               bond_for_each_slave(bond, slave)
+               bond_for_each_slave(bond, slave, iter)
                        slave->target_last_arp_rx[ind] = jiffies;
                targets[ind] = newtarget;
                write_unlock_bh(&bond->lock);
@@ -714,7 +664,7 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                        &newtarget);
 
                write_lock_bh(&bond->lock);
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave(bond, slave, iter) {
                        targets_rx = slave->target_last_arp_rx;
                        j = ind;
                        for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++)
@@ -734,6 +684,7 @@ static ssize_t bonding_store_arp_targets(struct device *d,
 
        ret = count;
 out:
+       rtnl_unlock();
        return ret;
 }
 static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets);
@@ -1111,6 +1062,7 @@ static ssize_t bonding_store_primary(struct device *d,
                                     const char *buf, size_t count)
 {
        struct bonding *bond = to_bond(d);
+       struct list_head *iter;
        char ifname[IFNAMSIZ];
        struct slave *slave;
 
@@ -1138,7 +1090,7 @@ static ssize_t bonding_store_primary(struct device *d,
                goto out;
        }
 
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
                        pr_info("%s: Setting %s as primary slave.\n",
                                bond->dev->name, slave->dev->name);
@@ -1268,13 +1220,13 @@ static ssize_t bonding_show_active_slave(struct device *d,
                                         char *buf)
 {
        struct bonding *bond = to_bond(d);
-       struct slave *curr;
+       struct net_device *slave_dev;
        int count = 0;
 
        rcu_read_lock();
-       curr = rcu_dereference(bond->curr_active_slave);
-       if (USES_PRIMARY(bond->params.mode) && curr)
-               count = sprintf(buf, "%s\n", curr->dev->name);
+       slave_dev = bond_option_active_slave_get_rcu(bond);
+       if (slave_dev)
+               count = sprintf(buf, "%s\n", slave_dev->name);
        rcu_read_unlock();
 
        return count;
@@ -1284,80 +1236,33 @@ static ssize_t bonding_store_active_slave(struct device *d,
                                          struct device_attribute *attr,
                                          const char *buf, size_t count)
 {
-       struct slave *slave, *old_active, *new_active;
+       int ret;
        struct bonding *bond = to_bond(d);
        char ifname[IFNAMSIZ];
+       struct net_device *dev;
 
        if (!rtnl_trylock())
                return restart_syscall();
 
-       old_active = new_active = NULL;
-       block_netpoll_tx();
-       read_lock(&bond->lock);
-       write_lock_bh(&bond->curr_slave_lock);
-
-       if (!USES_PRIMARY(bond->params.mode)) {
-               pr_info("%s: Unable to change active slave; %s is in mode %d\n",
-                       bond->dev->name, bond->dev->name, bond->params.mode);
-               goto out;
-       }
-
        sscanf(buf, "%15s", ifname); /* IFNAMSIZ */
-
-       /* check to see if we are clearing active */
        if (!strlen(ifname) || buf[0] == '\n') {
-               pr_info("%s: Clearing current active slave.\n",
-                       bond->dev->name);
-               rcu_assign_pointer(bond->curr_active_slave, NULL);
-               bond_select_active_slave(bond);
-               goto out;
-       }
-
-       bond_for_each_slave(bond, slave) {
-               if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
-                       old_active = bond->curr_active_slave;
-                       new_active = slave;
-                       if (new_active == old_active) {
-                               /* do nothing */
-                               pr_info("%s: %s is already the current"
-                                       " active slave.\n",
-                                       bond->dev->name,
-                                       slave->dev->name);
-                               goto out;
-                       } else {
-                               if ((new_active) &&
-                                   (old_active) &&
-                                   (new_active->link == BOND_LINK_UP) &&
-                                   IS_UP(new_active->dev)) {
-                                       pr_info("%s: Setting %s as active"
-                                               " slave.\n",
-                                               bond->dev->name,
-                                               slave->dev->name);
-                                       bond_change_active_slave(bond,
-                                                                new_active);
-                               } else {
-                                       pr_info("%s: Could not set %s as"
-                                               " active slave; either %s is"
-                                               " down or the link is down.\n",
-                                               bond->dev->name,
-                                               slave->dev->name,
-                                               slave->dev->name);
-                               }
-                               goto out;
-                       }
+               dev = NULL;
+       } else {
+               dev = __dev_get_by_name(dev_net(bond->dev), ifname);
+               if (!dev) {
+                       ret = -ENODEV;
+                       goto out;
                }
        }
 
-       pr_info("%s: Unable to set %.*s as active slave.\n",
-               bond->dev->name, (int)strlen(buf) - 1, buf);
- out:
-       write_unlock_bh(&bond->curr_slave_lock);
-       read_unlock(&bond->lock);
-       unblock_netpoll_tx();
+       ret = bond_option_active_slave_set(bond, dev);
+       if (!ret)
+               ret = count;
 
+ out:
        rtnl_unlock();
 
-       return count;
+       return ret;
 
 }
 static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
@@ -1493,14 +1398,14 @@ static ssize_t bonding_show_queue_id(struct device *d,
                                     char *buf)
 {
        struct bonding *bond = to_bond(d);
+       struct list_head *iter;
        struct slave *slave;
        int res = 0;
 
        if (!rtnl_trylock())
                return restart_syscall();
 
-       read_lock(&bond->lock);
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (res > (PAGE_SIZE - IFNAMSIZ - 6)) {
                        /* not enough space for another interface_name:queue_id pair */
                        if ((PAGE_SIZE - res) > 10)
@@ -1511,9 +1416,9 @@ static ssize_t bonding_show_queue_id(struct device *d,
                res += sprintf(buf + res, "%s:%d ",
                               slave->dev->name, slave->queue_id);
        }
-       read_unlock(&bond->lock);
        if (res)
                buf[res-1] = '\n'; /* eat the leftover space */
+
        rtnl_unlock();
 
        return res;
@@ -1529,6 +1434,7 @@ static ssize_t bonding_store_queue_id(struct device *d,
 {
        struct slave *slave, *update_slave;
        struct bonding *bond = to_bond(d);
+       struct list_head *iter;
        u16 qid;
        int ret = count;
        char *delim;
@@ -1561,11 +1467,9 @@ static ssize_t bonding_store_queue_id(struct device *d,
        if (!sdev)
                goto err_no_cmd;
 
-       read_lock(&bond->lock);
-
        /* Search for thes slave and check for duplicate qids */
        update_slave = NULL;
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (sdev == slave->dev)
                        /*
                         * We don't need to check the matching
@@ -1573,23 +1477,20 @@ static ssize_t bonding_store_queue_id(struct device *d,
                         */
                        update_slave = slave;
                else if (qid && qid == slave->queue_id) {
-                       goto err_no_cmd_unlock;
+                       goto err_no_cmd;
                }
        }
 
        if (!update_slave)
-               goto err_no_cmd_unlock;
+               goto err_no_cmd;
 
        /* Actually set the qids for the slave */
        update_slave->queue_id = qid;
 
-       read_unlock(&bond->lock);
 out:
        rtnl_unlock();
        return ret;
 
-err_no_cmd_unlock:
-       read_unlock(&bond->lock);
 err_no_cmd:
        pr_info("invalid input for queue_id set for %s.\n",
                bond->dev->name);
@@ -1619,8 +1520,12 @@ static ssize_t bonding_store_slaves_active(struct device *d,
 {
        struct bonding *bond = to_bond(d);
        int new_value, ret = count;
+       struct list_head *iter;
        struct slave *slave;
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        if (sscanf(buf, "%d", &new_value) != 1) {
                pr_err("%s: no all_slaves_active value specified.\n",
                       bond->dev->name);
@@ -1640,8 +1545,7 @@ static ssize_t bonding_store_slaves_active(struct device *d,
                goto out;
        }
 
-       read_lock(&bond->lock);
-       bond_for_each_slave(bond, slave) {
+       bond_for_each_slave(bond, slave, iter) {
                if (!bond_is_active_slave(slave)) {
                        if (new_value)
                                slave->inactive = 0;
@@ -1649,8 +1553,8 @@ static ssize_t bonding_store_slaves_active(struct device *d,
                                slave->inactive = 1;
                }
        }
-       read_unlock(&bond->lock);
 out:
+       rtnl_unlock();
        return ret;
 }
 static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
@@ -1737,6 +1641,53 @@ out:
 static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
                   bonding_show_lp_interval, bonding_store_lp_interval);
 
+static ssize_t bonding_show_packets_per_slave(struct device *d,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       struct bonding *bond = to_bond(d);
+       int packets_per_slave = bond->params.packets_per_slave;
+
+       if (packets_per_slave > 1)
+               packets_per_slave = reciprocal_value(packets_per_slave);
+
+       return sprintf(buf, "%d\n", packets_per_slave);
+}
+
+static ssize_t bonding_store_packets_per_slave(struct device *d,
+                                              struct device_attribute *attr,
+                                              const char *buf, size_t count)
+{
+       struct bonding *bond = to_bond(d);
+       int new_value, ret = count;
+
+       if (sscanf(buf, "%d", &new_value) != 1) {
+               pr_err("%s: no packets_per_slave value specified.\n",
+                      bond->dev->name);
+               ret = -EINVAL;
+               goto out;
+       }
+       if (new_value < 0 || new_value > USHRT_MAX) {
+               pr_err("%s: packets_per_slave must be between 0 and %u\n",
+                      bond->dev->name, USHRT_MAX);
+               ret = -EINVAL;
+               goto out;
+       }
+       if (bond->params.mode != BOND_MODE_ROUNDROBIN)
+               pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n",
+                       bond->dev->name);
+       if (new_value > 1)
+               bond->params.packets_per_slave = reciprocal_value(new_value);
+       else
+               bond->params.packets_per_slave = new_value;
+out:
+       return ret;
+}
+
+static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
+                  bonding_show_packets_per_slave,
+                  bonding_store_packets_per_slave);
+
 static struct attribute *per_bond_attrs[] = {
        &dev_attr_slaves.attr,
        &dev_attr_mode.attr,
@@ -1768,6 +1719,7 @@ static struct attribute *per_bond_attrs[] = {
        &dev_attr_resend_igmp.attr,
        &dev_attr_min_links.attr,
        &dev_attr_lp_interval.attr,
+       &dev_attr_packets_per_slave.attr,
        NULL,
 };
 
index 03cf3fd..77a07a1 100644 (file)
 #define TX_QUEUE_OVERRIDE(mode)                                \
                        (((mode) == BOND_MODE_ACTIVEBACKUP) ||  \
                         ((mode) == BOND_MODE_ROUNDROBIN))
+
+#define BOND_MODE_IS_LB(mode)                  \
+               (((mode) == BOND_MODE_TLB) ||   \
+                ((mode) == BOND_MODE_ALB))
+
 /*
  * Less bad way to call ioctl from within the kernel; this needs to be
  * done some other way to get the call out of interrupt context.
        res; })
 
 /* slave list primitives */
-#define bond_to_slave(ptr) list_entry(ptr, struct slave, list)
+#define bond_slave_list(bond) (&(bond)->dev->adj_list.lower)
+
+#define bond_has_slaves(bond) !list_empty(bond_slave_list(bond))
 
 /* IMPORTANT: bond_first/last_slave can return NULL in case of an empty list */
 #define bond_first_slave(bond) \
-       list_first_entry_or_null(&(bond)->slave_list, struct slave, list)
+       (bond_has_slaves(bond) ? \
+               netdev_adjacent_get_private(bond_slave_list(bond)->next) : \
+               NULL)
 #define bond_last_slave(bond) \
-       (list_empty(&(bond)->slave_list) ? NULL : \
-                                          bond_to_slave((bond)->slave_list.prev))
+       (bond_has_slaves(bond) ? \
+               netdev_adjacent_get_private(bond_slave_list(bond)->prev) : \
+               NULL)
 
-#define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list)
-#define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)
-
-/* Since bond_first/last_slave can return NULL, these can return NULL too */
-#define bond_next_slave(bond, pos) \
-       (bond_is_last_slave(bond, pos) ? bond_first_slave(bond) : \
-                                        bond_to_slave((pos)->list.next))
-
-#define bond_prev_slave(bond, pos) \
-       (bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
-                                         bond_to_slave((pos)->list.prev))
-
-/**
- * bond_for_each_slave_from - iterate the slaves list from a starting point
- * @bond:      the bond holding this list.
- * @pos:       current slave.
- * @cnt:       counter for max number of moves
- * @start:     starting point.
- *
- * Caller must hold bond->lock
- */
-#define bond_for_each_slave_from(bond, pos, cnt, start) \
-       for (cnt = 0, pos = start; pos && cnt < (bond)->slave_cnt; \
-            cnt++, pos = bond_next_slave(bond, pos))
+#define bond_is_first_slave(bond, pos) (pos == bond_first_slave(bond))
+#define bond_is_last_slave(bond, pos) (pos == bond_last_slave(bond))
 
 /**
  * bond_for_each_slave - iterate over all slaves
  * @bond:      the bond holding this list
  * @pos:       current slave
+ * @iter:      list_head * iterator
  *
  * Caller must hold bond->lock
  */
-#define bond_for_each_slave(bond, pos) \
-       list_for_each_entry(pos, &(bond)->slave_list, list)
+#define bond_for_each_slave(bond, pos, iter) \
+       netdev_for_each_lower_private((bond)->dev, pos, iter)
 
 /* Caller must have rcu_read_lock */
-#define bond_for_each_slave_rcu(bond, pos) \
-       list_for_each_entry_rcu(pos, &(bond)->slave_list, list)
-
-/**
- * bond_for_each_slave_reverse - iterate in reverse from a given position
- * @bond:      the bond holding this list
- * @pos:       slave to continue from
- *
- * Caller must hold bond->lock
- */
-#define bond_for_each_slave_continue_reverse(bond, pos) \
-       list_for_each_entry_continue_reverse(pos, &(bond)->slave_list, list)
+#define bond_for_each_slave_rcu(bond, pos, iter) \
+       netdev_for_each_lower_private_rcu((bond)->dev, pos, iter)
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
 extern atomic_t netpoll_block_tx;
@@ -177,6 +156,7 @@ struct bond_params {
        int all_slaves_active;
        int resend_igmp;
        int lp_interval;
+       int packets_per_slave;
 };
 
 struct bond_parm_tbl {
@@ -188,7 +168,6 @@ struct bond_parm_tbl {
 
 struct slave {
        struct net_device *dev; /* first - useful for panic debug */
-       struct list_head list;
        struct bonding *bond; /* our master */
        int    delay;
        unsigned long jiffies;
@@ -228,7 +207,6 @@ struct slave {
  */
 struct bonding {
        struct   net_device *dev; /* first - useful for panic debug */
-       struct   list_head slave_list;
        struct   slave *curr_active_slave;
        struct   slave *current_arp_slave;
        struct   slave *primary_slave;
@@ -245,8 +223,7 @@ struct bonding {
        char     proc_file_name[IFNAMSIZ];
 #endif /* CONFIG_PROC_FS */
        struct   list_head bond_list;
-       int      (*xmit_hash_policy)(struct sk_buff *, int);
-       u16      rr_tx_counter;
+       u32      rr_tx_counter;
        struct   ad_bond_info ad_info;
        struct   alb_bond_info alb_info;
        struct   bond_params params;
@@ -276,13 +253,7 @@ struct bonding {
 static inline struct slave *bond_get_slave_by_dev(struct bonding *bond,
                                                  struct net_device *slave_dev)
 {
-       struct slave *slave = NULL;
-
-       bond_for_each_slave(bond, slave)
-               if (slave->dev == slave_dev)
-                       return slave;
-
-       return NULL;
+       return netdev_lower_dev_get_private(bond->dev, slave_dev);
 }
 
 static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
@@ -294,8 +265,7 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
 
 static inline bool bond_is_lb(const struct bonding *bond)
 {
-       return (bond->params.mode == BOND_MODE_TLB ||
-               bond->params.mode == BOND_MODE_ALB);
+       return BOND_MODE_IS_LB(bond->params.mode);
 }
 
 static inline void bond_set_active_slave(struct slave *slave)
@@ -432,21 +402,18 @@ static inline bool slave_can_tx(struct slave *slave)
 struct bond_net;
 
 int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
-struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr);
 int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
 void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id);
 int bond_create(struct net *net, const char *name);
 int bond_create_sysfs(struct bond_net *net);
 void bond_destroy_sysfs(struct bond_net *net);
 void bond_prepare_sysfs_group(struct bonding *bond);
-int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave);
-void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave);
 int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
 int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
 void bond_mii_monitor(struct work_struct *);
 void bond_loadbalance_arp_mon(struct work_struct *);
 void bond_activebackup_arp_mon(struct work_struct *);
-void bond_set_mode_ops(struct bonding *bond, int mode);
+int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count);
 int bond_parse_parm(const char *mode_arg, const struct bond_parm_tbl *tbl);
 void bond_select_active_slave(struct bonding *bond);
 void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
@@ -456,6 +423,14 @@ void bond_debug_register(struct bonding *bond);
 void bond_debug_unregister(struct bonding *bond);
 void bond_debug_reregister(struct bonding *bond);
 const char *bond_mode_name(int mode);
+void bond_setup(struct net_device *bond_dev);
+unsigned int bond_get_num_tx_queues(void);
+int bond_netlink_init(void);
+void bond_netlink_fini(void);
+int bond_option_mode_set(struct bonding *bond, int mode);
+int bond_option_active_slave_set(struct bonding *bond, struct net_device *slave_dev);
+struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
+struct net_device *bond_option_active_slave_get(struct bonding *bond);
 
 struct bond_net {
        struct net *            net;    /* Associated network namespace */
@@ -492,9 +467,24 @@ static inline void bond_destroy_proc_dir(struct bond_net *bn)
 static inline struct slave *bond_slave_has_mac(struct bonding *bond,
                                               const u8 *mac)
 {
+       struct list_head *iter;
        struct slave *tmp;
 
-       bond_for_each_slave(bond, tmp)
+       bond_for_each_slave(bond, tmp, iter)
+               if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr))
+                       return tmp;
+
+       return NULL;
+}
+
+/* Caller must hold rcu_read_lock() for read */
+static inline struct slave *bond_slave_has_mac_rcu(struct bonding *bond,
+                                              const u8 *mac)
+{
+       struct list_head *iter;
+       struct slave *tmp;
+
+       bond_for_each_slave_rcu(bond, tmp, iter)
                if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr))
                        return tmp;
 
@@ -528,4 +518,7 @@ extern const struct bond_parm_tbl fail_over_mac_tbl[];
 extern const struct bond_parm_tbl pri_reselect_tbl[];
 extern struct bond_parm_tbl ad_select_tbl[];
 
+/* exported from bond_netlink.c */
+extern struct rtnl_link_ops bond_link_ops;
+
 #endif /* _LINUX_BONDING_H */
index 3b1ff61..cf0f63e 100644 (file)
@@ -1347,7 +1347,7 @@ static int at91_can_probe(struct platform_device *pdev)
        priv->reg_base = addr;
        priv->devtype_data = *devtype_data;
        priv->clk = clk;
-       priv->pdata = pdev->dev.platform_data;
+       priv->pdata = dev_get_platdata(&pdev->dev);
        priv->mb0_id = 0x7ff;
 
        netif_napi_add(dev, &priv->napi, at91_poll, get_mb_rx_num(priv));
@@ -1405,10 +1405,10 @@ static int at91_can_remove(struct platform_device *pdev)
 
 static const struct platform_device_id at91_can_id_table[] = {
        {
-               .name = "at91_can",
+               .name = "at91sam9x5_can",
                .driver_data = (kernel_ulong_t)&at91_at91sam9x5_data,
        }, {
-               .name = "at91sam9x5_can",
+               .name = "at91_can",
                .driver_data = (kernel_ulong_t)&at91_at91sam9263_data,
        }, {
                /* sentinel */
index a2700d2..8a0b515 100644 (file)
@@ -539,7 +539,7 @@ static int bfin_can_probe(struct platform_device *pdev)
        struct resource *res_mem, *rx_irq, *tx_irq, *err_irq;
        unsigned short *pdata;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev, "No platform data provided!\n");
                err = -EINVAL;
index a668cd4..e3fc07c 100644 (file)
@@ -814,9 +814,6 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
                        msg_ctrl_save = priv->read_reg(priv,
                                        C_CAN_IFACE(MSGCTRL_REG, 0));
 
-                       if (msg_ctrl_save & IF_MCONT_EOB)
-                               return num_rx_pkts;
-
                        if (msg_ctrl_save & IF_MCONT_MSGLST) {
                                c_can_handle_lost_msg_obj(dev, 0, msg_obj);
                                num_rx_pkts++;
@@ -824,6 +821,9 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
                                continue;
                        }
 
+                       if (msg_ctrl_save & IF_MCONT_EOB)
+                               return num_rx_pkts;
+
                        if (!(msg_ctrl_save & IF_MCONT_NEWDAT))
                                continue;
 
index b374be7..bce0be5 100644 (file)
@@ -160,7 +160,6 @@ static int c_can_pci_probe(struct pci_dev *pdev,
        return 0;
 
 out_free_c_can:
-       pci_set_drvdata(pdev, NULL);
        free_c_can_dev(dev);
 out_iounmap:
        pci_iounmap(pdev, addr);
@@ -181,7 +180,6 @@ static void c_can_pci_remove(struct pci_dev *pdev)
 
        unregister_c_can_dev(dev);
 
-       pci_set_drvdata(pdev, NULL);
        free_c_can_dev(dev);
 
        pci_iounmap(pdev, priv->base);
index 294ced3..d66ac26 100644 (file)
@@ -322,7 +322,7 @@ static struct platform_driver c_can_plat_driver = {
        .driver = {
                .name = KBUILD_MODNAME,
                .owner = THIS_MODULE,
-               .of_match_table = of_match_ptr(c_can_of_table),
+               .of_match_table = c_can_of_table,
        },
        .probe = c_can_plat_probe,
        .remove = c_can_plat_remove,
index 034bdd8..ad76734 100644 (file)
@@ -152,7 +152,7 @@ static int cc770_get_platform_data(struct platform_device *pdev,
                                   struct cc770_priv *priv)
 {
 
-       struct cc770_platform_data *pdata = pdev->dev.platform_data;
+       struct cc770_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
        priv->can.clock.freq = pdata->osc_freq;
        if (priv->cpu_interface & CPUIF_DSC)
@@ -203,7 +203,7 @@ static int cc770_platform_probe(struct platform_device *pdev)
 
        if (pdev->dev.of_node)
                err = cc770_get_of_node_data(pdev, priv);
-       else if (pdev->dev.platform_data)
+       else if (dev_get_platdata(&pdev->dev))
                err = cc770_get_platform_data(pdev, priv);
        else
                err = -ENODEV;
index f9cba41..bda1888 100644 (file)
@@ -645,19 +645,6 @@ static int can_changelink(struct net_device *dev,
        /* We need synchronization with dev->stop() */
        ASSERT_RTNL();
 
-       if (data[IFLA_CAN_CTRLMODE]) {
-               struct can_ctrlmode *cm;
-
-               /* Do not allow changing controller mode while running */
-               if (dev->flags & IFF_UP)
-                       return -EBUSY;
-               cm = nla_data(data[IFLA_CAN_CTRLMODE]);
-               if (cm->flags & ~priv->ctrlmode_supported)
-                       return -EOPNOTSUPP;
-               priv->ctrlmode &= ~cm->mask;
-               priv->ctrlmode |= cm->flags;
-       }
-
        if (data[IFLA_CAN_BITTIMING]) {
                struct can_bittiming bt;
 
@@ -680,6 +667,19 @@ static int can_changelink(struct net_device *dev,
                }
        }
 
+       if (data[IFLA_CAN_CTRLMODE]) {
+               struct can_ctrlmode *cm;
+
+               /* Do not allow changing controller mode while running */
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+               cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+               if (cm->flags & ~priv->ctrlmode_supported)
+                       return -EOPNOTSUPP;
+               priv->ctrlmode &= ~cm->mask;
+               priv->ctrlmode |= cm->flags;
+       }
+
        if (data[IFLA_CAN_RESTART_MS]) {
                /* Do not allow changing restart delay while running */
                if (dev->flags & IFF_UP)
@@ -702,17 +702,17 @@ static int can_changelink(struct net_device *dev,
 static size_t can_get_size(const struct net_device *dev)
 {
        struct can_priv *priv = netdev_priv(dev);
-       size_t size;
-
-       size = nla_total_size(sizeof(u32));   /* IFLA_CAN_STATE */
-       size += sizeof(struct can_ctrlmode);  /* IFLA_CAN_CTRLMODE */
-       size += nla_total_size(sizeof(u32));  /* IFLA_CAN_RESTART_MS */
-       size += sizeof(struct can_bittiming); /* IFLA_CAN_BITTIMING */
-       size += sizeof(struct can_clock);     /* IFLA_CAN_CLOCK */
-       if (priv->do_get_berr_counter)        /* IFLA_CAN_BERR_COUNTER */
-               size += sizeof(struct can_berr_counter);
-       if (priv->bittiming_const)            /* IFLA_CAN_BITTIMING_CONST */
-               size += sizeof(struct can_bittiming_const);
+       size_t size = 0;
+
+       size += nla_total_size(sizeof(struct can_bittiming));   /* IFLA_CAN_BITTIMING */
+       if (priv->bittiming_const)                              /* IFLA_CAN_BITTIMING_CONST */
+               size += nla_total_size(sizeof(struct can_bittiming_const));
+       size += nla_total_size(sizeof(struct can_clock));       /* IFLA_CAN_CLOCK */
+       size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_STATE */
+       size += nla_total_size(sizeof(struct can_ctrlmode));    /* IFLA_CAN_CTRLMODE */
+       size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_RESTART_MS */
+       if (priv->do_get_berr_counter)                          /* IFLA_CAN_BERR_COUNTER */
+               size += nla_total_size(sizeof(struct can_berr_counter));
 
        return size;
 }
@@ -726,23 +726,20 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
 
        if (priv->do_get_state)
                priv->do_get_state(dev, &state);
-       if (nla_put_u32(skb, IFLA_CAN_STATE, state) ||
-           nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
-           nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
-           nla_put(skb, IFLA_CAN_BITTIMING,
+       if (nla_put(skb, IFLA_CAN_BITTIMING,
                    sizeof(priv->bittiming), &priv->bittiming) ||
+           (priv->bittiming_const &&
+            nla_put(skb, IFLA_CAN_BITTIMING_CONST,
+                    sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
            nla_put(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock) ||
+           nla_put_u32(skb, IFLA_CAN_STATE, state) ||
+           nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
+           nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
            (priv->do_get_berr_counter &&
             !priv->do_get_berr_counter(dev, &bec) &&
-            nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
-           (priv->bittiming_const &&
-            nla_put(skb, IFLA_CAN_BITTIMING_CONST,
-                    sizeof(*priv->bittiming_const), priv->bittiming_const)))
-               goto nla_put_failure;
+            nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)))
+               return -EMSGSIZE;
        return 0;
-
-nla_put_failure:
-       return -EMSGSIZE;
 }
 
 static size_t can_get_xstats_size(const struct net_device *dev)
index 71c677e..ae08cf1 100644 (file)
@@ -62,7 +62,7 @@
 #define FLEXCAN_MCR_BCC                        BIT(16)
 #define FLEXCAN_MCR_LPRIO_EN           BIT(13)
 #define FLEXCAN_MCR_AEN                        BIT(12)
-#define FLEXCAN_MCR_MAXMB(x)           ((x) & 0xf)
+#define FLEXCAN_MCR_MAXMB(x)           ((x) & 0x1f)
 #define FLEXCAN_MCR_IDAM_A             (0 << 8)
 #define FLEXCAN_MCR_IDAM_B             (1 << 8)
 #define FLEXCAN_MCR_IDAM_C             (2 << 8)
@@ -702,7 +702,6 @@ static int flexcan_chip_start(struct net_device *dev)
 {
        struct flexcan_priv *priv = netdev_priv(dev);
        struct flexcan_regs __iomem *regs = priv->base;
-       unsigned int i;
        int err;
        u32 reg_mcr, reg_ctrl;
 
@@ -736,9 +735,11 @@ static int flexcan_chip_start(struct net_device *dev)
         *
         */
        reg_mcr = flexcan_read(&regs->mcr);
+       reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
        reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
                FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
-               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS;
+               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
+               FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        flexcan_write(reg_mcr, &regs->mcr);
 
@@ -772,16 +773,9 @@ static int flexcan_chip_start(struct net_device *dev)
        netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
        flexcan_write(reg_ctrl, &regs->ctrl);
 
-       for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) {
-               flexcan_write(0, &regs->cantxfg[i].can_ctrl);
-               flexcan_write(0, &regs->cantxfg[i].can_id);
-               flexcan_write(0, &regs->cantxfg[i].data[0]);
-               flexcan_write(0, &regs->cantxfg[i].data[1]);
-
-               /* put MB into rx queue */
-               flexcan_write(FLEXCAN_MB_CNT_CODE(0x4),
-                       &regs->cantxfg[i].can_ctrl);
-       }
+       /* Abort any pending TX, mark Mailbox as INACTIVE */
+       flexcan_write(FLEXCAN_MB_CNT_CODE(0x4),
+                     &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
 
        /* acceptance mask/acceptance code (accept everything) */
        flexcan_write(0x0, &regs->rxgmask);
@@ -991,9 +985,9 @@ static void unregister_flexcandev(struct net_device *dev)
 }
 
 static const struct of_device_id flexcan_of_match[] = {
-       { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
-       { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
        { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
+       { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
+       { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, flexcan_of_match);
@@ -1074,7 +1068,7 @@ static int flexcan_probe(struct platform_device *pdev)
        priv->dev = dev;
        priv->clk_ipg = clk_ipg;
        priv->clk_per = clk_per;
-       priv->pdata = pdev->dev.platform_data;
+       priv->pdata = dev_get_platdata(&pdev->dev);
        priv->devtype_data = devtype_data;
 
        priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
index 36bd6fa..ab5909a 100644 (file)
@@ -1769,7 +1769,7 @@ static int ican3_probe(struct platform_device *pdev)
        struct device *dev;
        int ret;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata)
                return -ENXIO;
 
index fe7dd69..08ac401 100644 (file)
@@ -999,7 +999,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
 {
        struct net_device *net;
        struct mcp251x_priv *priv;
-       struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+       struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev);
        int ret = -ENODEV;
 
        if (!pdata)
index 9c24d60..e98abb9 100644 (file)
@@ -297,8 +297,8 @@ struct mscan_priv {
        struct napi_struct napi;
 };
 
-extern struct net_device *alloc_mscandev(void);
-extern int register_mscandev(struct net_device *dev, int mscan_clksrc);
-extern void unregister_mscandev(struct net_device *dev);
+struct net_device *alloc_mscandev(void);
+int register_mscandev(struct net_device *dev, int mscan_clksrc);
+void unregister_mscandev(struct net_device *dev);
 
 #endif /* __MSCAN_H__ */
index 5c314a9..5f0e9b3 100644 (file)
@@ -964,7 +964,6 @@ static void pch_can_remove(struct pci_dev *pdev)
                pci_disable_msi(priv->dev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        pch_can_reset(priv);
        pci_iounmap(pdev, priv->regs);
        free_candev(priv->ndev);
index 3752342..8359213 100644 (file)
@@ -207,7 +207,6 @@ static void ems_pci_del_card(struct pci_dev *pdev)
        kfree(card);
 
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static void ems_pci_card_reset(struct ems_pci_card *card)
index 217585b..087b13b 100644 (file)
@@ -387,7 +387,6 @@ static void kvaser_pci_remove_one(struct pci_dev *pdev)
 
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static struct pci_driver kvaser_pci_driver = {
index 6b6f0ad..065ca49 100644 (file)
@@ -744,8 +744,6 @@ static void peak_pci_remove(struct pci_dev *pdev)
        pci_iounmap(pdev, cfg_base);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-
-       pci_set_drvdata(pdev, NULL);
 }
 
 static struct pci_driver peak_pci_driver = {
index c52c1e9..f9b4f81 100644 (file)
@@ -477,7 +477,6 @@ static void plx_pci_del_card(struct pci_dev *pdev)
        kfree(card);
 
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 /*
index 8e259c5..29f9b63 100644 (file)
@@ -76,7 +76,7 @@ static int sp_probe(struct platform_device *pdev)
        struct resource *res_mem, *res_irq;
        struct sja1000_platform_data *pdata;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev, "No platform data provided!\n");
                err = -ENODEV;
index 874188b..25377e5 100644 (file)
@@ -76,6 +76,10 @@ MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
 /* maximum rx buffer len: extended CAN frame with timestamp */
 #define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1)
 
+#define SLC_CMD_LEN 1
+#define SLC_SFF_ID_LEN 3
+#define SLC_EFF_ID_LEN 8
+
 struct slcan {
        int                     magic;
 
@@ -142,47 +146,63 @@ static void slc_bump(struct slcan *sl)
 {
        struct sk_buff *skb;
        struct can_frame cf;
-       int i, dlc_pos, tmp;
-       unsigned long ultmp;
-       char cmd = sl->rbuff[0];
-
-       if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R'))
+       int i, tmp;
+       u32 tmpid;
+       char *cmd = sl->rbuff;
+
+       cf.can_id = 0;
+
+       switch (*cmd) {
+       case 'r':
+               cf.can_id = CAN_RTR_FLAG;
+               /* fallthrough */
+       case 't':
+               /* store dlc ASCII value and terminate SFF CAN ID string */
+               cf.can_dlc = sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN];
+               sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN] = 0;
+               /* point to payload data behind the dlc */
+               cmd += SLC_CMD_LEN + SLC_SFF_ID_LEN + 1;
+               break;
+       case 'R':
+               cf.can_id = CAN_RTR_FLAG;
+               /* fallthrough */
+       case 'T':
+               cf.can_id |= CAN_EFF_FLAG;
+               /* store dlc ASCII value and terminate EFF CAN ID string */
+               cf.can_dlc = sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN];
+               sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN] = 0;
+               /* point to payload data behind the dlc */
+               cmd += SLC_CMD_LEN + SLC_EFF_ID_LEN + 1;
+               break;
+       default:
                return;
+       }
 
-       if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */
-               dlc_pos = 4; /* dlc position tiiid */
-       else
-               dlc_pos = 9; /* dlc position Tiiiiiiiid */
-
-       if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9')))
+       if (kstrtou32(sl->rbuff + SLC_CMD_LEN, 16, &tmpid))
                return;
 
-       cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */
+       cf.can_id |= tmpid;
 
-       sl->rbuff[dlc_pos] = 0; /* terminate can_id string */
-
-       if (kstrtoul(sl->rbuff+1, 16, &ultmp))
+       /* get can_dlc from sanitized ASCII value */
+       if (cf.can_dlc >= '0' && cf.can_dlc < '9')
+               cf.can_dlc -= '0';
+       else
                return;
 
-       cf.can_id = ultmp;
-
-       if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */
-               cf.can_id |= CAN_EFF_FLAG;
-
-       if ((cmd | 0x20) == 'r') /* RTR frame */
-               cf.can_id |= CAN_RTR_FLAG;
-
        *(u64 *) (&cf.data) = 0; /* clear payload */
 
-       for (i = 0, dlc_pos++; i < cf.can_dlc; i++) {
-               tmp = hex_to_bin(sl->rbuff[dlc_pos++]);
-               if (tmp < 0)
-                       return;
-               cf.data[i] = (tmp << 4);
-               tmp = hex_to_bin(sl->rbuff[dlc_pos++]);
-               if (tmp < 0)
-                       return;
-               cf.data[i] |= tmp;
+       /* RTR frames may have a dlc > 0 but they never have any data bytes */
+       if (!(cf.can_id & CAN_RTR_FLAG)) {
+               for (i = 0; i < cf.can_dlc; i++) {
+                       tmp = hex_to_bin(*cmd++);
+                       if (tmp < 0)
+                               return;
+                       cf.data[i] = (tmp << 4);
+                       tmp = hex_to_bin(*cmd++);
+                       if (tmp < 0)
+                               return;
+                       cf.data[i] |= tmp;
+               }
        }
 
        skb = dev_alloc_skb(sizeof(struct can_frame) +
@@ -209,7 +229,6 @@ static void slc_bump(struct slcan *sl)
 /* parse tty input stream */
 static void slcan_unesc(struct slcan *sl, unsigned char s)
 {
-
        if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */
                if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
                    (sl->rcount > 4))  {
@@ -236,27 +255,46 @@ static void slcan_unesc(struct slcan *sl, unsigned char s)
 /* Encapsulate one can_frame and stuff into a TTY queue. */
 static void slc_encaps(struct slcan *sl, struct can_frame *cf)
 {
-       int actual, idx, i;
-       char cmd;
+       int actual, i;
+       unsigned char *pos;
+       unsigned char *endpos;
+       canid_t id = cf->can_id;
+
+       pos = sl->xbuff;
 
        if (cf->can_id & CAN_RTR_FLAG)
-               cmd = 'R'; /* becomes 'r' in standard frame format */
+               *pos = 'R'; /* becomes 'r' in standard frame format (SFF) */
        else
-               cmd = 'T'; /* becomes 't' in standard frame format */
+               *pos = 'T'; /* becomes 't' in standard frame format (SSF) */
 
-       if (cf->can_id & CAN_EFF_FLAG)
-               sprintf(sl->xbuff, "%c%08X%d", cmd,
-                       cf->can_id & CAN_EFF_MASK, cf->can_dlc);
-       else
-               sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20,
-                       cf->can_id & CAN_SFF_MASK, cf->can_dlc);
+       /* determine number of chars for the CAN-identifier */
+       if (cf->can_id & CAN_EFF_FLAG) {
+               id &= CAN_EFF_MASK;
+               endpos = pos + SLC_EFF_ID_LEN;
+       } else {
+               *pos |= 0x20; /* convert R/T to lower case for SFF */
+               id &= CAN_SFF_MASK;
+               endpos = pos + SLC_SFF_ID_LEN;
+       }
 
-       idx = strlen(sl->xbuff);
+       /* build 3 (SFF) or 8 (EFF) digit CAN identifier */
+       pos++;
+       while (endpos >= pos) {
+               *endpos-- = hex_asc_upper[id & 0xf];
+               id >>= 4;
+       }
+
+       pos += (cf->can_id & CAN_EFF_FLAG) ? SLC_EFF_ID_LEN : SLC_SFF_ID_LEN;
 
-       for (i = 0; i < cf->can_dlc; i++)
-               sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]);
+       *pos++ = cf->can_dlc + '0';
+
+       /* RTR frames may have a dlc > 0 but they never have any data bytes */
+       if (!(cf->can_id & CAN_RTR_FLAG)) {
+               for (i = 0; i < cf->can_dlc; i++)
+                       pos = hex_byte_pack_upper(pos, cf->data[i]);
+       }
 
-       strcat(sl->xbuff, "\r"); /* add terminating character */
+       *pos++ = '\r';
 
        /* Order of next two lines is *very* important.
         * When we are sending a little amount of data,
@@ -267,8 +305,8 @@ static void slc_encaps(struct slcan *sl, struct can_frame *cf)
         *       14 Oct 1994  Dmitry Gorodchanin.
         */
        set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
-       actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff));
-       sl->xleft = strlen(sl->xbuff) - actual;
+       actual = sl->tty->ops->write(sl->tty, sl->xbuff, pos - sl->xbuff);
+       sl->xleft = (pos - sl->xbuff) - actual;
        sl->xhead = sl->xbuff + actual;
        sl->dev->stats.tx_bytes += cf->can_dlc;
 }
@@ -286,11 +324,13 @@ static void slcan_write_wakeup(struct tty_struct *tty)
        if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev))
                return;
 
+       spin_lock(&sl->lock);
        if (sl->xleft <= 0)  {
                /* Now serial buffer is almost free & we can start
                 * transmission of another packet */
                sl->dev->stats.tx_packets++;
                clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+               spin_unlock(&sl->lock);
                netif_wake_queue(sl->dev);
                return;
        }
@@ -298,6 +338,7 @@ static void slcan_write_wakeup(struct tty_struct *tty)
        actual = tty->ops->write(tty, sl->xhead, sl->xleft);
        sl->xleft -= actual;
        sl->xhead += actual;
+       spin_unlock(&sl->lock);
 }
 
 /* Send a can_frame to a TTY queue. */
index afd7d85..35f0622 100644 (file)
@@ -71,34 +71,34 @@ struct softing {
        } id;
 };
 
-extern int softing_default_output(struct net_device *netdev);
+int softing_default_output(struct net_device *netdev);
 
-extern ktime_t softing_raw2ktime(struct softing *card, u32 raw);
+ktime_t softing_raw2ktime(struct softing *card, u32 raw);
 
-extern int softing_chip_poweron(struct softing *card);
+int softing_chip_poweron(struct softing *card);
 
-extern int softing_bootloader_command(struct softing *card, int16_t cmd,
-               const char *msg);
+int softing_bootloader_command(struct softing *card, int16_t cmd,
+                              const char *msg);
 
 /* Load firmware after reset */
-extern int softing_load_fw(const char *file, struct softing *card,
-                       __iomem uint8_t *virt, unsigned int size, int offset);
+int softing_load_fw(const char *file, struct softing *card,
+                   __iomem uint8_t *virt, unsigned int size, int offset);
 
 /* Load final application firmware after bootloader */
-extern int softing_load_app_fw(const char *file, struct softing *card);
+int softing_load_app_fw(const char *file, struct softing *card);
 
 /*
  * enable or disable irq
  * only called with fw.lock locked
  */
-extern int softing_enable_irq(struct softing *card, int enable);
+int softing_enable_irq(struct softing *card, int enable);
 
 /* start/stop 1 bus on card */
-extern int softing_startstop(struct net_device *netdev, int up);
+int softing_startstop(struct net_device *netdev, int up);
 
 /* netif_rx() */
-extern int softing_netdev_rx(struct net_device *netdev,
-               const struct can_frame *msg, ktime_t ktime);
+int softing_netdev_rx(struct net_device *netdev, const struct can_frame *msg,
+                     ktime_t ktime);
 
 /* SOFTING DPRAM mappings */
 #define DPRAM_RX               0x0000
index 65eef1e..6cd5c01 100644 (file)
@@ -768,7 +768,7 @@ static int softing_pdev_remove(struct platform_device *pdev)
 
 static int softing_pdev_probe(struct platform_device *pdev)
 {
-       const struct softing_platform_data *pdat = pdev->dev.platform_data;
+       const struct softing_platform_data *pdat = dev_get_platdata(&pdev->dev);
        struct softing *card;
        struct net_device *netdev;
        struct softing_priv *priv;
index 3a349a2..60d95b4 100644 (file)
@@ -286,15 +286,6 @@ static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask)
        return (hecc_read(priv, reg) & bit_mask) ? 1 : 0;
 }
 
-static int ti_hecc_get_state(const struct net_device *ndev,
-       enum can_state *state)
-{
-       struct ti_hecc_priv *priv = netdev_priv(ndev);
-
-       *state = priv->can.state;
-       return 0;
-}
-
 static int ti_hecc_set_btc(struct ti_hecc_priv *priv)
 {
        struct can_bittiming *bit_timing = &priv->can.bittiming;
@@ -894,7 +885,7 @@ static int ti_hecc_probe(struct platform_device *pdev)
        void __iomem *addr;
        int err = -ENODEV;
 
-       pdata = pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev, "No platform data\n");
                goto probe_exit;
@@ -940,7 +931,6 @@ static int ti_hecc_probe(struct platform_device *pdev)
 
        priv->can.bittiming_const = &ti_hecc_bittiming_const;
        priv->can.do_set_mode = ti_hecc_do_set_mode;
-       priv->can.do_get_state = ti_hecc_get_state;
        priv->can.do_get_berr_counter = ti_hecc_get_berr_counter;
        priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
 
index 3b95465..4b2d5ed 100644 (file)
@@ -1544,9 +1544,9 @@ static int kvaser_usb_init_one(struct usb_interface *intf,
        return 0;
 }
 
-static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
-                                    struct usb_endpoint_descriptor **in,
-                                    struct usb_endpoint_descriptor **out)
+static int kvaser_usb_get_endpoints(const struct usb_interface *intf,
+                                   struct usb_endpoint_descriptor **in,
+                                   struct usb_endpoint_descriptor **out)
 {
        const struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
@@ -1557,12 +1557,18 @@ static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
-               if (usb_endpoint_is_bulk_in(endpoint))
+               if (!*in && usb_endpoint_is_bulk_in(endpoint))
                        *in = endpoint;
 
-               if (usb_endpoint_is_bulk_out(endpoint))
+               if (!*out && usb_endpoint_is_bulk_out(endpoint))
                        *out = endpoint;
+
+               /* use first bulk endpoint for in and out */
+               if (*in && *out)
+                       return 0;
        }
+
+       return -ENODEV;
 }
 
 static int kvaser_usb_probe(struct usb_interface *intf,
@@ -1576,8 +1582,8 @@ static int kvaser_usb_probe(struct usb_interface *intf,
        if (!dev)
                return -ENOMEM;
 
-       kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
-       if (!dev->bulk_in || !dev->bulk_out) {
+       err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+       if (err) {
                dev_err(&intf->dev, "Cannot get usb endpoint(s)");
                return err;
        }
index a0f647f..0b7a4c3 100644 (file)
@@ -463,7 +463,7 @@ static int peak_usb_start(struct peak_usb_device *dev)
        if (i < PCAN_USB_MAX_TX_URBS) {
                if (i == 0) {
                        netdev_err(netdev, "couldn't setup any tx URB\n");
-                       return err;
+                       goto err_tx;
                }
 
                netdev_warn(netdev, "tx performance may be slow\n");
@@ -472,7 +472,7 @@ static int peak_usb_start(struct peak_usb_device *dev)
        if (dev->adapter->dev_start) {
                err = dev->adapter->dev_start(dev);
                if (err)
-                       goto failed;
+                       goto err_adapter;
        }
 
        dev->state |= PCAN_USB_STATE_STARTED;
@@ -481,19 +481,26 @@ static int peak_usb_start(struct peak_usb_device *dev)
        if (dev->adapter->dev_set_bus) {
                err = dev->adapter->dev_set_bus(dev, 1);
                if (err)
-                       goto failed;
+                       goto err_adapter;
        }
 
        dev->can.state = CAN_STATE_ERROR_ACTIVE;
 
        return 0;
 
-failed:
+err_adapter:
        if (err == -ENODEV)
                netif_device_detach(dev->netdev);
 
        netdev_warn(netdev, "couldn't submit control: %d\n", err);
 
+       for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) {
+               usb_free_urb(dev->tx_contexts[i].urb);
+               dev->tx_contexts[i].urb = NULL;
+       }
+err_tx:
+       usb_kill_anchored_urbs(&dev->rx_submitted);
+
        return err;
 }
 
index f00c763..65b735d 100644 (file)
@@ -35,7 +35,7 @@ config EL3
 
 config 3C515
        tristate "3c515 ISA \"Fast EtherLink\""
-       depends on (ISA || EISA) && ISA_DMA_API
+       depends on ISA && ISA_DMA_API
        ---help---
          If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
          network card, say Y and read the Ethernet-HOWTO, available from
@@ -70,7 +70,7 @@ config VORTEX
        select MII
        ---help---
          This option enables driver support for a large number of 10Mbps and
-         10/100Mbps EISA, PCI and PCMCIA 3Com network cards:
+         10/100Mbps EISA, PCI and Cardbus 3Com network cards:
 
          "Vortex"    (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI
          "Boomerang" (EtherLink XL 3c900 or 3c905)            PCI
index 144942f..465cc71 100644 (file)
@@ -2525,7 +2525,6 @@ typhoon_remove_one(struct pci_dev *pdev)
        pci_release_regions(pdev);
        pci_clear_mwi(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 }
 
index ef325ff..2923c51 100644 (file)
@@ -28,42 +28,42 @@ extern int ei_debug;
 #endif
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
-extern void ei_poll(struct net_device *dev);
-extern void eip_poll(struct net_device *dev);
+void ei_poll(struct net_device *dev);
+void eip_poll(struct net_device *dev);
 #endif
 
 
 /* Without I/O delay - non ISA or later chips */
-extern void NS8390_init(struct net_device *dev, int startp);
-extern int ei_open(struct net_device *dev);
-extern int ei_close(struct net_device *dev);
-extern irqreturn_t ei_interrupt(int irq, void *dev_id);
-extern void ei_tx_timeout(struct net_device *dev);
-extern netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
-extern void ei_set_multicast_list(struct net_device *dev);
-extern struct net_device_stats *ei_get_stats(struct net_device *dev);
+void NS8390_init(struct net_device *dev, int startp);
+int ei_open(struct net_device *dev);
+int ei_close(struct net_device *dev);
+irqreturn_t ei_interrupt(int irq, void *dev_id);
+void ei_tx_timeout(struct net_device *dev);
+netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void ei_set_multicast_list(struct net_device *dev);
+struct net_device_stats *ei_get_stats(struct net_device *dev);
 
 extern const struct net_device_ops ei_netdev_ops;
 
-extern struct net_device *__alloc_ei_netdev(int size);
+struct net_device *__alloc_ei_netdev(int size);
 static inline struct net_device *alloc_ei_netdev(void)
 {
        return __alloc_ei_netdev(0);
 }
 
 /* With I/O delay form */
-extern void NS8390p_init(struct net_device *dev, int startp);
-extern int eip_open(struct net_device *dev);
-extern int eip_close(struct net_device *dev);
-extern irqreturn_t eip_interrupt(int irq, void *dev_id);
-extern void eip_tx_timeout(struct net_device *dev);
-extern netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
-extern void eip_set_multicast_list(struct net_device *dev);
-extern struct net_device_stats *eip_get_stats(struct net_device *dev);
+void NS8390p_init(struct net_device *dev, int startp);
+int eip_open(struct net_device *dev);
+int eip_close(struct net_device *dev);
+irqreturn_t eip_interrupt(int irq, void *dev_id);
+void eip_tx_timeout(struct net_device *dev);
+netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void eip_set_multicast_list(struct net_device *dev);
+struct net_device_stats *eip_get_stats(struct net_device *dev);
 
 extern const struct net_device_ops eip_netdev_ops;
 
-extern struct net_device *__alloc_eip_netdev(int size);
+struct net_device *__alloc_eip_netdev(int size);
 static inline struct net_device *alloc_eip_netdev(void)
 {
        return __alloc_eip_netdev(0);
index f92f001..36fa577 100644 (file)
@@ -702,7 +702,7 @@ static int ax_init_dev(struct net_device *dev)
                        for (i = 0; i < 16; i++)
                                SA_prom[i] = SA_prom[i+i];
 
-               memcpy(dev->dev_addr, SA_prom, 6);
+               memcpy(dev->dev_addr, SA_prom, ETH_ALEN);
        }
 
 #ifdef CONFIG_AX88796_93CX6
index 9220108..fc14a85 100644 (file)
@@ -389,9 +389,7 @@ err_out_free_netdev:
        free_netdev (dev);
 err_out_free_res:
        release_region (ioaddr, NE_IO_EXTENT);
-       pci_set_drvdata (pdev, NULL);
        return -ENODEV;
-
 }
 
 /*
@@ -655,7 +653,6 @@ static void ne2k_pci_remove_one(struct pci_dev *pdev)
        release_region(dev->base_addr, NE_IO_EXTENT);
        free_netdev(dev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
index 8b04bfc..171d73c 100644 (file)
@@ -835,7 +835,6 @@ static int starfire_init_one(struct pci_dev *pdev,
        return 0;
 
 err_out_cleardev:
-       pci_set_drvdata(pdev, NULL);
        iounmap(base);
 err_out_free_res:
        pci_release_regions (pdev);
@@ -2012,7 +2011,6 @@ static void starfire_remove_one(struct pci_dev *pdev)
        iounmap(np->base);
        pci_release_regions(pdev);
 
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);                       /* Will also free np!! */
 }
 
index 7a07ee0..6dec86a 100644 (file)
@@ -104,6 +104,6 @@ struct bfin_mac_local {
 #endif
 };
 
-extern int bfin_get_ether_addr(char *addr);
+int bfin_get_ether_addr(char *addr);
 
 #endif
index 0a5837b..ae33a99 100644 (file)
@@ -242,13 +242,13 @@ struct lance_private
 #define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
 
 /* Now the prototypes we export */
-extern int lance_open(struct net_device *dev);
-extern int lance_close (struct net_device *dev);
-extern int lance_start_xmit (struct sk_buff *skb, struct net_device *dev);
-extern void lance_set_multicast (struct net_device *dev);
-extern void lance_tx_timeout(struct net_device *dev);
+int lance_open(struct net_device *dev);
+int lance_close (struct net_device *dev);
+int lance_start_xmit (struct sk_buff *skb, struct net_device *dev);
+void lance_set_multicast (struct net_device *dev);
+void lance_tx_timeout(struct net_device *dev);
 #ifdef CONFIG_NET_POLL_CONTROLLER
-extern void lance_poll(struct net_device *dev);
+void lance_poll(struct net_device *dev);
 #endif
 
 #endif /* ndef _7990_H */
index 1b1429d..d042511 100644 (file)
@@ -1711,7 +1711,6 @@ static void amd8111e_remove_one(struct pci_dev *pdev)
                free_netdev(dev);
                pci_release_regions(pdev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 static void amd8111e_config_ipg(struct net_device* dev)
@@ -1967,7 +1966,6 @@ err_free_reg:
 
 err_disable_pdev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        return err;
 
 }
index 10ceca5..e07ce5f 100644 (file)
@@ -586,10 +586,10 @@ static unsigned long __init lance_probe1( struct net_device *dev,
        switch( lp->cardtype ) {
          case OLD_RIEBL:
                /* No ethernet address! (Set some default address) */
-               memcpy( dev->dev_addr, OldRieblDefHwaddr, 6 );
+               memcpy(dev->dev_addr, OldRieblDefHwaddr, ETH_ALEN);
                break;
          case NEW_RIEBL:
-               lp->memcpy_f( dev->dev_addr, RIEBL_HWADDR_ADDR, 6 );
+               lp->memcpy_f(dev->dev_addr, RIEBL_HWADDR_ADDR, ETH_ALEN);
                break;
          case PAM_CARD:
                i = IO->eeprom;
index 91d52b4..427c148 100644 (file)
@@ -1138,7 +1138,7 @@ static int au1000_probe(struct platform_device *pdev)
                aup->phy1_search_mac0 = 1;
        } else {
                if (is_valid_ether_addr(pd->mac)) {
-                       memcpy(dev->dev_addr, pd->mac, 6);
+                       memcpy(dev->dev_addr, pd->mac, ETH_ALEN);
                } else {
                        /* Set a random MAC since no valid provided by platform_data. */
                        eth_hw_addr_random(dev);
index 94edc9c..cc35f6f 100644 (file)
@@ -344,8 +344,8 @@ static void cp_to_buf(const int type, void *to, const void *from, int len)
                }
 
                clen = len & 1;
-               rtp = tp;
-               rfp = fp;
+               rtp = (unsigned char *)tp;
+               rfp = (const unsigned char *)fp;
                while (clen--) {
                        *rtp++ = *rfp++;
                }
@@ -372,8 +372,8 @@ static void cp_to_buf(const int type, void *to, const void *from, int len)
                 * do the rest, if any.
                 */
                clen = len & 15;
-               rtp = (unsigned char *) tp;
-               rfp = (unsigned char *) fp;
+               rtp = (unsigned char *)tp;
+               rfp = (const unsigned char *)fp;
                while (clen--) {
                        *rtp++ = *rfp++;
                }
@@ -403,8 +403,8 @@ static void cp_from_buf(const int type, void *to, const void *from, int len)
 
                clen = len & 1;
 
-               rtp = tp;
-               rfp = fp;
+               rtp = (unsigned char *)tp;
+               rfp = (const unsigned char *)fp;
 
                while (clen--) {
                        *rtp++ = *rfp++;
@@ -433,8 +433,8 @@ static void cp_from_buf(const int type, void *to, const void *from, int len)
                 * do the rest, if any.
                 */
                clen = len & 15;
-               rtp = (unsigned char *) tp;
-               rfp = (unsigned char *) fp;
+               rtp = (unsigned char *)tp;
+               rfp = (const unsigned char *)fp;
                while (clen--) {
                        *rtp++ = *rfp++;
                }
index 5c72843..256f590 100644 (file)
@@ -754,7 +754,7 @@ lance_open(struct net_device *dev)
        int i;
 
        if (dev->irq == 0 ||
-               request_irq(dev->irq, lance_interrupt, 0, lp->name, dev)) {
+               request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) {
                return -EAGAIN;
        }
 
index 2d8e288..38492e0 100644 (file)
@@ -1675,7 +1675,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
                                pr_cont(" warning: CSR address invalid,\n");
                                pr_info("    using instead PROM address of");
                        }
-                       memcpy(dev->dev_addr, promaddr, 6);
+                       memcpy(dev->dev_addr, promaddr, ETH_ALEN);
                }
        }
 
@@ -2818,7 +2818,6 @@ static void pcnet32_remove_one(struct pci_dev *pdev)
                                    lp->init_block, lp->init_dma_addr);
                free_netdev(dev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index a597b76..daae0e0 100644 (file)
@@ -1220,8 +1220,8 @@ static void bmac_reset_and_enable(struct net_device *dev)
        if (skb != NULL) {
                data = skb_put(skb, ETHERMINPACKET);
                memset(data, 0, ETHERMINPACKET);
-               memcpy(data, dev->dev_addr, 6);
-               memcpy(data+6, dev->dev_addr, 6);
+               memcpy(data, dev->dev_addr, ETH_ALEN);
+               memcpy(data + ETH_ALEN, dev->dev_addr, ETH_ALEN);
                bmac_transmit_packet(skb, dev);
        }
        spin_unlock_irqrestore(&bp->lock, flags);
index 9e16014..d818ded 100644 (file)
@@ -725,10 +725,10 @@ static int arc_emac_probe(struct platform_device *pdev)
        /* Get MAC address from device tree */
        mac_addr = of_get_mac_address(pdev->dev.of_node);
 
-       if (!mac_addr || !is_valid_ether_addr(mac_addr))
-               eth_hw_addr_random(ndev);
-       else
+       if (mac_addr)
                memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+       else
+               eth_hw_addr_random(ndev);
 
        dev_info(&pdev->dev, "MAC address is now %pM\n", ndev->dev_addr);
 
index fc95b23..5aa5e81 100644 (file)
@@ -1367,7 +1367,6 @@ static void alx_remove(struct pci_dev *pdev)
 
        pci_disable_pcie_error_reporting(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        free_netdev(alx->dev);
 }
index 0f05565..7f9369a 100644 (file)
@@ -600,7 +600,7 @@ struct atl1c_adapter {
 extern char atl1c_driver_name[];
 extern char atl1c_driver_version[];
 
-extern void atl1c_reinit_locked(struct atl1c_adapter *adapter);
-extern s32 atl1c_reset_hw(struct atl1c_hw *hw);
-extern void atl1c_set_ethtool_ops(struct net_device *netdev);
+void atl1c_reinit_locked(struct atl1c_adapter *adapter);
+s32 atl1c_reset_hw(struct atl1c_hw *hw);
+void atl1c_set_ethtool_ops(struct net_device *netdev);
 #endif /* _ATL1C_H_ */
index 3ef7092..1cda49a 100644 (file)
@@ -153,7 +153,7 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw)
 bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value)
 {
        int i;
-       int ret = false;
+       bool ret = false;
        u32 otp_ctrl_data;
        u32 control;
        u32 data;
index b5fd934..1b0fe2d 100644 (file)
@@ -499,10 +499,10 @@ struct atl1e_adapter {
 extern char atl1e_driver_name[];
 extern char atl1e_driver_version[];
 
-extern void atl1e_check_options(struct atl1e_adapter *adapter);
-extern int atl1e_up(struct atl1e_adapter *adapter);
-extern void atl1e_down(struct atl1e_adapter *adapter);
-extern void atl1e_reinit_locked(struct atl1e_adapter *adapter);
-extern s32 atl1e_reset_hw(struct atl1e_hw *hw);
-extern void atl1e_set_ethtool_ops(struct net_device *netdev);
+void atl1e_check_options(struct atl1e_adapter *adapter);
+int atl1e_up(struct atl1e_adapter *adapter);
+void atl1e_down(struct atl1e_adapter *adapter);
+void atl1e_reinit_locked(struct atl1e_adapter *adapter);
+s32 atl1e_reset_hw(struct atl1e_hw *hw);
+void atl1e_set_ethtool_ops(struct net_device *netdev);
 #endif /* _ATL1_E_H_ */
index 1966444..7a73f3a 100644 (file)
@@ -313,6 +313,34 @@ static void atl1e_set_multi(struct net_device *netdev)
        }
 }
 
+static void __atl1e_rx_mode(netdev_features_t features, u32 *mac_ctrl_data)
+{
+
+       if (features & NETIF_F_RXALL) {
+               /* enable RX of ALL frames */
+               *mac_ctrl_data |= MAC_CTRL_DBG;
+       } else {
+               /* disable RX of ALL frames */
+               *mac_ctrl_data &= ~MAC_CTRL_DBG;
+       }
+}
+
+static void atl1e_rx_mode(struct net_device *netdev,
+       netdev_features_t features)
+{
+       struct atl1e_adapter *adapter = netdev_priv(netdev);
+       u32 mac_ctrl_data = 0;
+
+       netdev_dbg(adapter->netdev, "%s\n", __func__);
+
+       atl1e_irq_disable(adapter);
+       mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL);
+       __atl1e_rx_mode(features, &mac_ctrl_data);
+       AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data);
+       atl1e_irq_enable(adapter);
+}
+
+
 static void __atl1e_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data)
 {
        if (features & NETIF_F_HW_VLAN_CTAG_RX) {
@@ -394,6 +422,10 @@ static int atl1e_set_features(struct net_device *netdev,
        if (changed & NETIF_F_HW_VLAN_CTAG_RX)
                atl1e_vlan_mode(netdev, features);
 
+       if (changed & NETIF_F_RXALL)
+               atl1e_rx_mode(netdev, features);
+
+
        return 0;
 }
 
@@ -1057,7 +1089,8 @@ static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter)
                value |= MAC_CTRL_PROMIS_EN;
        if (netdev->flags & IFF_ALLMULTI)
                value |= MAC_CTRL_MC_ALL_EN;
-
+       if (netdev->features & NETIF_F_RXALL)
+               value |= MAC_CTRL_DBG;
        AT_WRITE_REG(hw, REG_MAC_CTRL, value);
 }
 
@@ -1405,7 +1438,8 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
                        rx_page_desc[que].rx_nxseq++;
 
                        /* error packet */
-                       if (prrs->pkt_flag & RRS_IS_ERR_FRAME) {
+                       if ((prrs->pkt_flag & RRS_IS_ERR_FRAME) &&
+                           !(netdev->features & NETIF_F_RXALL)) {
                                if (prrs->err_flag & (RRS_ERR_BAD_CRC |
                                        RRS_ERR_DRIBBLE | RRS_ERR_CODE |
                                        RRS_ERR_TRUNC)) {
@@ -1418,7 +1452,10 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
                        }
 
                        packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
-                                       RRS_PKT_SIZE_MASK) - 4; /* CRC */
+                                       RRS_PKT_SIZE_MASK);
+                       if (likely(!(netdev->features & NETIF_F_RXFCS)))
+                               packet_size -= 4; /* CRC */
+
                        skb = netdev_alloc_skb_ip_align(netdev, packet_size);
                        if (skb == NULL)
                                goto skip_pkt;
@@ -2245,7 +2282,8 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
                              NETIF_F_HW_VLAN_CTAG_RX;
        netdev->features = netdev->hw_features | NETIF_F_LLTX |
                           NETIF_F_HW_VLAN_CTAG_TX;
-
+       /* not enabled by default */
+       netdev->hw_features |= NETIF_F_RXALL | NETIF_F_RXFCS;
        return 0;
 }
 
index 3ebe19f..2f27d4c 100644 (file)
@@ -42,7 +42,7 @@
 #include "atlx.h"
 
 #ifdef ETHTOOL_OPS_COMPAT
-extern int ethtool_ioctl(struct ifreq *ifr);
+int ethtool_ioctl(struct ifreq *ifr);
 #endif
 
 #define PCI_COMMAND_REGISTER   PCI_COMMAND
index 9b017d9..079a597 100644 (file)
@@ -596,6 +596,7 @@ static void b44_timer(unsigned long __opaque)
 static void b44_tx(struct b44 *bp)
 {
        u32 cur, cons;
+       unsigned bytes_compl = 0, pkts_compl = 0;
 
        cur  = br32(bp, B44_DMATX_STAT) & DMATX_STAT_CDMASK;
        cur /= sizeof(struct dma_desc);
@@ -612,9 +613,14 @@ static void b44_tx(struct b44 *bp)
                                 skb->len,
                                 DMA_TO_DEVICE);
                rp->skb = NULL;
+
+               bytes_compl += skb->len;
+               pkts_compl++;
+
                dev_kfree_skb_irq(skb);
        }
 
+       netdev_completed_queue(bp->dev, pkts_compl, bytes_compl);
        bp->tx_cons = cons;
        if (netif_queue_stopped(bp->dev) &&
            TX_BUFFS_AVAIL(bp) > B44_TX_WAKEUP_THRESH)
@@ -1018,6 +1024,8 @@ static netdev_tx_t b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (bp->flags & B44_FLAG_REORDER_BUG)
                br32(bp, B44_DMATX_PTR);
 
+       netdev_sent_queue(dev, skb->len);
+
        if (TX_BUFFS_AVAIL(bp) < 1)
                netif_stop_queue(dev);
 
@@ -1416,6 +1424,8 @@ static void b44_init_hw(struct b44 *bp, int reset_kind)
 
        val = br32(bp, B44_ENET_CTRL);
        bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE));
+
+       netdev_reset_queue(bp->dev);
 }
 
 static int b44_open(struct net_device *dev)
@@ -2101,7 +2111,7 @@ static int b44_get_invariants(struct b44 *bp)
         * valid PHY address. */
        bp->phy_addr &= 0x1F;
 
-       memcpy(bp->dev->dev_addr, addr, 6);
+       memcpy(bp->dev->dev_addr, addr, ETH_ALEN);
 
        if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){
                pr_err("Invalid MAC address found in EEPROM\n");
index 249468f..e2aa09c 100644 (file)
@@ -149,6 +149,8 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac,
        dma_desc->ctl0 = cpu_to_le32(ctl0);
        dma_desc->ctl1 = cpu_to_le32(ctl1);
 
+       netdev_sent_queue(net_dev, skb->len);
+
        wmb();
 
        /* Increase ring->end to point empty slot. We tell hardware the first
@@ -178,6 +180,7 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring)
        struct device *dma_dev = bgmac->core->dma_dev;
        int empty_slot;
        bool freed = false;
+       unsigned bytes_compl = 0, pkts_compl = 0;
 
        /* The last slot that hardware didn't consume yet */
        empty_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_STATUS);
@@ -195,6 +198,9 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring)
                                         slot->skb->len, DMA_TO_DEVICE);
                        slot->dma_addr = 0;
 
+                       bytes_compl += slot->skb->len;
+                       pkts_compl++;
+
                        /* Free memory! :) */
                        dev_kfree_skb(slot->skb);
                        slot->skb = NULL;
@@ -208,6 +214,8 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring)
                freed = true;
        }
 
+       netdev_completed_queue(bgmac->net_dev, pkts_compl, bytes_compl);
+
        if (freed && netif_queue_stopped(bgmac->net_dev))
                netif_wake_queue(bgmac->net_dev);
 }
@@ -244,31 +252,59 @@ static int bgmac_dma_rx_skb_for_slot(struct bgmac *bgmac,
                                     struct bgmac_slot_info *slot)
 {
        struct device *dma_dev = bgmac->core->dma_dev;
+       struct sk_buff *skb;
+       dma_addr_t dma_addr;
        struct bgmac_rx_header *rx;
 
        /* Alloc skb */
-       slot->skb = netdev_alloc_skb(bgmac->net_dev, BGMAC_RX_BUF_SIZE);
-       if (!slot->skb)
+       skb = netdev_alloc_skb(bgmac->net_dev, BGMAC_RX_BUF_SIZE);
+       if (!skb)
                return -ENOMEM;
 
        /* Poison - if everything goes fine, hardware will overwrite it */
-       rx = (struct bgmac_rx_header *)slot->skb->data;
+       rx = (struct bgmac_rx_header *)skb->data;
        rx->len = cpu_to_le16(0xdead);
        rx->flags = cpu_to_le16(0xbeef);
 
        /* Map skb for the DMA */
-       slot->dma_addr = dma_map_single(dma_dev, slot->skb->data,
-                                       BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
-       if (dma_mapping_error(dma_dev, slot->dma_addr)) {
+       dma_addr = dma_map_single(dma_dev, skb->data,
+                                 BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(dma_dev, dma_addr)) {
                bgmac_err(bgmac, "DMA mapping error\n");
+               dev_kfree_skb(skb);
                return -ENOMEM;
        }
+
+       /* Update the slot */
+       slot->skb = skb;
+       slot->dma_addr = dma_addr;
+
        if (slot->dma_addr & 0xC0000000)
                bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
 
        return 0;
 }
 
+static void bgmac_dma_rx_setup_desc(struct bgmac *bgmac,
+                                   struct bgmac_dma_ring *ring, int desc_idx)
+{
+       struct bgmac_dma_desc *dma_desc = ring->cpu_base + desc_idx;
+       u32 ctl0 = 0, ctl1 = 0;
+
+       if (desc_idx == ring->num_slots - 1)
+               ctl0 |= BGMAC_DESC_CTL0_EOT;
+       ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN;
+       /* Is there any BGMAC device that requires extension? */
+       /* ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) &
+        * B43_DMA64_DCTL1_ADDREXT_MASK;
+        */
+
+       dma_desc->addr_low = cpu_to_le32(lower_32_bits(ring->slots[desc_idx].dma_addr));
+       dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[desc_idx].dma_addr));
+       dma_desc->ctl0 = cpu_to_le32(ctl0);
+       dma_desc->ctl1 = cpu_to_le32(ctl1);
+}
+
 static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
                             int weight)
 {
@@ -287,7 +323,6 @@ static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
                struct device *dma_dev = bgmac->core->dma_dev;
                struct bgmac_slot_info *slot = &ring->slots[ring->start];
                struct sk_buff *skb = slot->skb;
-               struct sk_buff *new_skb;
                struct bgmac_rx_header *rx;
                u16 len, flags;
 
@@ -300,38 +335,51 @@ static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
                len = le16_to_cpu(rx->len);
                flags = le16_to_cpu(rx->flags);
 
-               /* Check for poison and drop or pass the packet */
-               if (len == 0xdead && flags == 0xbeef) {
-                       bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n",
-                                 ring->start);
-               } else {
+               do {
+                       dma_addr_t old_dma_addr = slot->dma_addr;
+                       int err;
+
+                       /* Check for poison and drop or pass the packet */
+                       if (len == 0xdead && flags == 0xbeef) {
+                               bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n",
+                                         ring->start);
+                               dma_sync_single_for_device(dma_dev,
+                                                          slot->dma_addr,
+                                                          BGMAC_RX_BUF_SIZE,
+                                                          DMA_FROM_DEVICE);
+                               break;
+                       }
+
                        /* Omit CRC. */
                        len -= ETH_FCS_LEN;
 
-                       new_skb = netdev_alloc_skb_ip_align(bgmac->net_dev, len);
-                       if (new_skb) {
-                               skb_put(new_skb, len);
-                               skb_copy_from_linear_data_offset(skb, BGMAC_RX_FRAME_OFFSET,
-                                                                new_skb->data,
-                                                                len);
-                               skb_checksum_none_assert(skb);
-                               new_skb->protocol =
-                                       eth_type_trans(new_skb, bgmac->net_dev);
-                               netif_receive_skb(new_skb);
-                               handled++;
-                       } else {
-                               bgmac->net_dev->stats.rx_dropped++;
-                               bgmac_err(bgmac, "Allocation of skb for copying packet failed!\n");
+                       /* Prepare new skb as replacement */
+                       err = bgmac_dma_rx_skb_for_slot(bgmac, slot);
+                       if (err) {
+                               /* Poison the old skb */
+                               rx->len = cpu_to_le16(0xdead);
+                               rx->flags = cpu_to_le16(0xbeef);
+
+                               dma_sync_single_for_device(dma_dev,
+                                                          slot->dma_addr,
+                                                          BGMAC_RX_BUF_SIZE,
+                                                          DMA_FROM_DEVICE);
+                               break;
                        }
+                       bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
 
-                       /* Poison the old skb */
-                       rx->len = cpu_to_le16(0xdead);
-                       rx->flags = cpu_to_le16(0xbeef);
-               }
+                       /* Unmap old skb, we'll pass it to the netfif */
+                       dma_unmap_single(dma_dev, old_dma_addr,
+                                        BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+
+                       skb_put(skb, BGMAC_RX_FRAME_OFFSET + len);
+                       skb_pull(skb, BGMAC_RX_FRAME_OFFSET);
 
-               /* Make it back accessible to the hardware */
-               dma_sync_single_for_device(dma_dev, slot->dma_addr,
-                                          BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+                       skb_checksum_none_assert(skb);
+                       skb->protocol = eth_type_trans(skb, bgmac->net_dev);
+                       netif_receive_skb(skb);
+                       handled++;
+               } while (0);
 
                if (++ring->start >= BGMAC_RX_RING_SLOTS)
                        ring->start = 0;
@@ -495,8 +543,6 @@ err_dma_free:
 static void bgmac_dma_init(struct bgmac *bgmac)
 {
        struct bgmac_dma_ring *ring;
-       struct bgmac_dma_desc *dma_desc;
-       u32 ctl0, ctl1;
        int i;
 
        for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
@@ -529,23 +575,8 @@ static void bgmac_dma_init(struct bgmac *bgmac)
                if (ring->unaligned)
                        bgmac_dma_rx_enable(bgmac, ring);
 
-               for (j = 0, dma_desc = ring->cpu_base; j < ring->num_slots;
-                    j++, dma_desc++) {
-                       ctl0 = ctl1 = 0;
-
-                       if (j == ring->num_slots - 1)
-                               ctl0 |= BGMAC_DESC_CTL0_EOT;
-                       ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN;
-                       /* Is there any BGMAC device that requires extension? */
-                       /* ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) &
-                        * B43_DMA64_DCTL1_ADDREXT_MASK;
-                        */
-
-                       dma_desc->addr_low = cpu_to_le32(lower_32_bits(ring->slots[j].dma_addr));
-                       dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[j].dma_addr));
-                       dma_desc->ctl0 = cpu_to_le32(ctl0);
-                       dma_desc->ctl1 = cpu_to_le32(ctl1);
-               }
+               for (j = 0; j < ring->num_slots; j++)
+                       bgmac_dma_rx_setup_desc(bgmac, ring, j);
 
                bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
                            ring->index_base +
@@ -988,6 +1019,8 @@ static void bgmac_chip_reset(struct bgmac *bgmac)
        bgmac_miiconfig(bgmac);
        bgmac_phy_init(bgmac);
 
+       netdev_reset_queue(bgmac->net_dev);
+
        bgmac->int_status = 0;
 }
 
index e838a3f..d9980ad 100644 (file)
@@ -5761,8 +5761,8 @@ bnx2_run_loopback(struct bnx2 *bp, int loopback_mode)
        if (!skb)
                return -ENOMEM;
        packet = skb_put(skb, pkt_size);
-       memcpy(packet, bp->dev->dev_addr, 6);
-       memset(packet + 6, 0x0, 8);
+       memcpy(packet, bp->dev->dev_addr, ETH_ALEN);
+       memset(packet + ETH_ALEN, 0x0, 8);
        for (i = 14; i < pkt_size; i++)
                packet[i] = (unsigned char) (i & 0xff);
 
@@ -8413,7 +8413,6 @@ err_out_release:
 
 err_out_disable:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
 err_out:
        return rc;
@@ -8514,7 +8513,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_drvdata(pdev, dev);
 
-       memcpy(dev->dev_addr, bp->mac_addr, 6);
+       memcpy(dev->dev_addr, bp->mac_addr, ETH_ALEN);
 
        dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
                NETIF_F_TSO | NETIF_F_TSO_ECN |
@@ -8546,7 +8545,6 @@ error:
        pci_iounmap(pdev, bp->regview);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 err_free:
        free_netdev(dev);
        return rc;
@@ -8578,7 +8576,6 @@ bnx2_remove_one(struct pci_dev *pdev)
 
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static int
index 97b3d32..4e01c57 100644 (file)
@@ -1197,8 +1197,9 @@ union cdu_context {
 /* TM (timers) host DB constants */
 #define TM_ILT_PAGE_SZ_HW      0
 #define TM_ILT_PAGE_SZ         (4096 << TM_ILT_PAGE_SZ_HW) /* 4K */
-/* #define TM_CONN_NUM         (CNIC_STARTING_CID+CNIC_ISCSI_CXT_MAX) */
-#define TM_CONN_NUM            1024
+#define TM_CONN_NUM            (BNX2X_FIRST_VF_CID + \
+                                BNX2X_VF_CIDS + \
+                                CNIC_ISCSI_CID_MAX)
 #define TM_ILT_SZ              (8 * TM_CONN_NUM)
 #define TM_ILT_LINES           DIV_ROUND_UP(TM_ILT_SZ, TM_ILT_PAGE_SZ)
 
@@ -1527,7 +1528,6 @@ struct bnx2x {
 #define PCI_32BIT_FLAG                 (1 << 1)
 #define ONE_PORT_FLAG                  (1 << 2)
 #define NO_WOL_FLAG                    (1 << 3)
-#define USING_DAC_FLAG                 (1 << 4)
 #define USING_MSIX_FLAG                        (1 << 5)
 #define USING_MSI_FLAG                 (1 << 6)
 #define DISABLE_MSI_FLAG               (1 << 7)
@@ -1546,6 +1546,7 @@ struct bnx2x {
 #define IS_VF_FLAG                     (1 << 22)
 #define INTERRUPTS_ENABLED_FLAG                (1 << 23)
 #define BC_SUPPORTS_RMMOD_CMD          (1 << 24)
+#define HAS_PHYS_PORT_ID               (1 << 25)
 
 #define BP_NOMCP(bp)                   ((bp)->flags & NO_MCP_FLAG)
 
@@ -1621,7 +1622,7 @@ struct bnx2x {
        u16                     rx_ticks_int;
        u16                     rx_ticks;
 /* Maximal coalescing timeout in us */
-#define BNX2X_MAX_COALESCE_TOUT                (0xf0*12)
+#define BNX2X_MAX_COALESCE_TOUT                (0xff*BNX2X_BTR)
 
        u32                     lin_cnt;
 
@@ -1876,6 +1877,8 @@ struct bnx2x {
        u32 dump_preset_idx;
        bool                                    stats_started;
        struct semaphore                        stats_sema;
+
+       u8                                      phys_port_id[ETH_ALEN];
 };
 
 /* Tx queues may be less or equal to Rx queues */
@@ -2072,7 +2075,8 @@ u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
 
 void bnx2x_prep_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
                               u8 src_type, u8 dst_type);
-int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae);
+int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
+                              u32 *comp);
 
 /* FLR related routines */
 u32 bnx2x_flr_clnup_poll_count(struct bnx2x *bp);
@@ -2231,7 +2235,7 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define BNX2X_NUM_TESTS_SF             7
 #define BNX2X_NUM_TESTS_MF             3
 #define BNX2X_NUM_TESTS(bp)            (IS_MF(bp) ? BNX2X_NUM_TESTS_MF : \
-                                                    BNX2X_NUM_TESTS_SF)
+                                            IS_VF(bp) ? 0 : BNX2X_NUM_TESTS_SF)
 
 #define BNX2X_PHY_LOOPBACK             0
 #define BNX2X_MAC_LOOPBACK             1
@@ -2491,11 +2495,9 @@ enum {
 
 #define NUM_MACS       8
 
-enum bnx2x_pci_bus_speed {
-       BNX2X_PCI_LINK_SPEED_2500 = 2500,
-       BNX2X_PCI_LINK_SPEED_5000 = 5000,
-       BNX2X_PCI_LINK_SPEED_8000 = 8000
-};
-
 void bnx2x_set_local_cmng(struct bnx2x *bp);
+
+#define MCPR_SCRATCH_BASE(bp) \
+       (CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
+
 #endif /* bnx2x.h */
index 61726af..dcafbda 100644 (file)
@@ -681,6 +681,7 @@ static void bnx2x_gro_receive(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                }
        }
 #endif
+       skb_record_rx_queue(skb, fp->rx_queue);
        napi_gro_receive(&fp->napi, skb);
 }
 
@@ -2481,8 +2482,7 @@ load_error_cnic2:
 load_error_cnic1:
        bnx2x_napi_disable_cnic(bp);
        /* Update the number of queues without the cnic queues */
-       rc = bnx2x_set_real_num_queues(bp, 0);
-       if (rc)
+       if (bnx2x_set_real_num_queues(bp, 0))
                BNX2X_ERR("Unable to set real_num_queues not including cnic\n");
 load_error_cnic0:
        BNX2X_ERR("CNIC-related load failed\n");
@@ -2545,10 +2545,6 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
                }
        }
 
-       /* Allocated memory for FW statistics  */
-       if (bnx2x_alloc_fw_stats_mem(bp))
-               LOAD_ERROR_EXIT(bp, load_error0);
-
        /* need to be done after alloc mem, since it's self adjusting to amount
         * of memory available for RSS queues
         */
@@ -2558,6 +2554,10 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
                LOAD_ERROR_EXIT(bp, load_error0);
        }
 
+       /* Allocated memory for FW statistics  */
+       if (bnx2x_alloc_fw_stats_mem(bp))
+               LOAD_ERROR_EXIT(bp, load_error0);
+
        /* request pf to initialize status blocks */
        if (IS_VF(bp)) {
                rc = bnx2x_vfpf_init(bp);
@@ -2812,8 +2812,8 @@ load_error1:
        if (IS_PF(bp))
                bnx2x_clear_pf_load(bp);
 load_error0:
-       bnx2x_free_fp_mem(bp);
        bnx2x_free_fw_stats_mem(bp);
+       bnx2x_free_fp_mem(bp);
        bnx2x_free_mem(bp);
 
        return rc;
@@ -3256,14 +3256,16 @@ static u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb)
        if (prot == IPPROTO_TCP)
                rc |= XMIT_CSUM_TCP;
 
-       if (skb_is_gso_v6(skb)) {
-               rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP);
-               if (rc & XMIT_CSUM_ENC)
-                       rc |= XMIT_GSO_ENC_V6;
-       } else if (skb_is_gso(skb)) {
-               rc |= (XMIT_GSO_V4 | XMIT_CSUM_TCP);
-               if (rc & XMIT_CSUM_ENC)
-                       rc |= XMIT_GSO_ENC_V4;
+       if (skb_is_gso(skb)) {
+               if (skb_is_gso_v6(skb)) {
+                       rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP);
+                       if (rc & XMIT_CSUM_ENC)
+                               rc |= XMIT_GSO_ENC_V6;
+               } else {
+                       rc |= (XMIT_GSO_V4 | XMIT_CSUM_TCP);
+                       if (rc & XMIT_CSUM_ENC)
+                               rc |= XMIT_GSO_ENC_V4;
+               }
        }
 
        return rc;
index 324de5f..32d0f14 100644 (file)
@@ -639,6 +639,9 @@ static int bnx2x_get_regs_len(struct net_device *dev)
        struct bnx2x *bp = netdev_priv(dev);
        int regdump_len = 0;
 
+       if (IS_VF(bp))
+               return 0;
+
        regdump_len = __bnx2x_get_regs_len(bp);
        regdump_len *= 4;
        regdump_len += sizeof(struct dump_header);
@@ -891,17 +894,8 @@ static void bnx2x_get_regs(struct net_device *dev,
         * will re-enable parity attentions right after the dump.
         */
 
-       /* Disable parity on path 0 */
-       bnx2x_pretend_func(bp, 0);
        bnx2x_disable_blocks_parity(bp);
 
-       /* Disable parity on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_disable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
-
        dump_hdr.header_size = (sizeof(struct dump_header) / 4) - 1;
        dump_hdr.preset = DUMP_ALL_PRESETS;
        dump_hdr.version = BNX2X_DUMP_VERSION;
@@ -928,18 +922,9 @@ static void bnx2x_get_regs(struct net_device *dev,
        /* Actually read the registers */
        __bnx2x_get_regs(bp, p);
 
-       /* Re-enable parity attentions on path 0 */
-       bnx2x_pretend_func(bp, 0);
+       /* Re-enable parity attentions */
        bnx2x_clear_blocks_parity(bp);
        bnx2x_enable_blocks_parity(bp);
-
-       /* Re-enable parity attentions on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_clear_blocks_parity(bp);
-       bnx2x_enable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
 }
 
 static int bnx2x_get_preset_regs_len(struct net_device *dev, u32 preset)
@@ -993,17 +978,8 @@ static int bnx2x_get_dump_data(struct net_device *dev,
         * will re-enable parity attentions right after the dump.
         */
 
-       /* Disable parity on path 0 */
-       bnx2x_pretend_func(bp, 0);
        bnx2x_disable_blocks_parity(bp);
 
-       /* Disable parity on path 1 */
-       bnx2x_pretend_func(bp, 1);
-       bnx2x_disable_blocks_parity(bp);
-
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
-
        dump_hdr.header_size = (sizeof(struct dump_header) / 4) - 1;
        dump_hdr.preset = bp->dump_preset_idx;
        dump_hdr.version = BNX2X_DUMP_VERSION;
@@ -1032,19 +1008,10 @@ static int bnx2x_get_dump_data(struct net_device *dev,
        /* Actually read the registers */
        __bnx2x_get_preset_regs(bp, p, dump_hdr.preset);
 
-       /* Re-enable parity attentions on path 0 */
-       bnx2x_pretend_func(bp, 0);
-       bnx2x_clear_blocks_parity(bp);
-       bnx2x_enable_blocks_parity(bp);
-
-       /* Re-enable parity attentions on path 1 */
-       bnx2x_pretend_func(bp, 1);
+       /* Re-enable parity attentions */
        bnx2x_clear_blocks_parity(bp);
        bnx2x_enable_blocks_parity(bp);
 
-       /* Return to current function */
-       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
-
        return 0;
 }
 
@@ -2900,9 +2867,16 @@ static void bnx2x_self_test(struct net_device *dev,
 
        memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS(bp));
 
+       if (bnx2x_test_nvram(bp) != 0) {
+               if (!IS_MF(bp))
+                       buf[4] = 1;
+               else
+                       buf[0] = 1;
+               etest->flags |= ETH_TEST_FL_FAILED;
+       }
+
        if (!netif_running(dev)) {
-               DP(BNX2X_MSG_ETHTOOL,
-                  "Can't perform self-test when interface is down\n");
+               DP(BNX2X_MSG_ETHTOOL, "Interface is down\n");
                return;
        }
 
@@ -2964,13 +2938,7 @@ static void bnx2x_self_test(struct net_device *dev,
                /* wait until link state is restored */
                bnx2x_wait_for_link(bp, link_up, is_serdes);
        }
-       if (bnx2x_test_nvram(bp) != 0) {
-               if (!IS_MF(bp))
-                       buf[4] = 1;
-               else
-                       buf[0] = 1;
-               etest->flags |= ETH_TEST_FL_FAILED;
-       }
+
        if (bnx2x_test_intr(bp) != 0) {
                if (!IS_MF(bp))
                        buf[5] = 1;
index 32767f6..cf1df8b 100644 (file)
@@ -172,6 +172,7 @@ struct shared_hw_cfg {                       /* NVRAM Offset */
                #define SHARED_HW_CFG_LED_MAC4                       0x000c0000
                #define SHARED_HW_CFG_LED_PHY8                       0x000d0000
                #define SHARED_HW_CFG_LED_EXTPHY1                    0x000e0000
+               #define SHARED_HW_CFG_LED_EXTPHY2                    0x000f0000
 
 
        #define SHARED_HW_CFG_AN_ENABLE_MASK                0x3f000000
index 76df015..c2dfea7 100644 (file)
@@ -640,23 +640,35 @@ static const struct {
  * [30] MCP Latched ump_tx_parity
  * [31] MCP Latched scpad_parity
  */
-#define MISC_AEU_ENABLE_MCP_PRTY_BITS  \
+#define MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS      \
        (AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY | \
         AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY | \
-        AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY | \
+        AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY)
+
+#define MISC_AEU_ENABLE_MCP_PRTY_BITS  \
+       (MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS | \
         AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY)
 
 /* Below registers control the MCP parity attention output. When
  * MISC_AEU_ENABLE_MCP_PRTY_BITS are set - attentions are
  * enabled, when cleared - disabled.
  */
-static const u32 mcp_attn_ctl_regs[] = {
-       MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
-       MISC_REG_AEU_ENABLE4_NIG_0,
-       MISC_REG_AEU_ENABLE4_PXP_0,
-       MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
-       MISC_REG_AEU_ENABLE4_NIG_1,
-       MISC_REG_AEU_ENABLE4_PXP_1
+static const struct {
+       u32 addr;
+       u32 bits;
+} mcp_attn_ctl_regs[] = {
+       { MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
+               MISC_AEU_ENABLE_MCP_PRTY_BITS },
+       { MISC_REG_AEU_ENABLE4_NIG_0,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS },
+       { MISC_REG_AEU_ENABLE4_PXP_0,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS },
+       { MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
+               MISC_AEU_ENABLE_MCP_PRTY_BITS },
+       { MISC_REG_AEU_ENABLE4_NIG_1,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS },
+       { MISC_REG_AEU_ENABLE4_PXP_1,
+               MISC_AEU_ENABLE_MCP_PRTY_SUB_BITS }
 };
 
 static inline void bnx2x_set_mcp_parity(struct bnx2x *bp, u8 enable)
@@ -665,14 +677,14 @@ static inline void bnx2x_set_mcp_parity(struct bnx2x *bp, u8 enable)
        u32 reg_val;
 
        for (i = 0; i < ARRAY_SIZE(mcp_attn_ctl_regs); i++) {
-               reg_val = REG_RD(bp, mcp_attn_ctl_regs[i]);
+               reg_val = REG_RD(bp, mcp_attn_ctl_regs[i].addr);
 
                if (enable)
-                       reg_val |= MISC_AEU_ENABLE_MCP_PRTY_BITS;
+                       reg_val |= mcp_attn_ctl_regs[i].bits;
                else
-                       reg_val &= ~MISC_AEU_ENABLE_MCP_PRTY_BITS;
+                       reg_val &= ~mcp_attn_ctl_regs[i].bits;
 
-               REG_WR(bp, mcp_attn_ctl_regs[i], reg_val);
+               REG_WR(bp, mcp_attn_ctl_regs[i].addr, reg_val);
        }
 }
 
index d60a2ea..20dcc02 100644 (file)
@@ -175,6 +175,7 @@ typedef int (*read_sfp_module_eeprom_func_p)(struct bnx2x_phy *phy,
 #define EDC_MODE_LINEAR                                0x0022
 #define EDC_MODE_LIMITING                              0x0044
 #define EDC_MODE_PASSIVE_DAC                   0x0055
+#define EDC_MODE_ACTIVE_DAC                    0x0066
 
 /* ETS defines*/
 #define DCBX_INVALID_COS                                       (0xFF)
@@ -3121,7 +3122,7 @@ static void bnx2x_bsc_module_sel(struct link_params *params)
 }
 
 static int bnx2x_bsc_read(struct link_params *params,
-                         struct bnx2x_phy *phy,
+                         struct bnx2x *bp,
                          u8 sl_devid,
                          u16 sl_addr,
                          u8 lc_addr,
@@ -3130,7 +3131,6 @@ static int bnx2x_bsc_read(struct link_params *params,
 {
        u32 val, i;
        int rc = 0;
-       struct bnx2x *bp = params->bp;
 
        if (xfer_cnt > 16) {
                DP(NETIF_MSG_LINK, "invalid xfer_cnt %d. Max is 16 bytes\n",
@@ -3684,6 +3684,41 @@ static void bnx2x_warpcore_enable_AN_KR2(struct bnx2x_phy *phy,
        bnx2x_update_link_attr(params, vars->link_attr_sync);
 }
 
+static void bnx2x_disable_kr2(struct link_params *params,
+                             struct link_vars *vars,
+                             struct bnx2x_phy *phy)
+{
+       struct bnx2x *bp = params->bp;
+       int i;
+       static struct bnx2x_reg_set reg_set[] = {
+               /* Step 1 - Program the TX/RX alignment markers */
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL5, 0x7690},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL7, 0xe647},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL6, 0xc4f0},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL9, 0x7690},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL11, 0xe647},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL10, 0xc4f0},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_USERB0_CTRL, 0x000c},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL1, 0x6000},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL3, 0x0000},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CODE_FIELD, 0x0002},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI1, 0x0000},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI2, 0x0af7},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI3, 0x0af7},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_BAM_CODE, 0x0002},
+               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_UD_CODE, 0x0000}
+       };
+       DP(NETIF_MSG_LINK, "Disabling 20G-KR2\n");
+
+       for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+               bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg,
+                                reg_set[i].val);
+       vars->link_attr_sync &= ~LINK_ATTR_SYNC_KR2_ENABLE;
+       bnx2x_update_link_attr(params, vars->link_attr_sync);
+
+       vars->check_kr2_recovery_cnt = CHECK_KR2_RECOVERY_CNT;
+}
+
 static void bnx2x_warpcore_set_lpi_passthrough(struct bnx2x_phy *phy,
                                               struct link_params *params)
 {
@@ -3715,7 +3750,6 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
                                        struct link_params *params,
                                        struct link_vars *vars) {
        u16 lane, i, cl72_ctrl, an_adv = 0;
-       u16 ucode_ver;
        struct bnx2x *bp = params->bp;
        static struct bnx2x_reg_set reg_set[] = {
                {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7},
@@ -3806,15 +3840,7 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
 
        /* Advertise pause */
        bnx2x_ext_phy_set_pause(params, phy, vars);
-       /* Set KR Autoneg Work-Around flag for Warpcore version older than D108
-        */
-       bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
-                       MDIO_WC_REG_UC_INFO_B1_VERSION, &ucode_ver);
-       if (ucode_ver < 0xd108) {
-               DP(NETIF_MSG_LINK, "Enable AN KR work-around. WC ver:0x%x\n",
-                              ucode_ver);
-               vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY;
-       }
+       vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY;
        bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD,
                                 MDIO_WC_REG_DIGITAL5_MISC7, 0x100);
 
@@ -3838,6 +3864,8 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
                bnx2x_set_aer_mmd(params, phy);
 
                bnx2x_warpcore_enable_AN_KR2(phy, params, vars);
+       } else {
+               bnx2x_disable_kr2(params, vars, phy);
        }
 
        /* Enable Autoneg: only on the main lane */
@@ -4347,20 +4375,14 @@ static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy,
        struct bnx2x *bp = params->bp;
        u32 serdes_net_if;
        u16 gp_status1 = 0, lnkup = 0, lnkup_kr = 0;
-       u16 lane = bnx2x_get_warpcore_lane(phy, params);
 
        vars->turn_to_run_wc_rt = vars->turn_to_run_wc_rt ? 0 : 1;
 
        if (!vars->turn_to_run_wc_rt)
                return;
 
-       /* Return if there is no link partner */
-       if (!(bnx2x_warpcore_get_sigdet(phy, params))) {
-               DP(NETIF_MSG_LINK, "bnx2x_warpcore_get_sigdet false\n");
-               return;
-       }
-
        if (vars->rx_tx_asic_rst) {
+               u16 lane = bnx2x_get_warpcore_lane(phy, params);
                serdes_net_if = (REG_RD(bp, params->shmem_base +
                                offsetof(struct shmem_region, dev_info.
                                port_hw_config[params->port].default_cfg)) &
@@ -4375,14 +4397,8 @@ static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy,
                                /*10G KR*/
                        lnkup_kr = (gp_status1 >> (12+lane)) & 0x1;
 
-                       DP(NETIF_MSG_LINK,
-                               "gp_status1 0x%x\n", gp_status1);
-
                        if (lnkup_kr || lnkup) {
-                                       vars->rx_tx_asic_rst = 0;
-                                       DP(NETIF_MSG_LINK,
-                                       "link up, rx_tx_asic_rst 0x%x\n",
-                                       vars->rx_tx_asic_rst);
+                               vars->rx_tx_asic_rst = 0;
                        } else {
                                /* Reset the lane to see if link comes up.*/
                                bnx2x_warpcore_reset_lane(bp, phy, 1);
@@ -4507,10 +4523,14 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy,
                         * enabled transmitter to avoid current leakage in case
                         * no module is connected
                         */
-                       if (bnx2x_is_sfp_module_plugged(phy, params))
-                               bnx2x_sfp_module_detection(phy, params);
-                       else
-                               bnx2x_sfp_e3_set_transmitter(params, phy, 1);
+                       if ((params->loopback_mode == LOOPBACK_NONE) ||
+                           (params->loopback_mode == LOOPBACK_EXT)) {
+                               if (bnx2x_is_sfp_module_plugged(phy, params))
+                                       bnx2x_sfp_module_detection(phy, params);
+                               else
+                                       bnx2x_sfp_e3_set_transmitter(params,
+                                                                    phy, 1);
+                       }
 
                        bnx2x_warpcore_config_sfi(phy, params);
                        break;
@@ -5757,6 +5777,11 @@ static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy,
        rc = bnx2x_get_link_speed_duplex(phy, params, vars, link_up, gp_speed,
                                         duplex);
 
+       /* In case of KR link down, start up the recovering procedure */
+       if ((!link_up) && (phy->media_type == ETH_PHY_KR) &&
+           (!(phy->flags & FLAGS_WC_DUAL_MODE)))
+               vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY;
+
        DP(NETIF_MSG_LINK, "duplex %x  flow_ctrl 0x%x link_status 0x%x\n",
                   vars->duplex, vars->flow_ctrl, vars->link_status);
        return rc;
@@ -6345,9 +6370,15 @@ int bnx2x_set_led(struct link_params *params,
                         * intended override.
                         */
                        break;
-               } else
+               } else {
+                       u32 nig_led_mode = ((params->hw_led_mode <<
+                                            SHARED_HW_CFG_LED_MODE_SHIFT) ==
+                                           SHARED_HW_CFG_LED_EXTPHY2) ?
+                               (SHARED_HW_CFG_LED_PHY1 >>
+                                SHARED_HW_CFG_LED_MODE_SHIFT) : hw_led_mode;
                        REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
-                              hw_led_mode);
+                              nig_led_mode);
+               }
 
                REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0);
                /* Set blinking rate to ~15.9Hz */
@@ -6507,6 +6538,11 @@ static int bnx2x_link_initialize(struct link_params *params,
                        params->phy[INT_PHY].config_init(phy, params, vars);
        }
 
+       /* Re-read this value in case it was changed inside config_init due to
+        * limitations of optic module
+        */
+       vars->line_speed = params->phy[INT_PHY].req_line_speed;
+
        /* Init external phy*/
        if (non_ext_phy) {
                if (params->phy[INT_PHY].supported &
@@ -7886,7 +7922,7 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy,
                        usleep_range(1000, 2000);
                        bnx2x_warpcore_power_module(params, 1);
                }
-               rc = bnx2x_bsc_read(params, phy, dev_addr, addr32, 0, byte_cnt,
+               rc = bnx2x_bsc_read(params, bp, dev_addr, addr32, 0, byte_cnt,
                                    data_array);
        } while ((rc != 0) && (++cnt < I2C_WA_RETRY_CNT));
 
@@ -8080,7 +8116,10 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy,
                if (copper_module_type &
                    SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_ACTIVE) {
                        DP(NETIF_MSG_LINK, "Active Copper cable detected\n");
-                       check_limiting_mode = 1;
+                       if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)
+                               *edc_mode = EDC_MODE_ACTIVE_DAC;
+                       else
+                               check_limiting_mode = 1;
                } else if (copper_module_type &
                        SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_PASSIVE) {
                                DP(NETIF_MSG_LINK,
@@ -8555,6 +8594,7 @@ static void bnx2x_warpcore_set_limiting_mode(struct link_params *params,
                mode = MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_DEFAULT;
                break;
        case EDC_MODE_PASSIVE_DAC:
+       case EDC_MODE_ACTIVE_DAC:
                mode = MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE_SFP_DAC;
                break;
        default:
@@ -9730,32 +9770,41 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
                         MDIO_AN_DEVAD, MDIO_AN_REG_8481_1000T_CTRL,
                         an_1000_val);
 
-       /* set 100 speed advertisement */
-       if ((phy->req_line_speed == SPEED_AUTO_NEG) &&
-            (phy->speed_cap_mask &
-             (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL |
-              PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF))) {
-               an_10_100_val |= (1<<7);
-               /* Enable autoneg and restart autoneg for legacy speeds */
-               autoneg_val |= (1<<9 | 1<<12);
-
-               if (phy->req_duplex == DUPLEX_FULL)
+       /* Set 10/100 speed advertisement */
+       if (phy->req_line_speed == SPEED_AUTO_NEG) {
+               if (phy->speed_cap_mask &
+                   PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL) {
+                       /* Enable autoneg and restart autoneg for legacy speeds
+                        */
+                       autoneg_val |= (1<<9 | 1<<12);
                        an_10_100_val |= (1<<8);
-               DP(NETIF_MSG_LINK, "Advertising 100M\n");
-       }
-       /* set 10 speed advertisement */
-       if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
-            (phy->speed_cap_mask &
-             (PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL |
-              PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)) &&
-            (phy->supported &
-             (SUPPORTED_10baseT_Half |
-              SUPPORTED_10baseT_Full)))) {
-               an_10_100_val |= (1<<5);
-               autoneg_val |= (1<<9 | 1<<12);
-               if (phy->req_duplex == DUPLEX_FULL)
+                       DP(NETIF_MSG_LINK, "Advertising 100M-FD\n");
+               }
+
+               if (phy->speed_cap_mask &
+                   PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF) {
+                       /* Enable autoneg and restart autoneg for legacy speeds
+                        */
+                       autoneg_val |= (1<<9 | 1<<12);
+                       an_10_100_val |= (1<<7);
+                       DP(NETIF_MSG_LINK, "Advertising 100M-HD\n");
+               }
+
+               if ((phy->speed_cap_mask &
+                    PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) &&
+                   (phy->supported & SUPPORTED_10baseT_Full)) {
                        an_10_100_val |= (1<<6);
-               DP(NETIF_MSG_LINK, "Advertising 10M\n");
+                       autoneg_val |= (1<<9 | 1<<12);
+                       DP(NETIF_MSG_LINK, "Advertising 10M-FD\n");
+               }
+
+               if ((phy->speed_cap_mask &
+                    PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF) &&
+                   (phy->supported & SUPPORTED_10baseT_Half)) {
+                       an_10_100_val |= (1<<5);
+                       autoneg_val |= (1<<9 | 1<<12);
+                       DP(NETIF_MSG_LINK, "Advertising 10M-HD\n");
+               }
        }
 
        /* Only 10/100 are allowed to work in FORCE mode */
@@ -10609,10 +10658,18 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
                                         0x40);
 
                } else {
+                       /* EXTPHY2 LED mode indicate that the 100M/1G/10G LED
+                        * sources are all wired through LED1, rather than only
+                        * 10G in other modes.
+                        */
+                       val = ((params->hw_led_mode <<
+                               SHARED_HW_CFG_LED_MODE_SHIFT) ==
+                              SHARED_HW_CFG_LED_EXTPHY2) ? 0x98 : 0x80;
+
                        bnx2x_cl45_write(bp, phy,
                                         MDIO_PMA_DEVAD,
                                         MDIO_PMA_REG_8481_LED1_MASK,
-                                        0x80);
+                                        val);
 
                        /* Tell LED3 to blink on source */
                        bnx2x_cl45_read(bp, phy,
@@ -13432,43 +13489,6 @@ static void bnx2x_sfp_tx_fault_detection(struct bnx2x_phy *phy,
                }
        }
 }
-static void bnx2x_disable_kr2(struct link_params *params,
-                             struct link_vars *vars,
-                             struct bnx2x_phy *phy)
-{
-       struct bnx2x *bp = params->bp;
-       int i;
-       static struct bnx2x_reg_set reg_set[] = {
-               /* Step 1 - Program the TX/RX alignment markers */
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL5, 0x7690},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL7, 0xe647},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL6, 0xc4f0},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL9, 0x7690},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL11, 0xe647},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL10, 0xc4f0},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_USERB0_CTRL, 0x000c},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL1, 0x6000},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL3, 0x0000},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CODE_FIELD, 0x0002},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI1, 0x0000},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI2, 0x0af7},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI3, 0x0af7},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_BAM_CODE, 0x0002},
-               {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_UD_CODE, 0x0000}
-       };
-       DP(NETIF_MSG_LINK, "Disabling 20G-KR2\n");
-
-       for (i = 0; i < ARRAY_SIZE(reg_set); i++)
-               bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg,
-                                reg_set[i].val);
-       vars->link_attr_sync &= ~LINK_ATTR_SYNC_KR2_ENABLE;
-       bnx2x_update_link_attr(params, vars->link_attr_sync);
-
-       vars->check_kr2_recovery_cnt = CHECK_KR2_RECOVERY_CNT;
-       /* Restart AN on leading lane */
-       bnx2x_warpcore_restart_AN_KR(phy, params);
-}
-
 static void bnx2x_kr2_recovery(struct link_params *params,
                               struct link_vars *vars,
                               struct bnx2x_phy *phy)
@@ -13546,6 +13566,8 @@ static void bnx2x_check_kr2_wa(struct link_params *params,
                /* Disable KR2 on both lanes */
                DP(NETIF_MSG_LINK, "BP=0x%x, NP=0x%x\n", base_page, next_page);
                bnx2x_disable_kr2(params, vars, phy);
+               /* Restart AN on leading lane */
+               bnx2x_warpcore_restart_AN_KR(phy, params);
                return;
        }
 }
index a6704b5..bb2f202 100644 (file)
@@ -503,9 +503,9 @@ void bnx2x_prep_dmae_with_comp(struct bnx2x *bp,
 }
 
 /* issue a dmae command over the init-channel and wait for completion */
-int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
+int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
+                              u32 *comp)
 {
-       u32 *wb_comp = bnx2x_sp(bp, wb_comp);
        int cnt = CHIP_REV_IS_SLOW(bp) ? (400000) : 4000;
        int rc = 0;
 
@@ -518,14 +518,14 @@ int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
        spin_lock_bh(&bp->dmae_lock);
 
        /* reset completion */
-       *wb_comp = 0;
+       *comp = 0;
 
        /* post the command on the channel used for initializations */
        bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
 
        /* wait for completion */
        udelay(5);
-       while ((*wb_comp & ~DMAE_PCI_ERR_FLAG) != DMAE_COMP_VAL) {
+       while ((*comp & ~DMAE_PCI_ERR_FLAG) != DMAE_COMP_VAL) {
 
                if (!cnt ||
                    (bp->recovery_state != BNX2X_RECOVERY_DONE &&
@@ -537,7 +537,7 @@ int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
                cnt--;
                udelay(50);
        }
-       if (*wb_comp & DMAE_PCI_ERR_FLAG) {
+       if (*comp & DMAE_PCI_ERR_FLAG) {
                BNX2X_ERR("DMAE PCI error!\n");
                rc = DMAE_PCI_ERROR;
        }
@@ -574,7 +574,7 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       rc = bnx2x_issue_dmae_with_comp(bp, &dmae);
+       rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
                bnx2x_panic();
@@ -611,7 +611,7 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       rc = bnx2x_issue_dmae_with_comp(bp, &dmae);
+       rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
                bnx2x_panic();
@@ -751,6 +751,10 @@ static int bnx2x_mc_assert(struct bnx2x *bp)
        return rc;
 }
 
+#define MCPR_TRACE_BUFFER_SIZE (0x800)
+#define SCRATCH_BUFFER_SIZE(bp)        \
+       (CHIP_IS_E1(bp) ? 0x10000 : (CHIP_IS_E1H(bp) ? 0x20000 : 0x28000))
+
 void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
 {
        u32 addr, val;
@@ -775,7 +779,17 @@ void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
                trace_shmem_base = bp->common.shmem_base;
        else
                trace_shmem_base = SHMEM2_RD(bp, other_shmem_base_addr);
-       addr = trace_shmem_base - 0x800;
+
+       /* sanity */
+       if (trace_shmem_base < MCPR_SCRATCH_BASE(bp) + MCPR_TRACE_BUFFER_SIZE ||
+           trace_shmem_base >= MCPR_SCRATCH_BASE(bp) +
+                               SCRATCH_BUFFER_SIZE(bp)) {
+               BNX2X_ERR("Unable to dump trace buffer (mark %x)\n",
+                         trace_shmem_base);
+               return;
+       }
+
+       addr = trace_shmem_base - MCPR_TRACE_BUFFER_SIZE;
 
        /* validate TRCB signature */
        mark = REG_RD(bp, addr);
@@ -787,14 +801,17 @@ void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
        /* read cyclic buffer pointer */
        addr += 4;
        mark = REG_RD(bp, addr);
-       mark = (CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
-                       + ((mark + 0x3) & ~0x3) - 0x08000000;
+       mark = MCPR_SCRATCH_BASE(bp) + ((mark + 0x3) & ~0x3) - 0x08000000;
+       if (mark >= trace_shmem_base || mark < addr + 4) {
+               BNX2X_ERR("Mark doesn't fall inside Trace Buffer\n");
+               return;
+       }
        printk("%s" "begin fw dump (mark 0x%x)\n", lvl, mark);
 
        printk("%s", lvl);
 
        /* dump buffer after the mark */
-       for (offset = mark; offset <= trace_shmem_base; offset += 0x8*4) {
+       for (offset = mark; offset < trace_shmem_base; offset += 0x8*4) {
                for (word = 0; word < 8; word++)
                        data[word] = htonl(REG_RD(bp, offset + 4*word));
                data[8] = 0x0;
@@ -4280,65 +4297,60 @@ static void _print_next_block(int idx, const char *blk)
        pr_cont("%s%s", idx ? ", " : "", blk);
 }
 
-static int bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool print)
+static bool bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
-                       switch (cur_bit) {
-                       case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "BRB");
+                       res |= true; /* Each bit is real error! */
+
+                       if (print) {
+                               switch (cur_bit) {
+                               case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "BRB");
                                        _print_parity(bp,
                                                      BRB1_REG_BRB1_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "PARSER");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "PARSER");
                                        _print_parity(bp, PRS_REG_PRS_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "TSDM");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "TSDM");
                                        _print_parity(bp,
                                                      TSDM_REG_TSDM_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++,
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
                                                          "SEARCHER");
                                        _print_parity(bp, SRC_REG_SRC_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "TCM");
-                                       _print_parity(bp,
-                                                     TCM_REG_TCM_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "TSEMI");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "TCM");
+                                       _print_parity(bp, TCM_REG_TCM_PRTY_STS);
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "TSEMI");
                                        _print_parity(bp,
                                                      TSEM_REG_TSEM_PRTY_STS_0);
                                        _print_parity(bp,
                                                      TSEM_REG_TSEM_PRTY_STS_1);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "XPB");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "XPB");
                                        _print_parity(bp, GRCBASE_XPB +
                                                          PB_REG_PB_PRTY_STS);
+                                       break;
                                }
-                               break;
                        }
 
                        /* Clear the bit */
@@ -4346,53 +4358,59 @@ static int bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool *global,
+static bool bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool *global,
                                            bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
+                       res |= true; /* Each bit is real error! */
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "PBF");
+                                       _print_next_block((*par_num)++, "PBF");
                                        _print_parity(bp, PBF_REG_PBF_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "QM");
+                                       _print_next_block((*par_num)++, "QM");
                                        _print_parity(bp, QM_REG_QM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "TM");
+                                       _print_next_block((*par_num)++, "TM");
                                        _print_parity(bp, TM_REG_TM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "XSDM");
+                                       _print_next_block((*par_num)++, "XSDM");
                                        _print_parity(bp,
                                                      XSDM_REG_XSDM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "XCM");
+                                       _print_next_block((*par_num)++, "XCM");
                                        _print_parity(bp, XCM_REG_XCM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "XSEMI");
+                                       _print_next_block((*par_num)++,
+                                                         "XSEMI");
                                        _print_parity(bp,
                                                      XSEM_REG_XSEM_PRTY_STS_0);
                                        _print_parity(bp,
@@ -4401,7 +4419,7 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "DOORBELLQ");
                                        _print_parity(bp,
                                                      DORQ_REG_DORQ_PRTY_STS);
@@ -4409,7 +4427,7 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "NIG");
+                                       _print_next_block((*par_num)++, "NIG");
                                        if (CHIP_IS_E1x(bp)) {
                                                _print_parity(bp,
                                                        NIG_REG_NIG_PRTY_STS);
@@ -4423,32 +4441,34 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "VAUX PCI CORE");
                                *global = true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "DEBUG");
+                                       _print_next_block((*par_num)++,
+                                                         "DEBUG");
                                        _print_parity(bp, DBG_REG_DBG_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "USDM");
+                                       _print_next_block((*par_num)++, "USDM");
                                        _print_parity(bp,
                                                      USDM_REG_USDM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "UCM");
+                                       _print_next_block((*par_num)++, "UCM");
                                        _print_parity(bp, UCM_REG_UCM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "USEMI");
+                                       _print_next_block((*par_num)++,
+                                                         "USEMI");
                                        _print_parity(bp,
                                                      USEM_REG_USEM_PRTY_STS_0);
                                        _print_parity(bp,
@@ -4457,21 +4477,21 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                                break;
                        case AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "UPB");
+                                       _print_next_block((*par_num)++, "UPB");
                                        _print_parity(bp, GRCBASE_UPB +
                                                          PB_REG_PB_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "CSDM");
+                                       _print_next_block((*par_num)++, "CSDM");
                                        _print_parity(bp,
                                                      CSDM_REG_CSDM_PRTY_STS);
                                }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR:
                                if (print) {
-                                       _print_next_block(par_num++, "CCM");
+                                       _print_next_block((*par_num)++, "CCM");
                                        _print_parity(bp, CCM_REG_CCM_PRTY_STS);
                                }
                                break;
@@ -4482,80 +4502,73 @@ static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool print)
+static bool bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
-                       switch (cur_bit) {
-                       case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "CSEMI");
+                       res |= true; /* Each bit is real error! */
+                       if (print) {
+                               switch (cur_bit) {
+                               case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "CSEMI");
                                        _print_parity(bp,
                                                      CSEM_REG_CSEM_PRTY_STS_0);
                                        _print_parity(bp,
                                                      CSEM_REG_CSEM_PRTY_STS_1);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "PXP");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "PXP");
                                        _print_parity(bp, PXP_REG_PXP_PRTY_STS);
                                        _print_parity(bp,
                                                      PXP2_REG_PXP2_PRTY_STS_0);
                                        _print_parity(bp,
                                                      PXP2_REG_PXP2_PRTY_STS_1);
-                               }
-                               break;
-                       case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
-                               if (print)
-                                       _print_next_block(par_num++,
-                                       "PXPPCICLOCKCLIENT");
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "CFC");
+                                       break;
+                               case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "PXPPCICLOCKCLIENT");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "CFC");
                                        _print_parity(bp,
                                                      CFC_REG_CFC_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "CDU");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "CDU");
                                        _print_parity(bp, CDU_REG_CDU_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "DMAE");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "DMAE");
                                        _print_parity(bp,
                                                      DMAE_REG_DMAE_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "IGU");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "IGU");
                                        if (CHIP_IS_E1x(bp))
                                                _print_parity(bp,
                                                        HC_REG_HC_PRTY_STS);
                                        else
                                                _print_parity(bp,
                                                        IGU_REG_IGU_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "MISC");
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "MISC");
                                        _print_parity(bp,
                                                      MISC_REG_MISC_PRTY_STS);
+                                       break;
                                }
-                               break;
                        }
 
                        /* Clear the bit */
@@ -4563,40 +4576,49 @@ static int bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity3(u32 sig, int par_num,
-                                          bool *global, bool print)
+static bool bnx2x_check_blocks_with_parity3(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool *global,
+                                           bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       bool res = false;
+       u32 cur_bit;
+       int i;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY:
                                if (print)
-                                       _print_next_block(par_num++, "MCP ROM");
+                                       _print_next_block((*par_num)++,
+                                                         "MCP ROM");
                                *global = true;
+                               res |= true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "MCP UMP RX");
                                *global = true;
+                               res |= true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "MCP UMP TX");
                                *global = true;
+                               res |= true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY:
                                if (print)
-                                       _print_next_block(par_num++,
+                                       _print_next_block((*par_num)++,
                                                          "MCP SCPAD");
-                               *global = true;
+                               /* clear latched SCPAD PATIRY from MCP */
+                               REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL,
+                                      1UL << 10);
                                break;
                        }
 
@@ -4605,45 +4627,50 @@ static int bnx2x_check_blocks_with_parity3(u32 sig, int par_num,
                }
        }
 
-       return par_num;
+       return res;
 }
 
-static int bnx2x_check_blocks_with_parity4(struct bnx2x *bp, u32 sig,
-                                           int par_num, bool print)
+static bool bnx2x_check_blocks_with_parity4(struct bnx2x *bp, u32 sig,
+                                           int *par_num, bool print)
 {
-       int i = 0;
-       u32 cur_bit = 0;
+       u32 cur_bit;
+       bool res;
+       int i;
+
+       res = false;
+
        for (i = 0; sig; i++) {
-               cur_bit = ((u32)0x1 << i);
+               cur_bit = (0x1UL << i);
                if (sig & cur_bit) {
-                       switch (cur_bit) {
-                       case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "PGLUE_B");
+                       res |= true; /* Each bit is real error! */
+                       if (print) {
+                               switch (cur_bit) {
+                               case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR:
+                                       _print_next_block((*par_num)++,
+                                                         "PGLUE_B");
                                        _print_parity(bp,
-                                               PGLUE_B_REG_PGLUE_B_PRTY_STS);
-                               }
-                               break;
-                       case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR:
-                               if (print) {
-                                       _print_next_block(par_num++, "ATC");
+                                                     PGLUE_B_REG_PGLUE_B_PRTY_STS);
+                                       break;
+                               case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR:
+                                       _print_next_block((*par_num)++, "ATC");
                                        _print_parity(bp,
                                                      ATC_REG_ATC_PRTY_STS);
+                                       break;
                                }
-                               break;
                        }
-
                        /* Clear the bit */
                        sig &= ~cur_bit;
                }
        }
 
-       return par_num;
+       return res;
 }
 
 static bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print,
                              u32 *sig)
 {
+       bool res = false;
+
        if ((sig[0] & HW_PRTY_ASSERT_SET_0) ||
            (sig[1] & HW_PRTY_ASSERT_SET_1) ||
            (sig[2] & HW_PRTY_ASSERT_SET_2) ||
@@ -4660,23 +4687,22 @@ static bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print,
                if (print)
                        netdev_err(bp->dev,
                                   "Parity errors detected in blocks: ");
-               par_num = bnx2x_check_blocks_with_parity0(bp,
-                       sig[0] & HW_PRTY_ASSERT_SET_0, par_num, print);
-               par_num = bnx2x_check_blocks_with_parity1(bp,
-                       sig[1] & HW_PRTY_ASSERT_SET_1, par_num, global, print);
-               par_num = bnx2x_check_blocks_with_parity2(bp,
-                       sig[2] & HW_PRTY_ASSERT_SET_2, par_num, print);
-               par_num = bnx2x_check_blocks_with_parity3(
-                       sig[3] & HW_PRTY_ASSERT_SET_3, par_num, global, print);
-               par_num = bnx2x_check_blocks_with_parity4(bp,
-                       sig[4] & HW_PRTY_ASSERT_SET_4, par_num, print);
+               res |= bnx2x_check_blocks_with_parity0(bp,
+                       sig[0] & HW_PRTY_ASSERT_SET_0, &par_num, print);
+               res |= bnx2x_check_blocks_with_parity1(bp,
+                       sig[1] & HW_PRTY_ASSERT_SET_1, &par_num, global, print);
+               res |= bnx2x_check_blocks_with_parity2(bp,
+                       sig[2] & HW_PRTY_ASSERT_SET_2, &par_num, print);
+               res |= bnx2x_check_blocks_with_parity3(bp,
+                       sig[3] & HW_PRTY_ASSERT_SET_3, &par_num, global, print);
+               res |= bnx2x_check_blocks_with_parity4(bp,
+                       sig[4] & HW_PRTY_ASSERT_SET_4, &par_num, print);
 
                if (print)
                        pr_cont("\n");
+       }
 
-               return true;
-       } else
-               return false;
+       return res;
 }
 
 /**
@@ -4703,6 +4729,14 @@ bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print)
        attn.sig[3] = REG_RD(bp,
                MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 +
                             port*4);
+       /* Since MCP attentions can't be disabled inside the block, we need to
+        * read AEU registers to see whether they're currently disabled
+        */
+       attn.sig[3] &= ((REG_RD(bp,
+                               !port ? MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0
+                                     : MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0) &
+                        MISC_AEU_ENABLE_MCP_PRTY_BITS) |
+                       ~MISC_AEU_ENABLE_MCP_PRTY_BITS);
 
        if (!CHIP_IS_E1x(bp))
                attn.sig[4] = REG_RD(bp,
@@ -5447,26 +5481,24 @@ static void bnx2x_timer(unsigned long data)
        if (IS_PF(bp) &&
            !BP_NOMCP(bp)) {
                int mb_idx = BP_FW_MB_IDX(bp);
-               u32 drv_pulse;
-               u32 mcp_pulse;
+               u16 drv_pulse;
+               u16 mcp_pulse;
 
                ++bp->fw_drv_pulse_wr_seq;
                bp->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK;
-               /* TBD - add SYSTEM_TIME */
                drv_pulse = bp->fw_drv_pulse_wr_seq;
                bnx2x_drv_pulse(bp);
 
                mcp_pulse = (SHMEM_RD(bp, func_mb[mb_idx].mcp_pulse_mb) &
                             MCP_PULSE_SEQ_MASK);
                /* The delta between driver pulse and mcp response
-                * should be 1 (before mcp response) or 0 (after mcp response)
+                * should not get too big. If the MFW is more than 5 pulses
+                * behind, we should worry about it enough to generate an error
+                * log.
                 */
-               if ((drv_pulse != mcp_pulse) &&
-                   (drv_pulse != ((mcp_pulse + 1) & MCP_PULSE_SEQ_MASK))) {
-                       /* someone lost a heartbeat... */
-                       BNX2X_ERR("drv_pulse (0x%x) != mcp_pulse (0x%x)\n",
+               if (((drv_pulse - mcp_pulse) & MCP_PULSE_SEQ_MASK) > 5)
+                       BNX2X_ERR("MFW seems hanged: drv_pulse (0x%x) != mcp_pulse (0x%x)\n",
                                  drv_pulse, mcp_pulse);
-               }
        }
 
        if (bp->state == BNX2X_STATE_OPEN)
@@ -7120,7 +7152,7 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
        int port = BP_PORT(bp);
        int init_phase = port ? PHASE_PORT1 : PHASE_PORT0;
        u32 low, high;
-       u32 val;
+       u32 val, reg;
 
        DP(NETIF_MSG_HW, "starting port init  port %d\n", port);
 
@@ -7265,6 +7297,17 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
        val |= CHIP_IS_E1(bp) ? 0 : 0x10;
        REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, val);
 
+       /* SCPAD_PARITY should NOT trigger close the gates */
+       reg = port ? MISC_REG_AEU_ENABLE4_NIG_1 : MISC_REG_AEU_ENABLE4_NIG_0;
+       REG_WR(bp, reg,
+              REG_RD(bp, reg) &
+              ~AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY);
+
+       reg = port ? MISC_REG_AEU_ENABLE4_PXP_1 : MISC_REG_AEU_ENABLE4_PXP_0;
+       REG_WR(bp, reg,
+              REG_RD(bp, reg) &
+              ~AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY);
+
        bnx2x_init_block(bp, BLOCK_NIG, init_phase);
 
        if (!CHIP_IS_E1x(bp)) {
@@ -9873,7 +9916,7 @@ static int bnx2x_prev_path_mark_eeh(struct bnx2x *bp)
 static bool bnx2x_prev_is_path_marked(struct bnx2x *bp)
 {
        struct bnx2x_prev_path_list *tmp_list;
-       int rc = false;
+       bool rc = false;
 
        if (down_trylock(&bnx2x_prev_sem))
                return false;
@@ -11143,6 +11186,14 @@ static void bnx2x_get_mac_hwinfo(struct bnx2x *bp)
                        bnx2x_get_cnic_mac_hwinfo(bp);
        }
 
+       if (!BP_NOMCP(bp)) {
+               /* Read physical port identifier from shmem */
+               val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_upper);
+               val = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_lower);
+               bnx2x_set_mac_buf(bp->phys_port_id, val, val2);
+               bp->flags |= HAS_PHYS_PORT_ID;
+       }
+
        memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN);
 
        if (!bnx2x_is_valid_ether_addr(bp, bp->dev->dev_addr))
@@ -11679,9 +11730,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 static int bnx2x_open(struct net_device *dev)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       bool global = false;
-       int other_engine = BP_PATH(bp) ? 0 : 1;
-       bool other_load_status, load_status;
        int rc;
 
        bp->stats_init = true;
@@ -11697,6 +11745,10 @@ static int bnx2x_open(struct net_device *dev)
         * Parity recovery is only relevant for PF driver.
         */
        if (IS_PF(bp)) {
+               int other_engine = BP_PATH(bp) ? 0 : 1;
+               bool other_load_status, load_status;
+               bool global = false;
+
                other_load_status = bnx2x_get_load_status(bp, other_engine);
                load_status = bnx2x_get_load_status(bp, BP_PATH(bp));
                if (!bnx2x_reset_is_done(bp, BP_PATH(bp)) ||
@@ -11740,7 +11792,7 @@ static int bnx2x_open(struct net_device *dev)
        rc = bnx2x_nic_load(bp, LOAD_OPEN);
        if (rc)
                return rc;
-       return bnx2x_open_epilog(bp);
+       return 0;
 }
 
 /* called with rtnl_lock */
@@ -12038,6 +12090,20 @@ static int bnx2x_validate_addr(struct net_device *dev)
        return 0;
 }
 
+static int bnx2x_get_phys_port_id(struct net_device *netdev,
+                                 struct netdev_phys_port_id *ppid)
+{
+       struct bnx2x *bp = netdev_priv(netdev);
+
+       if (!(bp->flags & HAS_PHYS_PORT_ID))
+               return -EOPNOTSUPP;
+
+       ppid->id_len = sizeof(bp->phys_port_id);
+       memcpy(ppid->id, bp->phys_port_id, ppid->id_len);
+
+       return 0;
+}
+
 static const struct net_device_ops bnx2x_netdev_ops = {
        .ndo_open               = bnx2x_open,
        .ndo_stop               = bnx2x_close,
@@ -12067,6 +12133,7 @@ static const struct net_device_ops bnx2x_netdev_ops = {
 #ifdef CONFIG_NET_RX_BUSY_POLL
        .ndo_busy_poll          = bnx2x_low_latency_recv,
 #endif
+       .ndo_get_phys_port_id   = bnx2x_get_phys_port_id,
 };
 
 static int bnx2x_set_coherency_mask(struct bnx2x *bp)
@@ -12074,7 +12141,6 @@ static int bnx2x_set_coherency_mask(struct bnx2x *bp)
        struct device *dev = &bp->pdev->dev;
 
        if (dma_set_mask(dev, DMA_BIT_MASK(64)) == 0) {
-               bp->flags |= USING_DAC_FLAG;
                if (dma_set_coherent_mask(dev, DMA_BIT_MASK(64)) != 0) {
                        dev_err(dev, "dma_set_coherent_mask failed, aborting\n");
                        return -EIO;
@@ -12231,10 +12297,13 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
                NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO |
                NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX;
        if (!CHIP_IS_E1x(bp)) {
-               dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL;
+               dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL |
+                                   NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT;
                dev->hw_enc_features =
                        NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
                        NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 |
+                       NETIF_F_GSO_IPIP |
+                       NETIF_F_GSO_SIT |
                        NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL;
        }
 
@@ -12242,8 +12311,7 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
                NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_HIGHDMA;
 
        dev->features |= dev->hw_features | NETIF_F_HW_VLAN_CTAG_RX;
-       if (bp->flags & USING_DAC_FLAG)
-               dev->features |= NETIF_F_HIGHDMA;
+       dev->features |= NETIF_F_HIGHDMA;
 
        /* Add Loopback capability to the device */
        dev->hw_features |= NETIF_F_LOOPBACK;
@@ -12268,34 +12336,11 @@ err_out_release:
 
 err_out_disable:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
 err_out:
        return rc;
 }
 
-static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width,
-                                      enum bnx2x_pci_bus_speed *speed)
-{
-       u32 link_speed, val = 0;
-
-       pci_read_config_dword(bp->pdev, PCICFG_LINK_CONTROL, &val);
-       *width = (val & PCICFG_LINK_WIDTH) >> PCICFG_LINK_WIDTH_SHIFT;
-
-       link_speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT;
-
-       switch (link_speed) {
-       case 3:
-               *speed = BNX2X_PCI_LINK_SPEED_8000;
-               break;
-       case 2:
-               *speed = BNX2X_PCI_LINK_SPEED_5000;
-               break;
-       default:
-               *speed = BNX2X_PCI_LINK_SPEED_2500;
-       }
-}
-
 static int bnx2x_check_firmware(struct bnx2x *bp)
 {
        const struct firmware *firmware = bp->firmware;
@@ -12606,24 +12651,24 @@ static int set_max_cos_est(int chip_id)
                return BNX2X_MULTI_TX_COS_E1X;
        case BCM57712:
        case BCM57712_MF:
-       case BCM57712_VF:
                return BNX2X_MULTI_TX_COS_E2_E3A0;
        case BCM57800:
        case BCM57800_MF:
-       case BCM57800_VF:
        case BCM57810:
        case BCM57810_MF:
        case BCM57840_4_10:
        case BCM57840_2_20:
        case BCM57840_O:
        case BCM57840_MFO:
-       case BCM57810_VF:
        case BCM57840_MF:
-       case BCM57840_VF:
        case BCM57811:
        case BCM57811_MF:
-       case BCM57811_VF:
                return BNX2X_MULTI_TX_COS_E3B0;
+       case BCM57712_VF:
+       case BCM57800_VF:
+       case BCM57810_VF:
+       case BCM57840_VF:
+       case BCM57811_VF:
                return 1;
        default:
                pr_err("Unknown board_type (%d), aborting\n", chip_id);
@@ -12652,8 +12697,8 @@ static int bnx2x_init_one(struct pci_dev *pdev,
 {
        struct net_device *dev = NULL;
        struct bnx2x *bp;
-       int pcie_width;
-       enum bnx2x_pci_bus_speed pcie_speed;
+       enum pcie_link_width pcie_width;
+       enum pci_bus_speed pcie_speed;
        int rc, max_non_def_sbs;
        int rx_count, tx_count, rss_count, doorbell_size;
        int max_cos_est;
@@ -12802,18 +12847,19 @@ static int bnx2x_init_one(struct pci_dev *pdev,
                dev_addr_add(bp->dev, bp->fip_mac, NETDEV_HW_ADDR_T_SAN);
                rtnl_unlock();
        }
-
-       bnx2x_get_pcie_width_speed(bp, &pcie_width, &pcie_speed);
-       BNX2X_DEV_INFO("got pcie width %d and speed %d\n",
-                      pcie_width, pcie_speed);
-
-       BNX2X_DEV_INFO("%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n",
+       if (pcie_get_minimum_link(bp->pdev, &pcie_speed, &pcie_width) ||
+           pcie_speed == PCI_SPEED_UNKNOWN ||
+           pcie_width == PCIE_LNK_WIDTH_UNKNOWN)
+               BNX2X_DEV_INFO("Failed to determine PCI Express Bandwidth\n");
+       else
+               BNX2X_DEV_INFO(
+                      "%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n",
                       board_info[ent->driver_data].name,
                       (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4),
                       pcie_width,
-                      pcie_speed == BNX2X_PCI_LINK_SPEED_2500 ? "2.5GHz" :
-                      pcie_speed == BNX2X_PCI_LINK_SPEED_5000 ? "5.0GHz" :
-                      pcie_speed == BNX2X_PCI_LINK_SPEED_8000 ? "8.0GHz" :
+                      pcie_speed == PCIE_SPEED_2_5GT ? "2.5GHz" :
+                      pcie_speed == PCIE_SPEED_5_0GT ? "5.0GHz" :
+                      pcie_speed == PCIE_SPEED_8_0GT ? "8.0GHz" :
                       "Unknown",
                       dev->base_addr, bp->pdev->irq, dev->dev_addr);
 
@@ -12832,7 +12878,6 @@ init_one_exit:
                pci_release_regions(pdev);
 
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        return rc;
 }
@@ -12915,7 +12960,6 @@ static void __bnx2x_remove(struct pci_dev *pdev,
                pci_release_regions(pdev);
 
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static void bnx2x_remove_one(struct pci_dev *pdev)
index 2604b62..0216d59 100644 (file)
@@ -470,10 +470,10 @@ static int bnx2x_vfop_qdtor_cmd(struct bnx2x *bp,
                                 bnx2x_vfop_qdtor, cmd->done);
                return bnx2x_vfop_transition(bp, vf, bnx2x_vfop_qdtor,
                                             cmd->block);
+       } else {
+               BNX2X_ERR("VF[%d] failed to add a vfop\n", vf->abs_vfid);
+               return -ENOMEM;
        }
-       DP(BNX2X_MSG_IOV, "VF[%d] failed to add a vfop. rc %d\n",
-          vf->abs_vfid, vfop->rc);
-       return -ENOMEM;
 }
 
 static void
@@ -1819,7 +1819,7 @@ bnx2x_get_vf_igu_cam_info(struct bnx2x *bp)
                fid = GET_FIELD((val), IGU_REG_MAPPING_MEMORY_FID);
                if (fid & IGU_FID_ENCODE_IS_PF)
                        current_pf = fid & IGU_FID_PF_NUM_MASK;
-               else if (current_pf == BP_ABS_FUNC(bp))
+               else if (current_pf == BP_FUNC(bp))
                        bnx2x_vf_set_igu_info(bp, sb_id,
                                              (fid & IGU_FID_VF_NUM_MASK));
                DP(BNX2X_MSG_IOV, "%s[%d], igu_sb_id=%d, msix=%d\n",
@@ -2018,6 +2018,8 @@ failed:
 
 void bnx2x_iov_remove_one(struct bnx2x *bp)
 {
+       int vf_idx;
+
        /* if SRIOV is not enabled there's nothing to do */
        if (!IS_SRIOV(bp))
                return;
@@ -2026,6 +2028,18 @@ void bnx2x_iov_remove_one(struct bnx2x *bp)
        pci_disable_sriov(bp->pdev);
        DP(BNX2X_MSG_IOV, "sriov disabled\n");
 
+       /* disable access to all VFs */
+       for (vf_idx = 0; vf_idx < bp->vfdb->sriov.total; vf_idx++) {
+               bnx2x_pretend_func(bp,
+                                  HW_VF_HANDLE(bp,
+                                               bp->vfdb->sriov.first_vf_in_pf +
+                                               vf_idx));
+               DP(BNX2X_MSG_IOV, "disabling internal access for vf %d\n",
+                  bp->vfdb->sriov.first_vf_in_pf + vf_idx);
+               bnx2x_vf_enable_internal(bp, 0);
+               bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
+       }
+
        /* free vf database */
        __bnx2x_iov_free_vfdb(bp);
 }
@@ -2802,7 +2816,7 @@ struct set_vf_state_cookie {
        u8 state;
 };
 
-void bnx2x_set_vf_state(void *cookie)
+static void bnx2x_set_vf_state(void *cookie)
 {
        struct set_vf_state_cookie *p = (struct set_vf_state_cookie *)cookie;
 
@@ -3180,6 +3194,7 @@ int bnx2x_enable_sriov(struct bnx2x *bp)
                /* set local queue arrays */
                vf->vfqs = &bp->vfdb->vfqs[qcount];
                qcount += vf_sb_count(vf);
+               bnx2x_iov_static_resc(bp, vf);
        }
 
        /* prepare msix vectors in VF configuration space */
@@ -3187,6 +3202,8 @@ int bnx2x_enable_sriov(struct bnx2x *bp)
                bnx2x_pretend_func(bp, HW_VF_HANDLE(bp, vf_idx));
                REG_WR(bp, PCICFG_OFFSET + GRC_CONFIG_REG_VF_MSIX_CONTROL,
                       num_vf_queues);
+               DP(BNX2X_MSG_IOV, "set msix vec num in VF %d cfg space to %d\n",
+                  vf_idx, num_vf_queues);
        }
        bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
 
@@ -3194,7 +3211,7 @@ int bnx2x_enable_sriov(struct bnx2x *bp)
         * the "acquire" messages to appear on the VF PF channel.
         */
        DP(BNX2X_MSG_IOV, "about to call enable sriov\n");
-       pci_disable_sriov(bp->pdev);
+       bnx2x_disable_sriov(bp);
        rc = pci_enable_sriov(bp->pdev, req_vfs);
        if (rc) {
                BNX2X_ERR("pci_enable_sriov failed with %d\n", rc);
@@ -3222,8 +3239,9 @@ void bnx2x_disable_sriov(struct bnx2x *bp)
        pci_disable_sriov(bp->pdev);
 }
 
-int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, struct bnx2x_virtf **vf,
-                       struct pf_vf_bulletin_content **bulletin)
+static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx,
+                            struct bnx2x_virtf **vf,
+                            struct pf_vf_bulletin_content **bulletin)
 {
        if (bp->state != BNX2X_STATE_OPEN) {
                BNX2X_ERR("vf ndo called though PF is down\n");
@@ -3387,14 +3405,16 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
                rc = bnx2x_del_all_macs(bp, mac_obj, BNX2X_ETH_MAC, true);
                if (rc) {
                        BNX2X_ERR("failed to delete eth macs\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                /* remove existing uc list macs */
                rc = bnx2x_del_all_macs(bp, mac_obj, BNX2X_UC_LIST_MAC, true);
                if (rc) {
                        BNX2X_ERR("failed to delete uc_list macs\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                /* configure the new mac to device */
@@ -3402,6 +3422,7 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
                bnx2x_set_mac_one(bp, (u8 *)&bulletin->mac, mac_obj, true,
                                  BNX2X_ETH_MAC, &ramrod_flags);
 
+out:
                bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_MAC);
        }
 
@@ -3464,7 +3485,8 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
                                          &ramrod_flags);
                if (rc) {
                        BNX2X_ERR("failed to delete vlans\n");
-                       return -EINVAL;
+                       rc = -EINVAL;
+                       goto out;
                }
 
                /* send queue update ramrod to configure default vlan and silent
@@ -3498,7 +3520,8 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
                        rc = bnx2x_config_vlan_mac(bp, &ramrod_param);
                        if (rc) {
                                BNX2X_ERR("failed to configure vlan\n");
-                               return -EINVAL;
+                               rc =  -EINVAL;
+                               goto out;
                        }
 
                        /* configure default vlan to vf queue and set silent
@@ -3516,18 +3539,18 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
                rc = bnx2x_queue_state_change(bp, &q_params);
                if (rc) {
                        BNX2X_ERR("Failed to configure default VLAN\n");
-                       return rc;
+                       goto out;
                }
 
                /* clear the flag indicating that this VF needs its vlan
-                * (will only be set if the HV configured th Vlan before vf was
-                * and we were called because the VF came up later
+                * (will only be set if the HV configured the Vlan before vf was
+                * up and we were called because the VF came up later
                 */
+out:
                vf->cfg_flags &= ~VF_CFG_VLAN;
-
                bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_VLAN);
        }
-       return 0;
+       return rc;
 }
 
 /* crc is the first field in the bulletin board. Compute the crc over the
@@ -3634,29 +3657,6 @@ alloc_mem_err:
        return -ENOMEM;
 }
 
-int bnx2x_open_epilog(struct bnx2x *bp)
-{
-       /* Enable sriov via delayed work. This must be done via delayed work
-        * because it causes the probe of the vf devices to be run, which invoke
-        * register_netdevice which must have rtnl lock taken. As we are holding
-        * the lock right now, that could only work if the probe would not take
-        * the lock. However, as the probe of the vf may be called from other
-        * contexts as well (such as passthrough to vm fails) it can't assume
-        * the lock is being held for it. Using delayed work here allows the
-        * probe code to simply take the lock (i.e. wait for it to be released
-        * if it is being held). We only want to do this if the number of VFs
-        * was set before PF driver was loaded.
-        */
-       if (IS_SRIOV(bp) && BNX2X_NR_VIRTFN(bp)) {
-               smp_mb__before_clear_bit();
-               set_bit(BNX2X_SP_RTNL_ENABLE_SRIOV, &bp->sp_rtnl_state);
-               smp_mb__after_clear_bit();
-               schedule_delayed_work(&bp->sp_rtnl_task, 0);
-       }
-
-       return 0;
-}
-
 void bnx2x_iov_channel_down(struct bnx2x *bp)
 {
        int vf_idx;
index 059f0d4..1ff6a93 100644 (file)
@@ -782,7 +782,6 @@ static inline int bnx2x_vf_headroom(struct bnx2x *bp)
 void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp);
 int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs);
 void bnx2x_iov_channel_down(struct bnx2x *bp);
-int bnx2x_open_epilog(struct bnx2x *bp);
 
 #else /* CONFIG_BNX2X_SRIOV */
 
@@ -842,7 +841,6 @@ static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; }
 static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {}
 static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; }
 static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {}
-static inline int bnx2x_open_epilog(struct bnx2x *bp) {return 0; }
 
 #endif /* CONFIG_BNX2X_SRIOV */
 #endif /* bnx2x_sriov.h */
index 86436c7..3b75070 100644 (file)
@@ -196,7 +196,7 @@ static void bnx2x_hw_stats_post(struct bnx2x *bp)
 
        } else if (bp->func_stx) {
                *stats_comp = 0;
-               bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
+               bnx2x_issue_dmae_with_comp(bp, dmae, stats_comp);
        }
 }
 
index 6cfb887..9199adf 100644 (file)
@@ -60,6 +60,30 @@ void bnx2x_vfpf_finalize(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv)
        mutex_unlock(&bp->vf2pf_mutex);
 }
 
+/* Finds a TLV by type in a TLV buffer; If found, returns pointer to the TLV */
+static void *bnx2x_search_tlv_list(struct bnx2x *bp, void *tlvs_list,
+                                  enum channel_tlvs req_tlv)
+{
+       struct channel_tlv *tlv = (struct channel_tlv *)tlvs_list;
+
+       do {
+               if (tlv->type == req_tlv)
+                       return tlv;
+
+               if (!tlv->length) {
+                       BNX2X_ERR("Found TLV with length 0\n");
+                       return NULL;
+               }
+
+               tlvs_list += tlv->length;
+               tlv = (struct channel_tlv *)tlvs_list;
+       } while (tlv->type != CHANNEL_TLV_LIST_END);
+
+       DP(BNX2X_MSG_IOV, "TLV list does not contain %d TLV\n", req_tlv);
+
+       return NULL;
+}
+
 /* list the types and lengths of the tlvs on the buffer */
 void bnx2x_dp_tlv_list(struct bnx2x *bp, void *tlvs_list)
 {
@@ -196,6 +220,7 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
        int rc = 0, attempts = 0;
        struct vfpf_acquire_tlv *req = &bp->vf2pf_mbox->req.acquire;
        struct pfvf_acquire_resp_tlv *resp = &bp->vf2pf_mbox->resp.acquire_resp;
+       struct vfpf_port_phys_id_resp_tlv *phys_port_resp;
        u32 vf_id;
        bool resources_acquired = false;
 
@@ -219,8 +244,14 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
        /* pf 2 vf bulletin board address */
        req->bulletin_addr = bp->pf2vf_bulletin_mapping;
 
+       /* Request physical port identifier */
+       bnx2x_add_tlv(bp, req, req->first_tlv.tl.length,
+                     CHANNEL_TLV_PHYS_PORT_ID, sizeof(struct channel_tlv));
+
        /* add list termination tlv */
-       bnx2x_add_tlv(bp, req, req->first_tlv.tl.length, CHANNEL_TLV_LIST_END,
+       bnx2x_add_tlv(bp, req,
+                     req->first_tlv.tl.length + sizeof(struct channel_tlv),
+                     CHANNEL_TLV_LIST_END,
                      sizeof(struct channel_list_end_tlv));
 
        /* output tlvs list */
@@ -287,6 +318,15 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
                }
        }
 
+       /* Retrieve physical port id (if possible) */
+       phys_port_resp = (struct vfpf_port_phys_id_resp_tlv *)
+                        bnx2x_search_tlv_list(bp, resp,
+                                              CHANNEL_TLV_PHYS_PORT_ID);
+       if (phys_port_resp) {
+               memcpy(bp->phys_port_id, phys_port_resp->id, ETH_ALEN);
+               bp->flags |= HAS_PHYS_PORT_ID;
+       }
+
        /* get HW info */
        bp->common.chip_id |= (bp->acquire_resp.pfdev_info.chip_num & 0xffff);
        bp->link_params.chip_id = bp->common.chip_id;
@@ -980,56 +1020,62 @@ static int bnx2x_copy32_vf_dmae(struct bnx2x *bp, u8 from_vf,
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       return bnx2x_issue_dmae_with_comp(bp, &dmae);
+       return bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
 }
 
-static void bnx2x_vf_mbx_resp(struct bnx2x *bp, struct bnx2x_virtf *vf)
+static void bnx2x_vf_mbx_resp_single_tlv(struct bnx2x *bp,
+                                        struct bnx2x_virtf *vf)
 {
        struct bnx2x_vf_mbx *mbx = BP_VF_MBX(bp, vf->index);
-       u64 vf_addr;
-       dma_addr_t pf_addr;
        u16 length, type;
-       int rc;
-       struct pfvf_general_resp_tlv *resp = &mbx->msg->resp.general_resp;
 
        /* prepare response */
        type = mbx->first_tlv.tl.type;
        length = type == CHANNEL_TLV_ACQUIRE ?
                sizeof(struct pfvf_acquire_resp_tlv) :
                sizeof(struct pfvf_general_resp_tlv);
-       bnx2x_add_tlv(bp, resp, 0, type, length);
-       resp->hdr.status = bnx2x_pfvf_status_codes(vf->op_rc);
-       bnx2x_add_tlv(bp, resp, length, CHANNEL_TLV_LIST_END,
+       bnx2x_add_tlv(bp, &mbx->msg->resp, 0, type, length);
+       bnx2x_add_tlv(bp, &mbx->msg->resp, length, CHANNEL_TLV_LIST_END,
                      sizeof(struct channel_list_end_tlv));
+}
+
+static void bnx2x_vf_mbx_resp_send_msg(struct bnx2x *bp,
+                                      struct bnx2x_virtf *vf)
+{
+       struct bnx2x_vf_mbx *mbx = BP_VF_MBX(bp, vf->index);
+       struct pfvf_general_resp_tlv *resp = &mbx->msg->resp.general_resp;
+       dma_addr_t pf_addr;
+       u64 vf_addr;
+       int rc;
+
        bnx2x_dp_tlv_list(bp, resp);
        DP(BNX2X_MSG_IOV, "mailbox vf address hi 0x%x, lo 0x%x, offset 0x%x\n",
           mbx->vf_addr_hi, mbx->vf_addr_lo, mbx->first_tlv.resp_msg_offset);
 
+       resp->hdr.status = bnx2x_pfvf_status_codes(vf->op_rc);
+
        /* send response */
        vf_addr = HILO_U64(mbx->vf_addr_hi, mbx->vf_addr_lo) +
                  mbx->first_tlv.resp_msg_offset;
        pf_addr = mbx->msg_mapping +
                  offsetof(struct bnx2x_vf_mbx_msg, resp);
 
-       /* copy the response body, if there is one, before the header, as the vf
-        * is sensitive to the header being written
+       /* Copy the response buffer. The first u64 is written afterwards, as
+        * the vf is sensitive to the header being written
         */
-       if (resp->hdr.tl.length > sizeof(u64)) {
-               length = resp->hdr.tl.length - sizeof(u64);
-               vf_addr += sizeof(u64);
-               pf_addr += sizeof(u64);
-               rc = bnx2x_copy32_vf_dmae(bp, false, pf_addr, vf->abs_vfid,
-                                         U64_HI(vf_addr),
-                                         U64_LO(vf_addr),
-                                         length/4);
-               if (rc) {
-                       BNX2X_ERR("Failed to copy response body to VF %d\n",
-                                 vf->abs_vfid);
-                       goto mbx_error;
-               }
-               vf_addr -= sizeof(u64);
-               pf_addr -= sizeof(u64);
+       vf_addr += sizeof(u64);
+       pf_addr += sizeof(u64);
+       rc = bnx2x_copy32_vf_dmae(bp, false, pf_addr, vf->abs_vfid,
+                                 U64_HI(vf_addr),
+                                 U64_LO(vf_addr),
+                                 (sizeof(union pfvf_tlvs) - sizeof(u64))/4);
+       if (rc) {
+               BNX2X_ERR("Failed to copy response body to VF %d\n",
+                         vf->abs_vfid);
+               goto mbx_error;
        }
+       vf_addr -= sizeof(u64);
+       pf_addr -= sizeof(u64);
 
        /* ack the FW */
        storm_memset_vf_mbx_ack(bp, vf->abs_vfid);
@@ -1060,6 +1106,36 @@ mbx_error:
        bnx2x_vf_release(bp, vf, false); /* non blocking */
 }
 
+static void bnx2x_vf_mbx_resp(struct bnx2x *bp,
+                                      struct bnx2x_virtf *vf)
+{
+       bnx2x_vf_mbx_resp_single_tlv(bp, vf);
+       bnx2x_vf_mbx_resp_send_msg(bp, vf);
+}
+
+static void bnx2x_vf_mbx_resp_phys_port(struct bnx2x *bp,
+                                       struct bnx2x_virtf *vf,
+                                       void *buffer,
+                                       u16 *offset)
+{
+       struct vfpf_port_phys_id_resp_tlv *port_id;
+
+       if (!(bp->flags & HAS_PHYS_PORT_ID))
+               return;
+
+       bnx2x_add_tlv(bp, buffer, *offset, CHANNEL_TLV_PHYS_PORT_ID,
+                     sizeof(struct vfpf_port_phys_id_resp_tlv));
+
+       port_id = (struct vfpf_port_phys_id_resp_tlv *)
+                 (((u8 *)buffer) + *offset);
+       memcpy(port_id->id, bp->phys_port_id, ETH_ALEN);
+
+       /* Offset should continue representing the offset to the tail
+        * of TLV data (outside this function scope)
+        */
+       *offset += sizeof(struct vfpf_port_phys_id_resp_tlv);
+}
+
 static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf,
                                      struct bnx2x_vf_mbx *mbx, int vfop_status)
 {
@@ -1067,6 +1143,7 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf,
        struct pfvf_acquire_resp_tlv *resp = &mbx->msg->resp.acquire_resp;
        struct pf_vf_resc *resc = &resp->resc;
        u8 status = bnx2x_pfvf_status_codes(vfop_status);
+       u16 length;
 
        memset(resp, 0, sizeof(*resp));
 
@@ -1140,9 +1217,24 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf,
                        resc->hw_sbs[i].sb_qid);
        DP_CONT(BNX2X_MSG_IOV, "]\n");
 
+       /* prepare response */
+       length = sizeof(struct pfvf_acquire_resp_tlv);
+       bnx2x_add_tlv(bp, &mbx->msg->resp, 0, CHANNEL_TLV_ACQUIRE, length);
+
+       /* Handle possible VF requests for physical port identifiers.
+        * 'length' should continue to indicate the offset of the first empty
+        * place in the buffer (i.e., where next TLV should be inserted)
+        */
+       if (bnx2x_search_tlv_list(bp, &mbx->msg->req,
+                                 CHANNEL_TLV_PHYS_PORT_ID))
+               bnx2x_vf_mbx_resp_phys_port(bp, vf, &mbx->msg->resp, &length);
+
+       bnx2x_add_tlv(bp, &mbx->msg->resp, length, CHANNEL_TLV_LIST_END,
+                     sizeof(struct channel_list_end_tlv));
+
        /* send the response */
        vf->op_rc = vfop_status;
-       bnx2x_vf_mbx_resp(bp, vf);
+       bnx2x_vf_mbx_resp_send_msg(bp, vf);
 }
 
 static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
@@ -1765,28 +1857,28 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
                switch (mbx->first_tlv.tl.type) {
                case CHANNEL_TLV_ACQUIRE:
                        bnx2x_vf_mbx_acquire(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_INIT:
                        bnx2x_vf_mbx_init_vf(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_SETUP_Q:
                        bnx2x_vf_mbx_setup_q(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_SET_Q_FILTERS:
                        bnx2x_vf_mbx_set_q_filters(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_TEARDOWN_Q:
                        bnx2x_vf_mbx_teardown_q(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_CLOSE:
                        bnx2x_vf_mbx_close_vf(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_RELEASE:
                        bnx2x_vf_mbx_release_vf(bp, vf, mbx);
-                       break;
+                       return;
                case CHANNEL_TLV_UPDATE_RSS:
                        bnx2x_vf_mbx_update_rss(bp, vf, mbx);
-                       break;
+                       return;
                }
 
        } else {
@@ -1802,26 +1894,24 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
                for (i = 0; i < 20; i++)
                        DP_CONT(BNX2X_MSG_IOV, "%x ",
                                mbx->msg->req.tlv_buf_size.tlv_buffer[i]);
+       }
 
-               /* test whether we can respond to the VF (do we have an address
-                * for it?)
-                */
-               if (vf->state == VF_ACQUIRED || vf->state == VF_ENABLED) {
-                       /* mbx_resp uses the op_rc of the VF */
-                       vf->op_rc = PFVF_STATUS_NOT_SUPPORTED;
+       /* can we respond to VF (do we have an address for it?) */
+       if (vf->state == VF_ACQUIRED || vf->state == VF_ENABLED) {
+               /* mbx_resp uses the op_rc of the VF */
+               vf->op_rc = PFVF_STATUS_NOT_SUPPORTED;
 
-                       /* notify the VF that we do not support this request */
-                       bnx2x_vf_mbx_resp(bp, vf);
-               } else {
-                       /* can't send a response since this VF is unknown to us
-                        * just ack the FW to release the mailbox and unlock
-                        * the channel.
-                        */
-                       storm_memset_vf_mbx_ack(bp, vf->abs_vfid);
-                       mmiowb();
-                       bnx2x_unlock_vf_pf_channel(bp, vf,
-                                                  mbx->first_tlv.tl.type);
-               }
+               /* notify the VF that we do not support this request */
+               bnx2x_vf_mbx_resp(bp, vf);
+       } else {
+               /* can't send a response since this VF is unknown to us
+                * just ack the FW to release the mailbox and unlock
+                * the channel.
+                */
+               storm_memset_vf_mbx_ack(bp, vf->abs_vfid);
+               /* Firmware ack should be written before unlocking channel */
+               mmiowb();
+               bnx2x_unlock_vf_pf_channel(bp, vf, mbx->first_tlv.tl.type);
        }
 }
 
@@ -1876,6 +1966,9 @@ void bnx2x_vf_mbx(struct bnx2x *bp, struct vf_pf_event_data *vfpf_event)
        /* process the VF message header */
        mbx->first_tlv = mbx->msg->req.first_tlv;
 
+       /* Clean response buffer to refrain from falsely seeing chains */
+       memset(&mbx->msg->resp, 0, sizeof(union pfvf_tlvs));
+
        /* dispatch the request (will prepare the response) */
        bnx2x_vf_mbx_request(bp, vf, mbx);
        goto mbx_done;
index 1179fe0..208568b 100644 (file)
@@ -188,6 +188,12 @@ struct pfvf_acquire_resp_tlv {
        } resc;
 };
 
+struct vfpf_port_phys_id_resp_tlv {
+       struct channel_tlv tl;
+       u8 id[ETH_ALEN];
+       u8 padding[2];
+};
+
 #define VFPF_INIT_FLG_STATS_COALESCE   (1 << 0) /* when set the VFs queues
                                                  * stats will be coalesced on
                                                  * the leading RSS queue
@@ -398,6 +404,7 @@ enum channel_tlvs {
        CHANNEL_TLV_PF_SET_MAC,
        CHANNEL_TLV_PF_SET_VLAN,
        CHANNEL_TLV_UPDATE_RSS,
+       CHANNEL_TLV_PHYS_PORT_ID,
        CHANNEL_TLV_MAX
 };
 
index 99394bd..f58a8b8 100644 (file)
@@ -393,7 +393,7 @@ static int cnic_iscsi_nl_msg_recv(struct cnic_dev *dev, u32 msg_type,
 
                        csk->vlan_id = path_resp->vlan_id;
 
-                       memcpy(csk->ha, path_resp->mac_addr, 6);
+                       memcpy(csk->ha, path_resp->mac_addr, ETH_ALEN);
                        if (test_bit(SK_F_IPV6, &csk->flags))
                                memcpy(&csk->src_ip[0], &path_resp->src.v6_addr,
                                       sizeof(struct in6_addr));
@@ -5572,7 +5572,7 @@ static struct cnic_dev *init_bnx2x_cnic(struct net_device *dev)
        if (cdev->max_fcoe_conn > BNX2X_FCOE_NUM_CONNECTIONS)
                cdev->max_fcoe_conn = BNX2X_FCOE_NUM_CONNECTIONS;
 
-       memcpy(cdev->mac_addr, ethdev->iscsi_mac, 6);
+       memcpy(cdev->mac_addr, ethdev->iscsi_mac, ETH_ALEN);
 
        cp->cnic_ops = &cnic_bnx2x_ops;
        cp->start_hw = cnic_start_bnx2x_hw;
index 0658b43..ebbfe25 100644 (file)
@@ -353,8 +353,8 @@ struct cnic_ulp_ops {
        atomic_t ref_count;
 };
 
-extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
+int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
 
-extern int cnic_unregister_driver(int ulp_type);
+int cnic_unregister_driver(int ulp_type);
 
 #endif
index 12d961c..00c5be8 100644 (file)
@@ -94,10 +94,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
 
 #define DRV_MODULE_NAME                "tg3"
 #define TG3_MAJ_NUM                    3
-#define TG3_MIN_NUM                    133
+#define TG3_MIN_NUM                    134
 #define DRV_MODULE_VERSION     \
        __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE     "Jul 29, 2013"
+#define DRV_MODULE_RELDATE     "Sep 16, 2013"
 
 #define RESET_KIND_SHUTDOWN    0
 #define RESET_KIND_INIT                1
@@ -337,6 +337,11 @@ static DEFINE_PCI_DEVICE_TABLE(tg3_pci_tbl) = {
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5762)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5725)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5727)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57764)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57767)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57787)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57782)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57786)},
        {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
        {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
        {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -1326,6 +1331,12 @@ static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable)
        return err;
 }
 
+static int tg3_phy_shdw_write(struct tg3 *tp, int reg, u32 val)
+{
+       return tg3_writephy(tp, MII_TG3_MISC_SHDW,
+                           reg | val | MII_TG3_MISC_SHDW_WREN);
+}
+
 static int tg3_bmcr_reset(struct tg3 *tp)
 {
        u32 phy_control;
@@ -1364,7 +1375,7 @@ static int tg3_mdio_read(struct mii_bus *bp, int mii_id, int reg)
 
        spin_lock_bh(&tp->lock);
 
-       if (tg3_readphy(tp, reg, &val))
+       if (__tg3_readphy(tp, mii_id, reg, &val))
                val = -EIO;
 
        spin_unlock_bh(&tp->lock);
@@ -1379,7 +1390,7 @@ static int tg3_mdio_write(struct mii_bus *bp, int mii_id, int reg, u16 val)
 
        spin_lock_bh(&tp->lock);
 
-       if (tg3_writephy(tp, reg, val))
+       if (__tg3_writephy(tp, mii_id, reg, val))
                ret = -EIO;
 
        spin_unlock_bh(&tp->lock);
@@ -1397,7 +1408,7 @@ static void tg3_mdio_config_5785(struct tg3 *tp)
        u32 val;
        struct phy_device *phydev;
 
-       phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+       phydev = tp->mdio_bus->phy_map[tp->phy_addr];
        switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
        case PHY_ID_BCM50610:
        case PHY_ID_BCM50610M:
@@ -1502,6 +1513,13 @@ static int tg3_mdio_init(struct tg3 *tp)
                                    TG3_CPMU_PHY_STRAP_IS_SERDES;
                if (is_serdes)
                        tp->phy_addr += 7;
+       } else if (tg3_flag(tp, IS_SSB_CORE) && tg3_flag(tp, ROBOSWITCH)) {
+               int addr;
+
+               addr = ssb_gige_get_phyaddr(tp->pdev);
+               if (addr < 0)
+                       return addr;
+               tp->phy_addr = addr;
        } else
                tp->phy_addr = TG3_PHY_MII_ADDR;
 
@@ -1522,7 +1540,7 @@ static int tg3_mdio_init(struct tg3 *tp)
        tp->mdio_bus->read     = &tg3_mdio_read;
        tp->mdio_bus->write    = &tg3_mdio_write;
        tp->mdio_bus->reset    = &tg3_mdio_reset;
-       tp->mdio_bus->phy_mask = ~(1 << TG3_PHY_MII_ADDR);
+       tp->mdio_bus->phy_mask = ~(1 << tp->phy_addr);
        tp->mdio_bus->irq      = &tp->mdio_irq[0];
 
        for (i = 0; i < PHY_MAX_ADDR; i++)
@@ -1543,7 +1561,7 @@ static int tg3_mdio_init(struct tg3 *tp)
                return i;
        }
 
-       phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+       phydev = tp->mdio_bus->phy_map[tp->phy_addr];
 
        if (!phydev || !phydev->drv) {
                dev_warn(&tp->pdev->dev, "No PHY devices\n");
@@ -1953,7 +1971,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
        u32 old_tx_mode = tp->tx_mode;
 
        if (tg3_flag(tp, USE_PHYLIB))
-               autoneg = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]->autoneg;
+               autoneg = tp->mdio_bus->phy_map[tp->phy_addr]->autoneg;
        else
                autoneg = tp->link_config.autoneg;
 
@@ -1989,7 +2007,7 @@ static void tg3_adjust_link(struct net_device *dev)
        u8 oldflowctrl, linkmesg = 0;
        u32 mac_mode, lcl_adv, rmt_adv;
        struct tg3 *tp = netdev_priv(dev);
-       struct phy_device *phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+       struct phy_device *phydev = tp->mdio_bus->phy_map[tp->phy_addr];
 
        spin_lock_bh(&tp->lock);
 
@@ -2078,7 +2096,7 @@ static int tg3_phy_init(struct tg3 *tp)
        /* Bring the PHY back to a known state. */
        tg3_bmcr_reset(tp);
 
-       phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+       phydev = tp->mdio_bus->phy_map[tp->phy_addr];
 
        /* Attach the MAC to the PHY. */
        phydev = phy_connect(tp->dev, dev_name(&phydev->dev),
@@ -2105,7 +2123,7 @@ static int tg3_phy_init(struct tg3 *tp)
                                      SUPPORTED_Asym_Pause);
                break;
        default:
-               phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
+               phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]);
                return -EINVAL;
        }
 
@@ -2123,7 +2141,7 @@ static void tg3_phy_start(struct tg3 *tp)
        if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                return;
 
-       phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+       phydev = tp->mdio_bus->phy_map[tp->phy_addr];
 
        if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
                tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER;
@@ -2143,13 +2161,13 @@ static void tg3_phy_stop(struct tg3 *tp)
        if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                return;
 
-       phy_stop(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
+       phy_stop(tp->mdio_bus->phy_map[tp->phy_addr]);
 }
 
 static void tg3_phy_fini(struct tg3 *tp)
 {
        if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
-               phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
+               phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]);
                tp->phy_flags &= ~TG3_PHYFLG_IS_CONNECTED;
        }
 }
@@ -2218,25 +2236,21 @@ static void tg3_phy_toggle_apd(struct tg3 *tp, bool enable)
                return;
        }
 
-       reg = MII_TG3_MISC_SHDW_WREN |
-             MII_TG3_MISC_SHDW_SCR5_SEL |
-             MII_TG3_MISC_SHDW_SCR5_LPED |
+       reg = MII_TG3_MISC_SHDW_SCR5_LPED |
              MII_TG3_MISC_SHDW_SCR5_DLPTLM |
              MII_TG3_MISC_SHDW_SCR5_SDTL |
              MII_TG3_MISC_SHDW_SCR5_C125OE;
        if (tg3_asic_rev(tp) != ASIC_REV_5784 || !enable)
                reg |= MII_TG3_MISC_SHDW_SCR5_DLLAPD;
 
-       tg3_writephy(tp, MII_TG3_MISC_SHDW, reg);
+       tg3_phy_shdw_write(tp, MII_TG3_MISC_SHDW_SCR5_SEL, reg);
 
 
-       reg = MII_TG3_MISC_SHDW_WREN |
-             MII_TG3_MISC_SHDW_APD_SEL |
-             MII_TG3_MISC_SHDW_APD_WKTM_84MS;
+       reg = MII_TG3_MISC_SHDW_APD_WKTM_84MS;
        if (enable)
                reg |= MII_TG3_MISC_SHDW_APD_ENABLE;
 
-       tg3_writephy(tp, MII_TG3_MISC_SHDW, reg);
+       tg3_phy_shdw_write(tp, MII_TG3_MISC_SHDW_APD_SEL, reg);
 }
 
 static void tg3_phy_toggle_automdix(struct tg3 *tp, bool enable)
@@ -4027,7 +4041,7 @@ static int tg3_power_down_prepare(struct tg3 *tp)
                        struct phy_device *phydev;
                        u32 phyid, advertising;
 
-                       phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+                       phydev = tp->mdio_bus->phy_map[tp->phy_addr];
 
                        tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER;
 
@@ -6848,12 +6862,6 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
                        pci_unmap_single(tp->pdev, dma_addr, skb_size,
                                         PCI_DMA_FROMDEVICE);
 
-                       skb = build_skb(data, frag_size);
-                       if (!skb) {
-                               tg3_frag_free(frag_size != 0, data);
-                               goto drop_it_no_recycle;
-                       }
-                       skb_reserve(skb, TG3_RX_OFFSET(tp));
                        /* Ensure that the update to the data happens
                         * after the usage of the old DMA mapping.
                         */
@@ -6861,6 +6869,12 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
 
                        ri->data = NULL;
 
+                       skb = build_skb(data, frag_size);
+                       if (!skb) {
+                               tg3_frag_free(frag_size != 0, data);
+                               goto drop_it_no_recycle;
+                       }
+                       skb_reserve(skb, TG3_RX_OFFSET(tp));
                } else {
                        tg3_recycle_rx(tnapi, tpr, opaque_key,
                                       desc_idx, *post_ptr);
@@ -9196,10 +9210,7 @@ static int tg3_halt(struct tg3 *tp, int kind, bool silent)
                memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats));
        }
 
-       if (err)
-               return err;
-
-       return 0;
+       return err;
 }
 
 static int tg3_set_mac_addr(struct net_device *dev, void *p)
@@ -11035,7 +11046,18 @@ static int tg3_request_irq(struct tg3 *tp, int irq_num)
                name = tp->dev->name;
        else {
                name = &tnapi->irq_lbl[0];
-               snprintf(name, IFNAMSIZ, "%s-%d", tp->dev->name, irq_num);
+               if (tnapi->tx_buffers && tnapi->rx_rcb)
+                       snprintf(name, IFNAMSIZ,
+                                "%s-txrx-%d", tp->dev->name, irq_num);
+               else if (tnapi->tx_buffers)
+                       snprintf(name, IFNAMSIZ,
+                                "%s-tx-%d", tp->dev->name, irq_num);
+               else if (tnapi->rx_rcb)
+                       snprintf(name, IFNAMSIZ,
+                                "%s-rx-%d", tp->dev->name, irq_num);
+               else
+                       snprintf(name, IFNAMSIZ,
+                                "%s-%d", tp->dev->name, irq_num);
                name[IFNAMSIZ-1] = 0;
        }
 
@@ -11907,7 +11929,7 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                struct phy_device *phydev;
                if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                        return -EAGAIN;
-               phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+               phydev = tp->mdio_bus->phy_map[tp->phy_addr];
                return phy_ethtool_gset(phydev, cmd);
        }
 
@@ -11974,7 +11996,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                struct phy_device *phydev;
                if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                        return -EAGAIN;
-               phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+               phydev = tp->mdio_bus->phy_map[tp->phy_addr];
                return phy_ethtool_sset(phydev, cmd);
        }
 
@@ -12093,12 +12115,10 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 
        device_set_wakeup_enable(dp, wol->wolopts & WAKE_MAGIC);
 
-       spin_lock_bh(&tp->lock);
        if (device_may_wakeup(dp))
                tg3_flag_set(tp, WOL_ENABLE);
        else
                tg3_flag_clear(tp, WOL_ENABLE);
-       spin_unlock_bh(&tp->lock);
 
        return 0;
 }
@@ -12131,7 +12151,7 @@ static int tg3_nway_reset(struct net_device *dev)
        if (tg3_flag(tp, USE_PHYLIB)) {
                if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                        return -EAGAIN;
-               r = phy_start_aneg(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
+               r = phy_start_aneg(tp->mdio_bus->phy_map[tp->phy_addr]);
        } else {
                u32 bmcr;
 
@@ -12247,7 +12267,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
                u32 newadv;
                struct phy_device *phydev;
 
-               phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+               phydev = tp->mdio_bus->phy_map[tp->phy_addr];
 
                if (!(phydev->supported & SUPPORTED_Pause) ||
                    (!(phydev->supported & SUPPORTED_Asym_Pause) &&
@@ -13194,8 +13214,8 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, bool tso_loopback)
                return -ENOMEM;
 
        tx_data = skb_put(skb, tx_len);
-       memcpy(tx_data, tp->dev->dev_addr, 6);
-       memset(tx_data + 6, 0x0, 8);
+       memcpy(tx_data, tp->dev->dev_addr, ETH_ALEN);
+       memset(tx_data + ETH_ALEN, 0x0, 8);
 
        tw32(MAC_RX_MTU_SIZE, tx_len + ETH_FCS_LEN);
 
@@ -13683,7 +13703,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                struct phy_device *phydev;
                if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
                        return -EAGAIN;
-               phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+               phydev = tp->mdio_bus->phy_map[tp->phy_addr];
                return phy_mii_ioctl(phydev, ifr, cmd);
        }
 
@@ -14921,6 +14941,12 @@ static void tg3_get_eeprom_hw_cfg(struct tg3 *tp)
                            tg3_chip_rev_id(tp) != CHIPREV_ID_5750_A1)
                                tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 |
                                                 LED_CTRL_MODE_PHY_2);
+
+                       if (tg3_flag(tp, 5717_PLUS) ||
+                           tg3_asic_rev(tp) == ASIC_REV_5762)
+                               tp->led_ctrl |= LED_CTRL_BLINK_RATE_OVERRIDE |
+                                               LED_CTRL_BLINK_RATE_MASK;
+
                        break;
 
                case SHASTA_EXT_LED_MAC:
@@ -15759,9 +15785,12 @@ static void tg3_detect_asic_rev(struct tg3 *tp, u32 misc_ctrl_reg)
                    tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 ||
                    tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 ||
                    tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720 ||
+                   tp->pdev->device == TG3PCI_DEVICE_TIGON3_57767 ||
+                   tp->pdev->device == TG3PCI_DEVICE_TIGON3_57764 ||
                    tp->pdev->device == TG3PCI_DEVICE_TIGON3_5762 ||
                    tp->pdev->device == TG3PCI_DEVICE_TIGON3_5725 ||
-                   tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727)
+                   tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727 ||
+                   tp->pdev->device == TG3PCI_DEVICE_TIGON3_57787)
                        reg = TG3PCI_GEN2_PRODID_ASICREV;
                else if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_57781 ||
                         tp->pdev->device == TG3PCI_DEVICE_TIGON3_57785 ||
@@ -16632,8 +16661,8 @@ static int tg3_get_macaddr_sparc(struct tg3 *tp)
        int len;
 
        addr = of_get_property(dp, "local-mac-address", &len);
-       if (addr && len == 6) {
-               memcpy(dev->dev_addr, addr, 6);
+       if (addr && len == ETH_ALEN) {
+               memcpy(dev->dev_addr, addr, ETH_ALEN);
                return 0;
        }
        return -ENODEV;
@@ -16643,7 +16672,7 @@ static int tg3_get_default_macaddr_sparc(struct tg3 *tp)
 {
        struct net_device *dev = tp->dev;
 
-       memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+       memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
        return 0;
 }
 #endif
@@ -17052,10 +17081,6 @@ static int tg3_test_dma(struct tg3 *tp)
 
        tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);
 
-#if 0
-       /* Unneeded, already done by tg3_get_invariants.  */
-       tg3_switch_clocks(tp);
-#endif
 
        if (tg3_asic_rev(tp) != ASIC_REV_5700 &&
            tg3_asic_rev(tp) != ASIC_REV_5701)
@@ -17083,20 +17108,6 @@ static int tg3_test_dma(struct tg3 *tp)
                        break;
                }
 
-#if 0
-               /* validate data reached card RAM correctly. */
-               for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) {
-                       u32 val;
-                       tg3_read_mem(tp, 0x2100 + (i*4), &val);
-                       if (le32_to_cpu(val) != p[i]) {
-                               dev_err(&tp->pdev->dev,
-                                       "%s: Buffer corrupted on device! "
-                                       "(%d != %d)\n", __func__, val, i);
-                               /* ret = -ENODEV here? */
-                       }
-                       p[i] = 0;
-               }
-#endif
                /* Now read it back. */
                ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, false);
                if (ret) {
@@ -17362,8 +17373,10 @@ static int tg3_init_one(struct pci_dev *pdev,
                        tg3_flag_set(tp, FLUSH_POSTED_WRITES);
                if (ssb_gige_one_dma_at_once(pdev))
                        tg3_flag_set(tp, ONE_DMA_AT_ONCE);
-               if (ssb_gige_have_roboswitch(pdev))
+               if (ssb_gige_have_roboswitch(pdev)) {
+                       tg3_flag_set(tp, USE_PHYLIB);
                        tg3_flag_set(tp, ROBOSWITCH);
+               }
                if (ssb_gige_is_rgmii(pdev))
                        tg3_flag_set(tp, RGMII_MODE);
        }
@@ -17409,9 +17422,12 @@ static int tg3_init_one(struct pci_dev *pdev,
            tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 ||
            tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 ||
            tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720 ||
+           tp->pdev->device == TG3PCI_DEVICE_TIGON3_57767 ||
+           tp->pdev->device == TG3PCI_DEVICE_TIGON3_57764 ||
            tp->pdev->device == TG3PCI_DEVICE_TIGON3_5762 ||
            tp->pdev->device == TG3PCI_DEVICE_TIGON3_5725 ||
-           tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727) {
+           tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727 ||
+           tp->pdev->device == TG3PCI_DEVICE_TIGON3_57787) {
                tg3_flag_set(tp, ENABLE_APE);
                tp->aperegs = pci_ioremap_bar(pdev, BAR_2);
                if (!tp->aperegs) {
@@ -17628,7 +17644,7 @@ static int tg3_init_one(struct pci_dev *pdev,
 
        if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
                struct phy_device *phydev;
-               phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+               phydev = tp->mdio_bus->phy_map[tp->phy_addr];
                netdev_info(dev,
                            "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
                            phydev->drv->name, dev_name(&phydev->dev));
@@ -17685,7 +17701,6 @@ err_out_free_res:
 err_out_disable_pdev:
        if (pci_is_enabled(pdev))
                pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -17717,7 +17732,6 @@ static void tg3_remove_one(struct pci_dev *pdev)
                free_netdev(dev);
                pci_release_regions(pdev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index 7025780..5c3835a 100644 (file)
@@ -68,6 +68,9 @@
 #define  TG3PCI_DEVICE_TIGON3_5762      0x1687
 #define  TG3PCI_DEVICE_TIGON3_5725      0x1643
 #define  TG3PCI_DEVICE_TIGON3_5727      0x16f3
+#define  TG3PCI_DEVICE_TIGON3_57764     0x1642
+#define  TG3PCI_DEVICE_TIGON3_57767     0x1683
+#define  TG3PCI_DEVICE_TIGON3_57787     0x1641
 /* 0x04 --> 0x2c unused */
 #define TG3PCI_SUBVENDOR_ID_BROADCOM           PCI_VENDOR_ID_BROADCOM
 #define TG3PCI_SUBDEVICE_ID_BROADCOM_95700A6   0x1644
index b78e69e..f276433 100644 (file)
@@ -3212,7 +3212,6 @@ bnad_init(struct bnad *bnad,
        bnad->bar0 = ioremap_nocache(bnad->mmio_start, bnad->mmio_len);
        if (!bnad->bar0) {
                dev_err(&pdev->dev, "ioremap for bar0 failed\n");
-               pci_set_drvdata(pdev, NULL);
                return -ENOMEM;
        }
        pr_info("bar0 mapped to %p, len %llu\n", bnad->bar0,
index aefee77..f7e033f 100644 (file)
@@ -372,38 +372,37 @@ extern u32                bnad_rxqs_per_cq;
 /*
  * EXTERN PROTOTYPES
  */
-extern u32 *cna_get_firmware_buf(struct pci_dev *pdev);
+u32 *cna_get_firmware_buf(struct pci_dev *pdev);
 /* Netdev entry point prototypes */
-extern void bnad_set_rx_mode(struct net_device *netdev);
-extern struct net_device_stats *bnad_get_netdev_stats(
-                               struct net_device *netdev);
-extern int bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr);
-extern int bnad_enable_default_bcast(struct bnad *bnad);
-extern void bnad_restore_vlans(struct bnad *bnad, u32 rx_id);
-extern void bnad_set_ethtool_ops(struct net_device *netdev);
-extern void bnad_cb_completion(void *arg, enum bfa_status status);
+void bnad_set_rx_mode(struct net_device *netdev);
+struct net_device_stats *bnad_get_netdev_stats(struct net_device *netdev);
+int bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr);
+int bnad_enable_default_bcast(struct bnad *bnad);
+void bnad_restore_vlans(struct bnad *bnad, u32 rx_id);
+void bnad_set_ethtool_ops(struct net_device *netdev);
+void bnad_cb_completion(void *arg, enum bfa_status status);
 
 /* Configuration & setup */
-extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad);
-extern void bnad_rx_coalescing_timeo_set(struct bnad *bnad);
+void bnad_tx_coalescing_timeo_set(struct bnad *bnad);
+void bnad_rx_coalescing_timeo_set(struct bnad *bnad);
 
-extern int bnad_setup_rx(struct bnad *bnad, u32 rx_id);
-extern int bnad_setup_tx(struct bnad *bnad, u32 tx_id);
-extern void bnad_destroy_tx(struct bnad *bnad, u32 tx_id);
-extern void bnad_destroy_rx(struct bnad *bnad, u32 rx_id);
+int bnad_setup_rx(struct bnad *bnad, u32 rx_id);
+int bnad_setup_tx(struct bnad *bnad, u32 tx_id);
+void bnad_destroy_tx(struct bnad *bnad, u32 tx_id);
+void bnad_destroy_rx(struct bnad *bnad, u32 rx_id);
 
 /* Timer start/stop protos */
-extern void bnad_dim_timer_start(struct bnad *bnad);
+void bnad_dim_timer_start(struct bnad *bnad);
 
 /* Statistics */
-extern void bnad_netdev_qstats_fill(struct bnad *bnad,
-               struct rtnl_link_stats64 *stats);
-extern void bnad_netdev_hwstats_fill(struct bnad *bnad,
-               struct rtnl_link_stats64 *stats);
+void bnad_netdev_qstats_fill(struct bnad *bnad,
+                            struct rtnl_link_stats64 *stats);
+void bnad_netdev_hwstats_fill(struct bnad *bnad,
+                             struct rtnl_link_stats64 *stats);
 
 /* Debugfs */
-void   bnad_debugfs_init(struct bnad *bnad);
-void   bnad_debugfs_uninit(struct bnad *bnad);
+void bnad_debugfs_init(struct bnad *bnad);
+void bnad_debugfs_uninit(struct bnad *bnad);
 
 /* MACROS */
 /* To set & get the stats counters */
index 78d6d6b..4fc5c8e 100644 (file)
 #define XGMAC_DMA_HW_FEATURE   0x00000f58      /* Enabled Hardware Features */
 
 #define XGMAC_ADDR_AE          0x80000000
-#define XGMAC_MAX_FILTER_ADDR  31
 
 /* PMT Control and Status */
 #define XGMAC_PMT_POINTER_RESET        0x80000000
@@ -384,6 +383,7 @@ struct xgmac_priv {
        struct device *device;
        struct napi_struct napi;
 
+       int max_macs;
        struct xgmac_extra_stats xstats;
 
        spinlock_t stats_lock;
@@ -1060,13 +1060,13 @@ static int xgmac_stop(struct net_device *dev)
 {
        struct xgmac_priv *priv = netdev_priv(dev);
 
-       netif_stop_queue(dev);
-
        if (readl(priv->base + XGMAC_DMA_INTR_ENA))
                napi_disable(&priv->napi);
 
        writel(0, priv->base + XGMAC_DMA_INTR_ENA);
 
+       netif_tx_disable(dev);
+
        /* Disable the MAC core */
        xgmac_mac_disable(priv->base);
 
@@ -1291,14 +1291,12 @@ static void xgmac_set_rx_mode(struct net_device *dev)
        netdev_dbg(priv->dev, "# mcasts %d, # unicast %d\n",
                 netdev_mc_count(dev), netdev_uc_count(dev));
 
-       if (dev->flags & IFF_PROMISC) {
-               writel(XGMAC_FRAME_FILTER_PR, ioaddr + XGMAC_FRAME_FILTER);
-               return;
-       }
+       if (dev->flags & IFF_PROMISC)
+               value |= XGMAC_FRAME_FILTER_PR;
 
        memset(hash_filter, 0, sizeof(hash_filter));
 
-       if (netdev_uc_count(dev) > XGMAC_MAX_FILTER_ADDR) {
+       if (netdev_uc_count(dev) > priv->max_macs) {
                use_hash = true;
                value |= XGMAC_FRAME_FILTER_HUC | XGMAC_FRAME_FILTER_HPF;
        }
@@ -1321,7 +1319,7 @@ static void xgmac_set_rx_mode(struct net_device *dev)
                goto out;
        }
 
-       if ((netdev_mc_count(dev) + reg - 1) > XGMAC_MAX_FILTER_ADDR) {
+       if ((netdev_mc_count(dev) + reg - 1) > priv->max_macs) {
                use_hash = true;
                value |= XGMAC_FRAME_FILTER_HMC | XGMAC_FRAME_FILTER_HPF;
        } else {
@@ -1342,8 +1340,8 @@ static void xgmac_set_rx_mode(struct net_device *dev)
        }
 
 out:
-       for (i = reg; i < XGMAC_MAX_FILTER_ADDR; i++)
-               xgmac_set_mac_addr(ioaddr, NULL, reg);
+       for (i = reg; i <= priv->max_macs; i++)
+               xgmac_set_mac_addr(ioaddr, NULL, i);
        for (i = 0; i < XGMAC_NUM_HASH; i++)
                writel(hash_filter[i], ioaddr + XGMAC_HASH(i));
 
@@ -1372,11 +1370,8 @@ static int xgmac_change_mtu(struct net_device *dev, int new_mtu)
        }
 
        old_mtu = dev->mtu;
-       dev->mtu = new_mtu;
 
        /* return early if the buffer sizes will not change */
-       if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
-               return 0;
        if (old_mtu == new_mtu)
                return 0;
 
@@ -1384,8 +1379,9 @@ static int xgmac_change_mtu(struct net_device *dev, int new_mtu)
        if (!netif_running(dev))
                return 0;
 
-       /* Bring the interface down and then back up */
+       /* Bring interface down, change mtu and bring interface back up */
        xgmac_stop(dev);
+       dev->mtu = new_mtu;
        return xgmac_open(dev);
 }
 
@@ -1761,6 +1757,13 @@ static int xgmac_probe(struct platform_device *pdev)
        uid = readl(priv->base + XGMAC_VERSION);
        netdev_info(ndev, "h/w version is 0x%x\n", uid);
 
+       /* Figure out how many valid mac address filter registers we have */
+       writel(1, priv->base + XGMAC_ADDR_HIGH(31));
+       if (readl(priv->base + XGMAC_ADDR_HIGH(31)) == 1)
+               priv->max_macs = 31;
+       else
+               priv->max_macs = 7;
+
        writel(0, priv->base + XGMAC_DMA_INTR_ENA);
        ndev->irq = platform_get_irq(pdev, 0);
        if (ndev->irq == -ENXIO) {
index 5ccbed1..8abb46b 100644 (file)
@@ -324,30 +324,30 @@ static inline unsigned int core_ticks_per_usec(const adapter_t *adap)
        return board_info(adap)->clock_core / 1000000;
 }
 
-extern int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp);
-extern int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value);
-extern int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value);
-extern int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *value);
-
-extern void t1_interrupts_enable(adapter_t *adapter);
-extern void t1_interrupts_disable(adapter_t *adapter);
-extern void t1_interrupts_clear(adapter_t *adapter);
-extern int t1_elmer0_ext_intr_handler(adapter_t *adapter);
-extern void t1_elmer0_ext_intr(adapter_t *adapter);
-extern int t1_slow_intr_handler(adapter_t *adapter);
-
-extern int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc);
-extern const struct board_info *t1_get_board_info(unsigned int board_id);
-extern const struct board_info *t1_get_board_info_from_ids(unsigned int devid,
+int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp);
+int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value);
+int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value);
+int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *value);
+
+void t1_interrupts_enable(adapter_t *adapter);
+void t1_interrupts_disable(adapter_t *adapter);
+void t1_interrupts_clear(adapter_t *adapter);
+int t1_elmer0_ext_intr_handler(adapter_t *adapter);
+void t1_elmer0_ext_intr(adapter_t *adapter);
+int t1_slow_intr_handler(adapter_t *adapter);
+
+int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc);
+const struct board_info *t1_get_board_info(unsigned int board_id);
+const struct board_info *t1_get_board_info_from_ids(unsigned int devid,
                                                    unsigned short ssid);
-extern int t1_seeprom_read(adapter_t *adapter, u32 addr, __le32 *data);
-extern int t1_get_board_rev(adapter_t *adapter, const struct board_info *bi,
+int t1_seeprom_read(adapter_t *adapter, u32 addr, __le32 *data);
+int t1_get_board_rev(adapter_t *adapter, const struct board_info *bi,
                     struct adapter_params *p);
-extern int t1_init_hw_modules(adapter_t *adapter);
-extern int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi);
-extern void t1_free_sw_modules(adapter_t *adapter);
-extern void t1_fatal_err(adapter_t *adapter);
-extern void t1_link_changed(adapter_t *adapter, int port_id);
-extern void t1_link_negotiated(adapter_t *adapter, int port_id, int link_stat,
+int t1_init_hw_modules(adapter_t *adapter);
+int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi);
+void t1_free_sw_modules(adapter_t *adapter);
+void t1_fatal_err(adapter_t *adapter);
+void t1_link_changed(adapter_t *adapter, int port_id);
+void t1_link_negotiated(adapter_t *adapter, int port_id, int link_stat,
                            int speed, int duplex, int pause);
 #endif /* _CXGB_COMMON_H_ */
index d7048db..1d02105 100644 (file)
@@ -1168,7 +1168,6 @@ out_free_dev:
        pci_release_regions(pdev);
 out_disable_pdev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -1347,7 +1346,6 @@ static void remove_one(struct pci_dev *pdev)
 
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        t1_sw_reset(pdev);
 }
 
index 40c7b93..eb33a31 100644 (file)
@@ -499,7 +499,7 @@ static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac,
 
 static int pm3393_macaddress_get(struct cmac *cmac, u8 mac_addr[6])
 {
-       memcpy(mac_addr, cmac->instance->mac_addr, 6);
+       memcpy(mac_addr, cmac->instance->mac_addr, ETH_ALEN);
        return 0;
 }
 
@@ -526,7 +526,7 @@ static int pm3393_macaddress_set(struct cmac *cmac, u8 ma[6])
         */
 
        /* Store local copy */
-       memcpy(cmac->instance->mac_addr, ma, 6);
+       memcpy(cmac->instance->mac_addr, ma, ETH_ALEN);
 
        lo  = ((u32) ma[1] << 8) | (u32) ma[0];
        mid = ((u32) ma[3] << 8) | (u32) ma[2];
index b650951..45d7733 100644 (file)
@@ -3374,7 +3374,6 @@ out_release_regions:
        pci_release_regions(pdev);
 out_disable_device:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 out:
        return err;
 }
@@ -3415,7 +3414,6 @@ static void remove_one(struct pci_dev *pdev)
                kfree(adapter);
                pci_release_regions(pdev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index 6990f6c..81029b8 100644 (file)
 #define V_BUSY(x) ((x) << S_BUSY)
 #define F_BUSY    V_BUSY(1U)
 
-#define S_BUSY    31
-#define V_BUSY(x) ((x) << S_BUSY)
-#define F_BUSY    V_BUSY(1U)
-
 #define A_MC7_EXT_MODE1 0x108
 
 #define A_MC7_EXT_MODE2 0x10c
 
 #define A_MC7_CAL 0x128
 
-#define S_BUSY    31
-#define V_BUSY(x) ((x) << S_BUSY)
-#define F_BUSY    V_BUSY(1U)
-
-#define S_BUSY    31
-#define V_BUSY(x) ((x) << S_BUSY)
-#define F_BUSY    V_BUSY(1U)
-
 #define S_CAL_FAULT    30
 #define V_CAL_FAULT(x) ((x) << S_CAL_FAULT)
 #define F_CAL_FAULT    V_CAL_FAULT(1U)
 #define V_OP(x) ((x) << S_OP)
 #define F_OP    V_OP(1U)
 
-#define F_OP    V_OP(1U)
-#define A_SF_OP 0x6dc
-
 #define A_MC7_BIST_ADDR_BEG 0x168
 
 #define A_MC7_BIST_ADDR_END 0x16c
 #define V_CONT(x) ((x) << S_CONT)
 #define F_CONT    V_CONT(1U)
 
-#define F_CONT    V_CONT(1U)
-
 #define A_MC7_INT_ENABLE 0x178
 
 #define S_AE    17
 #define V_NICMODE(x) ((x) << S_NICMODE)
 #define F_NICMODE    V_NICMODE(1U)
 
-#define F_NICMODE    V_NICMODE(1U)
-
 #define S_IPV6ENABLE    15
 #define V_IPV6ENABLE(x) ((x) << S_IPV6ENABLE)
 #define F_IPV6ENABLE    V_IPV6ENABLE(1U)
 
 #define A_ULPRX_STAG_ULIMIT 0x530
 
-#define A_ULPRX_RQ_LLIMIT 0x534
 #define A_ULPRX_RQ_LLIMIT 0x534
 
-#define A_ULPRX_RQ_ULIMIT 0x538
 #define A_ULPRX_RQ_ULIMIT 0x538
 
 #define A_ULPRX_PBL_LLIMIT 0x53c
 
-#define A_ULPRX_PBL_ULIMIT 0x540
 #define A_ULPRX_PBL_ULIMIT 0x540
 
 #define A_ULPRX_TDDP_TAGMASK 0x524
 
-#define A_ULPRX_RQ_LLIMIT 0x534
-#define A_ULPRX_RQ_LLIMIT 0x534
-
-#define A_ULPRX_RQ_ULIMIT 0x538
-#define A_ULPRX_RQ_ULIMIT 0x538
-
-#define A_ULPRX_PBL_ULIMIT 0x540
-#define A_ULPRX_PBL_ULIMIT 0x540
-
 #define A_ULPTX_CONFIG 0x580
 
 #define S_CFG_CQE_SOP_MASK    1
 #define V_TMMODE(x) ((x) << S_TMMODE)
 #define F_TMMODE    V_TMMODE(1U)
 
-#define F_TMMODE    V_TMMODE(1U)
-
 #define A_MC5_DB_ROUTING_TABLE_INDEX 0x70c
 
 #define A_MC5_DB_FILTER_TABLE 0x710
 #define V_TXACTENABLE(x) ((x) << S_TXACTENABLE)
 #define F_TXACTENABLE    V_TXACTENABLE(1U)
 
-#define A_XGM_SERDES_CTRL0 0x8e0
-
 #define S_RESET3    23
 #define V_RESET3(x) ((x) << S_RESET3)
 #define F_RESET3    V_RESET3(1U)
index 9c89dc8..632b318 100644 (file)
@@ -1599,7 +1599,8 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb,
        flits = skb_transport_offset(skb) / 8;
        sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
        sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb),
-                            skb->tail - skb->transport_header,
+                            skb_tail_pointer(skb) -
+                            skb_transport_header(skb),
                             adap->pdev);
        if (need_skb_unmap()) {
                setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits);
index dfd1e36..ecd2fb3 100644 (file)
@@ -48,7 +48,6 @@
 #include <linux/vmalloc.h>
 #include <asm/io.h>
 #include "cxgb4_uld.h"
-#include "t4_hw.h"
 
 #define FW_VERSION_MAJOR 1
 #define FW_VERSION_MINOR 4
index c73cabd..8b929ee 100644 (file)
@@ -3983,6 +3983,7 @@ static int cxgb4_inet6addr_handler(struct notifier_block *this,
        struct net_device *event_dev;
        int ret = NOTIFY_DONE;
        struct bonding *bond = netdev_priv(ifa->idev->dev);
+       struct list_head *iter;
        struct slave *slave;
        struct pci_dev *first_pdev = NULL;
 
@@ -3995,7 +3996,7 @@ static int cxgb4_inet6addr_handler(struct notifier_block *this,
                 * in all of them only once.
                 */
                read_lock(&bond->lock);
-               bond_for_each_slave(bond, slave) {
+               bond_for_each_slave(bond, slave, iter) {
                        if (!first_pdev) {
                                ret = clip_add(slave->dev, ifa, event);
                                /* If clip_add is success then only initialize
@@ -6074,7 +6075,6 @@ sriov:
        pci_disable_device(pdev);
  out_release_regions:
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -6122,7 +6122,6 @@ static void remove_one(struct pci_dev *pdev)
                pci_disable_pcie_error_reporting(pdev);
                pci_disable_device(pdev);
                pci_release_regions(pdev);
-               pci_set_drvdata(pdev, NULL);
        } else
                pci_release_regions(pdev);
 }
index 40c22e7..5f90ec5 100644 (file)
@@ -2782,11 +2782,9 @@ err_unmap_bar:
 
 err_free_adapter:
        kfree(adapter);
-       pci_set_drvdata(pdev, NULL);
 
 err_release_regions:
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
        pci_clear_master(pdev);
 
 err_disable_device:
@@ -2851,7 +2849,6 @@ static void cxgb4vf_pci_remove(struct pci_dev *pdev)
                }
                iounmap(adapter->regs);
                kfree(adapter);
-               pci_set_drvdata(pdev, NULL);
        }
 
        /*
@@ -2908,7 +2905,7 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev)
 #define CH_DEVICE(devid, idx) \
        { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, PCI_ANY_ID, 0, 0, idx }
 
-static struct pci_device_id cxgb4vf_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(cxgb4vf_pci_tbl) = {
        CH_DEVICE(0xb000, 0),   /* PE10K FPGA */
        CH_DEVICE(0x4800, 0),   /* T440-dbg */
        CH_DEVICE(0x4801, 0),   /* T420-cr */
index df296af..8475c4c 100644 (file)
@@ -1396,8 +1396,9 @@ static inline void copy_frags(struct sk_buff *skb,
  *     Builds an sk_buff from the given packet gather list.  Returns the
  *     sk_buff or %NULL if sk_buff allocation failed.
  */
-struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl,
-                                 unsigned int skb_len, unsigned int pull_len)
+static struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl,
+                                        unsigned int skb_len,
+                                        unsigned int pull_len)
 {
        struct sk_buff *skb;
 
@@ -1443,7 +1444,7 @@ out:
  *     Releases the pages of a packet gather list.  We do not own the last
  *     page on the list and do not free it.
  */
-void t4vf_pktgl_free(const struct pkt_gl *gl)
+static void t4vf_pktgl_free(const struct pkt_gl *gl)
 {
        int frag;
 
@@ -1640,7 +1641,7 @@ static inline void rspq_next(struct sge_rspq *rspq)
  *     on this queue.  If the system is under memory shortage use a fairly
  *     long delay to help recovery.
  */
-int process_responses(struct sge_rspq *rspq, int budget)
+static int process_responses(struct sge_rspq *rspq, int budget)
 {
        struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
        int budget_left = budget;
@@ -1893,7 +1894,7 @@ static unsigned int process_intrq(struct adapter *adapter)
  * The MSI interrupt handler handles data events from SGE response queues as
  * well as error and other async events as they all use the same MSI vector.
  */
-irqreturn_t t4vf_intr_msi(int irq, void *cookie)
+static irqreturn_t t4vf_intr_msi(int irq, void *cookie)
 {
        struct adapter *adapter = cookie;
 
index 7b756cf..ff78dfa 100644 (file)
@@ -2309,7 +2309,6 @@ err_out_release_regions:
 err_out_disable_device:
        pci_disable_device(pdev);
 err_out_free_netdev:
-       pci_set_drvdata(pdev, NULL);
        free_netdev(netdev);
 
        return err;
@@ -2338,7 +2337,6 @@ static void enic_remove(struct pci_dev *pdev)
                enic_iounmap(enic);
                pci_release_regions(pdev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
                free_netdev(netdev);
        }
 }
index 5f5896e..7080ad6 100644 (file)
@@ -158,18 +158,6 @@ static inline board_info_t *to_dm9000_board(struct net_device *dev)
 
 /* DM9000 network board routine ---------------------------- */
 
-static void
-dm9000_reset(board_info_t * db)
-{
-       dev_dbg(db->dev, "resetting device\n");
-
-       /* RESET device */
-       writeb(DM9000_NCR, db->io_addr);
-       udelay(200);
-       writeb(NCR_RST, db->io_data);
-       udelay(200);
-}
-
 /*
  *   Read a byte from I/O port
  */
@@ -191,6 +179,27 @@ iow(board_info_t * db, int reg, int value)
        writeb(value, db->io_data);
 }
 
+static void
+dm9000_reset(board_info_t *db)
+{
+       dev_dbg(db->dev, "resetting device\n");
+
+       /* Reset DM9000, see DM9000 Application Notes V1.22 Jun 11, 2004 page 29
+        * The essential point is that we have to do a double reset, and the
+        * instruction is to set LBK into MAC internal loopback mode.
+        */
+       iow(db, DM9000_NCR, 0x03);
+       udelay(100); /* Application note says at least 20 us */
+       if (ior(db, DM9000_NCR) & 1)
+               dev_err(db->dev, "dm9000 did not respond to first reset\n");
+
+       iow(db, DM9000_NCR, 0);
+       iow(db, DM9000_NCR, 0x03);
+       udelay(100);
+       if (ior(db, DM9000_NCR) & 1)
+               dev_err(db->dev, "dm9000 did not respond to second reset\n");
+}
+
 /* routines for sending block to chip */
 
 static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count)
@@ -744,15 +753,20 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
 static void dm9000_show_carrier(board_info_t *db,
                                unsigned carrier, unsigned nsr)
 {
+       int lpa;
        struct net_device *ndev = db->ndev;
+       struct mii_if_info *mii = &db->mii;
        unsigned ncr = dm9000_read_locked(db, DM9000_NCR);
 
-       if (carrier)
-               dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA\n",
+       if (carrier) {
+               lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
+               dev_info(db->dev,
+                        "%s: link up, %dMbps, %s-duplex, lpa 0x%04X\n",
                         ndev->name, (nsr & NSR_SPEED) ? 10 : 100,
-                        (ncr & NCR_FDX) ? "full" : "half");
-       else
+                        (ncr & NCR_FDX) ? "full" : "half", lpa);
+       } else {
                dev_info(db->dev, "%s: link down\n", ndev->name);
+       }
 }
 
 static void
@@ -890,9 +904,15 @@ dm9000_init_dm9000(struct net_device *dev)
                        (dev->features & NETIF_F_RXCSUM) ? RCSR_CSUM : 0);
 
        iow(db, DM9000_GPCR, GPCR_GEP_CNTL);    /* Let GPIO0 output */
+       iow(db, DM9000_GPR, 0);
 
-       dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
-       dm9000_phy_write(dev, 0, MII_DM_DSPCR, DSPCR_INIT_PARAM); /* Init */
+       /* If we are dealing with DM9000B, some extra steps are required: a
+        * manual phy reset, and setting init params.
+        */
+       if (db->type == TYPE_DM9000B) {
+               dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET);
+               dm9000_phy_write(dev, 0, MII_DM_DSPCR, DSPCR_INIT_PARAM);
+       }
 
        ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
 
@@ -1603,7 +1623,7 @@ dm9000_probe(struct platform_device *pdev)
 
        if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
                mac_src = "platform data";
-               memcpy(ndev->dev_addr, pdata->dev_addr, 6);
+               memcpy(ndev->dev_addr, pdata->dev_addr, ETH_ALEN);
        }
 
        if (!is_valid_ether_addr(ndev->dev_addr)) {
index eaab73c..38148b0 100644 (file)
@@ -2110,7 +2110,6 @@ static void de_remove_one(struct pci_dev *pdev)
        iounmap(de->regs);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 }
 
index 263b92c..c05b66d 100644 (file)
@@ -2328,7 +2328,7 @@ static void de4x5_pci_remove(struct pci_dev *pdev)
        pci_disable_device (pdev);
 }
 
-static struct pci_device_id de4x5_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(de4x5_pci_tbl) = {
         { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP,
           PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
         { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS,
index 8313930..5ad9e3e 100644 (file)
@@ -523,7 +523,6 @@ err_out_res:
 err_out_disable:
        pci_disable_device(pdev);
 err_out_free:
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 
        return err;
@@ -548,8 +547,6 @@ static void dmfe_remove_one(struct pci_dev *pdev)
                                        db->buf_pool_ptr, db->buf_pool_dma_ptr);
                pci_release_regions(pdev);
                free_netdev(dev);       /* free board information */
-
-               pci_set_drvdata(pdev, NULL);
        }
 
        DMFE_DBUG(0, "dmfe_remove_one() exit", 0);
index 4e8cfa2..add05f1 100644 (file)
@@ -1939,7 +1939,6 @@ static void tulip_remove_one(struct pci_dev *pdev)
        pci_iounmap(pdev, tp->base_addr);
        free_netdev (dev);
        pci_release_regions (pdev);
-       pci_set_drvdata (pdev, NULL);
 
        /* pci_power_off (pdev, -1); */
 }
index 93845af..a5397b1 100644 (file)
@@ -429,7 +429,6 @@ err_out_release:
 err_out_disable:
        pci_disable_device(pdev);
 err_out_free:
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 
        return err;
@@ -450,7 +449,6 @@ static void uli526x_remove_one(struct pci_dev *pdev)
                                db->buf_pool_ptr, db->buf_pool_dma_ptr);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 }
 
index c7b04ec..62fe512 100644 (file)
@@ -468,7 +468,6 @@ static int w840_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
        return 0;
 
 err_out_cleardev:
-       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
@@ -1542,8 +1541,6 @@ static void w840_remove1(struct pci_dev *pdev)
                pci_iounmap(pdev, np->base_addr);
                free_netdev(dev);
        }
-
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
index 9b84cb0..ab7ebac 100644 (file)
@@ -289,7 +289,6 @@ out:
 err_unmap:
        pci_iounmap(pdev, private->ioaddr);
 reg_fail:
-       pci_set_drvdata(pdev, NULL);
        dma_free_coherent(d, 8192, private->tx_buffer, private->tx_dma_handle);
 tx_buf_fail:
        dma_free_coherent(d, 8192, private->rx_buffer, private->rx_dma_handle);
@@ -317,7 +316,6 @@ static void xircom_remove(struct pci_dev *pdev)
 
        unregister_netdev(dev);
        pci_iounmap(pdev, card->ioaddr);
-       pci_set_drvdata(pdev, NULL);
        dma_free_coherent(d, 8192, card->tx_buffer, card->tx_dma_handle);
        dma_free_coherent(d, 8192, card->rx_buffer, card->rx_dma_handle);
        free_netdev(dev);
index afa8e3a..4fb756d 100644 (file)
@@ -1746,7 +1746,6 @@ rio_remove1 (struct pci_dev *pdev)
                pci_release_regions (pdev);
                pci_disable_device (pdev);
        }
-       pci_set_drvdata (pdev, NULL);
 }
 
 static struct pci_driver rio_driver = {
index bf3bf6f..113cd79 100644 (file)
@@ -703,7 +703,6 @@ err_out_unmap_tx:
        dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE,
                np->tx_ring, np->tx_ring_dma);
 err_out_cleardev:
-       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ioaddr);
 err_out_res:
        pci_release_regions(pdev);
@@ -1941,7 +1940,6 @@ static void sundance_remove1(struct pci_dev *pdev)
            pci_iounmap(pdev, np->base);
            pci_release_regions(pdev);
            free_netdev(dev);
-           pci_set_drvdata(pdev, NULL);
        }
 }
 
index ace5050..f4825db 100644 (file)
@@ -34,7 +34,7 @@
 #include "be_hw.h"
 #include "be_roce.h"
 
-#define DRV_VER                        "4.9.134.0u"
+#define DRV_VER                        "4.9.224.0u"
 #define DRV_NAME               "be2net"
 #define BE_NAME                        "Emulex BladeEngine2"
 #define BE3_NAME               "Emulex BladeEngine3"
@@ -88,7 +88,8 @@ static inline char *nic_name(struct pci_dev *pdev)
 #define BE_MIN_MTU             256
 
 #define BE_NUM_VLANS_SUPPORTED 64
-#define BE_MAX_EQD             96u
+#define BE_UMC_NUM_VLANS_SUPPORTED     15
+#define BE_MAX_EQD             128u
 #define        BE_MAX_TX_FRAG_COUNT    30
 
 #define EVNT_Q_LEN             1024
@@ -198,8 +199,37 @@ struct be_eq_obj {
        u16 spurious_intr;
        struct napi_struct napi;
        struct be_adapter *adapter;
+
+#ifdef CONFIG_NET_RX_BUSY_POLL
+#define BE_EQ_IDLE             0
+#define BE_EQ_NAPI             1       /* napi owns this EQ */
+#define BE_EQ_POLL             2       /* poll owns this EQ */
+#define BE_EQ_LOCKED           (BE_EQ_NAPI | BE_EQ_POLL)
+#define BE_EQ_NAPI_YIELD       4       /* napi yielded this EQ */
+#define BE_EQ_POLL_YIELD       8       /* poll yielded this EQ */
+#define BE_EQ_YIELD            (BE_EQ_NAPI_YIELD | BE_EQ_POLL_YIELD)
+#define BE_EQ_USER_PEND                (BE_EQ_POLL | BE_EQ_POLL_YIELD)
+       unsigned int state;
+       spinlock_t lock;        /* lock to serialize napi and busy-poll */
+#endif  /* CONFIG_NET_RX_BUSY_POLL */
 } ____cacheline_aligned_in_smp;
 
+struct be_aic_obj {            /* Adaptive interrupt coalescing (AIC) info */
+       bool enable;
+       u32 min_eqd;            /* in usecs */
+       u32 max_eqd;            /* in usecs */
+       u32 prev_eqd;           /* in usecs */
+       u32 et_eqd;             /* configured val when aic is off */
+       ulong jiffies;
+       u64 rx_pkts_prev;       /* Used to calculate RX pps */
+       u64 tx_reqs_prev;       /* Used to calculate TX pps */
+};
+
+enum {
+       NAPI_POLLING,
+       BUSY_POLLING
+};
+
 struct be_mcc_obj {
        struct be_queue_info q;
        struct be_queue_info cq;
@@ -214,6 +244,7 @@ struct be_tx_stats {
        u64 tx_compl;
        ulong tx_jiffies;
        u32 tx_stops;
+       u32 tx_drv_drops;       /* pkts dropped by driver */
        struct u64_stats_sync sync;
        struct u64_stats_sync sync_compl;
 };
@@ -238,15 +269,12 @@ struct be_rx_page_info {
 struct be_rx_stats {
        u64 rx_bytes;
        u64 rx_pkts;
-       u64 rx_pkts_prev;
-       ulong rx_jiffies;
        u32 rx_drops_no_skbs;   /* skb allocation errors */
        u32 rx_drops_no_frags;  /* HW has no fetched frags */
        u32 rx_post_fail;       /* page post alloc failures */
        u32 rx_compl;
        u32 rx_mcast_pkts;
        u32 rx_compl_err;       /* completions with err set */
-       u32 rx_pps;             /* pkts per second */
        struct u64_stats_sync sync;
 };
 
@@ -315,6 +343,11 @@ struct be_drv_stats {
        u32 rx_input_fifo_overflow_drop;
        u32 pmem_fifo_overflow_drop;
        u32 jabber_events;
+       u32 rx_roce_bytes_lsd;
+       u32 rx_roce_bytes_msd;
+       u32 rx_roce_frames;
+       u32 roce_drops_payload_len;
+       u32 roce_drops_crc;
 };
 
 struct be_vf_cfg {
@@ -333,6 +366,7 @@ enum vf_state {
 
 #define BE_FLAGS_LINK_STATUS_INIT              1
 #define BE_FLAGS_WORKER_SCHEDULED              (1 << 3)
+#define BE_FLAGS_VLAN_PROMISC                  (1 << 4)
 #define BE_FLAGS_NAPI_ENABLED                  (1 << 9)
 #define BE_UC_PMAC_COUNT               30
 #define BE_VF_UC_PMAC_COUNT            2
@@ -403,6 +437,7 @@ struct be_adapter {
        u32 big_page_size;      /* Compounded page size shared by rx wrbs */
 
        struct be_drv_stats drv_stats;
+       struct be_aic_obj aic_obj[MAX_EVT_QS];
        u16 vlans_added;
        u8 vlan_tag[VLAN_N_VID];
        u8 vlan_prio_bmap;      /* Available Priority BitMap */
@@ -435,7 +470,6 @@ struct be_adapter {
        u32 rx_fc;              /* Rx flow control */
        u32 tx_fc;              /* Tx flow control */
        bool stats_cmd_sent;
-       u32 if_type;
        struct {
                u32 size;
                u32 total_size;
@@ -470,8 +504,8 @@ struct be_adapter {
 
 #define be_physfn(adapter)             (!adapter->virtfn)
 #define        sriov_enabled(adapter)          (adapter->num_vfs > 0)
-#define sriov_want(adapter)             (be_max_vfs(adapter) && num_vfs && \
-                                        be_physfn(adapter))
+#define sriov_want(adapter)             (be_physfn(adapter) && \
+                                        (num_vfs || pci_num_vf(adapter->pdev)))
 #define for_all_vfs(adapter, vf_cfg, i)                                        \
        for (i = 0, vf_cfg = &adapter->vf_cfg[i]; i < adapter->num_vfs; \
                i++, vf_cfg++)
@@ -544,6 +578,10 @@ extern const struct ethtool_ops be_ethtool_ops;
        for (i = 0, eqo = &adapter->eq_obj[i]; i < adapter->num_evt_qs; \
                i++, eqo++)
 
+#define for_all_rx_queues_on_eq(adapter, eqo, rxo, i)                  \
+       for (i = eqo->idx, rxo = &adapter->rx_obj[i]; i < adapter->num_rx_qs;\
+                i += adapter->num_evt_qs, rxo += adapter->num_evt_qs)
+
 #define is_mcc_eqo(eqo)                        (eqo->idx == 0)
 #define mcc_eqo(adapter)               (&adapter->eq_obj[0])
 
@@ -694,27 +732,137 @@ static inline int qnq_async_evt_rcvd(struct be_adapter *adapter)
        return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD;
 }
 
-extern void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm,
-               u16 num_popped);
-extern void be_link_status_update(struct be_adapter *adapter, u8 link_status);
-extern void be_parse_stats(struct be_adapter *adapter);
-extern int be_load_fw(struct be_adapter *adapter, u8 *func);
-extern bool be_is_wol_supported(struct be_adapter *adapter);
-extern bool be_pause_supported(struct be_adapter *adapter);
-extern u32 be_get_fw_log_level(struct be_adapter *adapter);
+#ifdef CONFIG_NET_RX_BUSY_POLL
+static inline bool be_lock_napi(struct be_eq_obj *eqo)
+{
+       bool status = true;
+
+       spin_lock(&eqo->lock); /* BH is already disabled */
+       if (eqo->state & BE_EQ_LOCKED) {
+               WARN_ON(eqo->state & BE_EQ_NAPI);
+               eqo->state |= BE_EQ_NAPI_YIELD;
+               status = false;
+       } else {
+               eqo->state = BE_EQ_NAPI;
+       }
+       spin_unlock(&eqo->lock);
+       return status;
+}
+
+static inline void be_unlock_napi(struct be_eq_obj *eqo)
+{
+       spin_lock(&eqo->lock); /* BH is already disabled */
+
+       WARN_ON(eqo->state & (BE_EQ_POLL | BE_EQ_NAPI_YIELD));
+       eqo->state = BE_EQ_IDLE;
+
+       spin_unlock(&eqo->lock);
+}
+
+static inline bool be_lock_busy_poll(struct be_eq_obj *eqo)
+{
+       bool status = true;
+
+       spin_lock_bh(&eqo->lock);
+       if (eqo->state & BE_EQ_LOCKED) {
+               eqo->state |= BE_EQ_POLL_YIELD;
+               status = false;
+       } else {
+               eqo->state |= BE_EQ_POLL;
+       }
+       spin_unlock_bh(&eqo->lock);
+       return status;
+}
+
+static inline void be_unlock_busy_poll(struct be_eq_obj *eqo)
+{
+       spin_lock_bh(&eqo->lock);
+
+       WARN_ON(eqo->state & (BE_EQ_NAPI));
+       eqo->state = BE_EQ_IDLE;
+
+       spin_unlock_bh(&eqo->lock);
+}
+
+static inline void be_enable_busy_poll(struct be_eq_obj *eqo)
+{
+       spin_lock_init(&eqo->lock);
+       eqo->state = BE_EQ_IDLE;
+}
+
+static inline void be_disable_busy_poll(struct be_eq_obj *eqo)
+{
+       local_bh_disable();
+
+       /* It's enough to just acquire napi lock on the eqo to stop
+        * be_busy_poll() from processing any queueus.
+        */
+       while (!be_lock_napi(eqo))
+               mdelay(1);
+
+       local_bh_enable();
+}
+
+#else /* CONFIG_NET_RX_BUSY_POLL */
+
+static inline bool be_lock_napi(struct be_eq_obj *eqo)
+{
+       return true;
+}
+
+static inline void be_unlock_napi(struct be_eq_obj *eqo)
+{
+}
+
+static inline bool be_lock_busy_poll(struct be_eq_obj *eqo)
+{
+       return false;
+}
+
+static inline void be_unlock_busy_poll(struct be_eq_obj *eqo)
+{
+}
+
+static inline void be_enable_busy_poll(struct be_eq_obj *eqo)
+{
+}
+
+static inline void be_disable_busy_poll(struct be_eq_obj *eqo)
+{
+}
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
+void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm,
+                 u16 num_popped);
+void be_link_status_update(struct be_adapter *adapter, u8 link_status);
+void be_parse_stats(struct be_adapter *adapter);
+int be_load_fw(struct be_adapter *adapter, u8 *func);
+bool be_is_wol_supported(struct be_adapter *adapter);
+bool be_pause_supported(struct be_adapter *adapter);
+u32 be_get_fw_log_level(struct be_adapter *adapter);
+
+static inline int fw_major_num(const char *fw_ver)
+{
+       int fw_major = 0;
+
+       sscanf(fw_ver, "%d.", &fw_major);
+
+       return fw_major;
+}
+
 int be_update_queues(struct be_adapter *adapter);
 int be_poll(struct napi_struct *napi, int budget);
 
 /*
  * internal function to initialize-cleanup roce device.
  */
-extern void be_roce_dev_add(struct be_adapter *);
-extern void be_roce_dev_remove(struct be_adapter *);
+void be_roce_dev_add(struct be_adapter *);
+void be_roce_dev_remove(struct be_adapter *);
 
 /*
  * internal function to open-close roce device during ifup-ifdown.
  */
-extern void be_roce_dev_open(struct be_adapter *);
-extern void be_roce_dev_close(struct be_adapter *);
+void be_roce_dev_open(struct be_adapter *);
+void be_roce_dev_close(struct be_adapter *);
 
 #endif                         /* BE_H */
index 1ab5dab..7fb0edf 100644 (file)
@@ -180,6 +180,9 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
                        dev_err(&adapter->pdev->dev,
                                "opcode %d-%d failed:status %d-%d\n",
                                opcode, subsystem, compl_status, extd_status);
+
+                       if (extd_status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES)
+                               return extd_status;
                }
        }
 done:
@@ -519,7 +522,7 @@ static u16 be_POST_stage_get(struct be_adapter *adapter)
        return sem & POST_STAGE_MASK;
 }
 
-int lancer_wait_ready(struct be_adapter *adapter)
+static int lancer_wait_ready(struct be_adapter *adapter)
 {
 #define SLIPORT_READY_TIMEOUT 30
        u32 sliport_status;
@@ -1195,7 +1198,6 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 
        if (lancer_chip(adapter)) {
                req->hdr.version = 1;
-               req->if_id = cpu_to_le16(adapter->if_handle);
        } else if (BEx_chip(adapter)) {
                if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC)
                        req->hdr.version = 2;
@@ -1203,6 +1205,8 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
                req->hdr.version = 2;
        }
 
+       if (req->hdr.version > 0)
+               req->if_id = cpu_to_le16(adapter->if_handle);
        req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
        req->ulp_num = BE_ULP1_NUM;
        req->type = BE_ETH_TX_RING_TYPE_STANDARD;
@@ -1432,8 +1436,12 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
                OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb, nonemb_cmd);
 
        /* version 1 of the cmd is not supported only by BE2 */
-       if (!BE2_chip(adapter))
+       if (BE2_chip(adapter))
+               hdr->version = 0;
+       if (BE3_chip(adapter) || lancer_chip(adapter))
                hdr->version = 1;
+       else
+               hdr->version = 2;
 
        be_mcc_notify(adapter);
        adapter->stats_cmd_sent = true;
@@ -1715,11 +1723,12 @@ err:
 /* set the EQ delay interval of an EQ to specified value
  * Uses async mcc
  */
-int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd)
+int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd,
+                     int num)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_modify_eq_delay *req;
-       int status = 0;
+       int status = 0, i;
 
        spin_lock_bh(&adapter->mcc_lock);
 
@@ -1733,13 +1742,15 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd)
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
                OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb, NULL);
 
-       req->num_eq = cpu_to_le32(1);
-       req->delay[0].eq_id = cpu_to_le32(eq_id);
-       req->delay[0].phase = 0;
-       req->delay[0].delay_multiplier = cpu_to_le32(eqd);
+       req->num_eq = cpu_to_le32(num);
+       for (i = 0; i < num; i++) {
+               req->set_eqd[i].eq_id = cpu_to_le32(set_eqd[i].eq_id);
+               req->set_eqd[i].phase = 0;
+               req->set_eqd[i].delay_multiplier =
+                               cpu_to_le32(set_eqd[i].delay_multiplier);
+       }
 
        be_mcc_notify(adapter);
-
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
@@ -1812,6 +1823,12 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
        } else if (flags & IFF_ALLMULTI) {
                req->if_flags_mask = req->if_flags =
                                cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
+       } else if (flags & BE_FLAGS_VLAN_PROMISC) {
+               req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_VLAN_PROMISCUOUS);
+
+               if (value == ON)
+                       req->if_flags =
+                               cpu_to_le32(BE_IF_FLAGS_VLAN_PROMISCUOUS);
        } else {
                struct netdev_hw_addr *ha;
                int i = 0;
@@ -3510,7 +3527,7 @@ int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain)
        struct be_cmd_enable_disable_vf *req;
        int status;
 
-       if (!lancer_chip(adapter))
+       if (BEx_chip(adapter))
                return 0;
 
        spin_lock_bh(&adapter->mcc_lock);
index d026226..edf3e8a 100644 (file)
@@ -60,6 +60,8 @@ enum {
        MCC_STATUS_NOT_SUPPORTED = 66
 };
 
+#define MCC_ADDL_STS_INSUFFICIENT_RESOURCES    0x16
+
 #define CQE_STATUS_COMPL_MASK          0xFFFF
 #define CQE_STATUS_COMPL_SHIFT         0       /* bits 0 - 15 */
 #define CQE_STATUS_EXTD_MASK           0xFFFF
@@ -1055,14 +1057,16 @@ struct be_cmd_resp_get_flow_control {
 } __packed;
 
 /******************** Modify EQ Delay *******************/
+struct be_set_eqd {
+       u32 eq_id;
+       u32 phase;
+       u32 delay_multiplier;
+};
+
 struct be_cmd_req_modify_eq_delay {
        struct be_cmd_req_hdr hdr;
        u32 num_eq;
-       struct {
-               u32 eq_id;
-               u32 phase;
-               u32 delay_multiplier;
-       } delay[8];
+       struct be_set_eqd set_eqd[MAX_EVT_QS];
 } __packed;
 
 struct be_cmd_resp_modify_eq_delay {
@@ -1658,6 +1662,67 @@ struct be_erx_stats_v1 {
        u32 rsvd[4];
 };
 
+struct be_port_rxf_stats_v2 {
+       u32 rsvd0[10];
+       u32 roce_bytes_received_lsd;
+       u32 roce_bytes_received_msd;
+       u32 rsvd1[5];
+       u32 roce_frames_received;
+       u32 rx_crc_errors;
+       u32 rx_alignment_symbol_errors;
+       u32 rx_pause_frames;
+       u32 rx_priority_pause_frames;
+       u32 rx_control_frames;
+       u32 rx_in_range_errors;
+       u32 rx_out_range_errors;
+       u32 rx_frame_too_long;
+       u32 rx_address_filtered;
+       u32 rx_dropped_too_small;
+       u32 rx_dropped_too_short;
+       u32 rx_dropped_header_too_small;
+       u32 rx_dropped_tcp_length;
+       u32 rx_dropped_runt;
+       u32 rsvd2[10];
+       u32 rx_ip_checksum_errs;
+       u32 rx_tcp_checksum_errs;
+       u32 rx_udp_checksum_errs;
+       u32 rsvd3[7];
+       u32 rx_switched_unicast_packets;
+       u32 rx_switched_multicast_packets;
+       u32 rx_switched_broadcast_packets;
+       u32 rsvd4[3];
+       u32 tx_pauseframes;
+       u32 tx_priority_pauseframes;
+       u32 tx_controlframes;
+       u32 rsvd5[10];
+       u32 rxpp_fifo_overflow_drop;
+       u32 rx_input_fifo_overflow_drop;
+       u32 pmem_fifo_overflow_drop;
+       u32 jabber_events;
+       u32 rsvd6[3];
+       u32 rx_drops_payload_size;
+       u32 rx_drops_clipped_header;
+       u32 rx_drops_crc;
+       u32 roce_drops_payload_len;
+       u32 roce_drops_crc;
+       u32 rsvd7[19];
+};
+
+struct be_rxf_stats_v2 {
+       struct be_port_rxf_stats_v2 port[4];
+       u32 rsvd0[2];
+       u32 rx_drops_no_pbuf;
+       u32 rx_drops_no_txpb;
+       u32 rx_drops_no_erx_descr;
+       u32 rx_drops_no_tpre_descr;
+       u32 rsvd1[6];
+       u32 rx_drops_too_many_frags;
+       u32 rx_drops_invalid_ring;
+       u32 forwarded_packets;
+       u32 rx_drops_mtu;
+       u32 rsvd2[35];
+};
+
 struct be_hw_stats_v1 {
        struct be_rxf_stats_v1 rxf;
        u32 rsvd0[BE_TXP_SW_SZ];
@@ -1676,6 +1741,29 @@ struct be_cmd_resp_get_stats_v1 {
        struct be_hw_stats_v1 hw_stats;
 };
 
+struct be_erx_stats_v2 {
+       u32 rx_drops_no_fragments[136];     /* dwordS 0 to 135*/
+       u32 rsvd[3];
+};
+
+struct be_hw_stats_v2 {
+       struct be_rxf_stats_v2 rxf;
+       u32 rsvd0[BE_TXP_SW_SZ];
+       struct be_erx_stats_v2 erx;
+       struct be_pmem_stats pmem;
+       u32 rsvd1[18];
+};
+
+struct be_cmd_req_get_stats_v2 {
+       struct be_cmd_req_hdr hdr;
+       u8 rsvd[sizeof(struct be_hw_stats_v2)];
+};
+
+struct be_cmd_resp_get_stats_v2 {
+       struct be_cmd_resp_hdr hdr;
+       struct be_hw_stats_v2 hw_stats;
+};
+
 /************** get fat capabilites *******************/
 #define MAX_MODULES 27
 #define MAX_MODES 4
@@ -1791,7 +1879,7 @@ struct be_nic_res_desc {
        u8 acpi_params;
        u8 wol_param;
        u16 rsvd7;
-       u32 rsvd8[3];
+       u32 rsvd8[7];
 } __packed;
 
 struct be_cmd_req_get_func_config {
@@ -1863,137 +1951,119 @@ struct be_cmd_resp_get_iface_list {
        struct be_if_desc if_desc;
 };
 
-extern int be_pci_fnum_get(struct be_adapter *adapter);
-extern int be_fw_wait_ready(struct be_adapter *adapter);
-extern int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
-                                bool permanent, u32 if_handle, u32 pmac_id);
-extern int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
-                       u32 if_id, u32 *pmac_id, u32 domain);
-extern int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id,
-                       int pmac_id, u32 domain);
-extern int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags,
-                           u32 en_flags, u32 *if_handle, u32 domain);
-extern int be_cmd_if_destroy(struct be_adapter *adapter, int if_handle,
-                       u32 domain);
-extern int be_cmd_eq_create(struct be_adapter *adapter, struct be_eq_obj *eqo);
-extern int be_cmd_cq_create(struct be_adapter *adapter,
-                       struct be_queue_info *cq, struct be_queue_info *eq,
-                       bool no_delay, int num_cqe_dma_coalesce);
-extern int be_cmd_mccq_create(struct be_adapter *adapter,
-                       struct be_queue_info *mccq,
-                       struct be_queue_info *cq);
-extern int be_cmd_txq_create(struct be_adapter *adapter,
-                       struct be_tx_obj *txo);
-extern int be_cmd_rxq_create(struct be_adapter *adapter,
-                       struct be_queue_info *rxq, u16 cq_id,
-                       u16 frag_size, u32 if_id, u32 rss, u8 *rss_id);
-extern int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
-                       int type);
-extern int be_cmd_rxq_destroy(struct be_adapter *adapter,
-                       struct be_queue_info *q);
-extern int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed,
-                                   u8 *link_status, u32 dom);
-extern int be_cmd_reset(struct be_adapter *adapter);
-extern int be_cmd_get_stats(struct be_adapter *adapter,
-                       struct be_dma_mem *nonemb_cmd);
-extern int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
-                       struct be_dma_mem *nonemb_cmd);
-extern int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
-               char *fw_on_flash);
-
-extern int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd);
-extern int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id,
-                       u16 *vtag_array, u32 num, bool untagged,
-                       bool promiscuous);
-extern int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status);
-extern int be_cmd_set_flow_control(struct be_adapter *adapter,
-                       u32 tx_fc, u32 rx_fc);
-extern int be_cmd_get_flow_control(struct be_adapter *adapter,
-                       u32 *tx_fc, u32 *rx_fc);
-extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
+int be_pci_fnum_get(struct be_adapter *adapter);
+int be_fw_wait_ready(struct be_adapter *adapter);
+int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
+                         bool permanent, u32 if_handle, u32 pmac_id);
+int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, u32 if_id,
+                   u32 *pmac_id, u32 domain);
+int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, int pmac_id,
+                   u32 domain);
+int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
+                    u32 *if_handle, u32 domain);
+int be_cmd_if_destroy(struct be_adapter *adapter, int if_handle, u32 domain);
+int be_cmd_eq_create(struct be_adapter *adapter, struct be_eq_obj *eqo);
+int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
+                    struct be_queue_info *eq, bool no_delay,
+                    int num_cqe_dma_coalesce);
+int be_cmd_mccq_create(struct be_adapter *adapter, struct be_queue_info *mccq,
+                      struct be_queue_info *cq);
+int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo);
+int be_cmd_rxq_create(struct be_adapter *adapter, struct be_queue_info *rxq,
+                     u16 cq_id, u16 frag_size, u32 if_id, u32 rss, u8 *rss_id);
+int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
+                    int type);
+int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q);
+int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed,
+                            u8 *link_status, u32 dom);
+int be_cmd_reset(struct be_adapter *adapter);
+int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd);
+int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
+                              struct be_dma_mem *nonemb_cmd);
+int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
+                     char *fw_on_flash);
+int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *, int num);
+int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
+                      u32 num, bool untagged, bool promiscuous);
+int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status);
+int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc);
+int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc);
+int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
                        u32 *function_mode, u32 *function_caps, u16 *asic_rev);
-extern int be_cmd_reset_function(struct be_adapter *adapter);
-extern int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
-                            u32 rss_hash_opts, u16 table_size);
-extern int be_process_mcc(struct be_adapter *adapter);
-extern int be_cmd_set_beacon_state(struct be_adapter *adapter,
-                       u8 port_num, u8 beacon, u8 status, u8 state);
-extern int be_cmd_get_beacon_state(struct be_adapter *adapter,
-                       u8 port_num, u32 *state);
-extern int be_cmd_write_flashrom(struct be_adapter *adapter,
-                       struct be_dma_mem *cmd, u32 flash_oper,
-                       u32 flash_opcode, u32 buf_size);
-extern int lancer_cmd_write_object(struct be_adapter *adapter,
-                                  struct be_dma_mem *cmd,
-                                  u32 data_size, u32 data_offset,
-                                  const char *obj_name,
-                                  u32 *data_written, u8 *change_status,
-                                  u8 *addn_status);
+int be_cmd_reset_function(struct be_adapter *adapter);
+int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
+                     u32 rss_hash_opts, u16 table_size);
+int be_process_mcc(struct be_adapter *adapter);
+int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num, u8 beacon,
+                           u8 status, u8 state);
+int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num,
+                           u32 *state);
+int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
+                         u32 flash_oper, u32 flash_opcode, u32 buf_size);
+int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
+                           u32 data_size, u32 data_offset,
+                           const char *obj_name, u32 *data_written,
+                           u8 *change_status, u8 *addn_status);
 int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
-               u32 data_size, u32 data_offset, const char *obj_name,
-               u32 *data_read, u32 *eof, u8 *addn_status);
+                          u32 data_size, u32 data_offset, const char *obj_name,
+                          u32 *data_read, u32 *eof, u8 *addn_status);
 int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
-                               int offset);
-extern int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
-                               struct be_dma_mem *nonemb_cmd);
-extern int be_cmd_fw_init(struct be_adapter *adapter);
-extern int be_cmd_fw_clean(struct be_adapter *adapter);
-extern void be_async_mcc_enable(struct be_adapter *adapter);
-extern void be_async_mcc_disable(struct be_adapter *adapter);
-extern int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
-                               u32 loopback_type, u32 pkt_size,
-                               u32 num_pkts, u64 pattern);
-extern int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
-                       u32 byte_cnt, struct be_dma_mem *cmd);
-extern int be_cmd_get_seeprom_data(struct be_adapter *adapter,
-                               struct be_dma_mem *nonemb_cmd);
-extern int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
-                               u8 loopback_type, u8 enable);
-extern int be_cmd_get_phy_info(struct be_adapter *adapter);
-extern int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain);
-extern void be_detect_error(struct be_adapter *adapter);
-extern int be_cmd_get_die_temperature(struct be_adapter *adapter);
-extern int be_cmd_get_cntl_attributes(struct be_adapter *adapter);
-extern int be_cmd_req_native_mode(struct be_adapter *adapter);
-extern int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size);
-extern void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf);
-extern int be_cmd_get_fn_privileges(struct be_adapter *adapter,
-                                   u32 *privilege, u32 domain);
-extern int be_cmd_set_fn_privileges(struct be_adapter *adapter,
-                                   u32 privileges, u32 vf_num);
-extern int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
-                                   bool *pmac_id_active, u32 *pmac_id,
-                                   u8 domain);
-extern int be_cmd_get_active_mac(struct be_adapter *adapter, u32 pmac_id,
-                                u8 *mac);
-extern int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac);
-extern int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
-                                               u8 mac_count, u32 domain);
-extern int be_cmd_set_mac(struct be_adapter *adapter, u8 *mac, int if_id,
-                         u32 dom);
-extern int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid,
-                                u32 domain, u16 intf_id, u16 hsw_mode);
-extern int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
-                                u32 domain, u16 intf_id, u8 *mode);
-extern int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter);
-extern int be_cmd_get_ext_fat_capabilites(struct be_adapter *adapter,
-                                         struct be_dma_mem *cmd);
-extern int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter,
-                                         struct be_dma_mem *cmd,
-                                         struct be_fat_conf_params *cfgs);
-extern int lancer_wait_ready(struct be_adapter *adapter);
-extern int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask);
-extern int lancer_initiate_dump(struct be_adapter *adapter);
-extern bool dump_present(struct be_adapter *adapter);
-extern int lancer_test_and_set_rdy_state(struct be_adapter *adapter);
-extern int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name);
+                        int offset);
+int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
+                           struct be_dma_mem *nonemb_cmd);
+int be_cmd_fw_init(struct be_adapter *adapter);
+int be_cmd_fw_clean(struct be_adapter *adapter);
+void be_async_mcc_enable(struct be_adapter *adapter);
+void be_async_mcc_disable(struct be_adapter *adapter);
+int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
+                        u32 loopback_type, u32 pkt_size, u32 num_pkts,
+                        u64 pattern);
+int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern, u32 byte_cnt,
+                       struct be_dma_mem *cmd);
+int be_cmd_get_seeprom_data(struct be_adapter *adapter,
+                           struct be_dma_mem *nonemb_cmd);
+int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
+                       u8 loopback_type, u8 enable);
+int be_cmd_get_phy_info(struct be_adapter *adapter);
+int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain);
+void be_detect_error(struct be_adapter *adapter);
+int be_cmd_get_die_temperature(struct be_adapter *adapter);
+int be_cmd_get_cntl_attributes(struct be_adapter *adapter);
+int be_cmd_req_native_mode(struct be_adapter *adapter);
+int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size);
+void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf);
+int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege,
+                            u32 domain);
+int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges,
+                            u32 vf_num);
+int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
+                            bool *pmac_id_active, u32 *pmac_id, u8 domain);
+int be_cmd_get_active_mac(struct be_adapter *adapter, u32 pmac_id, u8 *mac);
+int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac);
+int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array, u8 mac_count,
+                       u32 domain);
+int be_cmd_set_mac(struct be_adapter *adapter, u8 *mac, int if_id, u32 dom);
+int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid, u32 domain,
+                         u16 intf_id, u16 hsw_mode);
+int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid, u32 domain,
+                         u16 intf_id, u8 *mode);
+int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter);
+int be_cmd_get_ext_fat_capabilites(struct be_adapter *adapter,
+                                  struct be_dma_mem *cmd);
+int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter,
+                                  struct be_dma_mem *cmd,
+                                  struct be_fat_conf_params *cfgs);
+int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask);
+int lancer_initiate_dump(struct be_adapter *adapter);
+bool dump_present(struct be_adapter *adapter);
+int lancer_test_and_set_rdy_state(struct be_adapter *adapter);
+int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name);
 int be_cmd_get_func_config(struct be_adapter *adapter,
                           struct be_resources *res);
 int be_cmd_get_profile_config(struct be_adapter *adapter,
                              struct be_resources *res, u8 domain);
-extern int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps,
-                                    u8 domain);
-extern int be_cmd_get_if_id(struct be_adapter *adapter,
-                           struct be_vf_cfg *vf_cfg, int vf_num);
-extern int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain);
-extern int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable);
+int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps, u8 domain);
+int be_cmd_get_if_id(struct be_adapter *adapter, struct be_vf_cfg *vf_cfg,
+                    int vf_num);
+int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain);
+int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable);
index b440a1f..0833003 100644 (file)
@@ -116,7 +116,12 @@ static const struct be_ethtool_stat et_stats[] = {
        {DRVSTAT_INFO(rx_drops_mtu)},
        /* Number of packets dropped due to random early drop function */
        {DRVSTAT_INFO(eth_red_drops)},
-       {DRVSTAT_INFO(be_on_die_temperature)}
+       {DRVSTAT_INFO(be_on_die_temperature)},
+       {DRVSTAT_INFO(rx_roce_bytes_lsd)},
+       {DRVSTAT_INFO(rx_roce_bytes_msd)},
+       {DRVSTAT_INFO(rx_roce_frames)},
+       {DRVSTAT_INFO(roce_drops_payload_len)},
+       {DRVSTAT_INFO(roce_drops_crc)}
 };
 #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats)
 
@@ -155,7 +160,9 @@ static const struct be_ethtool_stat et_tx_stats[] = {
        /* Number of times the TX queue was stopped due to lack
         * of spaces in the TXQ.
         */
-       {DRVSTAT_TX_INFO(tx_stops)}
+       {DRVSTAT_TX_INFO(tx_stops)},
+       /* Pkts dropped in the driver's transmit path */
+       {DRVSTAT_TX_INFO(tx_drv_drops)}
 };
 #define ETHTOOL_TXSTATS_NUM (ARRAY_SIZE(et_tx_stats))
 
@@ -290,19 +297,19 @@ static int be_get_coalesce(struct net_device *netdev,
                           struct ethtool_coalesce *et)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
-       struct be_eq_obj *eqo = &adapter->eq_obj[0];
+       struct be_aic_obj *aic = &adapter->aic_obj[0];
 
 
-       et->rx_coalesce_usecs = eqo->cur_eqd;
-       et->rx_coalesce_usecs_high = eqo->max_eqd;
-       et->rx_coalesce_usecs_low = eqo->min_eqd;
+       et->rx_coalesce_usecs = aic->prev_eqd;
+       et->rx_coalesce_usecs_high = aic->max_eqd;
+       et->rx_coalesce_usecs_low = aic->min_eqd;
 
-       et->tx_coalesce_usecs = eqo->cur_eqd;
-       et->tx_coalesce_usecs_high = eqo->max_eqd;
-       et->tx_coalesce_usecs_low = eqo->min_eqd;
+       et->tx_coalesce_usecs = aic->prev_eqd;
+       et->tx_coalesce_usecs_high = aic->max_eqd;
+       et->tx_coalesce_usecs_low = aic->min_eqd;
 
-       et->use_adaptive_rx_coalesce = eqo->enable_aic;
-       et->use_adaptive_tx_coalesce = eqo->enable_aic;
+       et->use_adaptive_rx_coalesce = aic->enable;
+       et->use_adaptive_tx_coalesce = aic->enable;
 
        return 0;
 }
@@ -314,14 +321,17 @@ static int be_set_coalesce(struct net_device *netdev,
                           struct ethtool_coalesce *et)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
+       struct be_aic_obj *aic = &adapter->aic_obj[0];
        struct be_eq_obj *eqo;
        int i;
 
        for_all_evt_queues(adapter, eqo, i) {
-               eqo->enable_aic = et->use_adaptive_rx_coalesce;
-               eqo->max_eqd = min(et->rx_coalesce_usecs_high, BE_MAX_EQD);
-               eqo->min_eqd = min(et->rx_coalesce_usecs_low, eqo->max_eqd);
-               eqo->eqd = et->rx_coalesce_usecs;
+               aic->enable = et->use_adaptive_rx_coalesce;
+               aic->max_eqd = min(et->rx_coalesce_usecs_high, BE_MAX_EQD);
+               aic->min_eqd = min(et->rx_coalesce_usecs_low, aic->max_eqd);
+               aic->et_eqd = min(et->rx_coalesce_usecs, aic->max_eqd);
+               aic->et_eqd = max(aic->et_eqd, aic->min_eqd);
+               aic++;
        }
 
        return 0;
index 100b528..741d3bf 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/div64.h>
 #include <linux/aer.h>
 #include <linux/if_bridge.h>
+#include <net/busy_poll.h>
 
 MODULE_VERSION(DRV_VER);
 MODULE_DEVICE_TABLE(pci, be_dev_ids);
@@ -306,9 +307,13 @@ static void *hw_stats_from_cmd(struct be_adapter *adapter)
                struct be_cmd_resp_get_stats_v0 *cmd = adapter->stats_cmd.va;
 
                return &cmd->hw_stats;
-       } else  {
+       } else if (BE3_chip(adapter)) {
                struct be_cmd_resp_get_stats_v1 *cmd = adapter->stats_cmd.va;
 
+               return &cmd->hw_stats;
+       } else {
+               struct be_cmd_resp_get_stats_v2 *cmd = adapter->stats_cmd.va;
+
                return &cmd->hw_stats;
        }
 }
@@ -320,9 +325,13 @@ static void *be_erx_stats_from_cmd(struct be_adapter *adapter)
                struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter);
 
                return &hw_stats->erx;
-       } else {
+       } else if (BE3_chip(adapter)) {
                struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter);
 
+               return &hw_stats->erx;
+       } else {
+               struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter);
+
                return &hw_stats->erx;
        }
 }
@@ -422,6 +431,60 @@ static void populate_be_v1_stats(struct be_adapter *adapter)
        adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
 }
 
+static void populate_be_v2_stats(struct be_adapter *adapter)
+{
+       struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter);
+       struct be_pmem_stats *pmem_sts = &hw_stats->pmem;
+       struct be_rxf_stats_v2 *rxf_stats = &hw_stats->rxf;
+       struct be_port_rxf_stats_v2 *port_stats =
+                                       &rxf_stats->port[adapter->port_num];
+       struct be_drv_stats *drvs = &adapter->drv_stats;
+
+       be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats));
+       drvs->pmem_fifo_overflow_drop = port_stats->pmem_fifo_overflow_drop;
+       drvs->rx_priority_pause_frames = port_stats->rx_priority_pause_frames;
+       drvs->rx_pause_frames = port_stats->rx_pause_frames;
+       drvs->rx_crc_errors = port_stats->rx_crc_errors;
+       drvs->rx_control_frames = port_stats->rx_control_frames;
+       drvs->rx_in_range_errors = port_stats->rx_in_range_errors;
+       drvs->rx_frame_too_long = port_stats->rx_frame_too_long;
+       drvs->rx_dropped_runt = port_stats->rx_dropped_runt;
+       drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs;
+       drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs;
+       drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs;
+       drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length;
+       drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small;
+       drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short;
+       drvs->rx_out_range_errors = port_stats->rx_out_range_errors;
+       drvs->rx_dropped_header_too_small =
+               port_stats->rx_dropped_header_too_small;
+       drvs->rx_input_fifo_overflow_drop =
+               port_stats->rx_input_fifo_overflow_drop;
+       drvs->rx_address_filtered = port_stats->rx_address_filtered;
+       drvs->rx_alignment_symbol_errors =
+               port_stats->rx_alignment_symbol_errors;
+       drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop;
+       drvs->tx_pauseframes = port_stats->tx_pauseframes;
+       drvs->tx_controlframes = port_stats->tx_controlframes;
+       drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes;
+       drvs->jabber_events = port_stats->jabber_events;
+       drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf;
+       drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr;
+       drvs->forwarded_packets = rxf_stats->forwarded_packets;
+       drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu;
+       drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr;
+       drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags;
+       adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
+       if (be_roce_supported(adapter))  {
+               drvs->rx_roce_bytes_lsd = port_stats->roce_bytes_received_lsd;
+               drvs->rx_roce_bytes_msd = port_stats->roce_bytes_received_msd;
+               drvs->rx_roce_frames = port_stats->roce_frames_received;
+               drvs->roce_drops_crc = port_stats->roce_drops_crc;
+               drvs->roce_drops_payload_len =
+                       port_stats->roce_drops_payload_len;
+       }
+}
+
 static void populate_lancer_stats(struct be_adapter *adapter)
 {
 
@@ -489,7 +552,7 @@ static void populate_erx_stats(struct be_adapter *adapter,
 
 void be_parse_stats(struct be_adapter *adapter)
 {
-       struct be_erx_stats_v1 *erx = be_erx_stats_from_cmd(adapter);
+       struct be_erx_stats_v2 *erx = be_erx_stats_from_cmd(adapter);
        struct be_rx_obj *rxo;
        int i;
        u32 erx_stat;
@@ -499,11 +562,13 @@ void be_parse_stats(struct be_adapter *adapter)
        } else {
                if (BE2_chip(adapter))
                        populate_be_v0_stats(adapter);
-               else
-                       /* for BE3 and Skyhawk */
+               else if (BE3_chip(adapter))
+                       /* for BE3 */
                        populate_be_v1_stats(adapter);
+               else
+                       populate_be_v2_stats(adapter);
 
-               /* as erx_v1 is longer than v0, ok to use v1 for v0 access */
+               /* erx_v2 is longer than v0, v1. use v2 for v0, v1 access */
                for_all_rx_queues(adapter, rxo, i) {
                        erx_stat = erx->rx_drops_no_fragments[rxo->q.id];
                        populate_erx_stats(adapter, rxo, erx_stat);
@@ -855,11 +920,11 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter,
        unsigned int eth_hdr_len;
        struct iphdr *ip;
 
-       /* Lancer ASIC has a bug wherein packets that are 32 bytes or less
+       /* Lancer, SH-R ASICs have a bug wherein Packets that are 32 bytes or less
         * may cause a transmit stall on that port. So the work-around is to
-        * pad such packets to a 36-byte length.
+        * pad short packets (<= 32 bytes) to a 36-byte length.
         */
-       if (unlikely(lancer_chip(adapter) && skb->len <= 32)) {
+       if (unlikely(!BEx_chip(adapter) && skb->len <= 32)) {
                if (skb_padto(skb, 36))
                        goto tx_drop;
                skb->len = 36;
@@ -935,8 +1000,10 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
        u32 start = txq->head;
 
        skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan);
-       if (!skb)
+       if (!skb) {
+               tx_stats(txo)->tx_drv_drops++;
                return NETDEV_TX_OK;
+       }
 
        wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb);
 
@@ -965,6 +1032,7 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
                be_tx_stats_update(txo, wrb_cnt, copied, gso_segs, stopped);
        } else {
                txq->head = start;
+               tx_stats(txo)->tx_drv_drops++;
                dev_kfree_skb_any(skb);
        }
        return NETDEV_TX_OK;
@@ -1013,18 +1081,40 @@ static int be_vid_config(struct be_adapter *adapter)
        status = be_cmd_vlan_config(adapter, adapter->if_handle,
                                    vids, num, 1, 0);
 
-       /* Set to VLAN promisc mode as setting VLAN filter failed */
        if (status) {
-               dev_info(&adapter->pdev->dev, "Exhausted VLAN HW filters.\n");
-               dev_info(&adapter->pdev->dev, "Disabling HW VLAN filtering.\n");
-               goto set_vlan_promisc;
+               /* Set to VLAN promisc mode as setting VLAN filter failed */
+               if (status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES)
+                       goto set_vlan_promisc;
+               dev_err(&adapter->pdev->dev,
+                       "Setting HW VLAN filtering failed.\n");
+       } else {
+               if (adapter->flags & BE_FLAGS_VLAN_PROMISC) {
+                       /* hw VLAN filtering re-enabled. */
+                       status = be_cmd_rx_filter(adapter,
+                                                 BE_FLAGS_VLAN_PROMISC, OFF);
+                       if (!status) {
+                               dev_info(&adapter->pdev->dev,
+                                        "Disabling VLAN Promiscuous mode.\n");
+                               adapter->flags &= ~BE_FLAGS_VLAN_PROMISC;
+                               dev_info(&adapter->pdev->dev,
+                                        "Re-Enabling HW VLAN filtering\n");
+                       }
+               }
        }
 
        return status;
 
 set_vlan_promisc:
-       status = be_cmd_vlan_config(adapter, adapter->if_handle,
-                                   NULL, 0, 1, 1);
+       dev_warn(&adapter->pdev->dev, "Exhausted VLAN HW filters.\n");
+
+       status = be_cmd_rx_filter(adapter, BE_FLAGS_VLAN_PROMISC, ON);
+       if (!status) {
+               dev_info(&adapter->pdev->dev, "Enable VLAN Promiscuous mode\n");
+               dev_info(&adapter->pdev->dev, "Disabling HW VLAN filtering\n");
+               adapter->flags |= BE_FLAGS_VLAN_PROMISC;
+       } else
+               dev_err(&adapter->pdev->dev,
+                       "Failed to enable VLAN Promiscuous mode.\n");
        return status;
 }
 
@@ -1033,10 +1123,6 @@ static int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
        struct be_adapter *adapter = netdev_priv(netdev);
        int status = 0;
 
-       if (!lancer_chip(adapter) && !be_physfn(adapter)) {
-               status = -EINVAL;
-               goto ret;
-       }
 
        /* Packets with VID 0 are always received by Lancer by default */
        if (lancer_chip(adapter) && vid == 0)
@@ -1059,11 +1145,6 @@ static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid)
        struct be_adapter *adapter = netdev_priv(netdev);
        int status = 0;
 
-       if (!lancer_chip(adapter) && !be_physfn(adapter)) {
-               status = -EINVAL;
-               goto ret;
-       }
-
        /* Packets with VID 0 are always received by Lancer by default */
        if (lancer_chip(adapter) && vid == 0)
                goto ret;
@@ -1188,8 +1269,8 @@ static int be_get_vf_config(struct net_device *netdev, int vf,
 
        vi->vf = vf;
        vi->tx_rate = vf_cfg->tx_rate;
-       vi->vlan = vf_cfg->vlan_tag;
-       vi->qos = 0;
+       vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK;
+       vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT;
        memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN);
 
        return 0;
@@ -1199,28 +1280,29 @@ static int be_set_vf_vlan(struct net_device *netdev,
                        int vf, u16 vlan, u8 qos)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
+       struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
        int status = 0;
 
        if (!sriov_enabled(adapter))
                return -EPERM;
 
-       if (vf >= adapter->num_vfs || vlan > 4095)
+       if (vf >= adapter->num_vfs || vlan > 4095 || qos > 7)
                return -EINVAL;
 
-       if (vlan) {
-               if (adapter->vf_cfg[vf].vlan_tag != vlan) {
+       if (vlan || qos) {
+               vlan |= qos << VLAN_PRIO_SHIFT;
+               if (vf_cfg->vlan_tag != vlan) {
                        /* If this is new value, program it. Else skip. */
-                       adapter->vf_cfg[vf].vlan_tag = vlan;
-
-                       status = be_cmd_set_hsw_config(adapter, vlan,
-                               vf + 1, adapter->vf_cfg[vf].if_handle, 0);
+                       vf_cfg->vlan_tag = vlan;
+                       status = be_cmd_set_hsw_config(adapter, vlan, vf + 1,
+                                                      vf_cfg->if_handle, 0);
                }
        } else {
                /* Reset Transparent Vlan Tagging. */
-               adapter->vf_cfg[vf].vlan_tag = 0;
-               vlan = adapter->vf_cfg[vf].def_vid;
+               vf_cfg->vlan_tag = 0;
+               vlan = vf_cfg->def_vid;
                status = be_cmd_set_hsw_config(adapter, vlan, vf + 1,
-                       adapter->vf_cfg[vf].if_handle, 0);
+                                              vf_cfg->if_handle, 0);
        }
 
 
@@ -1261,53 +1343,79 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
        return status;
 }
 
-static void be_eqd_update(struct be_adapter *adapter, struct be_eq_obj *eqo)
+static void be_aic_update(struct be_aic_obj *aic, u64 rx_pkts, u64 tx_pkts,
+                         ulong now)
 {
-       struct be_rx_stats *stats = rx_stats(&adapter->rx_obj[eqo->idx]);
-       ulong now = jiffies;
-       ulong delta = now - stats->rx_jiffies;
-       u64 pkts;
-       unsigned int start, eqd;
+       aic->rx_pkts_prev = rx_pkts;
+       aic->tx_reqs_prev = tx_pkts;
+       aic->jiffies = now;
+}
 
-       if (!eqo->enable_aic) {
-               eqd = eqo->eqd;
-               goto modify_eqd;
-       }
+static void be_eqd_update(struct be_adapter *adapter)
+{
+       struct be_set_eqd set_eqd[MAX_EVT_QS];
+       int eqd, i, num = 0, start;
+       struct be_aic_obj *aic;
+       struct be_eq_obj *eqo;
+       struct be_rx_obj *rxo;
+       struct be_tx_obj *txo;
+       u64 rx_pkts, tx_pkts;
+       ulong now;
+       u32 pps, delta;
 
-       if (eqo->idx >= adapter->num_rx_qs)
-               return;
+       for_all_evt_queues(adapter, eqo, i) {
+               aic = &adapter->aic_obj[eqo->idx];
+               if (!aic->enable) {
+                       if (aic->jiffies)
+                               aic->jiffies = 0;
+                       eqd = aic->et_eqd;
+                       goto modify_eqd;
+               }
 
-       stats = rx_stats(&adapter->rx_obj[eqo->idx]);
+               rxo = &adapter->rx_obj[eqo->idx];
+               do {
+                       start = u64_stats_fetch_begin_bh(&rxo->stats.sync);
+                       rx_pkts = rxo->stats.rx_pkts;
+               } while (u64_stats_fetch_retry_bh(&rxo->stats.sync, start));
 
-       /* Wrapped around */
-       if (time_before(now, stats->rx_jiffies)) {
-               stats->rx_jiffies = now;
-               return;
-       }
+               txo = &adapter->tx_obj[eqo->idx];
+               do {
+                       start = u64_stats_fetch_begin_bh(&txo->stats.sync);
+                       tx_pkts = txo->stats.tx_reqs;
+               } while (u64_stats_fetch_retry_bh(&txo->stats.sync, start));
 
-       /* Update once a second */
-       if (delta < HZ)
-               return;
 
-       do {
-               start = u64_stats_fetch_begin_bh(&stats->sync);
-               pkts = stats->rx_pkts;
-       } while (u64_stats_fetch_retry_bh(&stats->sync, start));
-
-       stats->rx_pps = (unsigned long)(pkts - stats->rx_pkts_prev) / (delta / HZ);
-       stats->rx_pkts_prev = pkts;
-       stats->rx_jiffies = now;
-       eqd = (stats->rx_pps / 110000) << 3;
-       eqd = min(eqd, eqo->max_eqd);
-       eqd = max(eqd, eqo->min_eqd);
-       if (eqd < 10)
-               eqd = 0;
+               /* Skip, if wrapped around or first calculation */
+               now = jiffies;
+               if (!aic->jiffies || time_before(now, aic->jiffies) ||
+                   rx_pkts < aic->rx_pkts_prev ||
+                   tx_pkts < aic->tx_reqs_prev) {
+                       be_aic_update(aic, rx_pkts, tx_pkts, now);
+                       continue;
+               }
+
+               delta = jiffies_to_msecs(now - aic->jiffies);
+               pps = (((u32)(rx_pkts - aic->rx_pkts_prev) * 1000) / delta) +
+                       (((u32)(tx_pkts - aic->tx_reqs_prev) * 1000) / delta);
+               eqd = (pps / 15000) << 2;
+
+               if (eqd < 8)
+                       eqd = 0;
+               eqd = min_t(u32, eqd, aic->max_eqd);
+               eqd = max_t(u32, eqd, aic->min_eqd);
 
+               be_aic_update(aic, rx_pkts, tx_pkts, now);
 modify_eqd:
-       if (eqd != eqo->cur_eqd) {
-               be_cmd_modify_eqd(adapter, eqo->q.id, eqd);
-               eqo->cur_eqd = eqd;
+               if (eqd != aic->prev_eqd) {
+                       set_eqd[num].delay_multiplier = (eqd * 65)/100;
+                       set_eqd[num].eq_id = eqo->q.id;
+                       aic->prev_eqd = eqd;
+                       num++;
+               }
        }
+
+       if (num)
+               be_cmd_modify_eqd(adapter, set_eqd, num);
 }
 
 static void be_rx_stats_update(struct be_rx_obj *rxo,
@@ -1449,7 +1557,7 @@ static void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb,
 }
 
 /* Process the RX completion indicated by rxcp when GRO is disabled */
-static void be_rx_compl_process(struct be_rx_obj *rxo,
+static void be_rx_compl_process(struct be_rx_obj *rxo, struct napi_struct *napi,
                                struct be_rx_compl_info *rxcp)
 {
        struct be_adapter *adapter = rxo->adapter;
@@ -1474,7 +1582,7 @@ static void be_rx_compl_process(struct be_rx_obj *rxo,
        skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]);
        if (netdev->features & NETIF_F_RXHASH)
                skb->rxhash = rxcp->rss_hash;
-
+       skb_mark_napi_id(skb, napi);
 
        if (rxcp->vlanf)
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag);
@@ -1532,6 +1640,7 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo,
        skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]);
        if (adapter->netdev->features & NETIF_F_RXHASH)
                skb->rxhash = rxcp->rss_hash;
+       skb_mark_napi_id(skb, napi);
 
        if (rxcp->vlanf)
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag);
@@ -1712,6 +1821,8 @@ static void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp)
 
        if (posted) {
                atomic_add(posted, &rxq->used);
+               if (rxo->rx_post_starved)
+                       rxo->rx_post_starved = false;
                be_rxq_notify(adapter, rxq->id, posted);
        } else if (atomic_read(&rxq->used) == 0) {
                /* Let be_worker replenish when memory is available */
@@ -1914,6 +2025,7 @@ static void be_evt_queues_destroy(struct be_adapter *adapter)
                if (eqo->q.created) {
                        be_eq_clean(eqo);
                        be_cmd_q_destroy(adapter, &eqo->q, QTYPE_EQ);
+                       napi_hash_del(&eqo->napi);
                        netif_napi_del(&eqo->napi);
                }
                be_queue_free(adapter, &eqo->q);
@@ -1924,6 +2036,7 @@ static int be_evt_queues_create(struct be_adapter *adapter)
 {
        struct be_queue_info *eq;
        struct be_eq_obj *eqo;
+       struct be_aic_obj *aic;
        int i, rc;
 
        adapter->num_evt_qs = min_t(u16, num_irqs(adapter),
@@ -1932,11 +2045,13 @@ static int be_evt_queues_create(struct be_adapter *adapter)
        for_all_evt_queues(adapter, eqo, i) {
                netif_napi_add(adapter->netdev, &eqo->napi, be_poll,
                               BE_NAPI_WEIGHT);
+               napi_hash_add(&eqo->napi);
+               aic = &adapter->aic_obj[i];
                eqo->adapter = adapter;
                eqo->tx_budget = BE_TX_BUDGET;
                eqo->idx = i;
-               eqo->max_eqd = BE_MAX_EQD;
-               eqo->enable_aic = true;
+               aic->max_eqd = BE_MAX_EQD;
+               aic->enable = true;
 
                eq = &eqo->q;
                rc = be_queue_alloc(adapter, eq, EVNT_Q_LEN,
@@ -2153,7 +2268,7 @@ static inline bool do_gro(struct be_rx_compl_info *rxcp)
 }
 
 static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi,
-                       int budget)
+                       int budget, int polling)
 {
        struct be_adapter *adapter = rxo->adapter;
        struct be_queue_info *rx_cq = &rxo->cq;
@@ -2184,10 +2299,12 @@ static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi,
                        goto loop_continue;
                }
 
-               if (do_gro(rxcp))
+               /* Don't do gro when we're busy_polling */
+               if (do_gro(rxcp) && polling != BUSY_POLLING)
                        be_rx_compl_process_gro(rxo, napi, rxcp);
                else
-                       be_rx_compl_process(rxo, rxcp);
+                       be_rx_compl_process(rxo, napi, rxcp);
+
 loop_continue:
                be_rx_stats_update(rxo, rxcp);
        }
@@ -2195,7 +2312,11 @@ loop_continue:
        if (work_done) {
                be_cq_notify(adapter, rx_cq->id, true, work_done);
 
-               if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM)
+               /* When an rx-obj gets into post_starved state, just
+                * let be_worker do the posting.
+                */
+               if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM &&
+                   !rxo->rx_post_starved)
                        be_post_rx_frags(rxo, GFP_ATOMIC);
        }
 
@@ -2240,6 +2361,7 @@ int be_poll(struct napi_struct *napi, int budget)
        struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi);
        struct be_adapter *adapter = eqo->adapter;
        int max_work = 0, work, i, num_evts;
+       struct be_rx_obj *rxo;
        bool tx_done;
 
        num_evts = events_get(eqo);
@@ -2252,13 +2374,18 @@ int be_poll(struct napi_struct *napi, int budget)
                        max_work = budget;
        }
 
-       /* This loop will iterate twice for EQ0 in which
-        * completions of the last RXQ (default one) are also processed
-        * For other EQs the loop iterates only once
-        */
-       for (i = eqo->idx; i < adapter->num_rx_qs; i += adapter->num_evt_qs) {
-               work = be_process_rx(&adapter->rx_obj[i], napi, budget);
-               max_work = max(work, max_work);
+       if (be_lock_napi(eqo)) {
+               /* This loop will iterate twice for EQ0 in which
+                * completions of the last RXQ (default one) are also processed
+                * For other EQs the loop iterates only once
+                */
+               for_all_rx_queues_on_eq(adapter, eqo, rxo, i) {
+                       work = be_process_rx(rxo, napi, budget, NAPI_POLLING);
+                       max_work = max(work, max_work);
+               }
+               be_unlock_napi(eqo);
+       } else {
+               max_work = budget;
        }
 
        if (is_mcc_eqo(eqo))
@@ -2274,6 +2401,28 @@ int be_poll(struct napi_struct *napi, int budget)
        return max_work;
 }
 
+#ifdef CONFIG_NET_RX_BUSY_POLL
+static int be_busy_poll(struct napi_struct *napi)
+{
+       struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi);
+       struct be_adapter *adapter = eqo->adapter;
+       struct be_rx_obj *rxo;
+       int i, work = 0;
+
+       if (!be_lock_busy_poll(eqo))
+               return LL_FLUSH_BUSY;
+
+       for_all_rx_queues_on_eq(adapter, eqo, rxo, i) {
+               work = be_process_rx(rxo, napi, 4, BUSY_POLLING);
+               if (work)
+                       break;
+       }
+
+       be_unlock_busy_poll(eqo);
+       return work;
+}
+#endif
+
 void be_detect_error(struct be_adapter *adapter)
 {
        u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0;
@@ -2505,9 +2654,11 @@ static int be_close(struct net_device *netdev)
 
        be_roce_dev_close(adapter);
 
-       if (adapter->flags & BE_FLAGS_NAPI_ENABLED) {
-               for_all_evt_queues(adapter, eqo, i)
+       for_all_evt_queues(adapter, eqo, i) {
+               if (adapter->flags & BE_FLAGS_NAPI_ENABLED) {
                        napi_disable(&eqo->napi);
+                       be_disable_busy_poll(eqo);
+               }
                adapter->flags &= ~BE_FLAGS_NAPI_ENABLED;
        }
 
@@ -2618,6 +2769,7 @@ static int be_open(struct net_device *netdev)
 
        for_all_evt_queues(adapter, eqo, i) {
                napi_enable(&eqo->napi);
+               be_enable_busy_poll(eqo);
                be_eq_notify(adapter, eqo->q.id, true, false, 0);
        }
        adapter->flags |= BE_FLAGS_NAPI_ENABLED;
@@ -2923,7 +3075,8 @@ static int be_vf_setup(struct be_adapter *adapter)
                        goto err;
                vf_cfg->def_vid = def_vlan;
 
-               be_cmd_enable_vf(adapter, vf + 1);
+               if (!old_vfs)
+                       be_cmd_enable_vf(adapter, vf + 1);
        }
 
        if (!old_vfs) {
@@ -2948,12 +3101,12 @@ static void BEx_get_resources(struct be_adapter *adapter,
        struct pci_dev *pdev = adapter->pdev;
        bool use_sriov = false;
 
-       if (BE3_chip(adapter) && be_physfn(adapter)) {
+       if (BE3_chip(adapter) && sriov_want(adapter)) {
                int max_vfs;
 
                max_vfs = pci_sriov_get_totalvfs(pdev);
                res->max_vfs = max_vfs > 0 ? min(MAX_VFS, max_vfs) : 0;
-               use_sriov = res->max_vfs && num_vfs;
+               use_sriov = res->max_vfs;
        }
 
        if (be_physfn(adapter))
@@ -2963,12 +3116,15 @@ static void BEx_get_resources(struct be_adapter *adapter,
 
        if (adapter->function_mode & FLEX10_MODE)
                res->max_vlans = BE_NUM_VLANS_SUPPORTED/8;
+       else if (adapter->function_mode & UMC_ENABLED)
+               res->max_vlans = BE_UMC_NUM_VLANS_SUPPORTED;
        else
                res->max_vlans = BE_NUM_VLANS_SUPPORTED;
        res->max_mcast_mac = BE_MAX_MC;
 
+       /* For BE3 1Gb ports, F/W does not properly support multiple TXQs */
        if (BE2_chip(adapter) || use_sriov || be_is_mc(adapter) ||
-           !be_physfn(adapter))
+           !be_physfn(adapter) || (adapter->port_num > 1))
                res->max_tx_qs = 1;
        else
                res->max_tx_qs = BE3_MAX_TX_QS;
@@ -3010,14 +3166,6 @@ static int be_get_resources(struct be_adapter *adapter)
                adapter->res = res;
        }
 
-       /* For BE3 only check if FW suggests a different max-txqs value */
-       if (BE3_chip(adapter)) {
-               status = be_cmd_get_profile_config(adapter, &res, 0);
-               if (!status && res.max_tx_qs)
-                       adapter->res.max_tx_qs =
-                               min(adapter->res.max_tx_qs, res.max_tx_qs);
-       }
-
        /* For Lancer, SH etc read per-function resource limits from FW.
         * GET_FUNC_CONFIG returns per function guaranteed limits.
         * GET_PROFILE_CONFIG returns PCI-E related limits PF-pool limits
@@ -3231,6 +3379,12 @@ static int be_setup(struct be_adapter *adapter)
 
        be_cmd_get_fw_ver(adapter, adapter->fw_ver, adapter->fw_on_flash);
 
+       if (BE2_chip(adapter) && fw_major_num(adapter->fw_ver) < 4) {
+               dev_err(dev, "Firmware on card is old(%s), IRQs may not work.",
+                       adapter->fw_ver);
+               dev_err(dev, "Please upgrade firmware to version >= 4.0\n");
+       }
+
        if (adapter->vlans_added)
                be_vid_config(adapter);
 
@@ -3242,7 +3396,7 @@ static int be_setup(struct be_adapter *adapter)
                be_cmd_set_flow_control(adapter, adapter->tx_fc,
                                        adapter->rx_fc);
 
-       if (be_physfn(adapter) && num_vfs) {
+       if (sriov_want(adapter)) {
                if (be_max_vfs(adapter))
                        be_vf_setup(adapter);
                else
@@ -3884,6 +4038,9 @@ static const struct net_device_ops be_netdev_ops = {
 #endif
        .ndo_bridge_setlink     = be_ndo_bridge_setlink,
        .ndo_bridge_getlink     = be_ndo_bridge_getlink,
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       .ndo_busy_poll          = be_busy_poll
+#endif
 };
 
 static void be_netdev_init(struct net_device *netdev)
@@ -3944,11 +4101,6 @@ static int be_roce_map_pci_bars(struct be_adapter *adapter)
 static int be_map_pci_bars(struct be_adapter *adapter)
 {
        u8 __iomem *addr;
-       u32 sli_intf;
-
-       pci_read_config_dword(adapter->pdev, SLI_INTF_REG_OFFSET, &sli_intf);
-       adapter->if_type = (sli_intf & SLI_INTF_IF_TYPE_MASK) >>
-                               SLI_INTF_IF_TYPE_SHIFT;
 
        if (BEx_chip(adapter) && be_physfn(adapter)) {
                adapter->csr = pci_iomap(adapter->pdev, 2, 0);
@@ -4061,9 +4213,11 @@ static int be_stats_init(struct be_adapter *adapter)
                cmd->size = sizeof(struct lancer_cmd_req_pport_stats);
        else if (BE2_chip(adapter))
                cmd->size = sizeof(struct be_cmd_req_get_stats_v0);
-       else
-               /* BE3 and Skyhawk */
+       else if (BE3_chip(adapter))
                cmd->size = sizeof(struct be_cmd_req_get_stats_v1);
+       else
+               /* ALL non-BE ASICs */
+               cmd->size = sizeof(struct be_cmd_req_get_stats_v2);
 
        cmd->va = dma_zalloc_coherent(&adapter->pdev->dev, cmd->size, &cmd->dma,
                                      GFP_KERNEL);
@@ -4097,7 +4251,6 @@ static void be_remove(struct pci_dev *pdev)
 
        pci_disable_pcie_error_reporting(pdev);
 
-       pci_set_drvdata(pdev, NULL);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
 
@@ -4246,7 +4399,6 @@ static void be_worker(struct work_struct *work)
        struct be_adapter *adapter =
                container_of(work, struct be_adapter, work.work);
        struct be_rx_obj *rxo;
-       struct be_eq_obj *eqo;
        int i;
 
        /* when interrupts are not yet enabled, just reap any pending
@@ -4271,14 +4423,14 @@ static void be_worker(struct work_struct *work)
                be_cmd_get_die_temperature(adapter);
 
        for_all_rx_queues(adapter, rxo, i) {
-               if (rxo->rx_post_starved) {
-                       rxo->rx_post_starved = false;
+               /* Replenish RX-queues starved due to memory
+                * allocation failures.
+                */
+               if (rxo->rx_post_starved)
                        be_post_rx_frags(rxo, GFP_KERNEL);
-               }
        }
 
-       for_all_evt_queues(adapter, eqo, i)
-               be_eqd_update(adapter, eqo);
+       be_eqd_update(adapter);
 
 reschedule:
        adapter->work_counter++;
@@ -4354,9 +4506,11 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id)
                }
        }
 
-       status = pci_enable_pcie_error_reporting(pdev);
-       if (status)
-               dev_info(&pdev->dev, "Could not use PCIe error reporting\n");
+       if (be_physfn(adapter)) {
+               status = pci_enable_pcie_error_reporting(pdev);
+               if (!status)
+                       dev_info(&pdev->dev, "PCIe error reporting enabled\n");
+       }
 
        status = be_ctrl_init(adapter);
        if (status)
@@ -4427,7 +4581,6 @@ ctrl_clean:
        be_ctrl_cleanup(adapter);
 free_netdev:
        free_netdev(netdev);
-       pci_set_drvdata(pdev, NULL);
 rel_reg:
        pci_release_regions(pdev);
 disable_dev:
index c706b7a..4b22a95 100644 (file)
@@ -699,7 +699,6 @@ static void fealnx_remove_one(struct pci_dev *pdev)
                pci_iounmap(pdev, np->mem);
                free_netdev(dev);
                pci_release_regions(pdev);
-               pci_set_drvdata(pdev, NULL);
        } else
                printk(KERN_ERR "fealnx: remove for unknown device\n");
 }
index 6b60582..56f2f60 100644 (file)
@@ -1083,7 +1083,7 @@ static int fs_enet_probe(struct platform_device *ofdev)
 
        mac_addr = of_get_mac_address(ofdev->dev.of_node);
        if (mac_addr)
-               memcpy(ndev->dev_addr, mac_addr, 6);
+               memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
 
        ret = fep->ops->allocate_bd(ndev);
        if (ret)
index c4eaade..d6d810c 100644 (file)
@@ -88,6 +88,7 @@
 
 #include <asm/io.h>
 #include <asm/reg.h>
+#include <asm/mpc85xx.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <linux/module.h>
@@ -939,9 +940,8 @@ static void gfar_init_filer_table(struct gfar_private *priv)
        }
 }
 
-static void gfar_detect_errata(struct gfar_private *priv)
+static void __gfar_detect_errata_83xx(struct gfar_private *priv)
 {
-       struct device *dev = &priv->ofdev->dev;
        unsigned int pvr = mfspr(SPRN_PVR);
        unsigned int svr = mfspr(SPRN_SVR);
        unsigned int mod = (svr >> 16) & 0xfff6; /* w/o E suffix */
@@ -957,15 +957,33 @@ static void gfar_detect_errata(struct gfar_private *priv)
            (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
                priv->errata |= GFAR_ERRATA_76;
 
-       /* MPC8313 and MPC837x all rev */
-       if ((pvr == 0x80850010 && mod == 0x80b0) ||
-           (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
-               priv->errata |= GFAR_ERRATA_A002;
+       /* MPC8313 Rev < 2.0 */
+       if (pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020)
+               priv->errata |= GFAR_ERRATA_12;
+}
+
+static void __gfar_detect_errata_85xx(struct gfar_private *priv)
+{
+       unsigned int svr = mfspr(SPRN_SVR);
 
-       /* MPC8313 Rev < 2.0, MPC8548 rev 2.0 */
-       if ((pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020) ||
-           (pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020))
+       if ((SVR_SOC_VER(svr) == SVR_8548) && (SVR_REV(svr) == 0x20))
                priv->errata |= GFAR_ERRATA_12;
+       if (((SVR_SOC_VER(svr) == SVR_P2020) && (SVR_REV(svr) < 0x20)) ||
+           ((SVR_SOC_VER(svr) == SVR_P2010) && (SVR_REV(svr) < 0x20)))
+               priv->errata |= GFAR_ERRATA_76; /* aka eTSEC 20 */
+}
+
+static void gfar_detect_errata(struct gfar_private *priv)
+{
+       struct device *dev = &priv->ofdev->dev;
+
+       /* no plans to fix */
+       priv->errata |= GFAR_ERRATA_A002;
+
+       if (pvr_version_is(PVR_VER_E500V1) || pvr_version_is(PVR_VER_E500V2))
+               __gfar_detect_errata_85xx(priv);
+       else /* non-mpc85xx parts, i.e. e300 core based */
+               __gfar_detect_errata_83xx(priv);
 
        if (priv->errata)
                dev_info(dev, "enabled errata workarounds, flags: 0x%x\n",
@@ -1599,7 +1617,7 @@ static int __gfar_is_rx_idle(struct gfar_private *priv)
        /* Normaly TSEC should not hang on GRS commands, so we should
         * actually wait for IEVENT_GRSC flag.
         */
-       if (likely(!gfar_has_errata(priv, GFAR_ERRATA_A002)))
+       if (!gfar_has_errata(priv, GFAR_ERRATA_A002))
                return 0;
 
        /* Read the eTSEC register at offset 0xD1C. If bits 7-14 are
@@ -2900,7 +2918,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
        struct gfar_priv_rx_q *rx_queue = NULL;
        int work_done = 0, work_done_per_q = 0;
        int i, budget_per_q = 0;
-       int has_tx_work;
+       int has_tx_work = 0;
        unsigned long rstat_rxf;
        int num_act_queues;
 
@@ -2915,62 +2933,51 @@ static int gfar_poll(struct napi_struct *napi, int budget)
        if (num_act_queues)
                budget_per_q = budget/num_act_queues;
 
-       while (1) {
-               has_tx_work = 0;
-               for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
-                       tx_queue = priv->tx_queue[i];
-                       /* run Tx cleanup to completion */
-                       if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
-                               gfar_clean_tx_ring(tx_queue);
-                               has_tx_work = 1;
-                       }
+       for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
+               tx_queue = priv->tx_queue[i];
+               /* run Tx cleanup to completion */
+               if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
+                       gfar_clean_tx_ring(tx_queue);
+                       has_tx_work = 1;
                }
+       }
 
-               for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
-                       /* skip queue if not active */
-                       if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
-                               continue;
-
-                       rx_queue = priv->rx_queue[i];
-                       work_done_per_q =
-                               gfar_clean_rx_ring(rx_queue, budget_per_q);
-                       work_done += work_done_per_q;
-
-                       /* finished processing this queue */
-                       if (work_done_per_q < budget_per_q) {
-                               /* clear active queue hw indication */
-                               gfar_write(&regs->rstat,
-                                          RSTAT_CLEAR_RXF0 >> i);
-                               rstat_rxf &= ~(RSTAT_CLEAR_RXF0 >> i);
-                               num_act_queues--;
-
-                               if (!num_act_queues)
-                                       break;
-                               /* recompute budget per Rx queue */
-                               budget_per_q =
-                                       (budget - work_done) / num_act_queues;
-                       }
-               }
+       for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
+               /* skip queue if not active */
+               if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
+                       continue;
 
-               if (work_done >= budget)
-                       break;
+               rx_queue = priv->rx_queue[i];
+               work_done_per_q =
+                       gfar_clean_rx_ring(rx_queue, budget_per_q);
+               work_done += work_done_per_q;
+
+               /* finished processing this queue */
+               if (work_done_per_q < budget_per_q) {
+                       /* clear active queue hw indication */
+                       gfar_write(&regs->rstat,
+                                  RSTAT_CLEAR_RXF0 >> i);
+                       num_act_queues--;
+
+                       if (!num_act_queues)
+                               break;
+               }
+       }
 
-               if (!num_act_queues && !has_tx_work) {
+       if (!num_act_queues && !has_tx_work) {
 
-                       napi_complete(napi);
+               napi_complete(napi);
 
-                       /* Clear the halt bit in RSTAT */
-                       gfar_write(&regs->rstat, gfargrp->rstat);
+               /* Clear the halt bit in RSTAT */
+               gfar_write(&regs->rstat, gfargrp->rstat);
 
-                       gfar_write(&regs->imask, IMASK_DEFAULT);
+               gfar_write(&regs->imask, IMASK_DEFAULT);
 
-                       /* If we are coalescing interrupts, update the timer
-                        * Otherwise, clear it
-                        */
-                       gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
-                                                 gfargrp->tx_bit_map);
-                       break;
-               }
+               /* If we are coalescing interrupts, update the timer
+                * Otherwise, clear it
+                */
+               gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
+                                         gfargrp->tx_bit_map);
        }
 
        return work_done;
index 04112b9..114c58f 100644 (file)
@@ -1177,21 +1177,21 @@ static inline void gfar_read_filer(struct gfar_private *priv,
        *fpr = gfar_read(&regs->rqfpr);
 }
 
-extern void lock_rx_qs(struct gfar_private *priv);
-extern void lock_tx_qs(struct gfar_private *priv);
-extern void unlock_rx_qs(struct gfar_private *priv);
-extern void unlock_tx_qs(struct gfar_private *priv);
-extern irqreturn_t gfar_receive(int irq, void *dev_id);
-extern int startup_gfar(struct net_device *dev);
-extern void stop_gfar(struct net_device *dev);
-extern void gfar_halt(struct net_device *dev);
-extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
-               int enable, u32 regnum, u32 read);
-extern void gfar_configure_coalescing_all(struct gfar_private *priv);
+void lock_rx_qs(struct gfar_private *priv);
+void lock_tx_qs(struct gfar_private *priv);
+void unlock_rx_qs(struct gfar_private *priv);
+void unlock_tx_qs(struct gfar_private *priv);
+irqreturn_t gfar_receive(int irq, void *dev_id);
+int startup_gfar(struct net_device *dev);
+void stop_gfar(struct net_device *dev);
+void gfar_halt(struct net_device *dev);
+void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
+                  u32 regnum, u32 read);
+void gfar_configure_coalescing_all(struct gfar_private *priv);
 void gfar_init_sysfs(struct net_device *dev);
 int gfar_set_features(struct net_device *dev, netdev_features_t features);
-extern void gfar_check_rx_parser_mode(struct gfar_private *priv);
-extern void gfar_vlan_mode(struct net_device *dev, netdev_features_t features);
+void gfar_check_rx_parser_mode(struct gfar_private *priv);
+void gfar_vlan_mode(struct net_device *dev, netdev_features_t features);
 
 extern const struct ethtool_ops gfar_ethtool_ops;
 
index 098f133..e006a09 100644 (file)
@@ -452,7 +452,9 @@ static int gianfar_ptp_probe(struct platform_device *dev)
        err = -ENODEV;
 
        etsects->caps = ptp_gianfar_caps;
-       etsects->cksel = DEFAULT_CKSEL;
+
+       if (get_of_u32(node, "fsl,cksel", &etsects->cksel))
+               etsects->cksel = DEFAULT_CKSEL;
 
        if (get_of_u32(node, "fsl,tclk-period", &etsects->tclk_period) ||
            get_of_u32(node, "fsl,tmr-prsc", &etsects->tmr_prsc) ||
index 5930c39..d58a3df 100644 (file)
@@ -3899,7 +3899,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
 
        mac_addr = of_get_mac_address(np);
        if (mac_addr)
-               memcpy(dev->dev_addr, mac_addr, 6);
+               memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
 
        ugeth->ug_info = ug_info;
        ugeth->dev = device;
index 6231bc0..1085257 100644 (file)
@@ -5,7 +5,7 @@
 config NET_VENDOR_FUJITSU
        bool "Fujitsu devices"
        default y
-       depends on ISA || PCMCIA
+       depends on PCMCIA
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
index 91227d0..3786009 100644 (file)
@@ -1098,7 +1098,7 @@ static int hp100_open(struct net_device *dev)
        if (request_irq(dev->irq, hp100_interrupt,
                        lp->bus == HP100_BUS_PCI || lp->bus ==
                        HP100_BUS_EISA ? IRQF_SHARED : 0,
-                       "hp100", dev)) {
+                       dev->name, dev)) {
                printk("hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq);
                return -EAGAIN;
        }
index e388161..7ce6379 100644 (file)
@@ -711,7 +711,7 @@ static int init_i596_mem(struct net_device *dev)
        i596_add_cmd(dev, &lp->cf_cmd.cmd);
 
        DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name));
-       memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, 6);
+       memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, ETH_ALEN);
        lp->sa_cmd.cmd.command = CmdSASetup;
        i596_add_cmd(dev, &lp->sa_cmd.cmd);
 
@@ -1155,7 +1155,7 @@ struct net_device * __init i82596_probe(int unit)
                        err = -ENODEV;
                        goto out;
                }
-               memcpy(eth_addr, (void *) 0xfffc1f2c, 6);       /* YUCK! Get addr from NOVRAM */
+               memcpy(eth_addr, (void *) 0xfffc1f2c, ETH_ALEN);        /* YUCK! Get addr from NOVRAM */
                dev->base_addr = MVME_I596_BASE;
                dev->irq = (unsigned) MVME16x_IRQ_I596;
                goto found;
@@ -1527,9 +1527,7 @@ int __init init_module(void)
        if (debug >= 0)
                i596_debug = debug;
        dev_82596 = i82596_probe(-1);
-       if (IS_ERR(dev_82596))
-               return PTR_ERR(dev_82596);
-       return 0;
+       return PTR_ERR_OR_ZERO(dev_82596);
 }
 
 void __exit cleanup_module(void)
index d653bac..861fa15 100644 (file)
@@ -607,7 +607,7 @@ static int init_i596_mem(struct net_device *dev)
        i596_add_cmd(dev, &dma->cf_cmd.cmd);
 
        DEB(DEB_INIT, printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name));
-       memcpy(dma->sa_cmd.eth_addr, dev->dev_addr, 6);
+       memcpy(dma->sa_cmd.eth_addr, dev->dev_addr, ETH_ALEN);
        dma->sa_cmd.cmd.command = SWAP16(CmdSASetup);
        DMA_WBACK(dev, &(dma->sa_cmd), sizeof(struct sa_cmd));
        i596_add_cmd(dev, &dma->sa_cmd.cmd);
@@ -1396,13 +1396,13 @@ static void set_multicast_list(struct net_device *dev)
                netdev_for_each_mc_addr(ha, dev) {
                        if (!cnt--)
                                break;
-                       memcpy(cp, ha->addr, 6);
+                       memcpy(cp, ha->addr, ETH_ALEN);
                        if (i596_debug > 1)
                                DEB(DEB_MULTI,
                                    printk(KERN_DEBUG
                                           "%s: Adding address %pM\n",
                                           dev->name, cp));
-                       cp += 6;
+                       cp += ETH_ALEN;
                }
                DMA_WBACK_INV(dev, &dma->mc_cmd, sizeof(struct mc_cmd));
                i596_add_cmd(dev, &cmd->cmd);
index 6b5c722..ef21a2e 100644 (file)
@@ -2676,7 +2676,7 @@ static int emac_init_config(struct emac_instance *dev)
                       np->full_name);
                return -ENXIO;
        }
-       memcpy(dev->ndev->dev_addr, p, 6);
+       memcpy(dev->ndev->dev_addr, p, ETH_ALEN);
 
        /* IAHT and GAHT filter parameterization */
        if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC)) {
index 59a92d5..9c45efe 100644 (file)
 struct emac_instance;
 struct mal_instance;
 
-extern void emac_dbg_register(struct emac_instance *dev);
-extern void emac_dbg_unregister(struct emac_instance *dev);
-extern void mal_dbg_register(struct mal_instance *mal);
-extern void mal_dbg_unregister(struct mal_instance *mal);
-extern int emac_init_debug(void) __init;
-extern void emac_fini_debug(void) __exit;
-extern void emac_dbg_dump_all(void);
+void emac_dbg_register(struct emac_instance *dev);
+void emac_dbg_unregister(struct emac_instance *dev);
+void mal_dbg_register(struct mal_instance *mal);
+void mal_dbg_unregister(struct mal_instance *mal);
+int emac_init_debug(void) __init;
+void emac_fini_debug(void) __exit;
+void emac_dbg_dump_all(void);
 
 # define DBG_LEVEL             1
 
index dac564c..e784751 100644 (file)
@@ -263,7 +263,9 @@ static inline void mal_schedule_poll(struct mal_instance *mal)
 {
        if (likely(napi_schedule_prep(&mal->napi))) {
                MAL_DBG2(mal, "schedule_poll" NL);
+               spin_lock(&mal->lock);
                mal_disable_eob_irq(mal);
+               spin_unlock(&mal->lock);
                __napi_schedule(&mal->napi);
        } else
                MAL_DBG2(mal, "already in poll" NL);
@@ -442,15 +444,13 @@ static int mal_poll(struct napi_struct *napi, int budget)
                if (unlikely(mc->ops->peek_rx(mc->dev) ||
                             test_bit(MAL_COMMAC_RX_STOPPED, &mc->flags))) {
                        MAL_DBG2(mal, "rotting packet" NL);
-                       if (napi_reschedule(napi))
-                               mal_disable_eob_irq(mal);
-                       else
-                               MAL_DBG2(mal, "already in poll list" NL);
-
-                       if (budget > 0)
-                               goto again;
-                       else
+                       if (!napi_reschedule(napi))
                                goto more_work;
+
+                       spin_lock_irqsave(&mal->lock, flags);
+                       mal_disable_eob_irq(mal);
+                       spin_unlock_irqrestore(&mal->lock, flags);
+                       goto again;
                }
                mc->ops->poll_tx(mc->dev);
        }
index 668bcee..d4f1374 100644 (file)
@@ -56,15 +56,15 @@ struct rgmii_instance {
 
 #ifdef CONFIG_IBM_EMAC_RGMII
 
-extern int rgmii_init(void);
-extern void rgmii_exit(void);
-extern int rgmii_attach(struct platform_device *ofdev, int input, int mode);
-extern void rgmii_detach(struct platform_device *ofdev, int input);
-extern void rgmii_get_mdio(struct platform_device *ofdev, int input);
-extern void rgmii_put_mdio(struct platform_device *ofdev, int input);
-extern void rgmii_set_speed(struct platform_device *ofdev, int input, int speed);
-extern int rgmii_get_regs_len(struct platform_device *ofdev);
-extern void *rgmii_dump_regs(struct platform_device *ofdev, void *buf);
+int rgmii_init(void);
+void rgmii_exit(void);
+int rgmii_attach(struct platform_device *ofdev, int input, int mode);
+void rgmii_detach(struct platform_device *ofdev, int input);
+void rgmii_get_mdio(struct platform_device *ofdev, int input);
+void rgmii_put_mdio(struct platform_device *ofdev, int input);
+void rgmii_set_speed(struct platform_device *ofdev, int input, int speed);
+int rgmii_get_regs_len(struct platform_device *ofdev);
+void *rgmii_dump_regs(struct platform_device *ofdev, void *buf);
 
 #else
 
index 350b709..4d5f336 100644 (file)
@@ -72,13 +72,13 @@ struct tah_instance {
 
 #ifdef CONFIG_IBM_EMAC_TAH
 
-extern int tah_init(void);
-extern void tah_exit(void);
-extern int tah_attach(struct platform_device *ofdev, int channel);
-extern void tah_detach(struct platform_device *ofdev, int channel);
-extern void tah_reset(struct platform_device *ofdev);
-extern int tah_get_regs_len(struct platform_device *ofdev);
-extern void *tah_dump_regs(struct platform_device *ofdev, void *buf);
+int tah_init(void);
+void tah_exit(void);
+int tah_attach(struct platform_device *ofdev, int channel);
+void tah_detach(struct platform_device *ofdev, int channel);
+void tah_reset(struct platform_device *ofdev);
+int tah_get_regs_len(struct platform_device *ofdev);
+void *tah_dump_regs(struct platform_device *ofdev, void *buf);
 
 #else
 
index 455bfb0..0959c55 100644 (file)
@@ -53,15 +53,15 @@ struct zmii_instance {
 
 #ifdef CONFIG_IBM_EMAC_ZMII
 
-extern int zmii_init(void);
-extern void zmii_exit(void);
-extern int zmii_attach(struct platform_device *ofdev, int input, int *mode);
-extern void zmii_detach(struct platform_device *ofdev, int input);
-extern void zmii_get_mdio(struct platform_device *ofdev, int input);
-extern void zmii_put_mdio(struct platform_device *ofdev, int input);
-extern void zmii_set_speed(struct platform_device *ofdev, int input, int speed);
-extern int zmii_get_regs_len(struct platform_device *ocpdev);
-extern void *zmii_dump_regs(struct platform_device *ofdev, void *buf);
+int zmii_init(void);
+void zmii_exit(void);
+int zmii_attach(struct platform_device *ofdev, int input, int *mode);
+void zmii_detach(struct platform_device *ofdev, int input);
+void zmii_get_mdio(struct platform_device *ofdev, int input);
+void zmii_put_mdio(struct platform_device *ofdev, int input);
+void zmii_set_speed(struct platform_device *ofdev, int input, int speed);
+int zmii_get_regs_len(struct platform_device *ocpdev);
+void *zmii_dump_regs(struct platform_device *ofdev, void *buf);
 
 #else
 # define zmii_init()           0
index 5d41aee..952d795 100644 (file)
@@ -1185,7 +1185,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
                netdev_for_each_mc_addr(ha, netdev) {
                        /* add the multicast address to the filter table */
                        unsigned long mcast_addr = 0;
-                       memcpy(((char *)&mcast_addr)+2, ha->addr, 6);
+                       memcpy(((char *)&mcast_addr)+2, ha->addr, ETH_ALEN);
                        lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address,
                                                   IbmVethMcastAddFilter,
                                                   mcast_addr);
@@ -1370,7 +1370,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
        netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16);
 
        adapter->mac_addr = 0;
-       memcpy(&adapter->mac_addr, mac_addr_p, 6);
+       memcpy(&adapter->mac_addr, mac_addr_p, ETH_ALEN);
 
        netdev->irq = dev->irq;
        netdev->netdev_ops = &ibmveth_netdev_ops;
index bdf5023..25045ae 100644 (file)
@@ -2183,7 +2183,6 @@ static void ipg_remove(struct pci_dev *pdev)
 
        free_netdev(dev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static const struct net_device_ops ipg_netdev_ops = {
index ada6e21..cbaba44 100644 (file)
@@ -2985,7 +2985,6 @@ err_out_free_res:
 err_out_disable_pdev:
        pci_disable_device(pdev);
 err_out_free_dev:
-       pci_set_drvdata(pdev, NULL);
        free_netdev(netdev);
        return err;
 }
@@ -3003,7 +3002,6 @@ static void e100_remove(struct pci_dev *pdev)
                free_netdev(netdev);
                pci_release_regions(pdev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index 26d9cd5..58c1472 100644 (file)
@@ -325,7 +325,7 @@ enum e1000_state_t {
 #undef pr_fmt
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-extern struct net_device *e1000_get_hw_dev(struct e1000_hw *hw);
+struct net_device *e1000_get_hw_dev(struct e1000_hw *hw);
 #define e_dbg(format, arg...) \
        netdev_dbg(e1000_get_hw_dev(hw), format, ## arg)
 #define e_err(msglvl, format, arg...) \
@@ -346,20 +346,20 @@ extern struct net_device *e1000_get_hw_dev(struct e1000_hw *hw);
 extern char e1000_driver_name[];
 extern const char e1000_driver_version[];
 
-extern int e1000_up(struct e1000_adapter *adapter);
-extern void e1000_down(struct e1000_adapter *adapter);
-extern void e1000_reinit_locked(struct e1000_adapter *adapter);
-extern void e1000_reset(struct e1000_adapter *adapter);
-extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx);
-extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
-extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
-extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
-extern void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
-extern void e1000_update_stats(struct e1000_adapter *adapter);
-extern bool e1000_has_link(struct e1000_adapter *adapter);
-extern void e1000_power_up_phy(struct e1000_adapter *);
-extern void e1000_set_ethtool_ops(struct net_device *netdev);
-extern void e1000_check_options(struct e1000_adapter *adapter);
-extern char *e1000_get_hw_dev_name(struct e1000_hw *hw);
+int e1000_up(struct e1000_adapter *adapter);
+void e1000_down(struct e1000_adapter *adapter);
+void e1000_reinit_locked(struct e1000_adapter *adapter);
+void e1000_reset(struct e1000_adapter *adapter);
+int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx);
+int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
+int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
+void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
+void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
+void e1000_update_stats(struct e1000_adapter *adapter);
+bool e1000_has_link(struct e1000_adapter *adapter);
+void e1000_power_up_phy(struct e1000_adapter *);
+void e1000_set_ethtool_ops(struct net_device *netdev);
+void e1000_check_options(struct e1000_adapter *adapter);
+char *e1000_get_hw_dev_name(struct e1000_hw *hw);
 
 #endif /* _E1000_H_ */
index 59ad007..ad6800a 100644 (file)
@@ -3917,8 +3917,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
                              "  next_to_watch        <%x>\n"
                              "  jiffies              <%lx>\n"
                              "  next_to_watch.status <%x>\n",
-                               (unsigned long)((tx_ring - adapter->tx_ring) /
-                                       sizeof(struct e1000_tx_ring)),
+                               (unsigned long)(tx_ring - adapter->tx_ring),
                                readl(hw->hw_addr + tx_ring->tdh),
                                readl(hw->hw_addr + tx_ring->tdt),
                                tx_ring->next_to_use,
index ad0edd1..0150f7f 100644 (file)
@@ -472,26 +472,25 @@ enum latency_range {
 extern char e1000e_driver_name[];
 extern const char e1000e_driver_version[];
 
-extern void e1000e_check_options(struct e1000_adapter *adapter);
-extern void e1000e_set_ethtool_ops(struct net_device *netdev);
-
-extern int e1000e_up(struct e1000_adapter *adapter);
-extern void e1000e_down(struct e1000_adapter *adapter);
-extern void e1000e_reinit_locked(struct e1000_adapter *adapter);
-extern void e1000e_reset(struct e1000_adapter *adapter);
-extern void e1000e_power_up_phy(struct e1000_adapter *adapter);
-extern int e1000e_setup_rx_resources(struct e1000_ring *ring);
-extern int e1000e_setup_tx_resources(struct e1000_ring *ring);
-extern void e1000e_free_rx_resources(struct e1000_ring *ring);
-extern void e1000e_free_tx_resources(struct e1000_ring *ring);
-extern struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev,
-                                                   struct rtnl_link_stats64
-                                                   *stats);
-extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter);
-extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter);
-extern void e1000e_get_hw_control(struct e1000_adapter *adapter);
-extern void e1000e_release_hw_control(struct e1000_adapter *adapter);
-extern void e1000e_write_itr(struct e1000_adapter *adapter, u32 itr);
+void e1000e_check_options(struct e1000_adapter *adapter);
+void e1000e_set_ethtool_ops(struct net_device *netdev);
+
+int e1000e_up(struct e1000_adapter *adapter);
+void e1000e_down(struct e1000_adapter *adapter);
+void e1000e_reinit_locked(struct e1000_adapter *adapter);
+void e1000e_reset(struct e1000_adapter *adapter);
+void e1000e_power_up_phy(struct e1000_adapter *adapter);
+int e1000e_setup_rx_resources(struct e1000_ring *ring);
+int e1000e_setup_tx_resources(struct e1000_ring *ring);
+void e1000e_free_rx_resources(struct e1000_ring *ring);
+void e1000e_free_tx_resources(struct e1000_ring *ring);
+struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev,
+                                            struct rtnl_link_stats64 *stats);
+void e1000e_set_interrupt_capability(struct e1000_adapter *adapter);
+void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter);
+void e1000e_get_hw_control(struct e1000_adapter *adapter);
+void e1000e_release_hw_control(struct e1000_adapter *adapter);
+void e1000e_write_itr(struct e1000_adapter *adapter, u32 itr);
 
 extern unsigned int copybreak;
 
@@ -508,8 +507,8 @@ extern const struct e1000_info e1000_pch2_info;
 extern const struct e1000_info e1000_pch_lpt_info;
 extern const struct e1000_info e1000_es2_info;
 
-extern void e1000e_ptp_init(struct e1000_adapter *adapter);
-extern void e1000e_ptp_remove(struct e1000_adapter *adapter);
+void e1000e_ptp_init(struct e1000_adapter *adapter);
+void e1000e_ptp_remove(struct e1000_adapter *adapter);
 
 static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw)
 {
@@ -536,7 +535,7 @@ static inline s32 e1e_wphy_locked(struct e1000_hw *hw, u32 offset, u16 data)
        return hw->phy.ops.write_reg_locked(hw, offset, data);
 }
 
-extern void e1000e_reload_nvm_generic(struct e1000_hw *hw);
+void e1000e_reload_nvm_generic(struct e1000_hw *hw);
 
 static inline s32 e1000e_read_mac_addr(struct e1000_hw *hw)
 {
index b5252eb..1ca9834 100644 (file)
@@ -46,7 +46,6 @@
 #include <linux/sctp.h>
 #include <linux/pkt_sched.h>
 #include <linux/ipv6.h>
-#include <linux/version.h>
 #include <net/checksum.h>
 #include <net/ip6_checksum.h>
 #include <linux/ethtool.h>
@@ -347,9 +346,9 @@ struct i40e_vsi {
        u32 rx_buf_failed;
        u32 rx_page_failed;
 
-       /* These are arrays of rings, allocated at run-time */
-       struct i40e_ring *rx_rings;
-       struct i40e_ring *tx_rings;
+       /* These are containers of ring pointers, allocated at run-time */
+       struct i40e_ring **rx_rings;
+       struct i40e_ring **tx_rings;
 
        u16 work_limit;
        /* high bit set means dynamic, use accessor routines to read/write.
@@ -366,7 +365,7 @@ struct i40e_vsi {
        u8  dtype;
 
        /* List of q_vectors allocated to this VSI */
-       struct i40e_q_vector *q_vectors;
+       struct i40e_q_vector **q_vectors;
        int num_q_vectors;
        int base_vector;
 
@@ -422,8 +421,9 @@ struct i40e_q_vector {
 
        u8 num_ringpairs;       /* total number of ring pairs in vector */
 
-       char name[IFNAMSIZ + 9];
        cpumask_t affinity_mask;
+       struct rcu_head rcu;    /* to avoid race with update stats on free */
+       char name[IFNAMSIZ + 9];
 } ____cacheline_internodealigned_in_smp;
 
 /* lan device */
@@ -544,6 +544,7 @@ static inline void i40e_dbg_init(void) {}
 static inline void i40e_dbg_exit(void) {}
 #endif /* CONFIG_DEBUG_FS*/
 void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector);
+void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf);
 int i40e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
 void i40e_vlan_stripping_disable(struct i40e_vsi *vsi);
 int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid);
index 0c524fa..cfef7fc 100644 (file)
@@ -701,8 +701,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
 
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
-               memcpy(details, cmd_details,
-                      sizeof(struct i40e_asq_cmd_details));
+               *details = *cmd_details;
 
                /* If the cmd_details are defined copy the cookie.  The
                 * cpu_to_le32 is not needed here because the data is ignored
@@ -760,7 +759,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
        desc_on_ring = I40E_ADMINQ_DESC(hw->aq.asq, hw->aq.asq.next_to_use);
 
        /* if the desc is available copy the temp desc to the right place */
-       memcpy(desc_on_ring, desc, sizeof(struct i40e_aq_desc));
+       *desc_on_ring = *desc;
 
        /* if buff is not NULL assume indirect command */
        if (buff != NULL) {
@@ -807,7 +806,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
 
        /* if ready, copy the desc back to temp */
        if (i40e_asq_done(hw)) {
-               memcpy(desc, desc_on_ring, sizeof(struct i40e_aq_desc));
+               *desc = *desc_on_ring;
                if (buff != NULL)
                        memcpy(buff, dma_buff->va, buff_size);
                retval = le16_to_cpu(desc->retval);
index c21df7b..1e4ea13 100644 (file)
@@ -507,7 +507,7 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw,
 
        /* save link status information */
        if (link)
-               memcpy(link, hw_link_info, sizeof(struct i40e_link_status));
+               *link = *hw_link_info;
 
        /* flag cleared so helper functions don't call AQ again */
        hw->phy.get_link_info = false;
index 8dbd91f..ef4cb1c 100644 (file)
@@ -151,9 +151,7 @@ static ssize_t i40e_dbg_dump_write(struct file *filp,
                                   size_t count, loff_t *ppos)
 {
        struct i40e_pf *pf = filp->private_data;
-       char dump_request_buf[16];
        bool seid_found = false;
-       int bytes_not_copied;
        long seid = -1;
        int buflen = 0;
        int i, ret;
@@ -163,21 +161,12 @@ static ssize_t i40e_dbg_dump_write(struct file *filp,
        /* don't allow partial writes */
        if (*ppos != 0)
                return 0;
-       if (count >= sizeof(dump_request_buf))
-               return -ENOSPC;
-
-       bytes_not_copied = copy_from_user(dump_request_buf, buffer, count);
-       if (bytes_not_copied < 0)
-               return bytes_not_copied;
-       if (bytes_not_copied > 0)
-               count -= bytes_not_copied;
-       dump_request_buf[count] = '\0';
 
        /* decode the SEID given to be dumped */
-       ret = kstrtol(dump_request_buf, 0, &seid);
-       if (ret < 0) {
-               dev_info(&pf->pdev->dev, "bad seid value '%s'\n",
-                        dump_request_buf);
+       ret = kstrtol_from_user(buffer, count, 0, &seid);
+
+       if (ret) {
+               dev_info(&pf->pdev->dev, "bad seid value\n");
        } else if (seid == 0) {
                seid_found = true;
 
@@ -245,26 +234,33 @@ static ssize_t i40e_dbg_dump_write(struct file *filp,
                        memcpy(p, vsi, len);
                        p += len;
 
-                       len = (sizeof(struct i40e_q_vector)
-                               * vsi->num_q_vectors);
-                       memcpy(p, vsi->q_vectors, len);
-                       p += len;
-
-                       len = (sizeof(struct i40e_ring) * vsi->num_queue_pairs);
-                       memcpy(p, vsi->tx_rings, len);
-                       p += len;
-                       memcpy(p, vsi->rx_rings, len);
-                       p += len;
+                       if (vsi->num_q_vectors) {
+                               len = (sizeof(struct i40e_q_vector)
+                                       * vsi->num_q_vectors);
+                               memcpy(p, vsi->q_vectors, len);
+                               p += len;
+                       }
 
-                       for (i = 0; i < vsi->num_queue_pairs; i++) {
-                               len = sizeof(struct i40e_tx_buffer);
-                               memcpy(p, vsi->tx_rings[i].tx_bi, len);
+                       if (vsi->num_queue_pairs) {
+                               len = (sizeof(struct i40e_ring) *
+                                     vsi->num_queue_pairs);
+                               memcpy(p, vsi->tx_rings, len);
+                               p += len;
+                               memcpy(p, vsi->rx_rings, len);
                                p += len;
                        }
-                       for (i = 0; i < vsi->num_queue_pairs; i++) {
+
+                       if (vsi->tx_rings[0]) {
+                               len = sizeof(struct i40e_tx_buffer);
+                               for (i = 0; i < vsi->num_queue_pairs; i++) {
+                                       memcpy(p, vsi->tx_rings[i]->tx_bi, len);
+                                       p += len;
+                               }
                                len = sizeof(struct i40e_rx_buffer);
-                               memcpy(p, vsi->rx_rings[i].rx_bi, len);
-                               p += len;
+                               for (i = 0; i < vsi->num_queue_pairs; i++) {
+                                       memcpy(p, vsi->rx_rings[i]->rx_bi, len);
+                                       p += len;
+                               }
                        }
 
                        /* macvlan filter list */
@@ -484,100 +480,104 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
                 "    tx_restart = %d, tx_busy = %d, rx_buf_failed = %d, rx_page_failed = %d\n",
                 vsi->tx_restart, vsi->tx_busy,
                 vsi->rx_buf_failed, vsi->rx_page_failed);
-       if (vsi->rx_rings) {
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: desc = %p\n",
-                                i, vsi->rx_rings[i].desc);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: dev = %p, netdev = %p, rx_bi = %p\n",
-                                i, vsi->rx_rings[i].dev,
-                                vsi->rx_rings[i].netdev,
-                                vsi->rx_rings[i].rx_bi);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n",
-                                i, vsi->rx_rings[i].state,
-                                vsi->rx_rings[i].queue_index,
-                                vsi->rx_rings[i].reg_idx);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: rx_hdr_len = %d, rx_buf_len = %d, dtype = %d\n",
-                                i, vsi->rx_rings[i].rx_hdr_len,
-                                vsi->rx_rings[i].rx_buf_len,
-                                vsi->rx_rings[i].dtype);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n",
-                                i, vsi->rx_rings[i].hsplit,
-                                vsi->rx_rings[i].next_to_use,
-                                vsi->rx_rings[i].next_to_clean,
-                                vsi->rx_rings[i].ring_active);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: rx_stats: packets = %lld, bytes = %lld, non_eop_descs = %lld\n",
-                                i, vsi->rx_rings[i].rx_stats.packets,
-                                vsi->rx_rings[i].rx_stats.bytes,
-                                vsi->rx_rings[i].rx_stats.non_eop_descs);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: rx_stats: alloc_rx_page_failed = %lld, alloc_rx_buff_failed = %lld\n",
-                                i,
-                                vsi->rx_rings[i].rx_stats.alloc_rx_page_failed,
-                               vsi->rx_rings[i].rx_stats.alloc_rx_buff_failed);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: size = %i, dma = 0x%08lx\n",
-                                i, vsi->rx_rings[i].size,
-                                (long unsigned int)vsi->rx_rings[i].dma);
-                       dev_info(&pf->pdev->dev,
-                                "    rx_rings[%i]: vsi = %p, q_vector = %p\n",
-                                i, vsi->rx_rings[i].vsi,
-                                vsi->rx_rings[i].q_vector);
-               }
+       rcu_read_lock();
+       for (i = 0; i < vsi->num_queue_pairs; i++) {
+               struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]);
+               if (!rx_ring)
+                       continue;
+
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: desc = %p\n",
+                        i, rx_ring->desc);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: dev = %p, netdev = %p, rx_bi = %p\n",
+                        i, rx_ring->dev,
+                        rx_ring->netdev,
+                        rx_ring->rx_bi);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n",
+                        i, rx_ring->state,
+                        rx_ring->queue_index,
+                        rx_ring->reg_idx);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: rx_hdr_len = %d, rx_buf_len = %d, dtype = %d\n",
+                        i, rx_ring->rx_hdr_len,
+                        rx_ring->rx_buf_len,
+                        rx_ring->dtype);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n",
+                        i, rx_ring->hsplit,
+                        rx_ring->next_to_use,
+                        rx_ring->next_to_clean,
+                        rx_ring->ring_active);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: rx_stats: packets = %lld, bytes = %lld, non_eop_descs = %lld\n",
+                        i, rx_ring->stats.packets,
+                        rx_ring->stats.bytes,
+                        rx_ring->rx_stats.non_eop_descs);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: rx_stats: alloc_rx_page_failed = %lld, alloc_rx_buff_failed = %lld\n",
+                        i,
+                        rx_ring->rx_stats.alloc_rx_page_failed,
+                       rx_ring->rx_stats.alloc_rx_buff_failed);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: size = %i, dma = 0x%08lx\n",
+                        i, rx_ring->size,
+                        (long unsigned int)rx_ring->dma);
+               dev_info(&pf->pdev->dev,
+                        "    rx_rings[%i]: vsi = %p, q_vector = %p\n",
+                        i, rx_ring->vsi,
+                        rx_ring->q_vector);
        }
-       if (vsi->tx_rings) {
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: desc = %p\n",
-                                i, vsi->tx_rings[i].desc);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: dev = %p, netdev = %p, tx_bi = %p\n",
-                                i, vsi->tx_rings[i].dev,
-                                vsi->tx_rings[i].netdev,
-                                vsi->tx_rings[i].tx_bi);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n",
-                                i, vsi->tx_rings[i].state,
-                                vsi->tx_rings[i].queue_index,
-                                vsi->tx_rings[i].reg_idx);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: dtype = %d\n",
-                                i, vsi->tx_rings[i].dtype);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n",
-                                i, vsi->tx_rings[i].hsplit,
-                                vsi->tx_rings[i].next_to_use,
-                                vsi->tx_rings[i].next_to_clean,
-                                vsi->tx_rings[i].ring_active);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: tx_stats: packets = %lld, bytes = %lld, restart_queue = %lld\n",
-                                i, vsi->tx_rings[i].tx_stats.packets,
-                                vsi->tx_rings[i].tx_stats.bytes,
-                                vsi->tx_rings[i].tx_stats.restart_queue);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: tx_stats: tx_busy = %lld, completed = %lld, tx_done_old = %lld\n",
-                                i,
-                                vsi->tx_rings[i].tx_stats.tx_busy,
-                                vsi->tx_rings[i].tx_stats.completed,
-                                vsi->tx_rings[i].tx_stats.tx_done_old);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: size = %i, dma = 0x%08lx\n",
-                                i, vsi->tx_rings[i].size,
-                                (long unsigned int)vsi->tx_rings[i].dma);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: vsi = %p, q_vector = %p\n",
-                                i, vsi->tx_rings[i].vsi,
-                                vsi->tx_rings[i].q_vector);
-                       dev_info(&pf->pdev->dev,
-                                "    tx_rings[%i]: DCB tc = %d\n",
-                                i, vsi->tx_rings[i].dcb_tc);
-               }
+       for (i = 0; i < vsi->num_queue_pairs; i++) {
+               struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]);
+               if (!tx_ring)
+                       continue;
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: desc = %p\n",
+                        i, tx_ring->desc);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: dev = %p, netdev = %p, tx_bi = %p\n",
+                        i, tx_ring->dev,
+                        tx_ring->netdev,
+                        tx_ring->tx_bi);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n",
+                        i, tx_ring->state,
+                        tx_ring->queue_index,
+                        tx_ring->reg_idx);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: dtype = %d\n",
+                        i, tx_ring->dtype);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n",
+                        i, tx_ring->hsplit,
+                        tx_ring->next_to_use,
+                        tx_ring->next_to_clean,
+                        tx_ring->ring_active);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: tx_stats: packets = %lld, bytes = %lld, restart_queue = %lld\n",
+                        i, tx_ring->stats.packets,
+                        tx_ring->stats.bytes,
+                        tx_ring->tx_stats.restart_queue);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: tx_stats: tx_busy = %lld, tx_done_old = %lld\n",
+                        i,
+                        tx_ring->tx_stats.tx_busy,
+                        tx_ring->tx_stats.tx_done_old);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: size = %i, dma = 0x%08lx\n",
+                        i, tx_ring->size,
+                        (long unsigned int)tx_ring->dma);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: vsi = %p, q_vector = %p\n",
+                        i, tx_ring->vsi,
+                        tx_ring->q_vector);
+               dev_info(&pf->pdev->dev,
+                        "    tx_rings[%i]: DCB tc = %d\n",
+                        i, tx_ring->dcb_tc);
        }
+       rcu_read_unlock();
        dev_info(&pf->pdev->dev,
                 "    work_limit = %d, rx_itr_setting = %d (%s), tx_itr_setting = %d (%s)\n",
                 vsi->work_limit, vsi->rx_itr_setting,
@@ -587,15 +587,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
        dev_info(&pf->pdev->dev,
                 "    max_frame = %d, rx_hdr_len = %d, rx_buf_len = %d dtype = %d\n",
                 vsi->max_frame, vsi->rx_hdr_len, vsi->rx_buf_len, vsi->dtype);
-       if (vsi->q_vectors) {
-               for (i = 0; i < vsi->num_q_vectors; i++) {
-                       dev_info(&pf->pdev->dev,
-                                "    q_vectors[%i]: base index = %ld\n",
-                                i, ((long int)*vsi->q_vectors[i].rx.ring-
-                                       (long int)*vsi->q_vectors[0].rx.ring)/
-                                       sizeof(struct i40e_ring));
-               }
-       }
        dev_info(&pf->pdev->dev,
                 "    num_q_vectors = %i, base_vector = %i\n",
                 vsi->num_q_vectors, vsi->base_vector);
@@ -792,9 +783,9 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n,
                return;
        }
        if (is_rx_ring)
-               ring = vsi->rx_rings[ring_id];
+               ring = *vsi->rx_rings[ring_id];
        else
-               ring = vsi->tx_rings[ring_id];
+               ring = *vsi->tx_rings[ring_id];
        if (cnt == 2) {
                dev_info(&pf->pdev->dev, "vsi = %02i %s ring = %02i\n",
                         vsi_seid, is_rx_ring ? "rx" : "tx", ring_id);
@@ -1028,11 +1019,11 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                                      size_t count, loff_t *ppos)
 {
        struct i40e_pf *pf = filp->private_data;
+       char *cmd_buf, *cmd_buf_tmp;
        int bytes_not_copied;
        struct i40e_vsi *vsi;
        u8 *print_buf_start;
        u8 *print_buf;
-       char *cmd_buf;
        int vsi_seid;
        int veb_seid;
        int cnt;
@@ -1051,6 +1042,12 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                count -= bytes_not_copied;
        cmd_buf[count] = '\0';
 
+       cmd_buf_tmp = strchr(cmd_buf, '\n');
+       if (cmd_buf_tmp) {
+               *cmd_buf_tmp = '\0';
+               count = cmd_buf_tmp - cmd_buf + 1;
+       }
+
        print_buf_start = kzalloc(I40E_MAX_DEBUG_OUT_BUFFER, GFP_KERNEL);
        if (!print_buf_start)
                goto command_write_done;
@@ -1157,9 +1154,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                i40e_veb_release(pf->veb[i]);
 
        } else if (strncmp(cmd_buf, "add macaddr", 11) == 0) {
-               u8 ma[6];
-               int vlan = 0;
                struct i40e_mac_filter *f;
+               int vlan = 0;
+               u8 ma[6];
                int ret;
 
                cnt = sscanf(&cmd_buf[11],
@@ -1195,8 +1192,8 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                                 ma, vlan, vsi_seid, f, ret);
 
        } else if (strncmp(cmd_buf, "del macaddr", 11) == 0) {
-               u8 ma[6];
                int vlan = 0;
+               u8 ma[6];
                int ret;
 
                cnt = sscanf(&cmd_buf[11],
@@ -1232,9 +1229,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                                 ma, vlan, vsi_seid, ret);
 
        } else if (strncmp(cmd_buf, "add pvid", 8) == 0) {
-               int v;
-               u16 vid;
                i40e_status ret;
+               u16 vid;
+               int v;
 
                cnt = sscanf(&cmd_buf[8], "%i %u", &vsi_seid, &v);
                if (cnt != 2) {
@@ -1545,10 +1542,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
        } else if ((strncmp(cmd_buf, "add fd_filter", 13) == 0) ||
                   (strncmp(cmd_buf, "rem fd_filter", 13) == 0)) {
                struct i40e_fdir_data fd_data;
-               int ret;
                u16 packet_len, i, j = 0;
                char *asc_packet;
                bool add = false;
+               int ret;
 
                asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
                                     GFP_KERNEL);
@@ -1636,9 +1633,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        }
                } else if (strncmp(&cmd_buf[5],
                           "get local", 9) == 0) {
+                       u16 llen, rlen;
                        int ret, i;
                        u8 *buff;
-                       u16 llen, rlen;
                        buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL);
                        if (!buff)
                                goto command_write_done;
@@ -1669,9 +1666,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        kfree(buff);
                        buff = NULL;
                } else if (strncmp(&cmd_buf[5], "get remote", 10) == 0) {
+                       u16 llen, rlen;
                        int ret, i;
                        u8 *buff;
-                       u16 llen, rlen;
                        buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL);
                        if (!buff)
                                goto command_write_done;
@@ -1747,11 +1744,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        goto command_write_done;
                }
 
-               /* Read at least 512 words */
-               if (buffer_len == 0)
-                       buffer_len = 512;
+               /* set the max length */
+               buffer_len = min_t(u16, buffer_len, I40E_MAX_AQ_BUF_SIZE/2);
 
                bytes = 2 * buffer_len;
+
+               /* read at least 1k bytes, no more than 4kB */
+               bytes = clamp(bytes, (u16)1024, (u16)I40E_MAX_AQ_BUF_SIZE);
                buff = kzalloc(bytes, GFP_KERNEL);
                if (!buff)
                        goto command_write_done;
@@ -1903,6 +1902,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
        struct i40e_pf *pf = filp->private_data;
        int bytes_not_copied;
        struct i40e_vsi *vsi;
+       char *buf_tmp;
        int vsi_seid;
        int i, cnt;
 
@@ -1921,6 +1921,12 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
                count -= bytes_not_copied;
        i40e_dbg_netdev_ops_buf[count] = '\0';
 
+       buf_tmp = strchr(i40e_dbg_netdev_ops_buf, '\n');
+       if (buf_tmp) {
+               *buf_tmp = '\0';
+               count = buf_tmp - i40e_dbg_netdev_ops_buf + 1;
+       }
+
        if (strncmp(i40e_dbg_netdev_ops_buf, "tx_timeout", 10) == 0) {
                cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i", &vsi_seid);
                if (cnt != 1) {
@@ -1996,7 +2002,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
                        goto netdev_ops_write_done;
                }
                for (i = 0; i < vsi->num_q_vectors; i++)
-                       napi_schedule(&vsi->q_vectors[i].napi);
+                       napi_schedule(&vsi->q_vectors[i]->napi);
                dev_info(&pf->pdev->dev, "napi called\n");
        } else {
                dev_info(&pf->pdev->dev, "unknown command '%s'\n",
@@ -2024,21 +2030,35 @@ static const struct file_operations i40e_dbg_netdev_ops_fops = {
  **/
 void i40e_dbg_pf_init(struct i40e_pf *pf)
 {
-       struct dentry *pfile __attribute__((unused));
+       struct dentry *pfile;
        const char *name = pci_name(pf->pdev);
+       const struct device *dev = &pf->pdev->dev;
 
        pf->i40e_dbg_pf = debugfs_create_dir(name, i40e_dbg_root);
-       if (pf->i40e_dbg_pf) {
-               pfile = debugfs_create_file("command", 0600, pf->i40e_dbg_pf,
-                                           pf, &i40e_dbg_command_fops);
-               pfile = debugfs_create_file("dump", 0600, pf->i40e_dbg_pf, pf,
-                                           &i40e_dbg_dump_fops);
-               pfile = debugfs_create_file("netdev_ops", 0600, pf->i40e_dbg_pf,
-                                           pf, &i40e_dbg_netdev_ops_fops);
-       } else {
-               dev_info(&pf->pdev->dev,
-                        "debugfs entry for %s failed\n", name);
-       }
+       if (!pf->i40e_dbg_pf)
+               return;
+
+       pfile = debugfs_create_file("command", 0600, pf->i40e_dbg_pf, pf,
+                                   &i40e_dbg_command_fops);
+       if (!pfile)
+               goto create_failed;
+
+       pfile = debugfs_create_file("dump", 0600, pf->i40e_dbg_pf, pf,
+                                   &i40e_dbg_dump_fops);
+       if (!pfile)
+               goto create_failed;
+
+       pfile = debugfs_create_file("netdev_ops", 0600, pf->i40e_dbg_pf, pf,
+                                   &i40e_dbg_netdev_ops_fops);
+       if (!pfile)
+               goto create_failed;
+
+       return;
+
+create_failed:
+       dev_info(dev, "debugfs dir/file for %s failed\n", name);
+       debugfs_remove_recursive(pf->i40e_dbg_pf);
+       return;
 }
 
 /**
index 9a76b8c..1b86138 100644 (file)
@@ -399,8 +399,8 @@ static void i40e_get_ringparam(struct net_device *netdev,
        ring->tx_max_pending = I40E_MAX_NUM_DESCRIPTORS;
        ring->rx_mini_max_pending = 0;
        ring->rx_jumbo_max_pending = 0;
-       ring->rx_pending = vsi->rx_rings[0].count;
-       ring->tx_pending = vsi->tx_rings[0].count;
+       ring->rx_pending = vsi->rx_rings[0]->count;
+       ring->tx_pending = vsi->tx_rings[0]->count;
        ring->rx_mini_pending = 0;
        ring->rx_jumbo_pending = 0;
 }
@@ -429,8 +429,8 @@ static int i40e_set_ringparam(struct net_device *netdev,
        new_rx_count = ALIGN(new_rx_count, I40E_REQ_DESCRIPTOR_MULTIPLE);
 
        /* if nothing to do return success */
-       if ((new_tx_count == vsi->tx_rings[0].count) &&
-           (new_rx_count == vsi->rx_rings[0].count))
+       if ((new_tx_count == vsi->tx_rings[0]->count) &&
+           (new_rx_count == vsi->rx_rings[0]->count))
                return 0;
 
        while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state))
@@ -439,8 +439,8 @@ static int i40e_set_ringparam(struct net_device *netdev,
        if (!netif_running(vsi->netdev)) {
                /* simple case - set for the next time the netdev is started */
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       vsi->tx_rings[i].count = new_tx_count;
-                       vsi->rx_rings[i].count = new_rx_count;
+                       vsi->tx_rings[i]->count = new_tx_count;
+                       vsi->rx_rings[i]->count = new_rx_count;
                }
                goto done;
        }
@@ -451,10 +451,10 @@ static int i40e_set_ringparam(struct net_device *netdev,
         */
 
        /* alloc updated Tx resources */
-       if (new_tx_count != vsi->tx_rings[0].count) {
+       if (new_tx_count != vsi->tx_rings[0]->count) {
                netdev_info(netdev,
                            "Changing Tx descriptor count from %d to %d.\n",
-                           vsi->tx_rings[0].count, new_tx_count);
+                           vsi->tx_rings[0]->count, new_tx_count);
                tx_rings = kcalloc(vsi->alloc_queue_pairs,
                                   sizeof(struct i40e_ring), GFP_KERNEL);
                if (!tx_rings) {
@@ -464,7 +464,7 @@ static int i40e_set_ringparam(struct net_device *netdev,
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
                        /* clone ring and setup updated count */
-                       tx_rings[i] = vsi->tx_rings[i];
+                       tx_rings[i] = *vsi->tx_rings[i];
                        tx_rings[i].count = new_tx_count;
                        err = i40e_setup_tx_descriptors(&tx_rings[i]);
                        if (err) {
@@ -481,10 +481,10 @@ static int i40e_set_ringparam(struct net_device *netdev,
        }
 
        /* alloc updated Rx resources */
-       if (new_rx_count != vsi->rx_rings[0].count) {
+       if (new_rx_count != vsi->rx_rings[0]->count) {
                netdev_info(netdev,
                            "Changing Rx descriptor count from %d to %d\n",
-                           vsi->rx_rings[0].count, new_rx_count);
+                           vsi->rx_rings[0]->count, new_rx_count);
                rx_rings = kcalloc(vsi->alloc_queue_pairs,
                                   sizeof(struct i40e_ring), GFP_KERNEL);
                if (!rx_rings) {
@@ -494,7 +494,7 @@ static int i40e_set_ringparam(struct net_device *netdev,
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
                        /* clone ring and setup updated count */
-                       rx_rings[i] = vsi->rx_rings[i];
+                       rx_rings[i] = *vsi->rx_rings[i];
                        rx_rings[i].count = new_rx_count;
                        err = i40e_setup_rx_descriptors(&rx_rings[i]);
                        if (err) {
@@ -517,8 +517,8 @@ static int i40e_set_ringparam(struct net_device *netdev,
 
        if (tx_rings) {
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       i40e_free_tx_resources(&vsi->tx_rings[i]);
-                       vsi->tx_rings[i] = tx_rings[i];
+                       i40e_free_tx_resources(vsi->tx_rings[i]);
+                       *vsi->tx_rings[i] = tx_rings[i];
                }
                kfree(tx_rings);
                tx_rings = NULL;
@@ -526,8 +526,8 @@ static int i40e_set_ringparam(struct net_device *netdev,
 
        if (rx_rings) {
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       i40e_free_rx_resources(&vsi->rx_rings[i]);
-                       vsi->rx_rings[i] = rx_rings[i];
+                       i40e_free_rx_resources(vsi->rx_rings[i]);
+                       *vsi->rx_rings[i] = rx_rings[i];
                }
                kfree(rx_rings);
                rx_rings = NULL;
@@ -579,6 +579,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
        char *p;
        int j;
        struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi);
+       unsigned int start;
 
        i40e_update_stats(vsi);
 
@@ -587,14 +588,30 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat ==
                        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
-       for (j = 0; j < vsi->num_queue_pairs; j++) {
-               data[i++] = vsi->tx_rings[j].tx_stats.packets;
-               data[i++] = vsi->tx_rings[j].tx_stats.bytes;
-       }
-       for (j = 0; j < vsi->num_queue_pairs; j++) {
-               data[i++] = vsi->rx_rings[j].rx_stats.packets;
-               data[i++] = vsi->rx_rings[j].rx_stats.bytes;
+       rcu_read_lock();
+       for (j = 0; j < vsi->num_queue_pairs; j++, i += 4) {
+               struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[j]);
+               struct i40e_ring *rx_ring;
+
+               if (!tx_ring)
+                       continue;
+
+               /* process Tx ring statistics */
+               do {
+                       start = u64_stats_fetch_begin_bh(&tx_ring->syncp);
+                       data[i] = tx_ring->stats.packets;
+                       data[i + 1] = tx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start));
+
+               /* Rx ring is the 2nd half of the queue pair */
+               rx_ring = &tx_ring[1];
+               do {
+                       start = u64_stats_fetch_begin_bh(&rx_ring->syncp);
+                       data[i + 2] = rx_ring->stats.packets;
+                       data[i + 3] = rx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start));
        }
+       rcu_read_unlock();
        if (vsi == pf->vsi[pf->lan_vsi]) {
                for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
                        p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
@@ -641,8 +658,6 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
                        p += ETH_GSTRING_LEN;
                        snprintf(p, ETH_GSTRING_LEN, "tx-%u.tx_bytes", i);
                        p += ETH_GSTRING_LEN;
-               }
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
                        snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_packets", i);
                        p += ETH_GSTRING_LEN;
                        snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i);
@@ -910,8 +925,8 @@ static int i40e_set_coalesce(struct net_device *netdev,
        }
 
        vector = vsi->base_vector;
-       q_vector = vsi->q_vectors;
-       for (i = 0; i < vsi->num_q_vectors; i++, vector++, q_vector++) {
+       for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
+               q_vector = vsi->q_vectors[i];
                q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
                wr32(hw, I40E_PFINT_ITRN(0, vector - 1), q_vector->rx.itr);
                q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
index 601d482..be15938 100644 (file)
@@ -36,7 +36,7 @@ static const char i40e_driver_string[] =
 
 #define DRV_VERSION_MAJOR 0
 #define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 9
+#define DRV_VERSION_BUILD 11
 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
             __stringify(DRV_VERSION_MINOR) "." \
             __stringify(DRV_VERSION_BUILD)    DRV_KERN
@@ -101,10 +101,10 @@ int i40e_allocate_dma_mem_d(struct i40e_hw *hw, struct i40e_dma_mem *mem,
        mem->size = ALIGN(size, alignment);
        mem->va = dma_zalloc_coherent(&pf->pdev->dev, mem->size,
                                      &mem->pa, GFP_KERNEL);
-       if (mem->va)
-               return 0;
+       if (!mem->va)
+               return -ENOMEM;
 
-       return -ENOMEM;
+       return 0;
 }
 
 /**
@@ -136,10 +136,10 @@ int i40e_allocate_virt_mem_d(struct i40e_hw *hw, struct i40e_virt_mem *mem,
        mem->size = size;
        mem->va = kzalloc(size, GFP_KERNEL);
 
-       if (mem->va)
-               return 0;
+       if (!mem->va)
+               return -ENOMEM;
 
-       return -ENOMEM;
+       return 0;
 }
 
 /**
@@ -174,8 +174,7 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile,
                         u16 needed, u16 id)
 {
        int ret = -ENOMEM;
-       int i = 0;
-       int j = 0;
+       int i, j;
 
        if (!pile || needed == 0 || id >= I40E_PILE_VALID_BIT) {
                dev_info(&pf->pdev->dev,
@@ -186,7 +185,7 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile,
 
        /* start the linear search with an imperfect hint */
        i = pile->search_hint;
-       while (i < pile->num_entries && ret < 0) {
+       while (i < pile->num_entries) {
                /* skip already allocated entries */
                if (pile->list[i] & I40E_PILE_VALID_BIT) {
                        i++;
@@ -205,6 +204,7 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile,
                                pile->list[i+j] = id | I40E_PILE_VALID_BIT;
                        ret = i;
                        pile->search_hint = i + j;
+                       break;
                } else {
                        /* not enough, so skip over it and continue looking */
                        i += j;
@@ -347,14 +347,53 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi)
  **/
 static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
                                             struct net_device *netdev,
-                                            struct rtnl_link_stats64 *storage)
+                                            struct rtnl_link_stats64 *stats)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
+       struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
+       int i;
+
+       rcu_read_lock();
+       for (i = 0; i < vsi->num_queue_pairs; i++) {
+               struct i40e_ring *tx_ring, *rx_ring;
+               u64 bytes, packets;
+               unsigned int start;
 
-       *storage = *i40e_get_vsi_stats_struct(vsi);
+               tx_ring = ACCESS_ONCE(vsi->tx_rings[i]);
+               if (!tx_ring)
+                       continue;
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&tx_ring->syncp);
+                       packets = tx_ring->stats.packets;
+                       bytes   = tx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start));
+
+               stats->tx_packets += packets;
+               stats->tx_bytes   += bytes;
+               rx_ring = &tx_ring[1];
 
-       return storage;
+               do {
+                       start = u64_stats_fetch_begin_bh(&rx_ring->syncp);
+                       packets = rx_ring->stats.packets;
+                       bytes   = rx_ring->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start));
+
+               stats->rx_packets += packets;
+               stats->rx_bytes   += bytes;
+       }
+       rcu_read_unlock();
+
+       /* following stats updated by ixgbe_watchdog_task() */
+       stats->multicast        = vsi_stats->multicast;
+       stats->tx_errors        = vsi_stats->tx_errors;
+       stats->tx_dropped       = vsi_stats->tx_dropped;
+       stats->rx_errors        = vsi_stats->rx_errors;
+       stats->rx_crc_errors    = vsi_stats->rx_crc_errors;
+       stats->rx_length_errors = vsi_stats->rx_length_errors;
+
+       return stats;
 }
 
 /**
@@ -376,10 +415,14 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi)
        memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets));
        if (vsi->rx_rings)
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       memset(&vsi->rx_rings[i].rx_stats, 0 ,
-                              sizeof(vsi->rx_rings[i].rx_stats));
-                       memset(&vsi->tx_rings[i].tx_stats, 0,
-                              sizeof(vsi->tx_rings[i].tx_stats));
+                       memset(&vsi->rx_rings[i]->stats, 0 ,
+                              sizeof(vsi->rx_rings[i]->stats));
+                       memset(&vsi->rx_rings[i]->rx_stats, 0 ,
+                              sizeof(vsi->rx_rings[i]->rx_stats));
+                       memset(&vsi->tx_rings[i]->stats, 0 ,
+                              sizeof(vsi->tx_rings[i]->stats));
+                       memset(&vsi->tx_rings[i]->tx_stats, 0,
+                              sizeof(vsi->tx_rings[i]->tx_stats));
                }
        vsi->stat_offsets_loaded = false;
 }
@@ -598,7 +641,7 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       struct i40e_ring *ring = &vsi->tx_rings[i];
+                       struct i40e_ring *ring = vsi->tx_rings[i];
                        clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state);
                }
        }
@@ -652,7 +695,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       struct i40e_ring *ring = &vsi->tx_rings[i];
+                       struct i40e_ring *ring = vsi->tx_rings[i];
 
                        tc = ring->dcb_tc;
                        if (xoff[tc])
@@ -704,21 +747,38 @@ void i40e_update_stats(struct i40e_vsi *vsi)
        tx_restart = tx_busy = 0;
        rx_page = 0;
        rx_buf = 0;
+       rcu_read_lock();
        for (q = 0; q < vsi->num_queue_pairs; q++) {
                struct i40e_ring *p;
+               u64 bytes, packets;
+               unsigned int start;
 
-               p = &vsi->rx_rings[q];
-               rx_b += p->rx_stats.bytes;
-               rx_p += p->rx_stats.packets;
-               rx_buf += p->rx_stats.alloc_rx_buff_failed;
-               rx_page += p->rx_stats.alloc_rx_page_failed;
+               /* locate Tx ring */
+               p = ACCESS_ONCE(vsi->tx_rings[q]);
 
-               p = &vsi->tx_rings[q];
-               tx_b += p->tx_stats.bytes;
-               tx_p += p->tx_stats.packets;
+               do {
+                       start = u64_stats_fetch_begin_bh(&p->syncp);
+                       packets = p->stats.packets;
+                       bytes = p->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+               tx_b += bytes;
+               tx_p += packets;
                tx_restart += p->tx_stats.restart_queue;
                tx_busy += p->tx_stats.tx_busy;
+
+               /* Rx queue is part of the same block as Tx queue */
+               p = &p[1];
+               do {
+                       start = u64_stats_fetch_begin_bh(&p->syncp);
+                       packets = p->stats.packets;
+                       bytes = p->stats.bytes;
+               } while (u64_stats_fetch_retry_bh(&p->syncp, start));
+               rx_b += bytes;
+               rx_p += packets;
+               rx_buf += p->rx_stats.alloc_rx_buff_failed;
+               rx_page += p->rx_stats.alloc_rx_page_failed;
        }
+       rcu_read_unlock();
        vsi->tx_restart = tx_restart;
        vsi->tx_busy = tx_busy;
        vsi->rx_page_failed = rx_page;
@@ -1388,7 +1448,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
        bool add_happened = false;
        int filter_list_len = 0;
        u32 changed_flags = 0;
-       i40e_status ret = 0;
+       i40e_status aq_ret = 0;
        struct i40e_pf *pf;
        int num_add = 0;
        int num_del = 0;
@@ -1449,28 +1509,28 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
 
                        /* flush a full buffer */
                        if (num_del == filter_list_len) {
-                               ret = i40e_aq_remove_macvlan(&pf->hw,
+                               aq_ret = i40e_aq_remove_macvlan(&pf->hw,
                                            vsi->seid, del_list, num_del,
                                            NULL);
                                num_del = 0;
                                memset(del_list, 0, sizeof(*del_list));
 
-                               if (ret)
+                               if (aq_ret)
                                        dev_info(&pf->pdev->dev,
                                                 "ignoring delete macvlan error, err %d, aq_err %d while flushing a full buffer\n",
-                                                ret,
+                                                aq_ret,
                                                 pf->hw.aq.asq_last_status);
                        }
                }
                if (num_del) {
-                       ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
+                       aq_ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
                                                     del_list, num_del, NULL);
                        num_del = 0;
 
-                       if (ret)
+                       if (aq_ret)
                                dev_info(&pf->pdev->dev,
                                         "ignoring delete macvlan error, err %d, aq_err %d\n",
-                                        ret, pf->hw.aq.asq_last_status);
+                                        aq_ret, pf->hw.aq.asq_last_status);
                }
 
                kfree(del_list);
@@ -1515,32 +1575,30 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
 
                        /* flush a full buffer */
                        if (num_add == filter_list_len) {
-                               ret = i40e_aq_add_macvlan(&pf->hw,
-                                                         vsi->seid,
-                                                         add_list,
-                                                         num_add,
-                                                         NULL);
+                               aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
+                                                            add_list, num_add,
+                                                            NULL);
                                num_add = 0;
 
-                               if (ret)
+                               if (aq_ret)
                                        break;
                                memset(add_list, 0, sizeof(*add_list));
                        }
                }
                if (num_add) {
-                       ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
-                                                 add_list, num_add, NULL);
+                       aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
+                                                    add_list, num_add, NULL);
                        num_add = 0;
                }
                kfree(add_list);
                add_list = NULL;
 
-               if (add_happened && (!ret)) {
+               if (add_happened && (!aq_ret)) {
                        /* do nothing */;
-               } else if (add_happened && (ret)) {
+               } else if (add_happened && (aq_ret)) {
                        dev_info(&pf->pdev->dev,
                                 "add filter failed, err %d, aq_err %d\n",
-                                ret, pf->hw.aq.asq_last_status);
+                                aq_ret, pf->hw.aq.asq_last_status);
                        if ((pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOSPC) &&
                            !test_bit(__I40E_FILTER_OVERFLOW_PROMISC,
                                      &vsi->state)) {
@@ -1556,28 +1614,27 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
        if (changed_flags & IFF_ALLMULTI) {
                bool cur_multipromisc;
                cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI);
-               ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
-                                                           vsi->seid,
-                                                           cur_multipromisc,
-                                                           NULL);
-               if (ret)
+               aq_ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
+                                                              vsi->seid,
+                                                              cur_multipromisc,
+                                                              NULL);
+               if (aq_ret)
                        dev_info(&pf->pdev->dev,
                                 "set multi promisc failed, err %d, aq_err %d\n",
-                                ret, pf->hw.aq.asq_last_status);
+                                aq_ret, pf->hw.aq.asq_last_status);
        }
        if ((changed_flags & IFF_PROMISC) || promisc_forced_on) {
                bool cur_promisc;
                cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) ||
                               test_bit(__I40E_FILTER_OVERFLOW_PROMISC,
                                        &vsi->state));
-               ret = i40e_aq_set_vsi_unicast_promiscuous(&vsi->back->hw,
-                                                         vsi->seid,
-                                                         cur_promisc,
-                                                         NULL);
-               if (ret)
+               aq_ret = i40e_aq_set_vsi_unicast_promiscuous(&vsi->back->hw,
+                                                            vsi->seid,
+                                                            cur_promisc, NULL);
+               if (aq_ret)
                        dev_info(&pf->pdev->dev,
                                 "set uni promisc failed, err %d, aq_err %d\n",
-                                ret, pf->hw.aq.asq_last_status);
+                                aq_ret, pf->hw.aq.asq_last_status);
        }
 
        clear_bit(__I40E_CONFIG_BUSY, &vsi->state);
@@ -1790,6 +1847,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
  * i40e_vsi_kill_vlan - Remove vsi membership for given vlan
  * @vsi: the vsi being configured
  * @vid: vlan id to be removed (0 = untagged only , -1 = any)
+ *
+ * Return: 0 on success or negative otherwise
  **/
 int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
 {
@@ -1863,37 +1922,39 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
  * i40e_vlan_rx_add_vid - Add a vlan id filter to HW offload
  * @netdev: network interface to be adjusted
  * @vid: vlan id to be added
+ *
+ * net_device_ops implementation for adding vlan ids
  **/
 static int i40e_vlan_rx_add_vid(struct net_device *netdev,
                                __always_unused __be16 proto, u16 vid)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
-       int ret;
+       int ret = 0;
 
        if (vid > 4095)
-               return 0;
+               return -EINVAL;
+
+       netdev_info(netdev, "adding %pM vid=%d\n", netdev->dev_addr, vid);
 
-       netdev_info(vsi->netdev, "adding %pM vid=%d\n",
-                   netdev->dev_addr, vid);
        /* If the network stack called us with vid = 0, we should
         * indicate to i40e_vsi_add_vlan() that we want to receive
         * any traffic (i.e. with any vlan tag, or untagged)
         */
        ret = i40e_vsi_add_vlan(vsi, vid ? vid : I40E_VLAN_ANY);
 
-       if (!ret) {
-               if (vid < VLAN_N_VID)
-                       set_bit(vid, vsi->active_vlans);
-       }
+       if (!ret && (vid < VLAN_N_VID))
+               set_bit(vid, vsi->active_vlans);
 
-       return 0;
+       return ret;
 }
 
 /**
  * i40e_vlan_rx_kill_vid - Remove a vlan id filter from HW offload
  * @netdev: network interface to be adjusted
  * @vid: vlan id to be removed
+ *
+ * net_device_ops implementation for adding vlan ids
  **/
 static int i40e_vlan_rx_kill_vid(struct net_device *netdev,
                                 __always_unused __be16 proto, u16 vid)
@@ -1901,15 +1962,16 @@ static int i40e_vlan_rx_kill_vid(struct net_device *netdev,
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
 
-       netdev_info(vsi->netdev, "removing %pM vid=%d\n",
-                   netdev->dev_addr, vid);
+       netdev_info(netdev, "removing %pM vid=%d\n", netdev->dev_addr, vid);
+
        /* return code is ignored as there is nothing a user
         * can do about failure to remove and a log message was
-        * already printed from another function
+        * already printed from the other function
         */
        i40e_vsi_kill_vlan(vsi, vid);
 
        clear_bit(vid, vsi->active_vlans);
+
        return 0;
 }
 
@@ -1936,10 +1998,10 @@ static void i40e_restore_vlan(struct i40e_vsi *vsi)
  * @vsi: the vsi being adjusted
  * @vid: the vlan id to set as a PVID
  **/
-i40e_status i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid)
+int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid)
 {
        struct i40e_vsi_context ctxt;
-       i40e_status ret;
+       i40e_status aq_ret;
 
        vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID);
        vsi->info.pvid = cpu_to_le16(vid);
@@ -1948,14 +2010,15 @@ i40e_status i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid)
 
        ctxt.seid = vsi->seid;
        memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
-       ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL);
-       if (ret) {
+       aq_ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL);
+       if (aq_ret) {
                dev_info(&vsi->back->pdev->dev,
                         "%s: update vsi failed, aq_err=%d\n",
                         __func__, vsi->back->hw.aq.asq_last_status);
+               return -ENOENT;
        }
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -1985,7 +2048,7 @@ static int i40e_vsi_setup_tx_resources(struct i40e_vsi *vsi)
        int i, err = 0;
 
        for (i = 0; i < vsi->num_queue_pairs && !err; i++)
-               err = i40e_setup_tx_descriptors(&vsi->tx_rings[i]);
+               err = i40e_setup_tx_descriptors(vsi->tx_rings[i]);
 
        return err;
 }
@@ -2001,8 +2064,8 @@ static void i40e_vsi_free_tx_resources(struct i40e_vsi *vsi)
        int i;
 
        for (i = 0; i < vsi->num_queue_pairs; i++)
-               if (vsi->tx_rings[i].desc)
-                       i40e_free_tx_resources(&vsi->tx_rings[i]);
+               if (vsi->tx_rings[i]->desc)
+                       i40e_free_tx_resources(vsi->tx_rings[i]);
 }
 
 /**
@@ -2020,7 +2083,7 @@ static int i40e_vsi_setup_rx_resources(struct i40e_vsi *vsi)
        int i, err = 0;
 
        for (i = 0; i < vsi->num_queue_pairs && !err; i++)
-               err = i40e_setup_rx_descriptors(&vsi->rx_rings[i]);
+               err = i40e_setup_rx_descriptors(vsi->rx_rings[i]);
        return err;
 }
 
@@ -2035,8 +2098,8 @@ static void i40e_vsi_free_rx_resources(struct i40e_vsi *vsi)
        int i;
 
        for (i = 0; i < vsi->num_queue_pairs; i++)
-               if (vsi->rx_rings[i].desc)
-                       i40e_free_rx_resources(&vsi->rx_rings[i]);
+               if (vsi->rx_rings[i]->desc)
+                       i40e_free_rx_resources(vsi->rx_rings[i]);
 }
 
 /**
@@ -2111,8 +2174,8 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring)
 
        /* Now associate this queue with this PCI function */
        qtx_ctl = I40E_QTX_CTL_PF_QUEUE;
-       qtx_ctl |= ((hw->hmc.hmc_fn_id << I40E_QTX_CTL_PF_INDX_SHIFT)
-                                               & I40E_QTX_CTL_PF_INDX_MASK);
+       qtx_ctl |= ((hw->pf_id << I40E_QTX_CTL_PF_INDX_SHIFT) &
+                   I40E_QTX_CTL_PF_INDX_MASK);
        wr32(hw, I40E_QTX_CTL(pf_q), qtx_ctl);
        i40e_flush(hw);
 
@@ -2220,8 +2283,8 @@ static int i40e_vsi_configure_tx(struct i40e_vsi *vsi)
        int err = 0;
        u16 i;
 
-       for (i = 0; (i < vsi->num_queue_pairs) && (!err); i++)
-               err = i40e_configure_tx_ring(&vsi->tx_rings[i]);
+       for (i = 0; (i < vsi->num_queue_pairs) && !err; i++)
+               err = i40e_configure_tx_ring(vsi->tx_rings[i]);
 
        return err;
 }
@@ -2271,7 +2334,7 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi)
 
        /* set up individual rings */
        for (i = 0; i < vsi->num_queue_pairs && !err; i++)
-               err = i40e_configure_rx_ring(&vsi->rx_rings[i]);
+               err = i40e_configure_rx_ring(vsi->rx_rings[i]);
 
        return err;
 }
@@ -2295,8 +2358,8 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi)
                qoffset = vsi->tc_config.tc_info[n].qoffset;
                qcount = vsi->tc_config.tc_info[n].qcount;
                for (i = qoffset; i < (qoffset + qcount); i++) {
-                       struct i40e_ring *rx_ring = &vsi->rx_rings[i];
-                       struct i40e_ring *tx_ring = &vsi->tx_rings[i];
+                       struct i40e_ring *rx_ring = vsi->rx_rings[i];
+                       struct i40e_ring *tx_ring = vsi->tx_rings[i];
                        rx_ring->dcb_tc = n;
                        tx_ring->dcb_tc = n;
                }
@@ -2351,8 +2414,8 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi)
         */
        qp = vsi->base_queue;
        vector = vsi->base_vector;
-       q_vector = vsi->q_vectors;
-       for (i = 0; i < vsi->num_q_vectors; i++, q_vector++, vector++) {
+       for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
+               q_vector = vsi->q_vectors[i];
                q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
                q_vector->rx.latency_range = I40E_LOW_LATENCY;
                wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1),
@@ -2432,7 +2495,7 @@ static void i40e_enable_misc_int_causes(struct i40e_hw *hw)
  **/
 static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi)
 {
-       struct i40e_q_vector *q_vector = vsi->q_vectors;
+       struct i40e_q_vector *q_vector = vsi->q_vectors[0];
        struct i40e_pf *pf = vsi->back;
        struct i40e_hw *hw = &pf->hw;
        u32 val;
@@ -2469,7 +2532,7 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi)
  * i40e_irq_dynamic_enable_icr0 - Enable default interrupt generation for icr0
  * @pf: board private structure
  **/
-static void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf)
+void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf)
 {
        struct i40e_hw *hw = &pf->hw;
        u32 val;
@@ -2497,7 +2560,7 @@ void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector)
              I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
              (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
        wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val);
-       i40e_flush(hw);
+       /* skip the flush */
 }
 
 /**
@@ -2509,7 +2572,7 @@ static irqreturn_t i40e_msix_clean_rings(int irq, void *data)
 {
        struct i40e_q_vector *q_vector = data;
 
-       if (!q_vector->tx.ring[0] && !q_vector->rx.ring[0])
+       if (!q_vector->tx.ring && !q_vector->rx.ring)
                return IRQ_HANDLED;
 
        napi_schedule(&q_vector->napi);
@@ -2526,7 +2589,7 @@ static irqreturn_t i40e_fdir_clean_rings(int irq, void *data)
 {
        struct i40e_q_vector *q_vector = data;
 
-       if (!q_vector->tx.ring[0] && !q_vector->rx.ring[0])
+       if (!q_vector->tx.ring && !q_vector->rx.ring)
                return IRQ_HANDLED;
 
        pr_info("fdir ring cleaning needed\n");
@@ -2551,16 +2614,16 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename)
        int vector, err;
 
        for (vector = 0; vector < q_vectors; vector++) {
-               struct i40e_q_vector *q_vector = &(vsi->q_vectors[vector]);
+               struct i40e_q_vector *q_vector = vsi->q_vectors[vector];
 
-               if (q_vector->tx.ring[0] && q_vector->rx.ring[0]) {
+               if (q_vector->tx.ring && q_vector->rx.ring) {
                        snprintf(q_vector->name, sizeof(q_vector->name) - 1,
                                 "%s-%s-%d", basename, "TxRx", rx_int_idx++);
                        tx_int_idx++;
-               } else if (q_vector->rx.ring[0]) {
+               } else if (q_vector->rx.ring) {
                        snprintf(q_vector->name, sizeof(q_vector->name) - 1,
                                 "%s-%s-%d", basename, "rx", rx_int_idx++);
-               } else if (q_vector->tx.ring[0]) {
+               } else if (q_vector->tx.ring) {
                        snprintf(q_vector->name, sizeof(q_vector->name) - 1,
                                 "%s-%s-%d", basename, "tx", tx_int_idx++);
                } else {
@@ -2608,8 +2671,8 @@ static void i40e_vsi_disable_irq(struct i40e_vsi *vsi)
        int i;
 
        for (i = 0; i < vsi->num_queue_pairs; i++) {
-               wr32(hw, I40E_QINT_TQCTL(vsi->tx_rings[i].reg_idx), 0);
-               wr32(hw, I40E_QINT_RQCTL(vsi->rx_rings[i].reg_idx), 0);
+               wr32(hw, I40E_QINT_TQCTL(vsi->tx_rings[i]->reg_idx), 0);
+               wr32(hw, I40E_QINT_RQCTL(vsi->rx_rings[i]->reg_idx), 0);
        }
 
        if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
@@ -2646,6 +2709,7 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi)
                i40e_irq_dynamic_enable_icr0(pf);
        }
 
+       i40e_flush(&pf->hw);
        return 0;
 }
 
@@ -2678,14 +2742,14 @@ static irqreturn_t i40e_intr(int irq, void *data)
 
        icr0 = rd32(hw, I40E_PFINT_ICR0);
 
-       /* if sharing a legacy IRQ, we might get called w/o an intr pending */
-       if ((icr0 & I40E_PFINT_ICR0_INTEVENT_MASK) == 0)
-               return IRQ_NONE;
-
        val = rd32(hw, I40E_PFINT_DYN_CTL0);
        val = val | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK;
        wr32(hw, I40E_PFINT_DYN_CTL0, val);
 
+       /* if sharing a legacy IRQ, we might get called w/o an intr pending */
+       if ((icr0 & I40E_PFINT_ICR0_INTEVENT_MASK) == 0)
+               return IRQ_NONE;
+
        ena_mask = rd32(hw, I40E_PFINT_ICR0_ENA);
 
        /* only q0 is used in MSI/Legacy mode, and none are used in MSIX */
@@ -2699,10 +2763,9 @@ static irqreturn_t i40e_intr(int irq, void *data)
                qval = rd32(hw, I40E_QINT_TQCTL(0));
                qval &= ~I40E_QINT_TQCTL_CAUSE_ENA_MASK;
                wr32(hw, I40E_QINT_TQCTL(0), qval);
-               i40e_flush(hw);
 
                if (!test_bit(__I40E_DOWN, &pf->state))
-                       napi_schedule(&pf->vsi[pf->lan_vsi]->q_vectors[0].napi);
+                       napi_schedule(&pf->vsi[pf->lan_vsi]->q_vectors[0]->napi);
        }
 
        if (icr0 & I40E_PFINT_ICR0_ADMINQ_MASK) {
@@ -2761,7 +2824,6 @@ static irqreturn_t i40e_intr(int irq, void *data)
 
        /* re-enable interrupt causes */
        wr32(hw, I40E_PFINT_ICR0_ENA, ena_mask);
-       i40e_flush(hw);
        if (!test_bit(__I40E_DOWN, &pf->state)) {
                i40e_service_event_schedule(pf);
                i40e_irq_dynamic_enable_icr0(pf);
@@ -2771,40 +2833,26 @@ static irqreturn_t i40e_intr(int irq, void *data)
 }
 
 /**
- * i40e_map_vector_to_rxq - Assigns the Rx queue to the vector
- * @vsi: the VSI being configured
- * @v_idx: vector index
- * @r_idx: rx queue index
- **/
-static void map_vector_to_rxq(struct i40e_vsi *vsi, int v_idx, int r_idx)
-{
-       struct i40e_q_vector *q_vector = &(vsi->q_vectors[v_idx]);
-       struct i40e_ring *rx_ring = &(vsi->rx_rings[r_idx]);
-
-       rx_ring->q_vector = q_vector;
-       q_vector->rx.ring[q_vector->rx.count] = rx_ring;
-       q_vector->rx.count++;
-       q_vector->rx.latency_range = I40E_LOW_LATENCY;
-       q_vector->vsi = vsi;
-}
-
-/**
- * i40e_map_vector_to_txq - Assigns the Tx queue to the vector
+ * i40e_map_vector_to_qp - Assigns the queue pair to the vector
  * @vsi: the VSI being configured
  * @v_idx: vector index
- * @t_idx: tx queue index
+ * @qp_idx: queue pair index
  **/
-static void map_vector_to_txq(struct i40e_vsi *vsi, int v_idx, int t_idx)
+static void map_vector_to_qp(struct i40e_vsi *vsi, int v_idx, int qp_idx)
 {
-       struct i40e_q_vector *q_vector = &(vsi->q_vectors[v_idx]);
-       struct i40e_ring *tx_ring = &(vsi->tx_rings[t_idx]);
+       struct i40e_q_vector *q_vector = vsi->q_vectors[v_idx];
+       struct i40e_ring *tx_ring = vsi->tx_rings[qp_idx];
+       struct i40e_ring *rx_ring = vsi->rx_rings[qp_idx];
 
        tx_ring->q_vector = q_vector;
-       q_vector->tx.ring[q_vector->tx.count] = tx_ring;
+       tx_ring->next = q_vector->tx.ring;
+       q_vector->tx.ring = tx_ring;
        q_vector->tx.count++;
-       q_vector->tx.latency_range = I40E_LOW_LATENCY;
-       q_vector->num_ringpairs++;
-       q_vector->vsi = vsi;
+
+       rx_ring->q_vector = q_vector;
+       rx_ring->next = q_vector->rx.ring;
+       q_vector->rx.ring = rx_ring;
+       q_vector->rx.count++;
 }
 
 /**
@@ -2820,7 +2868,7 @@ static void i40e_vsi_map_rings_to_vectors(struct i40e_vsi *vsi)
 {
        int qp_remaining = vsi->num_queue_pairs;
        int q_vectors = vsi->num_q_vectors;
-       int qp_per_vector;
+       int num_ringpairs;
        int v_start = 0;
        int qp_idx = 0;
 
@@ -2828,11 +2876,21 @@ static void i40e_vsi_map_rings_to_vectors(struct i40e_vsi *vsi)
         * group them so there are multiple queues per vector.
         */
        for (; v_start < q_vectors && qp_remaining; v_start++) {
-               qp_per_vector = DIV_ROUND_UP(qp_remaining, q_vectors - v_start);
-               for (; qp_per_vector;
-                    qp_per_vector--, qp_idx++, qp_remaining--) {
-                       map_vector_to_rxq(vsi, v_start, qp_idx);
-                       map_vector_to_txq(vsi, v_start, qp_idx);
+               struct i40e_q_vector *q_vector = vsi->q_vectors[v_start];
+
+               num_ringpairs = DIV_ROUND_UP(qp_remaining, q_vectors - v_start);
+
+               q_vector->num_ringpairs = num_ringpairs;
+
+               q_vector->rx.count = 0;
+               q_vector->tx.count = 0;
+               q_vector->rx.ring = NULL;
+               q_vector->tx.ring = NULL;
+
+               while (num_ringpairs--) {
+                       map_vector_to_qp(vsi, v_start, qp_idx);
+                       qp_idx++;
+                       qp_remaining--;
                }
        }
 }
@@ -2884,7 +2942,7 @@ static void i40e_netpoll(struct net_device *netdev)
        pf->flags |= I40E_FLAG_IN_NETPOLL;
        if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
                for (i = 0; i < vsi->num_q_vectors; i++)
-                       i40e_msix_clean_rings(0, &vsi->q_vectors[i]);
+                       i40e_msix_clean_rings(0, vsi->q_vectors[i]);
        } else {
                i40e_intr(pf->pdev->irq, netdev);
        }
@@ -3070,14 +3128,14 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi)
                        u16 vector = i + base;
 
                        /* free only the irqs that were actually requested */
-                       if (vsi->q_vectors[i].num_ringpairs == 0)
+                       if (vsi->q_vectors[i]->num_ringpairs == 0)
                                continue;
 
                        /* clear the affinity_mask in the IRQ descriptor */
                        irq_set_affinity_hint(pf->msix_entries[vector].vector,
                                              NULL);
                        free_irq(pf->msix_entries[vector].vector,
-                                &vsi->q_vectors[i]);
+                                vsi->q_vectors[i]);
 
                        /* Tear down the interrupt queue link list
                         *
@@ -3160,6 +3218,39 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi)
        }
 }
 
+/**
+ * i40e_free_q_vector - Free memory allocated for specific interrupt vector
+ * @vsi: the VSI being configured
+ * @v_idx: Index of vector to be freed
+ *
+ * This function frees the memory allocated to the q_vector.  In addition if
+ * NAPI is enabled it will delete any references to the NAPI struct prior
+ * to freeing the q_vector.
+ **/
+static void i40e_free_q_vector(struct i40e_vsi *vsi, int v_idx)
+{
+       struct i40e_q_vector *q_vector = vsi->q_vectors[v_idx];
+       struct i40e_ring *ring;
+
+       if (!q_vector)
+               return;
+
+       /* disassociate q_vector from rings */
+       i40e_for_each_ring(ring, q_vector->tx)
+               ring->q_vector = NULL;
+
+       i40e_for_each_ring(ring, q_vector->rx)
+               ring->q_vector = NULL;
+
+       /* only VSI w/ an associated netdev is set up w/ NAPI */
+       if (vsi->netdev)
+               netif_napi_del(&q_vector->napi);
+
+       vsi->q_vectors[v_idx] = NULL;
+
+       kfree_rcu(q_vector, rcu);
+}
+
 /**
  * i40e_vsi_free_q_vectors - Free memory allocated for interrupt vectors
  * @vsi: the VSI being un-configured
@@ -3171,24 +3262,8 @@ static void i40e_vsi_free_q_vectors(struct i40e_vsi *vsi)
 {
        int v_idx;
 
-       for (v_idx = 0; v_idx < vsi->num_q_vectors; v_idx++) {
-               struct i40e_q_vector *q_vector = &vsi->q_vectors[v_idx];
-               int r_idx;
-
-               if (!q_vector)
-                       continue;
-
-               /* disassociate q_vector from rings */
-               for (r_idx = 0; r_idx < q_vector->tx.count; r_idx++)
-                       q_vector->tx.ring[r_idx]->q_vector = NULL;
-               for (r_idx = 0; r_idx < q_vector->rx.count; r_idx++)
-                       q_vector->rx.ring[r_idx]->q_vector = NULL;
-
-               /* only VSI w/ an associated netdev is set up w/ NAPI */
-               if (vsi->netdev)
-                       netif_napi_del(&q_vector->napi);
-       }
-       kfree(vsi->q_vectors);
+       for (v_idx = 0; v_idx < vsi->num_q_vectors; v_idx++)
+               i40e_free_q_vector(vsi, v_idx);
 }
 
 /**
@@ -3238,7 +3313,7 @@ static void i40e_napi_enable_all(struct i40e_vsi *vsi)
                return;
 
        for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++)
-               napi_enable(&vsi->q_vectors[q_idx].napi);
+               napi_enable(&vsi->q_vectors[q_idx]->napi);
 }
 
 /**
@@ -3253,7 +3328,7 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi)
                return;
 
        for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++)
-               napi_disable(&vsi->q_vectors[q_idx].napi);
+               napi_disable(&vsi->q_vectors[q_idx]->napi);
 }
 
 /**
@@ -3326,7 +3401,8 @@ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf)
  **/
 static u8 i40e_dcb_get_num_tc(struct i40e_dcbx_config *dcbcfg)
 {
-       int num_tc = 0, i;
+       u8 num_tc = 0;
+       int i;
 
        /* Scan the ETS Config Priority Table to find
         * traffic class enabled for a given priority
@@ -3341,9 +3417,7 @@ static u8 i40e_dcb_get_num_tc(struct i40e_dcbx_config *dcbcfg)
        /* Traffic class index starts from zero so
         * increment to return the actual count
         */
-       num_tc++;
-
-       return num_tc;
+       return num_tc + 1;
 }
 
 /**
@@ -3451,28 +3525,27 @@ static int i40e_vsi_get_bw_info(struct i40e_vsi *vsi)
        struct i40e_aqc_query_vsi_bw_config_resp bw_config = {0};
        struct i40e_pf *pf = vsi->back;
        struct i40e_hw *hw = &pf->hw;
+       i40e_status aq_ret;
        u32 tc_bw_max;
-       int ret;
        int i;
 
        /* Get the VSI level BW configuration */
-       ret = i40e_aq_query_vsi_bw_config(hw, vsi->seid, &bw_config, NULL);
-       if (ret) {
+       aq_ret = i40e_aq_query_vsi_bw_config(hw, vsi->seid, &bw_config, NULL);
+       if (aq_ret) {
                dev_info(&pf->pdev->dev,
                         "couldn't get pf vsi bw config, err %d, aq_err %d\n",
-                        ret, pf->hw.aq.asq_last_status);
-               return ret;
+                        aq_ret, pf->hw.aq.asq_last_status);
+               return -EINVAL;
        }
 
        /* Get the VSI level BW configuration per TC */
-       ret = i40e_aq_query_vsi_ets_sla_config(hw, vsi->seid,
-                                              &bw_ets_config,
-                                              NULL);
-       if (ret) {
+       aq_ret = i40e_aq_query_vsi_ets_sla_config(hw, vsi->seid, &bw_ets_config,
+                                                 NULL);
+       if (aq_ret) {
                dev_info(&pf->pdev->dev,
                         "couldn't get pf vsi ets bw config, err %d, aq_err %d\n",
-                        ret, pf->hw.aq.asq_last_status);
-               return ret;
+                        aq_ret, pf->hw.aq.asq_last_status);
+               return -EINVAL;
        }
 
        if (bw_config.tc_valid_bits != bw_ets_config.tc_valid_bits) {
@@ -3494,7 +3567,8 @@ static int i40e_vsi_get_bw_info(struct i40e_vsi *vsi)
                /* 3 bits out of 4 for each TC */
                vsi->bw_ets_max_quanta[i] = (u8)((tc_bw_max >> (i*4)) & 0x7);
        }
-       return ret;
+
+       return 0;
 }
 
 /**
@@ -3505,30 +3579,30 @@ static int i40e_vsi_get_bw_info(struct i40e_vsi *vsi)
  *
  * Returns 0 on success, negative value on failure
  **/
-static int i40e_vsi_configure_bw_alloc(struct i40e_vsi *vsi,
-                                      u8 enabled_tc,
+static int i40e_vsi_configure_bw_alloc(struct i40e_vsi *vsi, u8 enabled_tc,
                                       u8 *bw_share)
 {
        struct i40e_aqc_configure_vsi_tc_bw_data bw_data;
-       int i, ret = 0;
+       i40e_status aq_ret;
+       int i;
 
        bw_data.tc_valid_bits = enabled_tc;
        for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
                bw_data.tc_bw_credits[i] = bw_share[i];
 
-       ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, vsi->seid,
-                                      &bw_data, NULL);
-       if (ret) {
+       aq_ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, vsi->seid, &bw_data,
+                                         NULL);
+       if (aq_ret) {
                dev_info(&vsi->back->pdev->dev,
                         "%s: AQ command Config VSI BW allocation per TC failed = %d\n",
                         __func__, vsi->back->hw.aq.asq_last_status);
-               return ret;
+               return -EINVAL;
        }
 
        for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
                vsi->info.qs_handle[i] = bw_data.qs_handles[i];
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -3701,8 +3775,11 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
 
        if ((pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP) &&
            (vsi->netdev)) {
+               netdev_info(vsi->netdev, "NIC Link is Up\n");
                netif_tx_start_all_queues(vsi->netdev);
                netif_carrier_on(vsi->netdev);
+       } else if (vsi->netdev) {
+               netdev_info(vsi->netdev, "NIC Link is Down\n");
        }
        i40e_service_event_schedule(pf);
 
@@ -3770,8 +3847,8 @@ void i40e_down(struct i40e_vsi *vsi)
        i40e_napi_disable_all(vsi);
 
        for (i = 0; i < vsi->num_queue_pairs; i++) {
-               i40e_clean_tx_ring(&vsi->tx_rings[i]);
-               i40e_clean_rx_ring(&vsi->rx_rings[i]);
+               i40e_clean_tx_ring(vsi->tx_rings[i]);
+               i40e_clean_rx_ring(vsi->rx_rings[i]);
        }
 }
 
@@ -4151,8 +4228,9 @@ static void i40e_link_event(struct i40e_pf *pf)
        if (new_link == old_link)
                return;
 
-       netdev_info(pf->vsi[pf->lan_vsi]->netdev,
-                   "NIC Link is %s\n", (new_link ? "Up" : "Down"));
+       if (!test_bit(__I40E_DOWN, &pf->vsi[pf->lan_vsi]->state))
+               netdev_info(pf->vsi[pf->lan_vsi]->netdev,
+                           "NIC Link is %s\n", (new_link ? "Up" : "Down"));
 
        /* Notify the base of the switch tree connected to
         * the link.  Floating VEBs are not notified.
@@ -4197,9 +4275,9 @@ static void i40e_check_hang_subtask(struct i40e_pf *pf)
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       set_check_for_tx_hang(&vsi->tx_rings[i]);
+                       set_check_for_tx_hang(vsi->tx_rings[i]);
                        if (test_bit(__I40E_HANG_CHECK_ARMED,
-                                    &vsi->tx_rings[i].state))
+                                    &vsi->tx_rings[i]->state))
                                armed++;
                }
 
@@ -4535,7 +4613,8 @@ static void i40e_fdir_setup(struct i40e_pf *pf)
        bool new_vsi = false;
        int err, i;
 
-       if (!(pf->flags & (I40E_FLAG_FDIR_ENABLED|I40E_FLAG_FDIR_ATR_ENABLED)))
+       if (!(pf->flags & (I40E_FLAG_FDIR_ENABLED |
+                          I40E_FLAG_FDIR_ATR_ENABLED)))
                return;
 
        pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
@@ -4935,6 +5014,8 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
 {
        int ret = -ENODEV;
        struct i40e_vsi *vsi;
+       int sz_vectors;
+       int sz_rings;
        int vsi_idx;
        int i;
 
@@ -4960,14 +5041,14 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
                vsi_idx = i;             /* Found one! */
        } else {
                ret = -ENODEV;
-               goto err_alloc_vsi;  /* out of VSI slots! */
+               goto unlock_pf;  /* out of VSI slots! */
        }
        pf->next_vsi = ++i;
 
        vsi = kzalloc(sizeof(*vsi), GFP_KERNEL);
        if (!vsi) {
                ret = -ENOMEM;
-               goto err_alloc_vsi;
+               goto unlock_pf;
        }
        vsi->type = type;
        vsi->back = pf;
@@ -4980,14 +5061,40 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
        vsi->work_limit = I40E_DEFAULT_IRQ_WORK;
        INIT_LIST_HEAD(&vsi->mac_filter_list);
 
-       i40e_set_num_rings_in_vsi(vsi);
+       ret = i40e_set_num_rings_in_vsi(vsi);
+       if (ret)
+               goto err_rings;
+
+       /* allocate memory for ring pointers */
+       sz_rings = sizeof(struct i40e_ring *) * vsi->alloc_queue_pairs * 2;
+       vsi->tx_rings = kzalloc(sz_rings, GFP_KERNEL);
+       if (!vsi->tx_rings) {
+               ret = -ENOMEM;
+               goto err_rings;
+       }
+       vsi->rx_rings = &vsi->tx_rings[vsi->alloc_queue_pairs];
+
+       /* allocate memory for q_vector pointers */
+       sz_vectors = sizeof(struct i40e_q_vectors *) * vsi->num_q_vectors;
+       vsi->q_vectors = kzalloc(sz_vectors, GFP_KERNEL);
+       if (!vsi->q_vectors) {
+               ret = -ENOMEM;
+               goto err_vectors;
+       }
 
        /* Setup default MSIX irq handler for VSI */
        i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings);
 
        pf->vsi[vsi_idx] = vsi;
        ret = vsi_idx;
-err_alloc_vsi:
+       goto unlock_pf;
+
+err_vectors:
+       kfree(vsi->tx_rings);
+err_rings:
+       pf->next_vsi = i - 1;
+       kfree(vsi);
+unlock_pf:
        mutex_unlock(&pf->switch_mutex);
        return ret;
 }
@@ -5028,6 +5135,10 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi)
        i40e_put_lump(pf->qp_pile, vsi->base_queue, vsi->idx);
        i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx);
 
+       /* free the ring and vector containers */
+       kfree(vsi->q_vectors);
+       kfree(vsi->tx_rings);
+
        pf->vsi[vsi->idx] = NULL;
        if (vsi->idx < pf->next_vsi)
                pf->next_vsi = vsi->idx;
@@ -5040,6 +5151,24 @@ free_vsi:
        return 0;
 }
 
+/**
+ * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI
+ * @vsi: the VSI being cleaned
+ **/
+static s32 i40e_vsi_clear_rings(struct i40e_vsi *vsi)
+{
+       int i;
+
+       if (vsi->tx_rings[0])
+               for (i = 0; i < vsi->alloc_queue_pairs; i++) {
+                       kfree_rcu(vsi->tx_rings[i], rcu);
+                       vsi->tx_rings[i] = NULL;
+                       vsi->rx_rings[i] = NULL;
+               }
+
+       return 0;
+}
+
 /**
  * i40e_alloc_rings - Allocates the Rx and Tx rings for the provided VSI
  * @vsi: the VSI being configured
@@ -5047,28 +5176,16 @@ free_vsi:
 static int i40e_alloc_rings(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
-       int ret = 0;
        int i;
 
-       vsi->rx_rings = kcalloc(vsi->alloc_queue_pairs,
-                               sizeof(struct i40e_ring), GFP_KERNEL);
-       if (!vsi->rx_rings) {
-               ret = -ENOMEM;
-               goto err_alloc_rings;
-       }
-
-       vsi->tx_rings = kcalloc(vsi->alloc_queue_pairs,
-                               sizeof(struct i40e_ring), GFP_KERNEL);
-       if (!vsi->tx_rings) {
-               ret = -ENOMEM;
-               kfree(vsi->rx_rings);
-               goto err_alloc_rings;
-       }
-
        /* Set basic values in the rings to be used later during open() */
        for (i = 0; i < vsi->alloc_queue_pairs; i++) {
-               struct i40e_ring *rx_ring = &vsi->rx_rings[i];
-               struct i40e_ring *tx_ring = &vsi->tx_rings[i];
+               struct i40e_ring *tx_ring;
+               struct i40e_ring *rx_ring;
+
+               tx_ring = kzalloc(sizeof(struct i40e_ring) * 2, GFP_KERNEL);
+               if (!tx_ring)
+                       goto err_out;
 
                tx_ring->queue_index = i;
                tx_ring->reg_idx = vsi->base_queue + i;
@@ -5079,7 +5196,9 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi)
                tx_ring->count = vsi->num_desc;
                tx_ring->size = 0;
                tx_ring->dcb_tc = 0;
+               vsi->tx_rings[i] = tx_ring;
 
+               rx_ring = &tx_ring[1];
                rx_ring->queue_index = i;
                rx_ring->reg_idx = vsi->base_queue + i;
                rx_ring->ring_active = false;
@@ -5093,24 +5212,14 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi)
                        set_ring_16byte_desc_enabled(rx_ring);
                else
                        clear_ring_16byte_desc_enabled(rx_ring);
-       }
-
-err_alloc_rings:
-       return ret;
-}
-
-/**
- * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI
- * @vsi: the VSI being cleaned
- **/
-static int i40e_vsi_clear_rings(struct i40e_vsi *vsi)
-{
-       if (vsi) {
-               kfree(vsi->rx_rings);
-               kfree(vsi->tx_rings);
+               vsi->rx_rings[i] = rx_ring;
        }
 
        return 0;
+
+err_out:
+       i40e_vsi_clear_rings(vsi);
+       return -ENOMEM;
 }
 
 /**
@@ -5246,6 +5355,38 @@ static int i40e_init_msix(struct i40e_pf *pf)
        return err;
 }
 
+/**
+ * i40e_alloc_q_vector - Allocate memory for a single interrupt vector
+ * @vsi: the VSI being configured
+ * @v_idx: index of the vector in the vsi struct
+ *
+ * We allocate one q_vector.  If allocation fails we return -ENOMEM.
+ **/
+static int i40e_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
+{
+       struct i40e_q_vector *q_vector;
+
+       /* allocate q_vector */
+       q_vector = kzalloc(sizeof(struct i40e_q_vector), GFP_KERNEL);
+       if (!q_vector)
+               return -ENOMEM;
+
+       q_vector->vsi = vsi;
+       q_vector->v_idx = v_idx;
+       cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
+       if (vsi->netdev)
+               netif_napi_add(vsi->netdev, &q_vector->napi,
+                              i40e_napi_poll, vsi->work_limit);
+
+       q_vector->rx.latency_range = I40E_LOW_LATENCY;
+       q_vector->tx.latency_range = I40E_LOW_LATENCY;
+
+       /* tie q_vector and vsi together */
+       vsi->q_vectors[v_idx] = q_vector;
+
+       return 0;
+}
+
 /**
  * i40e_alloc_q_vectors - Allocate memory for interrupt vectors
  * @vsi: the VSI being configured
@@ -5257,6 +5398,7 @@ static int i40e_alloc_q_vectors(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
        int v_idx, num_q_vectors;
+       int err;
 
        /* if not MSIX, give the one vector only to the LAN VSI */
        if (pf->flags & I40E_FLAG_MSIX_ENABLED)
@@ -5266,22 +5408,19 @@ static int i40e_alloc_q_vectors(struct i40e_vsi *vsi)
        else
                return -EINVAL;
 
-       vsi->q_vectors = kcalloc(num_q_vectors,
-                                sizeof(struct i40e_q_vector),
-                                GFP_KERNEL);
-       if (!vsi->q_vectors)
-               return -ENOMEM;
-
        for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
-               vsi->q_vectors[v_idx].vsi = vsi;
-               vsi->q_vectors[v_idx].v_idx = v_idx;
-               cpumask_set_cpu(v_idx, &vsi->q_vectors[v_idx].affinity_mask);
-               if (vsi->netdev)
-                       netif_napi_add(vsi->netdev, &vsi->q_vectors[v_idx].napi,
-                                      i40e_napi_poll, vsi->work_limit);
+               err = i40e_alloc_q_vector(vsi, v_idx);
+               if (err)
+                       goto err_out;
        }
 
        return 0;
+
+err_out:
+       while (v_idx--)
+               i40e_free_q_vector(vsi, v_idx);
+
+       return err;
 }
 
 /**
@@ -5295,7 +5434,8 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
        if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
                err = i40e_init_msix(pf);
                if (err) {
-                       pf->flags &= ~(I40E_FLAG_RSS_ENABLED       |
+                       pf->flags &= ~(I40E_FLAG_MSIX_ENABLED      |
+                                       I40E_FLAG_RSS_ENABLED      |
                                        I40E_FLAG_MQ_ENABLED       |
                                        I40E_FLAG_DCB_ENABLED      |
                                        I40E_FLAG_SRIOV_ENABLED    |
@@ -5310,14 +5450,17 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
 
        if (!(pf->flags & I40E_FLAG_MSIX_ENABLED) &&
            (pf->flags & I40E_FLAG_MSI_ENABLED)) {
+               dev_info(&pf->pdev->dev, "MSIX not available, trying MSI\n");
                err = pci_enable_msi(pf->pdev);
                if (err) {
-                       dev_info(&pf->pdev->dev,
-                                "MSI init failed (%d), trying legacy.\n", err);
+                       dev_info(&pf->pdev->dev, "MSI init failed - %d\n", err);
                        pf->flags &= ~I40E_FLAG_MSI_ENABLED;
                }
        }
 
+       if (!(pf->flags & (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED)))
+               dev_info(&pf->pdev->dev, "MSIX and MSI not available, falling back to Legacy IRQ\n");
+
        /* track first vector for misc interrupts */
        err = i40e_get_lump(pf, pf->irq_pile, 1, I40E_PILE_VALID_BIT-1);
 }
@@ -5948,7 +6091,7 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi)
        int ret = -ENOENT;
        struct i40e_pf *pf = vsi->back;
 
-       if (vsi->q_vectors) {
+       if (vsi->q_vectors[0]) {
                dev_info(&pf->pdev->dev, "VSI %d has existing q_vectors\n",
                         vsi->seid);
                return -EEXIST;
@@ -5970,8 +6113,9 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi)
                goto vector_setup_out;
        }
 
-       vsi->base_vector = i40e_get_lump(pf, pf->irq_pile,
-                                        vsi->num_q_vectors, vsi->idx);
+       if (vsi->num_q_vectors)
+               vsi->base_vector = i40e_get_lump(pf, pf->irq_pile,
+                                                vsi->num_q_vectors, vsi->idx);
        if (vsi->base_vector < 0) {
                dev_info(&pf->pdev->dev,
                         "failed to get q tracking for VSI %d, err=%d\n",
@@ -7060,8 +7204,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         */
        len = sizeof(struct i40e_vsi *) * pf->hw.func_caps.num_vsis;
        pf->vsi = kzalloc(len, GFP_KERNEL);
-       if (!pf->vsi)
+       if (!pf->vsi) {
+               err = -ENOMEM;
                goto err_switch_setup;
+       }
 
        err = i40e_setup_pf_switch(pf);
        if (err) {
index 49d2cfa..f1f03bc 100644 (file)
@@ -37,6 +37,7 @@ static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size,
                           ((u64)td_tag  << I40E_TXD_QW1_L2TAG1_SHIFT));
 }
 
+#define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS)
 /**
  * i40e_program_fdir_filter - Program a Flow Director filter
  * @fdir_input: Packet data that will be filter parameters
@@ -50,6 +51,7 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
        struct i40e_tx_buffer *tx_buf;
        struct i40e_tx_desc *tx_desc;
        struct i40e_ring *tx_ring;
+       unsigned int fpt, dcc;
        struct i40e_vsi *vsi;
        struct device *dev;
        dma_addr_t dma;
@@ -64,93 +66,78 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
        if (!vsi)
                return -ENOENT;
 
-       tx_ring = &vsi->tx_rings[0];
+       tx_ring = vsi->tx_rings[0];
        dev = tx_ring->dev;
 
        dma = dma_map_single(dev, fdir_data->raw_packet,
-                               I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE);
+                            I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE);
        if (dma_mapping_error(dev, dma))
                goto dma_fail;
 
        /* grab the next descriptor */
-       fdir_desc = I40E_TX_FDIRDESC(tx_ring, tx_ring->next_to_use);
-       tx_buf = &tx_ring->tx_bi[tx_ring->next_to_use];
-       tx_ring->next_to_use++;
-       if (tx_ring->next_to_use == tx_ring->count)
-               tx_ring->next_to_use = 0;
+       i = tx_ring->next_to_use;
+       fdir_desc = I40E_TX_FDIRDESC(tx_ring, i);
+       tx_buf = &tx_ring->tx_bi[i];
+
+       tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0;
 
-       fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32((fdir_data->q_index
-                                            << I40E_TXD_FLTR_QW0_QINDEX_SHIFT)
-                                            & I40E_TXD_FLTR_QW0_QINDEX_MASK);
+       fpt = (fdir_data->q_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) &
+             I40E_TXD_FLTR_QW0_QINDEX_MASK;
 
-       fdir_desc->qindex_flex_ptype_vsi |= cpu_to_le32((fdir_data->flex_off
-                                           << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT)
-                                           & I40E_TXD_FLTR_QW0_FLEXOFF_MASK);
+       fpt |= (fdir_data->flex_off << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT) &
+              I40E_TXD_FLTR_QW0_FLEXOFF_MASK;
 
-       fdir_desc->qindex_flex_ptype_vsi |= cpu_to_le32((fdir_data->pctype
-                                            << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT)
-                                            & I40E_TXD_FLTR_QW0_PCTYPE_MASK);
+       fpt |= (fdir_data->pctype << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT) &
+              I40E_TXD_FLTR_QW0_PCTYPE_MASK;
 
        /* Use LAN VSI Id if not programmed by user */
        if (fdir_data->dest_vsi == 0)
-               fdir_desc->qindex_flex_ptype_vsi |=
-                                         cpu_to_le32((pf->vsi[pf->lan_vsi]->id)
-                                          << I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT);
+               fpt |= (pf->vsi[pf->lan_vsi]->id) <<
+                      I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT;
        else
-               fdir_desc->qindex_flex_ptype_vsi |=
-                                           cpu_to_le32((fdir_data->dest_vsi
-                                           << I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT)
-                                           & I40E_TXD_FLTR_QW0_DEST_VSI_MASK);
+               fpt |= ((u32)fdir_data->dest_vsi <<
+                       I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT) &
+                      I40E_TXD_FLTR_QW0_DEST_VSI_MASK;
 
-       fdir_desc->dtype_cmd_cntindex =
-                                   cpu_to_le32(I40E_TX_DESC_DTYPE_FILTER_PROG);
+       fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(fpt);
+
+       dcc = I40E_TX_DESC_DTYPE_FILTER_PROG;
 
        if (add)
-               fdir_desc->dtype_cmd_cntindex |= cpu_to_le32(
-                                      I40E_FILTER_PROGRAM_DESC_PCMD_ADD_UPDATE
-                                       << I40E_TXD_FLTR_QW1_PCMD_SHIFT);
+               dcc |= I40E_FILTER_PROGRAM_DESC_PCMD_ADD_UPDATE <<
+                      I40E_TXD_FLTR_QW1_PCMD_SHIFT;
        else
-               fdir_desc->dtype_cmd_cntindex |= cpu_to_le32(
-                                          I40E_FILTER_PROGRAM_DESC_PCMD_REMOVE
-                                          << I40E_TXD_FLTR_QW1_PCMD_SHIFT);
+               dcc |= I40E_FILTER_PROGRAM_DESC_PCMD_REMOVE <<
+                      I40E_TXD_FLTR_QW1_PCMD_SHIFT;
 
-       fdir_desc->dtype_cmd_cntindex |= cpu_to_le32((fdir_data->dest_ctl
-                                         << I40E_TXD_FLTR_QW1_DEST_SHIFT)
-                                         & I40E_TXD_FLTR_QW1_DEST_MASK);
+       dcc |= (fdir_data->dest_ctl << I40E_TXD_FLTR_QW1_DEST_SHIFT) &
+              I40E_TXD_FLTR_QW1_DEST_MASK;
 
-       fdir_desc->dtype_cmd_cntindex |= cpu_to_le32(
-                    (fdir_data->fd_status << I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT)
-                     & I40E_TXD_FLTR_QW1_FD_STATUS_MASK);
+       dcc |= (fdir_data->fd_status << I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT) &
+              I40E_TXD_FLTR_QW1_FD_STATUS_MASK;
 
        if (fdir_data->cnt_index != 0) {
-               fdir_desc->dtype_cmd_cntindex |=
-                                   cpu_to_le32(I40E_TXD_FLTR_QW1_CNT_ENA_MASK);
-               fdir_desc->dtype_cmd_cntindex |=
-                                           cpu_to_le32((fdir_data->cnt_index
-                                           << I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT)
-                                           & I40E_TXD_FLTR_QW1_CNTINDEX_MASK);
+               dcc |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
+               dcc |= ((u32)fdir_data->cnt_index <<
+                       I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
+                      I40E_TXD_FLTR_QW1_CNTINDEX_MASK;
        }
 
+       fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dcc);
        fdir_desc->fd_id = cpu_to_le32(fdir_data->fd_id);
 
        /* Now program a dummy descriptor */
-       tx_desc = I40E_TX_DESC(tx_ring, tx_ring->next_to_use);
-       tx_buf = &tx_ring->tx_bi[tx_ring->next_to_use];
-       tx_ring->next_to_use++;
-       if (tx_ring->next_to_use == tx_ring->count)
-               tx_ring->next_to_use = 0;
+       i = tx_ring->next_to_use;
+       tx_desc = I40E_TX_DESC(tx_ring, i);
+
+       tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0;
 
        tx_desc->buffer_addr = cpu_to_le64(dma);
-       td_cmd = I40E_TX_DESC_CMD_EOP |
-                I40E_TX_DESC_CMD_RS  |
-                I40E_TX_DESC_CMD_DUMMY;
+       td_cmd = I40E_TXD_CMD | I40E_TX_DESC_CMD_DUMMY;
 
        tx_desc->cmd_type_offset_bsz =
                build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_LOOKUP, 0);
 
-       /* Mark the data descriptor to be watched */
-       tx_buf->next_to_watch = tx_desc;
-
        /* Force memory writes to complete before letting h/w
         * know there are new descriptors to fetch.  (Only
         * applicable for weak-ordered memory model archs,
@@ -158,6 +145,9 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
         */
        wmb();
 
+       /* Mark the data descriptor to be watched */
+       tx_buf->next_to_watch = tx_desc;
+
        writel(tx_ring->next_to_use, tx_ring->tail);
        return 0;
 
@@ -188,27 +178,30 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, u32 qw, u8 prog_id)
 }
 
 /**
- * i40e_unmap_tx_resource - Release a Tx buffer
+ * i40e_unmap_and_free_tx_resource - Release a Tx buffer
  * @ring:      the ring that owns the buffer
  * @tx_buffer: the buffer to free
  **/
-static inline void i40e_unmap_tx_resource(struct i40e_ring *ring,
-                                         struct i40e_tx_buffer *tx_buffer)
+static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
+                                           struct i40e_tx_buffer *tx_buffer)
 {
-       if (tx_buffer->dma) {
-               if (tx_buffer->tx_flags & I40E_TX_FLAGS_MAPPED_AS_PAGE)
-                       dma_unmap_page(ring->dev,
-                                      tx_buffer->dma,
-                                      tx_buffer->length,
-                                      DMA_TO_DEVICE);
-               else
+       if (tx_buffer->skb) {
+               dev_kfree_skb_any(tx_buffer->skb);
+               if (dma_unmap_len(tx_buffer, len))
                        dma_unmap_single(ring->dev,
-                                        tx_buffer->dma,
-                                        tx_buffer->length,
+                                        dma_unmap_addr(tx_buffer, dma),
+                                        dma_unmap_len(tx_buffer, len),
                                         DMA_TO_DEVICE);
+       } else if (dma_unmap_len(tx_buffer, len)) {
+               dma_unmap_page(ring->dev,
+                              dma_unmap_addr(tx_buffer, dma),
+                              dma_unmap_len(tx_buffer, len),
+                              DMA_TO_DEVICE);
        }
-       tx_buffer->dma = 0;
-       tx_buffer->time_stamp = 0;
+       tx_buffer->next_to_watch = NULL;
+       tx_buffer->skb = NULL;
+       dma_unmap_len_set(tx_buffer, len, 0);
+       /* tx_buffer must be completely set up in the transmit path */
 }
 
 /**
@@ -217,7 +210,6 @@ static inline void i40e_unmap_tx_resource(struct i40e_ring *ring,
  **/
 void i40e_clean_tx_ring(struct i40e_ring *tx_ring)
 {
-       struct i40e_tx_buffer *tx_buffer;
        unsigned long bi_size;
        u16 i;
 
@@ -226,13 +218,8 @@ void i40e_clean_tx_ring(struct i40e_ring *tx_ring)
                return;
 
        /* Free all the Tx ring sk_buffs */
-       for (i = 0; i < tx_ring->count; i++) {
-               tx_buffer = &tx_ring->tx_bi[i];
-               i40e_unmap_tx_resource(tx_ring, tx_buffer);
-               if (tx_buffer->skb)
-                       dev_kfree_skb_any(tx_buffer->skb);
-               tx_buffer->skb = NULL;
-       }
+       for (i = 0; i < tx_ring->count; i++)
+               i40e_unmap_and_free_tx_resource(tx_ring, &tx_ring->tx_bi[i]);
 
        bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count;
        memset(tx_ring->tx_bi, 0, bi_size);
@@ -242,6 +229,13 @@ void i40e_clean_tx_ring(struct i40e_ring *tx_ring)
 
        tx_ring->next_to_use = 0;
        tx_ring->next_to_clean = 0;
+
+       if (!tx_ring->netdev)
+               return;
+
+       /* cleanup Tx queue statistics */
+       netdev_tx_reset_queue(netdev_get_tx_queue(tx_ring->netdev,
+                                                 tx_ring->queue_index));
 }
 
 /**
@@ -300,14 +294,14 @@ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
         * run the check_tx_hang logic with a transmit completion
         * pending but without time to complete it yet.
         */
-       if ((tx_ring->tx_stats.tx_done_old == tx_ring->tx_stats.packets) &&
+       if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) &&
            tx_pending) {
                /* make sure it is true for two checks in a row */
                ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED,
                                       &tx_ring->state);
        } else {
                /* update completed stats and disarm the hang check */
-               tx_ring->tx_stats.tx_done_old = tx_ring->tx_stats.packets;
+               tx_ring->tx_stats.tx_done_old = tx_ring->stats.packets;
                clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state);
        }
 
@@ -331,62 +325,88 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
 
        tx_buf = &tx_ring->tx_bi[i];
        tx_desc = I40E_TX_DESC(tx_ring, i);
+       i -= tx_ring->count;
 
-       for (; budget; budget--) {
-               struct i40e_tx_desc *eop_desc;
-
-               eop_desc = tx_buf->next_to_watch;
+       do {
+               struct i40e_tx_desc *eop_desc = tx_buf->next_to_watch;
 
                /* if next_to_watch is not set then there is no work pending */
                if (!eop_desc)
                        break;
 
+               /* prevent any other reads prior to eop_desc */
+               read_barrier_depends();
+
                /* if the descriptor isn't done, no work yet to do */
                if (!(eop_desc->cmd_type_offset_bsz &
                      cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE)))
                        break;
 
-               /* count the packet as being completed */
-               tx_ring->tx_stats.completed++;
+               /* clear next_to_watch to prevent false hangs */
                tx_buf->next_to_watch = NULL;
-               tx_buf->time_stamp = 0;
-
-               /* set memory barrier before eop_desc is verified */
-               rmb();
 
-               do {
-                       i40e_unmap_tx_resource(tx_ring, tx_buf);
+               /* update the statistics for this packet */
+               total_bytes += tx_buf->bytecount;
+               total_packets += tx_buf->gso_segs;
 
-                       /* clear dtype status */
-                       tx_desc->cmd_type_offset_bsz &=
-                               ~cpu_to_le64(I40E_TXD_QW1_DTYPE_MASK);
+               /* free the skb */
+               dev_kfree_skb_any(tx_buf->skb);
 
-                       if (likely(tx_desc == eop_desc)) {
-                               eop_desc = NULL;
+               /* unmap skb header data */
+               dma_unmap_single(tx_ring->dev,
+                                dma_unmap_addr(tx_buf, dma),
+                                dma_unmap_len(tx_buf, len),
+                                DMA_TO_DEVICE);
 
-                               dev_kfree_skb_any(tx_buf->skb);
-                               tx_buf->skb = NULL;
+               /* clear tx_buffer data */
+               tx_buf->skb = NULL;
+               dma_unmap_len_set(tx_buf, len, 0);
 
-                               total_bytes += tx_buf->bytecount;
-                               total_packets += tx_buf->gso_segs;
-                       }
+               /* unmap remaining buffers */
+               while (tx_desc != eop_desc) {
 
                        tx_buf++;
                        tx_desc++;
                        i++;
-                       if (unlikely(i == tx_ring->count)) {
-                               i = 0;
+                       if (unlikely(!i)) {
+                               i -= tx_ring->count;
                                tx_buf = tx_ring->tx_bi;
                                tx_desc = I40E_TX_DESC(tx_ring, 0);
                        }
-               } while (eop_desc);
-       }
 
+                       /* unmap any remaining paged data */
+                       if (dma_unmap_len(tx_buf, len)) {
+                               dma_unmap_page(tx_ring->dev,
+                                              dma_unmap_addr(tx_buf, dma),
+                                              dma_unmap_len(tx_buf, len),
+                                              DMA_TO_DEVICE);
+                               dma_unmap_len_set(tx_buf, len, 0);
+                       }
+               }
+
+               /* move us one more past the eop_desc for start of next pkt */
+               tx_buf++;
+               tx_desc++;
+               i++;
+               if (unlikely(!i)) {
+                       i -= tx_ring->count;
+                       tx_buf = tx_ring->tx_bi;
+                       tx_desc = I40E_TX_DESC(tx_ring, 0);
+               }
+
+               /* update budget accounting */
+               budget--;
+       } while (likely(budget));
+
+       i += tx_ring->count;
        tx_ring->next_to_clean = i;
-       tx_ring->tx_stats.bytes += total_bytes;
-       tx_ring->tx_stats.packets += total_packets;
+       u64_stats_update_begin(&tx_ring->syncp);
+       tx_ring->stats.bytes += total_bytes;
+       tx_ring->stats.packets += total_packets;
+       u64_stats_update_end(&tx_ring->syncp);
        tx_ring->q_vector->tx.total_bytes += total_bytes;
        tx_ring->q_vector->tx.total_packets += total_packets;
+
        if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) {
                /* schedule immediate reset if we believe we hung */
                dev_info(tx_ring->dev, "Detected Tx Unit Hang\n"
@@ -414,6 +434,10 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
                return true;
        }
 
+       netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev,
+                                                     tx_ring->queue_index),
+                                 total_packets, total_bytes);
+
 #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
        if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
                     (I40E_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) {
@@ -524,8 +548,6 @@ static void i40e_update_dynamic_itr(struct i40e_q_vector *q_vector)
        i40e_set_new_dynamic_itr(&q_vector->tx);
        if (old_itr != q_vector->tx.itr)
                wr32(hw, reg_addr, q_vector->tx.itr);
-
-       i40e_flush(hw);
 }
 
 /**
@@ -1042,8 +1064,10 @@ next_desc:
        }
 
        rx_ring->next_to_clean = i;
-       rx_ring->rx_stats.packets += total_rx_packets;
-       rx_ring->rx_stats.bytes += total_rx_bytes;
+       u64_stats_update_begin(&rx_ring->syncp);
+       rx_ring->stats.packets += total_rx_packets;
+       rx_ring->stats.bytes += total_rx_bytes;
+       u64_stats_update_end(&rx_ring->syncp);
        rx_ring->q_vector->rx.total_packets += total_rx_packets;
        rx_ring->q_vector->rx.total_bytes += total_rx_bytes;
 
@@ -1067,27 +1091,28 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
        struct i40e_q_vector *q_vector =
                               container_of(napi, struct i40e_q_vector, napi);
        struct i40e_vsi *vsi = q_vector->vsi;
+       struct i40e_ring *ring;
        bool clean_complete = true;
        int budget_per_ring;
-       int i;
 
        if (test_bit(__I40E_DOWN, &vsi->state)) {
                napi_complete(napi);
                return 0;
        }
 
+       /* Since the actual Tx work is minimal, we can give the Tx a larger
+        * budget and be more aggressive about cleaning up the Tx descriptors.
+        */
+       i40e_for_each_ring(ring, q_vector->tx)
+               clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
+
        /* We attempt to distribute budget to each Rx queue fairly, but don't
         * allow the budget to go below 1 because that would exit polling early.
-        * Since the actual Tx work is minimal, we can give the Tx a larger
-        * budget and be more aggressive about cleaning up the Tx descriptors.
         */
        budget_per_ring = max(budget/q_vector->num_ringpairs, 1);
-       for (i = 0; i < q_vector->num_ringpairs; i++) {
-               clean_complete &= i40e_clean_tx_irq(q_vector->tx.ring[i],
-                                                   vsi->work_limit);
-               clean_complete &= i40e_clean_rx_irq(q_vector->rx.ring[i],
-                                                   budget_per_ring);
-       }
+
+       i40e_for_each_ring(ring, q_vector->rx)
+               clean_complete &= i40e_clean_rx_irq(ring, budget_per_ring);
 
        /* If work not completed, return budget and polling will return */
        if (!clean_complete)
@@ -1117,7 +1142,8 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
                        qval = rd32(hw, I40E_QINT_TQCTL(0));
                        qval |= I40E_QINT_TQCTL_CAUSE_ENA_MASK;
                        wr32(hw, I40E_QINT_TQCTL(0), qval);
-                       i40e_flush(hw);
+
+                       i40e_irq_dynamic_enable_icr0(vsi->back);
                }
        }
 
@@ -1144,6 +1170,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
        struct tcphdr *th;
        unsigned int hlen;
        u32 flex_ptype, dtype_cmd;
+       u16 i;
 
        /* make sure ATR is enabled */
        if (!(pf->flags & I40E_FLAG_FDIR_ATR_ENABLED))
@@ -1183,10 +1210,11 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
        tx_ring->atr_count = 0;
 
        /* grab the next descriptor */
-       fdir_desc = I40E_TX_FDIRDESC(tx_ring, tx_ring->next_to_use);
-       tx_ring->next_to_use++;
-       if (tx_ring->next_to_use == tx_ring->count)
-               tx_ring->next_to_use = 0;
+       i = tx_ring->next_to_use;
+       fdir_desc = I40E_TX_FDIRDESC(tx_ring, i);
+
+       i++;
+       tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
 
        flex_ptype = (tx_ring->queue_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) &
                      I40E_TXD_FLTR_QW0_QINDEX_MASK;
@@ -1216,7 +1244,6 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
        fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dtype_cmd);
 }
 
-#define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS)
 /**
  * i40e_tx_prepare_vlan_flags - prepare generic TX VLAN tagging flags for HW
  * @skb:     send buffer
@@ -1275,27 +1302,6 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb,
        return 0;
 }
 
-/**
- * i40e_tx_csum - is checksum offload requested
- * @tx_ring:  ptr to the ring to send
- * @skb:      ptr to the skb we're sending
- * @tx_flags: the collected send information
- * @protocol: the send protocol
- *
- * Returns true if checksum offload is requested
- **/
-static bool i40e_tx_csum(struct i40e_ring *tx_ring, struct sk_buff *skb,
-                        u32 tx_flags, __be16 protocol)
-{
-       if ((skb->ip_summed != CHECKSUM_PARTIAL) &&
-           !(tx_flags & I40E_TX_FLAGS_TXSW)) {
-               if (!(tx_flags & I40E_TX_FLAGS_HW_VLAN))
-                       return false;
-       }
-
-       return skb->ip_summed == CHECKSUM_PARTIAL;
-}
-
 /**
  * i40e_tso - set up the tso context descriptor
  * @tx_ring:  ptr to the ring to send
@@ -1482,15 +1488,16 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
                               const u32 cd_tunneling, const u32 cd_l2tag2)
 {
        struct i40e_tx_context_desc *context_desc;
+       int i = tx_ring->next_to_use;
 
        if (!cd_type_cmd_tso_mss && !cd_tunneling && !cd_l2tag2)
                return;
 
        /* grab the next descriptor */
-       context_desc = I40E_TX_CTXTDESC(tx_ring, tx_ring->next_to_use);
-       tx_ring->next_to_use++;
-       if (tx_ring->next_to_use == tx_ring->count)
-               tx_ring->next_to_use = 0;
+       context_desc = I40E_TX_CTXTDESC(tx_ring, i);
+
+       i++;
+       tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
 
        /* cpu_to_le32 and assign to struct fields */
        context_desc->tunneling_params = cpu_to_le32(cd_tunneling);
@@ -1512,68 +1519,71 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
                        struct i40e_tx_buffer *first, u32 tx_flags,
                        const u8 hdr_len, u32 td_cmd, u32 td_offset)
 {
-       struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0];
        unsigned int data_len = skb->data_len;
        unsigned int size = skb_headlen(skb);
-       struct device *dev = tx_ring->dev;
-       u32 paylen = skb->len - hdr_len;
-       u16 i = tx_ring->next_to_use;
+       struct skb_frag_struct *frag;
        struct i40e_tx_buffer *tx_bi;
        struct i40e_tx_desc *tx_desc;
-       u32 buf_offset = 0;
+       u16 i = tx_ring->next_to_use;
        u32 td_tag = 0;
        dma_addr_t dma;
        u16 gso_segs;
 
-       dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE);
-       if (dma_mapping_error(dev, dma))
-               goto dma_error;
-
        if (tx_flags & I40E_TX_FLAGS_HW_VLAN) {
                td_cmd |= I40E_TX_DESC_CMD_IL2TAG1;
                td_tag = (tx_flags & I40E_TX_FLAGS_VLAN_MASK) >>
                         I40E_TX_FLAGS_VLAN_SHIFT;
        }
 
+       if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO))
+               gso_segs = skb_shinfo(skb)->gso_segs;
+       else
+               gso_segs = 1;
+
+       /* multiply data chunks by size of headers */
+       first->bytecount = skb->len - hdr_len + (gso_segs * hdr_len);
+       first->gso_segs = gso_segs;
+       first->skb = skb;
+       first->tx_flags = tx_flags;
+
+       dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
+
        tx_desc = I40E_TX_DESC(tx_ring, i);
-       for (;;) {
-               while (size > I40E_MAX_DATA_PER_TXD) {
-                       tx_desc->buffer_addr = cpu_to_le64(dma + buf_offset);
+       tx_bi = first;
+
+       for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
+               if (dma_mapping_error(tx_ring->dev, dma))
+                       goto dma_error;
+
+               /* record length, and DMA address */
+               dma_unmap_len_set(tx_bi, len, size);
+               dma_unmap_addr_set(tx_bi, dma, dma);
+
+               tx_desc->buffer_addr = cpu_to_le64(dma);
+
+               while (unlikely(size > I40E_MAX_DATA_PER_TXD)) {
                        tx_desc->cmd_type_offset_bsz =
                                build_ctob(td_cmd, td_offset,
                                           I40E_MAX_DATA_PER_TXD, td_tag);
 
-                       buf_offset += I40E_MAX_DATA_PER_TXD;
-                       size -= I40E_MAX_DATA_PER_TXD;
-
                        tx_desc++;
                        i++;
                        if (i == tx_ring->count) {
                                tx_desc = I40E_TX_DESC(tx_ring, 0);
                                i = 0;
                        }
-               }
 
-               tx_bi = &tx_ring->tx_bi[i];
-               tx_bi->length = buf_offset + size;
-               tx_bi->tx_flags = tx_flags;
-               tx_bi->dma = dma;
+                       dma += I40E_MAX_DATA_PER_TXD;
+                       size -= I40E_MAX_DATA_PER_TXD;
 
-               tx_desc->buffer_addr = cpu_to_le64(dma + buf_offset);
-               tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset,
-                                                         size, td_tag);
+                       tx_desc->buffer_addr = cpu_to_le64(dma);
+               }
 
                if (likely(!data_len))
                        break;
 
-               size = skb_frag_size(frag);
-               data_len -= size;
-               buf_offset = 0;
-               tx_flags |= I40E_TX_FLAGS_MAPPED_AS_PAGE;
-
-               dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
-               if (dma_mapping_error(dev, dma))
-                       goto dma_error;
+               tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset,
+                                                         size, td_tag);
 
                tx_desc++;
                i++;
@@ -1582,31 +1592,25 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
                        i = 0;
                }
 
-               frag++;
-       }
-
-       tx_desc->cmd_type_offset_bsz |=
-                      cpu_to_le64((u64)I40E_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT);
+               size = skb_frag_size(frag);
+               data_len -= size;
 
-       i++;
-       if (i == tx_ring->count)
-               i = 0;
+               dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size,
+                                      DMA_TO_DEVICE);
 
-       tx_ring->next_to_use = i;
+               tx_bi = &tx_ring->tx_bi[i];
+       }
 
-       if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO))
-               gso_segs = skb_shinfo(skb)->gso_segs;
-       else
-               gso_segs = 1;
+       tx_desc->cmd_type_offset_bsz =
+               build_ctob(td_cmd, td_offset, size, td_tag) |
+               cpu_to_le64((u64)I40E_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT);
 
-       /* multiply data chunks by size of headers */
-       tx_bi->bytecount = paylen + (gso_segs * hdr_len);
-       tx_bi->gso_segs = gso_segs;
-       tx_bi->skb = skb;
+       netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
+                                                tx_ring->queue_index),
+                            first->bytecount);
 
-       /* set the timestamp and next to watch values */
+       /* set the timestamp */
        first->time_stamp = jiffies;
-       first->next_to_watch = tx_desc;
 
        /* Force memory writes to complete before letting h/w
         * know there are new descriptors to fetch.  (Only
@@ -1615,16 +1619,27 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
         */
        wmb();
 
+       /* set next_to_watch value indicating a packet is present */
+       first->next_to_watch = tx_desc;
+
+       i++;
+       if (i == tx_ring->count)
+               i = 0;
+
+       tx_ring->next_to_use = i;
+
+       /* notify HW of packet */
        writel(i, tx_ring->tail);
+
        return;
 
 dma_error:
-       dev_info(dev, "TX DMA map failed\n");
+       dev_info(tx_ring->dev, "TX DMA map failed\n");
 
        /* clear dma mappings for failed tx_bi map */
        for (;;) {
                tx_bi = &tx_ring->tx_bi[i];
-               i40e_unmap_tx_resource(tx_ring, tx_bi);
+               i40e_unmap_and_free_tx_resource(tx_ring, tx_bi);
                if (tx_bi == first)
                        break;
                if (i == 0)
@@ -1632,8 +1647,6 @@ dma_error:
                i--;
        }
 
-       dev_kfree_skb_any(skb);
-
        tx_ring->next_to_use = i;
 }
 
@@ -1758,16 +1771,16 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
 
        skb_tx_timestamp(skb);
 
+       /* always enable CRC insertion offload */
+       td_cmd |= I40E_TX_DESC_CMD_ICRC;
+
        /* Always offload the checksum, since it's in the data descriptor */
-       if (i40e_tx_csum(tx_ring, skb, tx_flags, protocol))
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
                tx_flags |= I40E_TX_FLAGS_CSUM;
 
-       /* always enable offload insertion */
-       td_cmd |= I40E_TX_DESC_CMD_ICRC;
-
-       if (tx_flags & I40E_TX_FLAGS_CSUM)
                i40e_tx_enable_csum(skb, tx_flags, &td_cmd, &td_offset,
                                    tx_ring, &cd_tunneling);
+       }
 
        i40e_create_tx_ctx(tx_ring, cd_type_cmd_tso_mss,
                           cd_tunneling, cd_l2tag2);
@@ -1801,7 +1814,7 @@ netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
-       struct i40e_ring *tx_ring = &vsi->tx_rings[skb->queue_mapping];
+       struct i40e_ring *tx_ring = vsi->tx_rings[skb->queue_mapping];
 
        /* hardware can't handle really short frames, hardware padding works
         * beyond this point
index b1d7722..db55d99 100644 (file)
 #define I40E_TX_FLAGS_IPV6             (u32)(1 << 5)
 #define I40E_TX_FLAGS_FCCRC            (u32)(1 << 6)
 #define I40E_TX_FLAGS_FSO              (u32)(1 << 7)
-#define I40E_TX_FLAGS_TXSW             (u32)(1 << 8)
-#define I40E_TX_FLAGS_MAPPED_AS_PAGE   (u32)(1 << 9)
 #define I40E_TX_FLAGS_VLAN_MASK                0xffff0000
 #define I40E_TX_FLAGS_VLAN_PRIO_MASK   0xe0000000
 #define I40E_TX_FLAGS_VLAN_PRIO_SHIFT  29
 #define I40E_TX_FLAGS_VLAN_SHIFT       16
 
 struct i40e_tx_buffer {
-       struct sk_buff *skb;
-       dma_addr_t dma;
-       unsigned long time_stamp;
-       u16 length;
-       u32 tx_flags;
        struct i40e_tx_desc *next_to_watch;
+       unsigned long time_stamp;
+       struct sk_buff *skb;
        unsigned int bytecount;
-       u16 gso_segs;
-       u8 mapped_as_page;
+       unsigned short gso_segs;
+       DEFINE_DMA_UNMAP_ADDR(dma);
+       DEFINE_DMA_UNMAP_LEN(len);
+       u32 tx_flags;
 };
 
 struct i40e_rx_buffer {
@@ -129,18 +126,18 @@ struct i40e_rx_buffer {
        unsigned int page_offset;
 };
 
-struct i40e_tx_queue_stats {
+struct i40e_queue_stats {
        u64 packets;
        u64 bytes;
+};
+
+struct i40e_tx_queue_stats {
        u64 restart_queue;
        u64 tx_busy;
-       u64 completed;
        u64 tx_done_old;
 };
 
 struct i40e_rx_queue_stats {
-       u64 packets;
-       u64 bytes;
        u64 non_eop_descs;
        u64 alloc_rx_page_failed;
        u64 alloc_rx_buff_failed;
@@ -183,6 +180,7 @@ enum i40e_ring_state_t {
 
 /* struct that defines a descriptor ring, associated with a VSI */
 struct i40e_ring {
+       struct i40e_ring *next;         /* pointer to next ring in q_vector */
        void *desc;                     /* Descriptor ring memory */
        struct device *dev;             /* Used for DMA mapping */
        struct net_device *netdev;      /* netdev ring maps to */
@@ -219,6 +217,8 @@ struct i40e_ring {
        bool ring_active;               /* is ring online or not */
 
        /* stats structs */
+       struct i40e_queue_stats stats;
+       struct u64_stats_sync syncp;
        union {
                struct i40e_tx_queue_stats tx_stats;
                struct i40e_rx_queue_stats rx_stats;
@@ -229,6 +229,8 @@ struct i40e_ring {
 
        struct i40e_vsi *vsi;           /* Backreference to associated VSI */
        struct i40e_q_vector *q_vector; /* Backreference to associated vector */
+
+       struct rcu_head rcu;            /* to avoid race on free */
 } ____cacheline_internodealigned_in_smp;
 
 enum i40e_latency_range {
@@ -238,9 +240,8 @@ enum i40e_latency_range {
 };
 
 struct i40e_ring_container {
-#define I40E_MAX_RINGPAIR_PER_VECTOR 8
        /* array of pointers to rings */
-       struct i40e_ring *ring[I40E_MAX_RINGPAIR_PER_VECTOR];
+       struct i40e_ring *ring;
        unsigned int total_bytes;       /* total bytes processed this int */
        unsigned int total_packets;     /* total packets processed this int */
        u16 count;
@@ -248,6 +249,10 @@ struct i40e_ring_container {
        u16 itr;
 };
 
+/* iterator for handling rings in ring container */
+#define i40e_for_each_ring(pos, head) \
+       for (pos = (head).ring; pos != NULL; pos = pos->next)
+
 void i40e_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count);
 netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
 void i40e_clean_tx_ring(struct i40e_ring *tx_ring);
index 8967e58..0759698 100644 (file)
@@ -251,7 +251,7 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx,
                reg_idx = I40E_VPINT_LNKLST0(vf->vf_id);
        else
                reg_idx = I40E_VPINT_LNKLSTN(
-                           ((pf->hw.func_caps.num_msix_vectors_vf - 1)
+                                          (pf->hw.func_caps.num_msix_vectors_vf
                                              * vf->vf_id) + (vector_id - 1));
 
        if (vecmap->rxq_map == 0 && vecmap->txq_map == 0) {
@@ -383,7 +383,7 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx,
 
        /* associate this queue with the PCI VF function */
        qtx_ctl = I40E_QTX_CTL_VF_QUEUE;
-       qtx_ctl |= ((hw->hmc.hmc_fn_id << I40E_QTX_CTL_PF_INDX_SHIFT)
+       qtx_ctl |= ((hw->pf_id << I40E_QTX_CTL_PF_INDX_SHIFT)
                    & I40E_QTX_CTL_PF_INDX_MASK);
        qtx_ctl |= (((vf->vf_id + hw->func_caps.vf_base_id)
                     << I40E_QTX_CTL_VFVM_INDX_SHIFT)
index 74a1506..8c24377 100644 (file)
 #ifndef _E1000_82575_H_
 #define _E1000_82575_H_
 
-extern void igb_shutdown_serdes_link_82575(struct e1000_hw *hw);
-extern void igb_power_up_serdes_link_82575(struct e1000_hw *hw);
-extern void igb_power_down_phy_copper_82575(struct e1000_hw *hw);
-extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
-extern s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
-                               u8 dev_addr, u8 *data);
-extern s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 data);
+void igb_shutdown_serdes_link_82575(struct e1000_hw *hw);
+void igb_power_up_serdes_link_82575(struct e1000_hw *hw);
+void igb_power_down_phy_copper_82575(struct e1000_hw *hw);
+void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
+s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr,
+                     u8 *data);
+s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr,
+                      u8 data);
 
 #define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \
                                      (ID_LED_DEF1_DEF2 <<  8) | \
index 37a9c06..2e166b2 100644 (file)
@@ -562,11 +562,11 @@ struct e1000_hw {
        u8  revision_id;
 };
 
-extern struct net_device *igb_get_hw_dev(struct e1000_hw *hw);
+struct net_device *igb_get_hw_dev(struct e1000_hw *hw);
 #define hw_dbg(format, arg...) \
        netdev_dbg(igb_get_hw_dev(hw), format, ##arg)
 
 /* These functions must be implemented by drivers */
-s32  igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
-s32  igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
+s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
+s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
 #endif /* _E1000_HW_H_ */
index dde3c4b..2d91371 100644 (file)
 #ifndef _E1000_I210_H_
 #define _E1000_I210_H_
 
-extern s32 igb_update_flash_i210(struct e1000_hw *hw);
-extern s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw);
-extern s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw);
-extern s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset,
-                             u16 words, u16 *data);
-extern s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset,
-                            u16 words, u16 *data);
-extern s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
-extern void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
-extern s32 igb_acquire_nvm_i210(struct e1000_hw *hw);
-extern void igb_release_nvm_i210(struct e1000_hw *hw);
-extern s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data);
-extern s32 igb_read_invm_version(struct e1000_hw *hw,
-                                struct e1000_fw_version *invm_ver);
-extern s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr,
-                             u16 *data);
-extern s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr,
-                              u16 data);
-extern s32 igb_init_nvm_params_i210(struct e1000_hw *hw);
-extern bool igb_get_flash_presence_i210(struct e1000_hw *hw);
+s32 igb_update_flash_i210(struct e1000_hw *hw);
+s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw);
+s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw);
+s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
+                           u16 *data);
+s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
+                          u16 *data);
+s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
+void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
+s32 igb_acquire_nvm_i210(struct e1000_hw *hw);
+void igb_release_nvm_i210(struct e1000_hw *hw);
+s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data);
+s32 igb_read_invm_version(struct e1000_hw *hw,
+                         struct e1000_fw_version *invm_ver);
+s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data);
+s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data);
+s32 igb_init_nvm_params_i210(struct e1000_hw *hw);
+bool igb_get_flash_presence_i210(struct e1000_hw *hw);
 
 #define E1000_STM_OPCODE               0xDB00
 #define E1000_EEPROM_FLASH_SIZE_WORD   0x11
index 5e13e83..e4cbe8e 100644 (file)
@@ -86,6 +86,6 @@ enum e1000_mng_mode {
 
 #define E1000_MNG_DHCP_COOKIE_STATUS_VLAN      0x2
 
-extern void e1000_init_function_pointers_82575(struct e1000_hw *hw);
+void e1000_init_function_pointers_82575(struct e1000_hw *hw);
 
 #endif
index e726675..c4c4fe3 100644 (file)
@@ -708,11 +708,6 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw)
                hw_dbg("Error committing the PHY changes\n");
                goto out;
        }
-       if (phy->type == e1000_phy_i210) {
-               ret_val = igb_set_master_slave_mode(hw);
-               if (ret_val)
-                       return ret_val;
-       }
 
 out:
        return ret_val;
@@ -806,6 +801,9 @@ s32 igb_copper_link_setup_m88_gen2(struct e1000_hw *hw)
                hw_dbg("Error committing the PHY changes\n");
                return ret_val;
        }
+       ret_val = igb_set_master_slave_mode(hw);
+       if (ret_val)
+               return ret_val;
 
        return 0;
 }
index 6807b09..5e9ed89 100644 (file)
@@ -483,40 +483,38 @@ enum igb_boards {
 extern char igb_driver_name[];
 extern char igb_driver_version[];
 
-extern int igb_up(struct igb_adapter *);
-extern void igb_down(struct igb_adapter *);
-extern void igb_reinit_locked(struct igb_adapter *);
-extern void igb_reset(struct igb_adapter *);
-extern void igb_write_rss_indir_tbl(struct igb_adapter *);
-extern int igb_set_spd_dplx(struct igb_adapter *, u32, u8);
-extern int igb_setup_tx_resources(struct igb_ring *);
-extern int igb_setup_rx_resources(struct igb_ring *);
-extern void igb_free_tx_resources(struct igb_ring *);
-extern void igb_free_rx_resources(struct igb_ring *);
-extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *);
-extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *);
-extern void igb_setup_tctl(struct igb_adapter *);
-extern void igb_setup_rctl(struct igb_adapter *);
-extern netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *);
-extern void igb_unmap_and_free_tx_resource(struct igb_ring *,
-                                          struct igb_tx_buffer *);
-extern void igb_alloc_rx_buffers(struct igb_ring *, u16);
-extern void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *);
-extern bool igb_has_link(struct igb_adapter *adapter);
-extern void igb_set_ethtool_ops(struct net_device *);
-extern void igb_power_up_link(struct igb_adapter *);
-extern void igb_set_fw_version(struct igb_adapter *);
-extern void igb_ptp_init(struct igb_adapter *adapter);
-extern void igb_ptp_stop(struct igb_adapter *adapter);
-extern void igb_ptp_reset(struct igb_adapter *adapter);
-extern void igb_ptp_tx_work(struct work_struct *work);
-extern void igb_ptp_rx_hang(struct igb_adapter *adapter);
-extern void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter);
-extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
-                               struct sk_buff *skb);
-extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector,
-                               unsigned char *va,
-                               struct sk_buff *skb);
+int igb_up(struct igb_adapter *);
+void igb_down(struct igb_adapter *);
+void igb_reinit_locked(struct igb_adapter *);
+void igb_reset(struct igb_adapter *);
+int igb_reinit_queues(struct igb_adapter *);
+void igb_write_rss_indir_tbl(struct igb_adapter *);
+int igb_set_spd_dplx(struct igb_adapter *, u32, u8);
+int igb_setup_tx_resources(struct igb_ring *);
+int igb_setup_rx_resources(struct igb_ring *);
+void igb_free_tx_resources(struct igb_ring *);
+void igb_free_rx_resources(struct igb_ring *);
+void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *);
+void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *);
+void igb_setup_tctl(struct igb_adapter *);
+void igb_setup_rctl(struct igb_adapter *);
+netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *);
+void igb_unmap_and_free_tx_resource(struct igb_ring *, struct igb_tx_buffer *);
+void igb_alloc_rx_buffers(struct igb_ring *, u16);
+void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *);
+bool igb_has_link(struct igb_adapter *adapter);
+void igb_set_ethtool_ops(struct net_device *);
+void igb_power_up_link(struct igb_adapter *);
+void igb_set_fw_version(struct igb_adapter *);
+void igb_ptp_init(struct igb_adapter *adapter);
+void igb_ptp_stop(struct igb_adapter *adapter);
+void igb_ptp_reset(struct igb_adapter *adapter);
+void igb_ptp_tx_work(struct work_struct *work);
+void igb_ptp_rx_hang(struct igb_adapter *adapter);
+void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter);
+void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb);
+void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va,
+                        struct sk_buff *skb);
 static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring,
                                       union e1000_adv_rx_desc *rx_desc,
                                       struct sk_buff *skb)
@@ -531,11 +529,11 @@ static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring,
        rx_ring->last_rx_timestamp = jiffies;
 }
 
-extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
-                                 struct ifreq *ifr, int cmd);
+int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr,
+                          int cmd);
 #ifdef CONFIG_IGB_HWMON
-extern void igb_sysfs_exit(struct igb_adapter *adapter);
-extern int igb_sysfs_init(struct igb_adapter *adapter);
+void igb_sysfs_exit(struct igb_adapter *adapter);
+int igb_sysfs_init(struct igb_adapter *adapter);
 #endif
 static inline s32 igb_reset_phy(struct e1000_hw *hw)
 {
index 48cbc83..b0f3666 100644 (file)
@@ -146,6 +146,7 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
        u32 status;
 
+       status = rd32(E1000_STATUS);
        if (hw->phy.media_type == e1000_media_type_copper) {
 
                ecmd->supported = (SUPPORTED_10baseT_Half |
@@ -169,13 +170,22 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ecmd->transceiver = XCVR_INTERNAL;
        } else {
                ecmd->supported = (SUPPORTED_FIBRE |
+                                  SUPPORTED_1000baseKX_Full |
                                   SUPPORTED_Autoneg |
                                   SUPPORTED_Pause);
-               ecmd->advertising = ADVERTISED_FIBRE;
-
-               if ((eth_flags->e1000_base_lx) || (eth_flags->e1000_base_sx)) {
-                       ecmd->supported |= SUPPORTED_1000baseT_Full;
-                       ecmd->advertising |= ADVERTISED_1000baseT_Full;
+               ecmd->advertising = (ADVERTISED_FIBRE |
+                                    ADVERTISED_1000baseKX_Full);
+               if (hw->mac.type == e1000_i354) {
+                       if ((hw->device_id ==
+                            E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) &&
+                           !(status & E1000_STATUS_2P5_SKU_OVER)) {
+                               ecmd->supported |= SUPPORTED_2500baseX_Full;
+                               ecmd->supported &=
+                                       ~SUPPORTED_1000baseKX_Full;
+                               ecmd->advertising |= ADVERTISED_2500baseX_Full;
+                               ecmd->advertising &=
+                                       ~ADVERTISED_1000baseKX_Full;
+                       }
                }
                if (eth_flags->e100_base_fx) {
                        ecmd->supported |= SUPPORTED_100baseT_Full;
@@ -187,35 +197,29 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ecmd->port = PORT_FIBRE;
                ecmd->transceiver = XCVR_EXTERNAL;
        }
-
        if (hw->mac.autoneg != 1)
                ecmd->advertising &= ~(ADVERTISED_Pause |
                                       ADVERTISED_Asym_Pause);
 
-       if (hw->fc.requested_mode == e1000_fc_full)
+       switch (hw->fc.requested_mode) {
+       case e1000_fc_full:
                ecmd->advertising |= ADVERTISED_Pause;
-       else if (hw->fc.requested_mode == e1000_fc_rx_pause)
+               break;
+       case e1000_fc_rx_pause:
                ecmd->advertising |= (ADVERTISED_Pause |
                                      ADVERTISED_Asym_Pause);
-       else if (hw->fc.requested_mode == e1000_fc_tx_pause)
+               break;
+       case e1000_fc_tx_pause:
                ecmd->advertising |=  ADVERTISED_Asym_Pause;
-       else
+               break;
+       default:
                ecmd->advertising &= ~(ADVERTISED_Pause |
                                       ADVERTISED_Asym_Pause);
-
-       status = rd32(E1000_STATUS);
-
+       }
        if (status & E1000_STATUS_LU) {
-               if (hw->mac.type == e1000_i354) {
-                       if ((status & E1000_STATUS_2P5_SKU) &&
-                           !(status & E1000_STATUS_2P5_SKU_OVER)) {
-                               ecmd->supported = SUPPORTED_2500baseX_Full;
-                               ecmd->advertising = ADVERTISED_2500baseX_Full;
-                               ecmd->speed = SPEED_2500;
-                       } else {
-                               ecmd->supported = SUPPORTED_1000baseT_Full;
-                               ecmd->advertising = ADVERTISED_1000baseT_Full;
-                       }
+               if ((status & E1000_STATUS_2P5_SKU) &&
+                   !(status & E1000_STATUS_2P5_SKU_OVER)) {
+                       ecmd->speed = SPEED_2500;
                } else if (status & E1000_STATUS_SPEED_1000) {
                        ecmd->speed = SPEED_1000;
                } else if (status & E1000_STATUS_SPEED_100) {
@@ -232,7 +236,6 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ecmd->speed = -1;
                ecmd->duplex = -1;
        }
-
        if ((hw->phy.media_type == e1000_media_type_fiber) ||
            hw->mac.autoneg)
                ecmd->autoneg = AUTONEG_ENABLE;
@@ -771,8 +774,10 @@ static int igb_set_eeprom(struct net_device *netdev,
        if (eeprom->len == 0)
                return -EOPNOTSUPP;
 
-       if (hw->mac.type == e1000_i211)
+       if ((hw->mac.type >= e1000_i210) &&
+           !igb_get_flash_presence_i210(hw)) {
                return -EOPNOTSUPP;
+       }
 
        if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
                return -EFAULT;
@@ -1607,6 +1612,9 @@ static int igb_integrated_phy_loopback(struct igb_adapter *adapter)
                        igb_write_phy_reg(hw, I347AT4_PAGE_SELECT, 0);
                        igb_write_phy_reg(hw, PHY_CONTROL, 0x4140);
                }
+       } else if (hw->phy.type == e1000_phy_82580) {
+               /* enable MII loopback */
+               igb_write_phy_reg(hw, I82580_PHY_LBK_CTRL, 0x8041);
        }
 
        /* add small delay to avoid loopback test failure */
@@ -1656,7 +1664,8 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
                if ((hw->device_id == E1000_DEV_ID_DH89XXCC_SGMII) ||
                (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) ||
                (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) ||
-               (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP)) {
+               (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP) ||
+               (hw->device_id == E1000_DEV_ID_I354_SGMII)) {
 
                        /* Enable DH89xxCC MPHY for near end loopback */
                        reg = rd32(E1000_MPHY_ADDR_CTL);
@@ -1722,7 +1731,8 @@ static void igb_loopback_cleanup(struct igb_adapter *adapter)
        if ((hw->device_id == E1000_DEV_ID_DH89XXCC_SGMII) ||
        (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) ||
        (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) ||
-       (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP)) {
+       (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP) ||
+       (hw->device_id == E1000_DEV_ID_I354_SGMII)) {
                u32 reg;
 
                /* Disable near end loopback on DH89xxCC */
@@ -2652,6 +2662,8 @@ static int igb_set_eee(struct net_device *netdev,
            (hw->phy.media_type != e1000_media_type_copper))
                return -EOPNOTSUPP;
 
+       memset(&eee_curr, 0, sizeof(struct ethtool_eee));
+
        ret_val = igb_get_eee(netdev, &eee_curr);
        if (ret_val)
                return ret_val;
@@ -2872,6 +2884,88 @@ static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir)
        return 0;
 }
 
+static unsigned int igb_max_channels(struct igb_adapter *adapter)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       unsigned int max_combined = 0;
+
+       switch (hw->mac.type) {
+       case e1000_i211:
+               max_combined = IGB_MAX_RX_QUEUES_I211;
+               break;
+       case e1000_82575:
+       case e1000_i210:
+               max_combined = IGB_MAX_RX_QUEUES_82575;
+               break;
+       case e1000_i350:
+               if (!!adapter->vfs_allocated_count) {
+                       max_combined = 1;
+                       break;
+               }
+               /* fall through */
+       case e1000_82576:
+               if (!!adapter->vfs_allocated_count) {
+                       max_combined = 2;
+                       break;
+               }
+               /* fall through */
+       case e1000_82580:
+       case e1000_i354:
+       default:
+               max_combined = IGB_MAX_RX_QUEUES;
+               break;
+       }
+
+       return max_combined;
+}
+
+static void igb_get_channels(struct net_device *netdev,
+                            struct ethtool_channels *ch)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+
+       /* Report maximum channels */
+       ch->max_combined = igb_max_channels(adapter);
+
+       /* Report info for other vector */
+       if (adapter->msix_entries) {
+               ch->max_other = NON_Q_VECTORS;
+               ch->other_count = NON_Q_VECTORS;
+       }
+
+       ch->combined_count = adapter->rss_queues;
+}
+
+static int igb_set_channels(struct net_device *netdev,
+                           struct ethtool_channels *ch)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       unsigned int count = ch->combined_count;
+
+       /* Verify they are not requesting separate vectors */
+       if (!count || ch->rx_count || ch->tx_count)
+               return -EINVAL;
+
+       /* Verify other_count is valid and has not been changed */
+       if (ch->other_count != NON_Q_VECTORS)
+               return -EINVAL;
+
+       /* Verify the number of channels doesn't exceed hw limits */
+       if (count > igb_max_channels(adapter))
+               return -EINVAL;
+
+       if (count != adapter->rss_queues) {
+               adapter->rss_queues = count;
+
+               /* Hardware has to reinitialize queues and interrupts to
+                * match the new configuration.
+                */
+               return igb_reinit_queues(adapter);
+       }
+
+       return 0;
+}
+
 static const struct ethtool_ops igb_ethtool_ops = {
        .get_settings           = igb_get_settings,
        .set_settings           = igb_set_settings,
@@ -2908,6 +3002,8 @@ static const struct ethtool_ops igb_ethtool_ops = {
        .get_rxfh_indir_size    = igb_get_rxfh_indir_size,
        .get_rxfh_indir         = igb_get_rxfh_indir,
        .set_rxfh_indir         = igb_set_rxfh_indir,
+       .get_channels           = igb_get_channels,
+       .set_channels           = igb_set_channels,
        .begin                  = igb_ethtool_begin,
        .complete               = igb_ethtool_complete,
 };
index 8cf44f2..ebe6370 100644 (file)
@@ -182,6 +182,7 @@ static void igb_check_vf_rate_limit(struct igb_adapter *);
 
 #ifdef CONFIG_PCI_IOV
 static int igb_vf_configure(struct igb_adapter *adapter, int vf);
+static int igb_pci_enable_sriov(struct pci_dev *dev, int num_vfs);
 #endif
 
 #ifdef CONFIG_PM
@@ -2429,7 +2430,7 @@ err_dma:
 }
 
 #ifdef CONFIG_PCI_IOV
-static int  igb_disable_sriov(struct pci_dev *pdev)
+static int igb_disable_sriov(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct igb_adapter *adapter = netdev_priv(netdev);
@@ -2470,27 +2471,19 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs)
        int err = 0;
        int i;
 
-       if (!adapter->msix_entries) {
+       if (!adapter->msix_entries || num_vfs > 7) {
                err = -EPERM;
                goto out;
        }
-
        if (!num_vfs)
                goto out;
-       else if (old_vfs && old_vfs == num_vfs)
-               goto out;
-       else if (old_vfs && old_vfs != num_vfs)
-               err = igb_disable_sriov(pdev);
-
-       if (err)
-               goto out;
-
-       if (num_vfs > 7) {
-               err = -EPERM;
-               goto out;
-       }
 
-       adapter->vfs_allocated_count = num_vfs;
+       if (old_vfs) {
+               dev_info(&pdev->dev, "%d pre-allocated VFs found - override max_vfs setting of %d\n",
+                        old_vfs, max_vfs);
+               adapter->vfs_allocated_count = old_vfs;
+       } else
+               adapter->vfs_allocated_count = num_vfs;
 
        adapter->vf_data = kcalloc(adapter->vfs_allocated_count,
                                sizeof(struct vf_data_storage), GFP_KERNEL);
@@ -2504,10 +2497,12 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs)
                goto out;
        }
 
-       err = pci_enable_sriov(pdev, adapter->vfs_allocated_count);
-       if (err)
-               goto err_out;
-
+       /* only call pci_enable_sriov() if no VFs are allocated already */
+       if (!old_vfs) {
+               err = pci_enable_sriov(pdev, adapter->vfs_allocated_count);
+               if (err)
+                       goto err_out;
+       }
        dev_info(&pdev->dev, "%d VFs allocated\n",
                 adapter->vfs_allocated_count);
        for (i = 0; i < adapter->vfs_allocated_count; i++)
@@ -2623,7 +2618,7 @@ static void igb_probe_vfs(struct igb_adapter *adapter)
                return;
 
        pci_sriov_set_totalvfs(pdev, 7);
-       igb_enable_sriov(pdev, max_vfs);
+       igb_pci_enable_sriov(pdev, max_vfs);
 
 #endif /* CONFIG_PCI_IOV */
 }
@@ -5708,7 +5703,7 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
 
        /* reply to reset with ack and vf mac address */
        msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK;
-       memcpy(addr, vf_mac, 6);
+       memcpy(addr, vf_mac, ETH_ALEN);
        igb_write_mbx(hw, msgbuf, 3, vf);
 }
 
@@ -7838,4 +7833,26 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
                return E1000_SUCCESS;
 
 }
+
+int igb_reinit_queues(struct igb_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       struct pci_dev *pdev = adapter->pdev;
+       int err = 0;
+
+       if (netif_running(netdev))
+               igb_close(netdev);
+
+       igb_clear_interrupt_scheme(adapter);
+
+       if (igb_init_interrupt_scheme(adapter, true)) {
+               dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
+               return -ENOMEM;
+       }
+
+       if (netif_running(netdev))
+               err = igb_open(netdev);
+
+       return err;
+}
 /* igb_main.c */
index a1463e3..7d6a25c 100644 (file)
@@ -312,17 +312,17 @@ enum igbvf_state_t {
 extern char igbvf_driver_name[];
 extern const char igbvf_driver_version[];
 
-extern void igbvf_check_options(struct igbvf_adapter *);
-extern void igbvf_set_ethtool_ops(struct net_device *);
-
-extern int igbvf_up(struct igbvf_adapter *);
-extern void igbvf_down(struct igbvf_adapter *);
-extern void igbvf_reinit_locked(struct igbvf_adapter *);
-extern int igbvf_setup_rx_resources(struct igbvf_adapter *, struct igbvf_ring *);
-extern int igbvf_setup_tx_resources(struct igbvf_adapter *, struct igbvf_ring *);
-extern void igbvf_free_rx_resources(struct igbvf_ring *);
-extern void igbvf_free_tx_resources(struct igbvf_ring *);
-extern void igbvf_update_stats(struct igbvf_adapter *);
+void igbvf_check_options(struct igbvf_adapter *);
+void igbvf_set_ethtool_ops(struct net_device *);
+
+int igbvf_up(struct igbvf_adapter *);
+void igbvf_down(struct igbvf_adapter *);
+void igbvf_reinit_locked(struct igbvf_adapter *);
+int igbvf_setup_rx_resources(struct igbvf_adapter *, struct igbvf_ring *);
+int igbvf_setup_tx_resources(struct igbvf_adapter *, struct igbvf_ring *);
+void igbvf_free_rx_resources(struct igbvf_ring *);
+void igbvf_free_tx_resources(struct igbvf_ring *);
+void igbvf_update_stats(struct igbvf_adapter *);
 
 extern unsigned int copybreak;
 
index 93eb7ee..9fadbb2 100644 (file)
@@ -2343,10 +2343,9 @@ static int igbvf_change_mtu(struct net_device *netdev, int new_mtu)
        struct igbvf_adapter *adapter = netdev_priv(netdev);
        int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
 
-       if ((new_mtu < 68) || (max_frame > MAX_JUMBO_FRAME_SIZE)) {
-               dev_err(&adapter->pdev->dev, "Invalid MTU setting\n");
+       if (new_mtu < 68 || new_mtu > INT_MAX - ETH_HLEN - ETH_FCS_LEN ||
+           max_frame > MAX_JUMBO_FRAME_SIZE)
                return -EINVAL;
-       }
 
 #define MAX_STD_JUMBO_FRAME_SIZE 9234
        if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) {
@@ -2699,7 +2698,7 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ei->get_variants) {
                err = ei->get_variants(adapter);
                if (err)
-                       goto err_ioremap;
+                       goto err_get_variants;
        }
 
        /* setup adapter struct */
@@ -2796,6 +2795,7 @@ err_hw_init:
        kfree(adapter->rx_ring);
 err_sw_init:
        igbvf_reset_interrupt_capability(adapter);
+err_get_variants:
        iounmap(adapter->hw.hw_addr);
 err_ioremap:
        free_netdev(netdev);
index eea0e10..955ad8c 100644 (file)
@@ -154,7 +154,7 @@ static s32 e1000_reset_hw_vf(struct e1000_hw *hw)
                ret_val = mbx->ops.read_posted(hw, msgbuf, 3);
                if (!ret_val) {
                        if (msgbuf[0] == (E1000_VF_RESET | E1000_VT_MSGTYPE_ACK))
-                               memcpy(hw->mac.perm_addr, addr, 6);
+                               memcpy(hw->mac.perm_addr, addr, ETH_ALEN);
                        else
                                ret_val = -E1000_ERR_MAC_INIT;
                }
@@ -314,7 +314,7 @@ static void e1000_rar_set_vf(struct e1000_hw *hw, u8 * addr, u32 index)
 
        memset(msgbuf, 0, 12);
        msgbuf[0] = E1000_VF_SET_MAC_ADDR;
-       memcpy(msg_addr, addr, 6);
+       memcpy(msg_addr, addr, ETH_ALEN);
        ret_val = mbx->ops.write_posted(hw, msgbuf, 3);
 
        if (!ret_val)
index 4d2ae97..2224cc2 100644 (file)
@@ -187,21 +187,21 @@ enum ixgb_state_t {
 };
 
 /* Exported from other modules */
-extern void ixgb_check_options(struct ixgb_adapter *adapter);
-extern void ixgb_set_ethtool_ops(struct net_device *netdev);
+void ixgb_check_options(struct ixgb_adapter *adapter);
+void ixgb_set_ethtool_ops(struct net_device *netdev);
 extern char ixgb_driver_name[];
 extern const char ixgb_driver_version[];
 
-extern void ixgb_set_speed_duplex(struct net_device *netdev);
+void ixgb_set_speed_duplex(struct net_device *netdev);
 
-extern int ixgb_up(struct ixgb_adapter *adapter);
-extern void ixgb_down(struct ixgb_adapter *adapter, bool kill_watchdog);
-extern void ixgb_reset(struct ixgb_adapter *adapter);
-extern int ixgb_setup_rx_resources(struct ixgb_adapter *adapter);
-extern int ixgb_setup_tx_resources(struct ixgb_adapter *adapter);
-extern void ixgb_free_rx_resources(struct ixgb_adapter *adapter);
-extern void ixgb_free_tx_resources(struct ixgb_adapter *adapter);
-extern void ixgb_update_stats(struct ixgb_adapter *adapter);
+int ixgb_up(struct ixgb_adapter *adapter);
+void ixgb_down(struct ixgb_adapter *adapter, bool kill_watchdog);
+void ixgb_reset(struct ixgb_adapter *adapter);
+int ixgb_setup_rx_resources(struct ixgb_adapter *adapter);
+int ixgb_setup_tx_resources(struct ixgb_adapter *adapter);
+void ixgb_free_rx_resources(struct ixgb_adapter *adapter);
+void ixgb_free_tx_resources(struct ixgb_adapter *adapter);
+void ixgb_update_stats(struct ixgb_adapter *adapter);
 
 
 #endif /* _IXGB_H_ */
index 2a99a35..0bd5d72 100644 (file)
@@ -759,27 +759,20 @@ struct ixgb_hw_stats {
 };
 
 /* Function Prototypes */
-extern bool ixgb_adapter_stop(struct ixgb_hw *hw);
-extern bool ixgb_init_hw(struct ixgb_hw *hw);
-extern bool ixgb_adapter_start(struct ixgb_hw *hw);
-extern void ixgb_check_for_link(struct ixgb_hw *hw);
-extern bool ixgb_check_for_bad_link(struct ixgb_hw *hw);
-
-extern void ixgb_rar_set(struct ixgb_hw *hw,
-                               u8 *addr,
-                               u32 index);
+bool ixgb_adapter_stop(struct ixgb_hw *hw);
+bool ixgb_init_hw(struct ixgb_hw *hw);
+bool ixgb_adapter_start(struct ixgb_hw *hw);
+void ixgb_check_for_link(struct ixgb_hw *hw);
+bool ixgb_check_for_bad_link(struct ixgb_hw *hw);
 
+void ixgb_rar_set(struct ixgb_hw *hw, u8 *addr, u32 index);
 
 /* Filters (multicast, vlan, receive) */
-extern void ixgb_mc_addr_list_update(struct ixgb_hw *hw,
-                                  u8 *mc_addr_list,
-                                  u32 mc_addr_count,
-                                  u32 pad);
+void ixgb_mc_addr_list_update(struct ixgb_hw *hw, u8 *mc_addr_list,
+                             u32 mc_addr_count, u32 pad);
 
 /* Vfta functions */
-extern void ixgb_write_vfta(struct ixgb_hw *hw,
-                                u32 offset,
-                                u32 value);
+void ixgb_write_vfta(struct ixgb_hw *hw, u32 offset, u32 value);
 
 /* Access functions to eeprom data */
 void ixgb_get_ee_mac_addr(struct ixgb_hw *hw, u8 *mac_addr);
index 0ac6b11..f38fc0a 100644 (file)
@@ -55,7 +55,7 @@
 #include <net/busy_poll.h>
 
 #ifdef CONFIG_NET_RX_BUSY_POLL
-#define LL_EXTENDED_STATS
+#define BP_EXTENDED_STATS
 #endif
 /* common prefix used by pr_<> macros */
 #undef pr_fmt
 #define IXGBE_MAX_TXD                     4096
 #define IXGBE_MIN_TXD                       64
 
+#if (PAGE_SIZE < 8192)
 #define IXGBE_DEFAULT_RXD                  512
+#else
+#define IXGBE_DEFAULT_RXD                  128
+#endif
 #define IXGBE_MAX_RXD                     4096
 #define IXGBE_MIN_RXD                       64
 
@@ -187,11 +191,11 @@ struct ixgbe_rx_buffer {
 struct ixgbe_queue_stats {
        u64 packets;
        u64 bytes;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
        u64 yields;
        u64 misses;
        u64 cleaned;
-#endif  /* LL_EXTENDED_STATS */
+#endif  /* BP_EXTENDED_STATS */
 };
 
 struct ixgbe_tx_queue_stats {
@@ -219,6 +223,15 @@ enum ixgbe_ring_state_t {
        __IXGBE_RX_FCOE,
 };
 
+struct ixgbe_fwd_adapter {
+       unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+       struct net_device *netdev;
+       struct ixgbe_adapter *real_adapter;
+       unsigned int tx_base_queue;
+       unsigned int rx_base_queue;
+       int pool;
+};
+
 #define check_for_tx_hang(ring) \
        test_bit(__IXGBE_TX_DETECT_HANG, &(ring)->state)
 #define set_check_for_tx_hang(ring) \
@@ -236,6 +249,7 @@ struct ixgbe_ring {
        struct ixgbe_q_vector *q_vector; /* backpointer to host q_vector */
        struct net_device *netdev;      /* netdev ring belongs to */
        struct device *dev;             /* device for DMA mapping */
+       struct ixgbe_fwd_adapter *l2_accel_priv;
        void *desc;                     /* descriptor ring memory */
        union {
                struct ixgbe_tx_buffer *tx_buffer_info;
@@ -293,6 +307,12 @@ enum ixgbe_ring_f_enum {
 #define IXGBE_MAX_FCOE_INDICES  8
 #define MAX_RX_QUEUES (IXGBE_MAX_FDIR_INDICES + 1)
 #define MAX_TX_QUEUES (IXGBE_MAX_FDIR_INDICES + 1)
+#define IXGBE_MAX_L2A_QUEUES 4
+#define IXGBE_MAX_L2A_QUEUES 4
+#define IXGBE_BAD_L2A_QUEUE 3
+#define IXGBE_MAX_MACVLANS     31
+#define IXGBE_MAX_DCBMACVLANS  8
+
 struct ixgbe_ring_feature {
        u16 limit;      /* upper limit on feature indices */
        u16 indices;    /* current value of indices */
@@ -369,11 +389,13 @@ struct ixgbe_q_vector {
 #ifdef CONFIG_NET_RX_BUSY_POLL
        unsigned int state;
 #define IXGBE_QV_STATE_IDLE        0
-#define IXGBE_QV_STATE_NAPI       1    /* NAPI owns this QV */
-#define IXGBE_QV_STATE_POLL       2    /* poll owns this QV */
-#define IXGBE_QV_LOCKED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL)
-#define IXGBE_QV_STATE_NAPI_YIELD  4    /* NAPI yielded this QV */
-#define IXGBE_QV_STATE_POLL_YIELD  8    /* poll yielded this QV */
+#define IXGBE_QV_STATE_NAPI       1     /* NAPI owns this QV */
+#define IXGBE_QV_STATE_POLL       2     /* poll owns this QV */
+#define IXGBE_QV_STATE_DISABLED           4     /* QV is disabled */
+#define IXGBE_QV_OWNED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL)
+#define IXGBE_QV_LOCKED (IXGBE_QV_OWNED | IXGBE_QV_STATE_DISABLED)
+#define IXGBE_QV_STATE_NAPI_YIELD  8     /* NAPI yielded this QV */
+#define IXGBE_QV_STATE_POLL_YIELD  16    /* poll yielded this QV */
 #define IXGBE_QV_YIELD (IXGBE_QV_STATE_NAPI_YIELD | IXGBE_QV_STATE_POLL_YIELD)
 #define IXGBE_QV_USER_PEND (IXGBE_QV_STATE_POLL | IXGBE_QV_STATE_POLL_YIELD)
        spinlock_t lock;
@@ -394,18 +416,18 @@ static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector)
 static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector)
 {
        int rc = true;
-       spin_lock(&q_vector->lock);
+       spin_lock_bh(&q_vector->lock);
        if (q_vector->state & IXGBE_QV_LOCKED) {
                WARN_ON(q_vector->state & IXGBE_QV_STATE_NAPI);
                q_vector->state |= IXGBE_QV_STATE_NAPI_YIELD;
                rc = false;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                q_vector->tx.ring->stats.yields++;
 #endif
        } else
                /* we don't care if someone yielded */
                q_vector->state = IXGBE_QV_STATE_NAPI;
-       spin_unlock(&q_vector->lock);
+       spin_unlock_bh(&q_vector->lock);
        return rc;
 }
 
@@ -413,14 +435,15 @@ static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector)
 static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector)
 {
        int rc = false;
-       spin_lock(&q_vector->lock);
+       spin_lock_bh(&q_vector->lock);
        WARN_ON(q_vector->state & (IXGBE_QV_STATE_POLL |
                               IXGBE_QV_STATE_NAPI_YIELD));
 
        if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD)
                rc = true;
-       q_vector->state = IXGBE_QV_STATE_IDLE;
-       spin_unlock(&q_vector->lock);
+       /* will reset state to idle, unless QV is disabled */
+       q_vector->state &= IXGBE_QV_STATE_DISABLED;
+       spin_unlock_bh(&q_vector->lock);
        return rc;
 }
 
@@ -432,7 +455,7 @@ static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector)
        if ((q_vector->state & IXGBE_QV_LOCKED)) {
                q_vector->state |= IXGBE_QV_STATE_POLL_YIELD;
                rc = false;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                q_vector->rx.ring->stats.yields++;
 #endif
        } else
@@ -451,17 +474,32 @@ static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector)
 
        if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD)
                rc = true;
-       q_vector->state = IXGBE_QV_STATE_IDLE;
+       /* will reset state to idle, unless QV is disabled */
+       q_vector->state &= IXGBE_QV_STATE_DISABLED;
        spin_unlock_bh(&q_vector->lock);
        return rc;
 }
 
 /* true if a socket is polling, even if it did not get the lock */
-static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector)
+static inline bool ixgbe_qv_busy_polling(struct ixgbe_q_vector *q_vector)
 {
-       WARN_ON(!(q_vector->state & IXGBE_QV_LOCKED));
+       WARN_ON(!(q_vector->state & IXGBE_QV_OWNED));
        return q_vector->state & IXGBE_QV_USER_PEND;
 }
+
+/* false if QV is currently owned */
+static inline bool ixgbe_qv_disable(struct ixgbe_q_vector *q_vector)
+{
+       int rc = true;
+       spin_lock_bh(&q_vector->lock);
+       if (q_vector->state & IXGBE_QV_OWNED)
+               rc = false;
+       q_vector->state |= IXGBE_QV_STATE_DISABLED;
+       spin_unlock_bh(&q_vector->lock);
+
+       return rc;
+}
+
 #else /* CONFIG_NET_RX_BUSY_POLL */
 static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector)
 {
@@ -487,10 +525,16 @@ static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector)
        return false;
 }
 
-static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector)
+static inline bool ixgbe_qv_busy_polling(struct ixgbe_q_vector *q_vector)
 {
        return false;
 }
+
+static inline bool ixgbe_qv_disable(struct ixgbe_q_vector *q_vector)
+{
+       return true;
+}
+
 #endif /* CONFIG_NET_RX_BUSY_POLL */
 
 #ifdef CONFIG_IXGBE_HWMON
@@ -738,6 +782,7 @@ struct ixgbe_adapter {
 #endif /*CONFIG_DEBUG_FS*/
 
        u8 default_up;
+       unsigned long fwd_bitmask; /* Bitmask indicating in use pools */
 };
 
 struct ixgbe_fdir_filter {
@@ -786,93 +831,89 @@ extern const char ixgbe_driver_version[];
 extern char ixgbe_default_device_descr[];
 #endif /* IXGBE_FCOE */
 
-extern void ixgbe_up(struct ixgbe_adapter *adapter);
-extern void ixgbe_down(struct ixgbe_adapter *adapter);
-extern void ixgbe_reinit_locked(struct ixgbe_adapter *adapter);
-extern void ixgbe_reset(struct ixgbe_adapter *adapter);
-extern void ixgbe_set_ethtool_ops(struct net_device *netdev);
-extern int ixgbe_setup_rx_resources(struct ixgbe_ring *);
-extern int ixgbe_setup_tx_resources(struct ixgbe_ring *);
-extern void ixgbe_free_rx_resources(struct ixgbe_ring *);
-extern void ixgbe_free_tx_resources(struct ixgbe_ring *);
-extern void ixgbe_configure_rx_ring(struct ixgbe_adapter *,struct ixgbe_ring *);
-extern void ixgbe_configure_tx_ring(struct ixgbe_adapter *,struct ixgbe_ring *);
-extern void ixgbe_disable_rx_queue(struct ixgbe_adapter *adapter,
-                                  struct ixgbe_ring *);
-extern void ixgbe_update_stats(struct ixgbe_adapter *adapter);
-extern int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter);
-extern int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
+void ixgbe_up(struct ixgbe_adapter *adapter);
+void ixgbe_down(struct ixgbe_adapter *adapter);
+void ixgbe_reinit_locked(struct ixgbe_adapter *adapter);
+void ixgbe_reset(struct ixgbe_adapter *adapter);
+void ixgbe_set_ethtool_ops(struct net_device *netdev);
+int ixgbe_setup_rx_resources(struct ixgbe_ring *);
+int ixgbe_setup_tx_resources(struct ixgbe_ring *);
+void ixgbe_free_rx_resources(struct ixgbe_ring *);
+void ixgbe_free_tx_resources(struct ixgbe_ring *);
+void ixgbe_configure_rx_ring(struct ixgbe_adapter *, struct ixgbe_ring *);
+void ixgbe_configure_tx_ring(struct ixgbe_adapter *, struct ixgbe_ring *);
+void ixgbe_disable_rx_queue(struct ixgbe_adapter *adapter, struct ixgbe_ring *);
+void ixgbe_update_stats(struct ixgbe_adapter *adapter);
+int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter);
+int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
                               u16 subdevice_id);
-extern void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter);
-extern netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *,
-                                        struct ixgbe_adapter *,
-                                        struct ixgbe_ring *);
-extern void ixgbe_unmap_and_free_tx_resource(struct ixgbe_ring *,
-                                             struct ixgbe_tx_buffer *);
-extern void ixgbe_alloc_rx_buffers(struct ixgbe_ring *, u16);
-extern void ixgbe_write_eitr(struct ixgbe_q_vector *);
-extern int ixgbe_poll(struct napi_struct *napi, int budget);
-extern int ethtool_ioctl(struct ifreq *ifr);
-extern s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw);
-extern s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl);
-extern s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl);
-extern s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
-                                                union ixgbe_atr_hash_dword input,
-                                                union ixgbe_atr_hash_dword common,
-                                                 u8 queue);
-extern s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
-                                          union ixgbe_atr_input *input_mask);
-extern s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
-                                                union ixgbe_atr_input *input,
-                                                u16 soft_id, u8 queue);
-extern s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw,
-                                                union ixgbe_atr_input *input,
-                                                u16 soft_id);
-extern void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
-                                                union ixgbe_atr_input *mask);
-extern bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw);
-extern void ixgbe_set_rx_mode(struct net_device *netdev);
+void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter);
+netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *,
+                                 struct ixgbe_ring *);
+void ixgbe_unmap_and_free_tx_resource(struct ixgbe_ring *,
+                                     struct ixgbe_tx_buffer *);
+void ixgbe_alloc_rx_buffers(struct ixgbe_ring *, u16);
+void ixgbe_write_eitr(struct ixgbe_q_vector *);
+int ixgbe_poll(struct napi_struct *napi, int budget);
+int ethtool_ioctl(struct ifreq *ifr);
+s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw);
+s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl);
+s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl);
+s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
+                                         union ixgbe_atr_hash_dword input,
+                                         union ixgbe_atr_hash_dword common,
+                                         u8 queue);
+s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
+                                   union ixgbe_atr_input *input_mask);
+s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
+                                         union ixgbe_atr_input *input,
+                                         u16 soft_id, u8 queue);
+s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw,
+                                         union ixgbe_atr_input *input,
+                                         u16 soft_id);
+void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
+                                         union ixgbe_atr_input *mask);
+bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw);
+void ixgbe_set_rx_mode(struct net_device *netdev);
 #ifdef CONFIG_IXGBE_DCB
-extern void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter);
+void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter);
 #endif
-extern int ixgbe_setup_tc(struct net_device *dev, u8 tc);
-extern void ixgbe_tx_ctxtdesc(struct ixgbe_ring *, u32, u32, u32, u32);
-extern void ixgbe_do_reset(struct net_device *netdev);
+int ixgbe_setup_tc(struct net_device *dev, u8 tc);
+void ixgbe_tx_ctxtdesc(struct ixgbe_ring *, u32, u32, u32, u32);
+void ixgbe_do_reset(struct net_device *netdev);
 #ifdef CONFIG_IXGBE_HWMON
-extern void ixgbe_sysfs_exit(struct ixgbe_adapter *adapter);
-extern int ixgbe_sysfs_init(struct ixgbe_adapter *adapter);
+void ixgbe_sysfs_exit(struct ixgbe_adapter *adapter);
+int ixgbe_sysfs_init(struct ixgbe_adapter *adapter);
 #endif /* CONFIG_IXGBE_HWMON */
 #ifdef IXGBE_FCOE
-extern void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter);
-extern int ixgbe_fso(struct ixgbe_ring *tx_ring,
-                    struct ixgbe_tx_buffer *first,
-                    u8 *hdr_len);
-extern int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
-                         union ixgbe_adv_rx_desc *rx_desc,
-                         struct sk_buff *skb);
-extern int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid,
-                              struct scatterlist *sgl, unsigned int sgc);
-extern int ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid,
-                                struct scatterlist *sgl, unsigned int sgc);
-extern int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid);
-extern int ixgbe_setup_fcoe_ddp_resources(struct ixgbe_adapter *adapter);
-extern void ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter);
-extern int ixgbe_fcoe_enable(struct net_device *netdev);
-extern int ixgbe_fcoe_disable(struct net_device *netdev);
+void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter);
+int ixgbe_fso(struct ixgbe_ring *tx_ring, struct ixgbe_tx_buffer *first,
+             u8 *hdr_len);
+int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
+                  union ixgbe_adv_rx_desc *rx_desc, struct sk_buff *skb);
+int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid,
+                      struct scatterlist *sgl, unsigned int sgc);
+int ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid,
+                         struct scatterlist *sgl, unsigned int sgc);
+int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid);
+int ixgbe_setup_fcoe_ddp_resources(struct ixgbe_adapter *adapter);
+void ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter);
+int ixgbe_fcoe_enable(struct net_device *netdev);
+int ixgbe_fcoe_disable(struct net_device *netdev);
 #ifdef CONFIG_IXGBE_DCB
-extern u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter);
-extern u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up);
+u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter);
+u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up);
 #endif /* CONFIG_IXGBE_DCB */
-extern int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type);
-extern int ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
-                                 struct netdev_fcoe_hbainfo *info);
-extern u8 ixgbe_fcoe_get_tc(struct ixgbe_adapter *adapter);
+int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type);
+int ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
+                          struct netdev_fcoe_hbainfo *info);
+u8 ixgbe_fcoe_get_tc(struct ixgbe_adapter *adapter);
 #endif /* IXGBE_FCOE */
 #ifdef CONFIG_DEBUG_FS
-extern void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter);
-extern void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter);
-extern void ixgbe_dbg_init(void);
-extern void ixgbe_dbg_exit(void);
+void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter);
+void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter);
+void ixgbe_dbg_init(void);
+void ixgbe_dbg_exit(void);
 #else
 static inline void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter) {}
 static inline void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter) {}
@@ -884,12 +925,12 @@ static inline struct netdev_queue *txring_txq(const struct ixgbe_ring *ring)
        return netdev_get_tx_queue(ring->netdev, ring->queue_index);
 }
 
-extern void ixgbe_ptp_init(struct ixgbe_adapter *adapter);
-extern void ixgbe_ptp_stop(struct ixgbe_adapter *adapter);
-extern void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter);
-extern void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter);
-extern void __ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector,
-                                   struct sk_buff *skb);
+void ixgbe_ptp_init(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_stop(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter);
+void __ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector,
+                            struct sk_buff *skb);
 static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring,
                                         union ixgbe_adv_rx_desc *rx_desc,
                                         struct sk_buff *skb)
@@ -906,13 +947,16 @@ static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring,
        rx_ring->last_rx_timestamp = jiffies;
 }
 
-extern int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter,
-                                   struct ifreq *ifr, int cmd);
-extern void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter);
-extern void ixgbe_ptp_reset(struct ixgbe_adapter *adapter);
-extern void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr);
+int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, struct ifreq *ifr,
+                            int cmd);
+void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_reset(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr);
 #ifdef CONFIG_PCI_IOV
 void ixgbe_sriov_reinit(struct ixgbe_adapter *adapter);
 #endif
 
+netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
+                                 struct ixgbe_adapter *adapter,
+                                 struct ixgbe_ring *tx_ring);
 #endif /* _IXGBE_H_ */
index e8649ab..4e7c9b0 100644 (file)
@@ -442,7 +442,7 @@ static void ixgbe_set_msglevel(struct net_device *netdev, u32 data)
 
 static int ixgbe_get_regs_len(struct net_device *netdev)
 {
-#define IXGBE_REGS_LEN  1129
+#define IXGBE_REGS_LEN  1139
        return IXGBE_REGS_LEN * sizeof(u32);
 }
 
@@ -602,22 +602,53 @@ static void ixgbe_get_regs(struct net_device *netdev,
        regs_buff[828] = IXGBE_READ_REG(hw, IXGBE_FHFT(0));
 
        /* DCB */
-       regs_buff[829] = IXGBE_READ_REG(hw, IXGBE_RMCS);
-       regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_DPMCS);
-       regs_buff[831] = IXGBE_READ_REG(hw, IXGBE_PDPMCS);
-       regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RUPPBMR);
-       for (i = 0; i < 8; i++)
-               regs_buff[833 + i] = IXGBE_READ_REG(hw, IXGBE_RT2CR(i));
-       for (i = 0; i < 8; i++)
-               regs_buff[841 + i] = IXGBE_READ_REG(hw, IXGBE_RT2SR(i));
-       for (i = 0; i < 8; i++)
-               regs_buff[849 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCCR(i));
-       for (i = 0; i < 8; i++)
-               regs_buff[857 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCSR(i));
+       regs_buff[829] = IXGBE_READ_REG(hw, IXGBE_RMCS);   /* same as FCCFG  */
+       regs_buff[831] = IXGBE_READ_REG(hw, IXGBE_PDPMCS); /* same as RTTPCS */
+
+       switch (hw->mac.type) {
+       case ixgbe_mac_82598EB:
+               regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_DPMCS);
+               regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RUPPBMR);
+               for (i = 0; i < 8; i++)
+                       regs_buff[833 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_RT2CR(i));
+               for (i = 0; i < 8; i++)
+                       regs_buff[841 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_RT2SR(i));
+               for (i = 0; i < 8; i++)
+                       regs_buff[849 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_TDTQ2TCCR(i));
+               for (i = 0; i < 8; i++)
+                       regs_buff[857 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_TDTQ2TCSR(i));
+               break;
+       case ixgbe_mac_82599EB:
+       case ixgbe_mac_X540:
+               regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_RTTDCS);
+               regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RTRPCS);
+               for (i = 0; i < 8; i++)
+                       regs_buff[833 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_RTRPT4C(i));
+               for (i = 0; i < 8; i++)
+                       regs_buff[841 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_RTRPT4S(i));
+               for (i = 0; i < 8; i++)
+                       regs_buff[849 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_RTTDT2C(i));
+               for (i = 0; i < 8; i++)
+                       regs_buff[857 + i] =
+                               IXGBE_READ_REG(hw, IXGBE_RTTDT2S(i));
+               break;
+       default:
+               break;
+       }
+
        for (i = 0; i < 8; i++)
-               regs_buff[865 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCCR(i));
+               regs_buff[865 + i] =
+               IXGBE_READ_REG(hw, IXGBE_TDPT2TCCR(i)); /* same as RTTPT2C */
        for (i = 0; i < 8; i++)
-               regs_buff[873 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCSR(i));
+               regs_buff[873 + i] =
+               IXGBE_READ_REG(hw, IXGBE_TDPT2TCSR(i)); /* same as RTTPT2S */
 
        /* Statistics */
        regs_buff[881] = IXGBE_GET_STAT(adapter, crcerrs);
@@ -757,6 +788,20 @@ static void ixgbe_get_regs(struct net_device *netdev,
 
        /* 82599 X540 specific registers  */
        regs_buff[1128] = IXGBE_READ_REG(hw, IXGBE_MFLCN);
+
+       /* 82599 X540 specific DCB registers  */
+       regs_buff[1129] = IXGBE_READ_REG(hw, IXGBE_RTRUP2TC);
+       regs_buff[1130] = IXGBE_READ_REG(hw, IXGBE_RTTUP2TC);
+       for (i = 0; i < 4; i++)
+               regs_buff[1131 + i] = IXGBE_READ_REG(hw, IXGBE_TXLLQ(i));
+       regs_buff[1135] = IXGBE_READ_REG(hw, IXGBE_RTTBCNRM);
+                                       /* same as RTTQCNRM */
+       regs_buff[1136] = IXGBE_READ_REG(hw, IXGBE_RTTBCNRD);
+                                       /* same as RTTQCNRR */
+
+       /* X540 specific DCB registers  */
+       regs_buff[1137] = IXGBE_READ_REG(hw, IXGBE_RTTQCNCR);
+       regs_buff[1138] = IXGBE_READ_REG(hw, IXGBE_RTTQCNTG);
 }
 
 static int ixgbe_get_eeprom_len(struct net_device *netdev)
@@ -1072,7 +1117,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i] = 0;
                        data[i+1] = 0;
                        i += 2;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                        data[i] = 0;
                        data[i+1] = 0;
                        data[i+2] = 0;
@@ -1087,7 +1132,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i+1] = ring->stats.bytes;
                } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
                i += 2;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                data[i] = ring->stats.yields;
                data[i+1] = ring->stats.misses;
                data[i+2] = ring->stats.cleaned;
@@ -1100,7 +1145,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i] = 0;
                        data[i+1] = 0;
                        i += 2;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                        data[i] = 0;
                        data[i+1] = 0;
                        data[i+2] = 0;
@@ -1115,7 +1160,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i+1] = ring->stats.bytes;
                } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
                i += 2;
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                data[i] = ring->stats.yields;
                data[i+1] = ring->stats.misses;
                data[i+2] = ring->stats.cleaned;
@@ -1157,28 +1202,28 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
                        p += ETH_GSTRING_LEN;
                        sprintf(p, "tx_queue_%u_bytes", i);
                        p += ETH_GSTRING_LEN;
-#ifdef LL_EXTENDED_STATS
-                       sprintf(p, "tx_queue_%u_ll_napi_yield", i);
+#ifdef BP_EXTENDED_STATS
+                       sprintf(p, "tx_queue_%u_bp_napi_yield", i);
                        p += ETH_GSTRING_LEN;
-                       sprintf(p, "tx_queue_%u_ll_misses", i);
+                       sprintf(p, "tx_queue_%u_bp_misses", i);
                        p += ETH_GSTRING_LEN;
-                       sprintf(p, "tx_queue_%u_ll_cleaned", i);
+                       sprintf(p, "tx_queue_%u_bp_cleaned", i);
                        p += ETH_GSTRING_LEN;
-#endif /* LL_EXTENDED_STATS */
+#endif /* BP_EXTENDED_STATS */
                }
                for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) {
                        sprintf(p, "rx_queue_%u_packets", i);
                        p += ETH_GSTRING_LEN;
                        sprintf(p, "rx_queue_%u_bytes", i);
                        p += ETH_GSTRING_LEN;
-#ifdef LL_EXTENDED_STATS
-                       sprintf(p, "rx_queue_%u_ll_poll_yield", i);
+#ifdef BP_EXTENDED_STATS
+                       sprintf(p, "rx_queue_%u_bp_poll_yield", i);
                        p += ETH_GSTRING_LEN;
-                       sprintf(p, "rx_queue_%u_ll_misses", i);
+                       sprintf(p, "rx_queue_%u_bp_misses", i);
                        p += ETH_GSTRING_LEN;
-                       sprintf(p, "rx_queue_%u_ll_cleaned", i);
+                       sprintf(p, "rx_queue_%u_bp_cleaned", i);
                        p += ETH_GSTRING_LEN;
-#endif /* LL_EXTENDED_STATS */
+#endif /* BP_EXTENDED_STATS */
                }
                for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) {
                        sprintf(p, "tx_pb_%u_pxon", i);
@@ -2212,13 +2257,13 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
 
 #if IS_ENABLED(CONFIG_BQL)
        /* detect ITR changes that require update of TXDCTL.WTHRESH */
-       if ((adapter->tx_itr_setting > 1) &&
+       if ((adapter->tx_itr_setting != 1) &&
            (adapter->tx_itr_setting < IXGBE_100K_ITR)) {
                if ((tx_itr_prev == 1) ||
-                   (tx_itr_prev > IXGBE_100K_ITR))
+                   (tx_itr_prev >= IXGBE_100K_ITR))
                        need_reset = true;
        } else {
-               if ((tx_itr_prev > 1) &&
+               if ((tx_itr_prev != 1) &&
                    (tx_itr_prev < IXGBE_100K_ITR))
                        need_reset = true;
        }
index 90b4e10..32e3eaa 100644 (file)
@@ -498,6 +498,7 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter)
 #ifdef IXGBE_FCOE
        u16 fcoe_i = 0;
 #endif
+       bool pools = (find_first_zero_bit(&adapter->fwd_bitmask, 32) > 1);
 
        /* only proceed if SR-IOV is enabled */
        if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
@@ -510,7 +511,7 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter)
        vmdq_i = min_t(u16, IXGBE_MAX_VMDQ_INDICES, vmdq_i);
 
        /* 64 pool mode with 2 queues per pool */
-       if ((vmdq_i > 32) || (rss_i < 4)) {
+       if ((vmdq_i > 32) || (rss_i < 4) || (vmdq_i > 16 && pools)) {
                vmdq_m = IXGBE_82599_VMDQ_2Q_MASK;
                rss_m = IXGBE_RSS_2Q_MASK;
                rss_i = min_t(u16, rss_i, 2);
@@ -852,7 +853,11 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
 
                /* apply Tx specific ring traits */
                ring->count = adapter->tx_ring_count;
-               ring->queue_index = txr_idx;
+               if (adapter->num_rx_pools > 1)
+                       ring->queue_index =
+                               txr_idx % adapter->num_rx_queues_per_pool;
+               else
+                       ring->queue_index = txr_idx;
 
                /* assign ring to adapter */
                adapter->tx_ring[txr_idx] = ring;
@@ -895,7 +900,11 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
 #endif /* IXGBE_FCOE */
                /* apply Rx specific ring traits */
                ring->count = adapter->rx_ring_count;
-               ring->queue_index = rxr_idx;
+               if (adapter->num_rx_pools > 1)
+                       ring->queue_index =
+                               rxr_idx % adapter->num_rx_queues_per_pool;
+               else
+                       ring->queue_index = rxr_idx;
 
                /* assign ring to adapter */
                adapter->rx_ring[rxr_idx] = ring;
index 0ade0cd..bd8f523 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/ethtool.h>
 #include <linux/if.h>
 #include <linux/if_vlan.h>
+#include <linux/if_macvlan.h>
 #include <linux/if_bridge.h>
 #include <linux/prefetch.h>
 #include <scsi/fc/fc_fcoe.h>
@@ -132,7 +133,7 @@ static struct notifier_block dca_notifier = {
 static unsigned int max_vfs;
 module_param(max_vfs, uint, 0);
 MODULE_PARM_DESC(max_vfs,
-                "Maximum number of virtual functions to allocate per physical function - default is zero and maximum value is 63");
+                "Maximum number of virtual functions to allocate per physical function - default is zero and maximum value is 63. (Deprecated)");
 #endif /* CONFIG_PCI_IOV */
 
 static unsigned int allow_unsupported_sfp;
@@ -153,7 +154,6 @@ MODULE_VERSION(DRV_VERSION);
 static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
                                          u32 reg, u16 *value)
 {
-       int pos = 0;
        struct pci_dev *parent_dev;
        struct pci_bus *parent_bus;
 
@@ -165,11 +165,10 @@ static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
        if (!parent_dev)
                return -1;
 
-       pos = pci_find_capability(parent_dev, PCI_CAP_ID_EXP);
-       if (!pos)
+       if (!pci_is_pcie(parent_dev))
                return -1;
 
-       pci_read_config_word(parent_dev, pos + reg, value);
+       pcie_capability_read_word(parent_dev, reg, value);
        return 0;
 }
 
@@ -247,7 +246,7 @@ static void ixgbe_check_minimum_link(struct ixgbe_adapter *adapter,
                max_gts = 4 * width;
                break;
        case PCIE_SPEED_8_0GT:
-               /* 128b/130b encoding only reduces throughput by 1% */
+               /* 128b/130b encoding reduces throughput by less than 2% */
                max_gts = 8 * width;
                break;
        default:
@@ -265,7 +264,7 @@ static void ixgbe_check_minimum_link(struct ixgbe_adapter *adapter,
                   width,
                   (speed == PCIE_SPEED_2_5GT ? "20%" :
                    speed == PCIE_SPEED_5_0GT ? "20%" :
-                   speed == PCIE_SPEED_8_0GT ? "N/a" :
+                   speed == PCIE_SPEED_8_0GT ? "<2%" :
                    "Unknown"));
 
        if (max_gts < expected_gts) {
@@ -872,11 +871,18 @@ static u64 ixgbe_get_tx_completed(struct ixgbe_ring *ring)
 
 static u64 ixgbe_get_tx_pending(struct ixgbe_ring *ring)
 {
-       struct ixgbe_adapter *adapter = netdev_priv(ring->netdev);
-       struct ixgbe_hw *hw = &adapter->hw;
+       struct ixgbe_adapter *adapter;
+       struct ixgbe_hw *hw;
+       u32 head, tail;
 
-       u32 head = IXGBE_READ_REG(hw, IXGBE_TDH(ring->reg_idx));
-       u32 tail = IXGBE_READ_REG(hw, IXGBE_TDT(ring->reg_idx));
+       if (ring->l2_accel_priv)
+               adapter = ring->l2_accel_priv->real_adapter;
+       else
+               adapter = netdev_priv(ring->netdev);
+
+       hw = &adapter->hw;
+       head = IXGBE_READ_REG(hw, IXGBE_TDH(ring->reg_idx));
+       tail = IXGBE_READ_REG(hw, IXGBE_TDT(ring->reg_idx));
 
        if (head != tail)
                return (head < tail) ?
@@ -1585,7 +1591,7 @@ static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector,
 {
        struct ixgbe_adapter *adapter = q_vector->adapter;
 
-       if (ixgbe_qv_ll_polling(q_vector))
+       if (ixgbe_qv_busy_polling(q_vector))
                netif_receive_skb(skb);
        else if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL))
                napi_gro_receive(&q_vector->napi, skb);
@@ -2097,7 +2103,7 @@ static int ixgbe_low_latency_recv(struct napi_struct *napi)
 
        ixgbe_for_each_ring(ring, q_vector->rx) {
                found = ixgbe_clean_rx_irq(q_vector, ring, 4);
-#ifdef LL_EXTENDED_STATS
+#ifdef BP_EXTENDED_STATS
                if (found)
                        ring->stats.cleaned += found;
                else
@@ -3005,7 +3011,7 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter,
                struct ixgbe_q_vector *q_vector = ring->q_vector;
 
                if (q_vector)
-                       netif_set_xps_queue(adapter->netdev,
+                       netif_set_xps_queue(ring->netdev,
                                            &q_vector->affinity_mask,
                                            ring->queue_index);
        }
@@ -3395,7 +3401,7 @@ static void ixgbe_setup_psrtype(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        int rss_i = adapter->ring_feature[RING_F_RSS].indices;
-       int p;
+       u16 pool;
 
        /* PSRTYPE must be initialized in non 82598 adapters */
        u32 psrtype = IXGBE_PSRTYPE_TCPHDR |
@@ -3412,9 +3418,8 @@ static void ixgbe_setup_psrtype(struct ixgbe_adapter *adapter)
        else if (rss_i > 1)
                psrtype |= 1 << 29;
 
-       for (p = 0; p < adapter->num_rx_pools; p++)
-               IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(VMDQ_P(p)),
-                               psrtype);
+       for_each_set_bit(pool, &adapter->fwd_bitmask, 32)
+               IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(VMDQ_P(pool)), psrtype);
 }
 
 static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
@@ -3683,7 +3688,11 @@ static void ixgbe_vlan_strip_disable(struct ixgbe_adapter *adapter)
        case ixgbe_mac_82599EB:
        case ixgbe_mac_X540:
                for (i = 0; i < adapter->num_rx_queues; i++) {
-                       j = adapter->rx_ring[i]->reg_idx;
+                       struct ixgbe_ring *ring = adapter->rx_ring[i];
+
+                       if (ring->l2_accel_priv)
+                               continue;
+                       j = ring->reg_idx;
                        vlnctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(j));
                        vlnctrl &= ~IXGBE_RXDCTL_VME;
                        IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(j), vlnctrl);
@@ -3713,7 +3722,11 @@ static void ixgbe_vlan_strip_enable(struct ixgbe_adapter *adapter)
        case ixgbe_mac_82599EB:
        case ixgbe_mac_X540:
                for (i = 0; i < adapter->num_rx_queues; i++) {
-                       j = adapter->rx_ring[i]->reg_idx;
+                       struct ixgbe_ring *ring = adapter->rx_ring[i];
+
+                       if (ring->l2_accel_priv)
+                               continue;
+                       j = ring->reg_idx;
                        vlnctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(j));
                        vlnctrl |= IXGBE_RXDCTL_VME;
                        IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(j), vlnctrl);
@@ -3750,7 +3763,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev)
        unsigned int rar_entries = hw->mac.num_rar_entries - 1;
        int count = 0;
 
-       /* In SR-IOV mode significantly less RAR entries are available */
+       /* In SR-IOV/VMDQ modes significantly less RAR entries are available */
        if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
                rar_entries = IXGBE_MAX_PF_MACVLANS - 1;
 
@@ -3825,14 +3838,6 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
                if (netdev->flags & IFF_ALLMULTI) {
                        fctrl |= IXGBE_FCTRL_MPE;
                        vmolr |= IXGBE_VMOLR_MPE;
-               } else {
-                       /*
-                        * Write addresses to the MTA, if the attempt fails
-                        * then we should just turn on promiscuous mode so
-                        * that we can at least receive multicast traffic
-                        */
-                       hw->mac.ops.update_mc_addr_list(hw, netdev);
-                       vmolr |= IXGBE_VMOLR_ROMPE;
                }
                ixgbe_vlan_filter_enable(adapter);
                hw->addr_ctrl.user_set_promisc = false;
@@ -3849,6 +3854,13 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
                vmolr |= IXGBE_VMOLR_ROPE;
        }
 
+       /* Write addresses to the MTA, if the attempt fails
+        * then we should just turn on promiscuous mode so
+        * that we can at least receive multicast traffic
+        */
+       hw->mac.ops.update_mc_addr_list(hw, netdev);
+       vmolr |= IXGBE_VMOLR_ROMPE;
+
        if (adapter->num_vfs)
                ixgbe_restore_vf_multicasts(adapter);
 
@@ -3893,15 +3905,13 @@ static void ixgbe_napi_disable_all(struct ixgbe_adapter *adapter)
 {
        int q_idx;
 
-       local_bh_disable(); /* for ixgbe_qv_lock_napi() */
        for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) {
                napi_disable(&adapter->q_vector[q_idx]->napi);
-               while (!ixgbe_qv_lock_napi(adapter->q_vector[q_idx])) {
+               while (!ixgbe_qv_disable(adapter->q_vector[q_idx])) {
                        pr_info("QV %d locked\n", q_idx);
-                       mdelay(1);
+                       usleep_range(1000, 20000);
                }
        }
-       local_bh_enable();
 }
 
 #ifdef CONFIG_IXGBE_DCB
@@ -4118,6 +4128,228 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter)
        spin_unlock(&adapter->fdir_perfect_lock);
 }
 
+static void ixgbe_macvlan_set_rx_mode(struct net_device *dev, unsigned int pool,
+                                     struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 vmolr;
+
+       /* No unicast promiscuous support for VMDQ devices. */
+       vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(pool));
+       vmolr |= (IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE);
+
+       /* clear the affected bit */
+       vmolr &= ~IXGBE_VMOLR_MPE;
+
+       if (dev->flags & IFF_ALLMULTI) {
+               vmolr |= IXGBE_VMOLR_MPE;
+       } else {
+               vmolr |= IXGBE_VMOLR_ROMPE;
+               hw->mac.ops.update_mc_addr_list(hw, dev);
+       }
+       ixgbe_write_uc_addr_list(adapter->netdev);
+       IXGBE_WRITE_REG(hw, IXGBE_VMOLR(pool), vmolr);
+}
+
+static void ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
+                                u8 *addr, u16 pool)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       unsigned int entry;
+
+       entry = hw->mac.num_rar_entries - pool;
+       hw->mac.ops.set_rar(hw, entry, addr, VMDQ_P(pool), IXGBE_RAH_AV);
+}
+
+static void ixgbe_fwd_psrtype(struct ixgbe_fwd_adapter *vadapter)
+{
+       struct ixgbe_adapter *adapter = vadapter->real_adapter;
+       int rss_i = adapter->num_rx_queues_per_pool;
+       struct ixgbe_hw *hw = &adapter->hw;
+       u16 pool = vadapter->pool;
+       u32 psrtype = IXGBE_PSRTYPE_TCPHDR |
+                     IXGBE_PSRTYPE_UDPHDR |
+                     IXGBE_PSRTYPE_IPV4HDR |
+                     IXGBE_PSRTYPE_L2HDR |
+                     IXGBE_PSRTYPE_IPV6HDR;
+
+       if (hw->mac.type == ixgbe_mac_82598EB)
+               return;
+
+       if (rss_i > 3)
+               psrtype |= 2 << 29;
+       else if (rss_i > 1)
+               psrtype |= 1 << 29;
+
+       IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(VMDQ_P(pool)), psrtype);
+}
+
+/**
+ * ixgbe_clean_rx_ring - Free Rx Buffers per Queue
+ * @rx_ring: ring to free buffers from
+ **/
+static void ixgbe_clean_rx_ring(struct ixgbe_ring *rx_ring)
+{
+       struct device *dev = rx_ring->dev;
+       unsigned long size;
+       u16 i;
+
+       /* ring already cleared, nothing to do */
+       if (!rx_ring->rx_buffer_info)
+               return;
+
+       /* Free all the Rx ring sk_buffs */
+       for (i = 0; i < rx_ring->count; i++) {
+               struct ixgbe_rx_buffer *rx_buffer;
+
+               rx_buffer = &rx_ring->rx_buffer_info[i];
+               if (rx_buffer->skb) {
+                       struct sk_buff *skb = rx_buffer->skb;
+                       if (IXGBE_CB(skb)->page_released) {
+                               dma_unmap_page(dev,
+                                              IXGBE_CB(skb)->dma,
+                                              ixgbe_rx_bufsz(rx_ring),
+                                              DMA_FROM_DEVICE);
+                               IXGBE_CB(skb)->page_released = false;
+                       }
+                       dev_kfree_skb(skb);
+               }
+               rx_buffer->skb = NULL;
+               if (rx_buffer->dma)
+                       dma_unmap_page(dev, rx_buffer->dma,
+                                      ixgbe_rx_pg_size(rx_ring),
+                                      DMA_FROM_DEVICE);
+               rx_buffer->dma = 0;
+               if (rx_buffer->page)
+                       __free_pages(rx_buffer->page,
+                                    ixgbe_rx_pg_order(rx_ring));
+               rx_buffer->page = NULL;
+       }
+
+       size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count;
+       memset(rx_ring->rx_buffer_info, 0, size);
+
+       /* Zero out the descriptor ring */
+       memset(rx_ring->desc, 0, rx_ring->size);
+
+       rx_ring->next_to_alloc = 0;
+       rx_ring->next_to_clean = 0;
+       rx_ring->next_to_use = 0;
+}
+
+static void ixgbe_disable_fwd_ring(struct ixgbe_fwd_adapter *vadapter,
+                                  struct ixgbe_ring *rx_ring)
+{
+       struct ixgbe_adapter *adapter = vadapter->real_adapter;
+       int index = rx_ring->queue_index + vadapter->rx_base_queue;
+
+       /* shutdown specific queue receive and wait for dma to settle */
+       ixgbe_disable_rx_queue(adapter, rx_ring);
+       usleep_range(10000, 20000);
+       ixgbe_irq_disable_queues(adapter, ((u64)1 << index));
+       ixgbe_clean_rx_ring(rx_ring);
+       rx_ring->l2_accel_priv = NULL;
+}
+
+int ixgbe_fwd_ring_down(struct net_device *vdev,
+                       struct ixgbe_fwd_adapter *accel)
+{
+       struct ixgbe_adapter *adapter = accel->real_adapter;
+       unsigned int rxbase = accel->rx_base_queue;
+       unsigned int txbase = accel->tx_base_queue;
+       int i;
+
+       netif_tx_stop_all_queues(vdev);
+
+       for (i = 0; i < adapter->num_rx_queues_per_pool; i++) {
+               ixgbe_disable_fwd_ring(accel, adapter->rx_ring[rxbase + i]);
+               adapter->rx_ring[rxbase + i]->netdev = adapter->netdev;
+       }
+
+       for (i = 0; i < adapter->num_rx_queues_per_pool; i++) {
+               adapter->tx_ring[txbase + i]->l2_accel_priv = NULL;
+               adapter->tx_ring[txbase + i]->netdev = adapter->netdev;
+       }
+
+
+       return 0;
+}
+
+static int ixgbe_fwd_ring_up(struct net_device *vdev,
+                            struct ixgbe_fwd_adapter *accel)
+{
+       struct ixgbe_adapter *adapter = accel->real_adapter;
+       unsigned int rxbase, txbase, queues;
+       int i, baseq, err = 0;
+
+       if (!test_bit(accel->pool, &adapter->fwd_bitmask))
+               return 0;
+
+       baseq = accel->pool * adapter->num_rx_queues_per_pool;
+       netdev_dbg(vdev, "pool %i:%i queues %i:%i VSI bitmask %lx\n",
+                  accel->pool, adapter->num_rx_pools,
+                  baseq, baseq + adapter->num_rx_queues_per_pool,
+                  adapter->fwd_bitmask);
+
+       accel->netdev = vdev;
+       accel->rx_base_queue = rxbase = baseq;
+       accel->tx_base_queue = txbase = baseq;
+
+       for (i = 0; i < adapter->num_rx_queues_per_pool; i++)
+               ixgbe_disable_fwd_ring(accel, adapter->rx_ring[rxbase + i]);
+
+       for (i = 0; i < adapter->num_rx_queues_per_pool; i++) {
+               adapter->rx_ring[rxbase + i]->netdev = vdev;
+               adapter->rx_ring[rxbase + i]->l2_accel_priv = accel;
+               ixgbe_configure_rx_ring(adapter, adapter->rx_ring[rxbase + i]);
+       }
+
+       for (i = 0; i < adapter->num_rx_queues_per_pool; i++) {
+               adapter->tx_ring[txbase + i]->netdev = vdev;
+               adapter->tx_ring[txbase + i]->l2_accel_priv = accel;
+       }
+
+       queues = min_t(unsigned int,
+                      adapter->num_rx_queues_per_pool, vdev->num_tx_queues);
+       err = netif_set_real_num_tx_queues(vdev, queues);
+       if (err)
+               goto fwd_queue_err;
+
+       err = netif_set_real_num_rx_queues(vdev, queues);
+       if (err)
+               goto fwd_queue_err;
+
+       if (is_valid_ether_addr(vdev->dev_addr))
+               ixgbe_add_mac_filter(adapter, vdev->dev_addr, accel->pool);
+
+       ixgbe_fwd_psrtype(accel);
+       ixgbe_macvlan_set_rx_mode(vdev, accel->pool, adapter);
+       return err;
+fwd_queue_err:
+       ixgbe_fwd_ring_down(vdev, accel);
+       return err;
+}
+
+static void ixgbe_configure_dfwd(struct ixgbe_adapter *adapter)
+{
+       struct net_device *upper;
+       struct list_head *iter;
+       int err;
+
+       netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) {
+               if (netif_is_macvlan(upper)) {
+                       struct macvlan_dev *dfwd = netdev_priv(upper);
+                       struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv;
+
+                       if (dfwd->fwd_priv) {
+                               err = ixgbe_fwd_ring_up(upper, vadapter);
+                               if (err)
+                                       continue;
+                       }
+               }
+       }
+}
+
 static void ixgbe_configure(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
@@ -4169,6 +4401,7 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter)
 #endif /* IXGBE_FCOE */
        ixgbe_configure_tx(adapter);
        ixgbe_configure_rx(adapter);
+       ixgbe_configure_dfwd(adapter);
 }
 
 static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw)
@@ -4322,6 +4555,8 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter)
 static void ixgbe_up_complete(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
+       struct net_device *upper;
+       struct list_head *iter;
        int err;
        u32 ctrl_ext;
 
@@ -4365,6 +4600,16 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter)
        /* enable transmits */
        netif_tx_start_all_queues(adapter->netdev);
 
+       /* enable any upper devices */
+       netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) {
+               if (netif_is_macvlan(upper)) {
+                       struct macvlan_dev *vlan = netdev_priv(upper);
+
+                       if (vlan->fwd_priv)
+                               netif_tx_start_all_queues(upper);
+               }
+       }
+
        /* bring the link up in the watchdog, this could race with our first
         * link up interrupt but shouldn't be a problem */
        adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE;
@@ -4455,59 +4700,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
                ixgbe_ptp_reset(adapter);
 }
 
-/**
- * ixgbe_clean_rx_ring - Free Rx Buffers per Queue
- * @rx_ring: ring to free buffers from
- **/
-static void ixgbe_clean_rx_ring(struct ixgbe_ring *rx_ring)
-{
-       struct device *dev = rx_ring->dev;
-       unsigned long size;
-       u16 i;
-
-       /* ring already cleared, nothing to do */
-       if (!rx_ring->rx_buffer_info)
-               return;
-
-       /* Free all the Rx ring sk_buffs */
-       for (i = 0; i < rx_ring->count; i++) {
-               struct ixgbe_rx_buffer *rx_buffer;
-
-               rx_buffer = &rx_ring->rx_buffer_info[i];
-               if (rx_buffer->skb) {
-                       struct sk_buff *skb = rx_buffer->skb;
-                       if (IXGBE_CB(skb)->page_released) {
-                               dma_unmap_page(dev,
-                                              IXGBE_CB(skb)->dma,
-                                              ixgbe_rx_bufsz(rx_ring),
-                                              DMA_FROM_DEVICE);
-                               IXGBE_CB(skb)->page_released = false;
-                       }
-                       dev_kfree_skb(skb);
-               }
-               rx_buffer->skb = NULL;
-               if (rx_buffer->dma)
-                       dma_unmap_page(dev, rx_buffer->dma,
-                                      ixgbe_rx_pg_size(rx_ring),
-                                      DMA_FROM_DEVICE);
-               rx_buffer->dma = 0;
-               if (rx_buffer->page)
-                       __free_pages(rx_buffer->page,
-                                    ixgbe_rx_pg_order(rx_ring));
-               rx_buffer->page = NULL;
-       }
-
-       size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count;
-       memset(rx_ring->rx_buffer_info, 0, size);
-
-       /* Zero out the descriptor ring */
-       memset(rx_ring->desc, 0, rx_ring->size);
-
-       rx_ring->next_to_alloc = 0;
-       rx_ring->next_to_clean = 0;
-       rx_ring->next_to_use = 0;
-}
-
 /**
  * ixgbe_clean_tx_ring - Free Tx Buffers
  * @tx_ring: ring to be cleaned
@@ -4585,6 +4777,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
        struct ixgbe_hw *hw = &adapter->hw;
+       struct net_device *upper;
+       struct list_head *iter;
        u32 rxctrl;
        int i;
 
@@ -4608,6 +4802,19 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
        netif_carrier_off(netdev);
        netif_tx_disable(netdev);
 
+       /* disable any upper devices */
+       netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) {
+               if (netif_is_macvlan(upper)) {
+                       struct macvlan_dev *vlan = netdev_priv(upper);
+
+                       if (vlan->fwd_priv) {
+                               netif_tx_stop_all_queues(upper);
+                               netif_carrier_off(upper);
+                               netif_tx_disable(upper);
+                       }
+               }
+       }
+
        ixgbe_irq_disable(adapter);
 
        ixgbe_napi_disable_all(adapter);
@@ -4816,11 +5023,20 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
        hw->fc.disable_fc_autoneg = ixgbe_device_supports_autoneg_fc(hw);
 
 #ifdef CONFIG_PCI_IOV
+       if (max_vfs > 0)
+               e_dev_warn("Enabling SR-IOV VFs using the max_vfs module parameter is deprecated - please use the pci sysfs interface instead.\n");
+
        /* assign number of SR-IOV VFs */
-       if (hw->mac.type != ixgbe_mac_82598EB)
-               adapter->num_vfs = (max_vfs > 63) ? 0 : max_vfs;
+       if (hw->mac.type != ixgbe_mac_82598EB) {
+               if (max_vfs > 63) {
+                       adapter->num_vfs = 0;
+                       e_dev_warn("max_vfs parameter out of range. Not assigning any SR-IOV VFs\n");
+               } else {
+                       adapter->num_vfs = max_vfs;
+               }
+       }
+#endif /* CONFIG_PCI_IOV */
 
-#endif
        /* enable itr by default in dynamic mode */
        adapter->rx_itr_setting = 1;
        adapter->tx_itr_setting = 1;
@@ -4838,6 +5054,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
                return -EIO;
        }
 
+       /* PF holds first pool slot */
+       set_bit(0, &adapter->fwd_bitmask);
        set_bit(__IXGBE_DOWN, &adapter->state);
 
        return 0;
@@ -5143,7 +5361,7 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu)
 static int ixgbe_open(struct net_device *netdev)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       int err;
+       int err, queues;
 
        /* disallow open during test */
        if (test_bit(__IXGBE_TESTING, &adapter->state))
@@ -5168,16 +5386,21 @@ static int ixgbe_open(struct net_device *netdev)
                goto err_req_irq;
 
        /* Notify the stack of the actual queue counts. */
-       err = netif_set_real_num_tx_queues(netdev,
-                                          adapter->num_rx_pools > 1 ? 1 :
-                                          adapter->num_tx_queues);
+       if (adapter->num_rx_pools > 1)
+               queues = adapter->num_rx_queues_per_pool;
+       else
+               queues = adapter->num_tx_queues;
+
+       err = netif_set_real_num_tx_queues(netdev, queues);
        if (err)
                goto err_set_queues;
 
-
-       err = netif_set_real_num_rx_queues(netdev,
-                                          adapter->num_rx_pools > 1 ? 1 :
-                                          adapter->num_rx_queues);
+       if (adapter->num_rx_pools > 1 &&
+           adapter->num_rx_queues > IXGBE_MAX_L2A_QUEUES)
+               queues = IXGBE_MAX_L2A_QUEUES;
+       else
+               queues = adapter->num_rx_queues;
+       err = netif_set_real_num_rx_queues(netdev, queues);
        if (err)
                goto err_set_queues;
 
@@ -6767,8 +6990,9 @@ out_drop:
        return NETDEV_TX_OK;
 }
 
-static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
-                                   struct net_device *netdev)
+static netdev_tx_t __ixgbe_xmit_frame(struct sk_buff *skb,
+                                     struct net_device *netdev,
+                                     struct ixgbe_ring *ring)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_ring *tx_ring;
@@ -6784,10 +7008,17 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
                skb_set_tail_pointer(skb, 17);
        }
 
-       tx_ring = adapter->tx_ring[skb->queue_mapping];
+       tx_ring = ring ? ring : adapter->tx_ring[skb->queue_mapping];
+
        return ixgbe_xmit_frame_ring(skb, adapter, tx_ring);
 }
 
+static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
+                                   struct net_device *netdev)
+{
+       return __ixgbe_xmit_frame(skb, netdev, NULL);
+}
+
 /**
  * ixgbe_set_mac - Change the Ethernet Address of the NIC
  * @netdev: network interface device structure
@@ -7044,6 +7275,7 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc)
 {
        struct ixgbe_adapter *adapter = netdev_priv(dev);
        struct ixgbe_hw *hw = &adapter->hw;
+       bool pools;
 
        /* Hardware supports up to 8 traffic classes */
        if (tc > adapter->dcb_cfg.num_tcs.pg_tcs ||
@@ -7051,6 +7283,10 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc)
             tc < MAX_TRAFFIC_CLASS))
                return -EINVAL;
 
+       pools = (find_first_zero_bit(&adapter->fwd_bitmask, 32) > 1);
+       if (tc && pools && adapter->num_rx_pools > IXGBE_MAX_DCBMACVLANS)
+               return -EBUSY;
+
        /* Hardware has to reinitialize queues and interrupts to
         * match packet buffer alignment. Unfortunately, the
         * hardware is not flexible enough to do this dynamically.
@@ -7305,6 +7541,104 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
        return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode);
 }
 
+static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
+{
+       struct ixgbe_fwd_adapter *fwd_adapter = NULL;
+       struct ixgbe_adapter *adapter = netdev_priv(pdev);
+       unsigned int limit;
+       int pool, err;
+
+#ifdef CONFIG_RPS
+       if (vdev->num_rx_queues != vdev->num_tx_queues) {
+               netdev_info(pdev, "%s: Only supports a single queue count for TX and RX\n",
+                           vdev->name);
+               return ERR_PTR(-EINVAL);
+       }
+#endif
+       /* Check for hardware restriction on number of rx/tx queues */
+       if (vdev->num_tx_queues > IXGBE_MAX_L2A_QUEUES ||
+           vdev->num_tx_queues == IXGBE_BAD_L2A_QUEUE) {
+               netdev_info(pdev,
+                           "%s: Supports RX/TX Queue counts 1,2, and 4\n",
+                           pdev->name);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (((adapter->flags & IXGBE_FLAG_DCB_ENABLED) &&
+             adapter->num_rx_pools > IXGBE_MAX_DCBMACVLANS - 1) ||
+           (adapter->num_rx_pools > IXGBE_MAX_MACVLANS))
+               return ERR_PTR(-EBUSY);
+
+       fwd_adapter = kcalloc(1, sizeof(struct ixgbe_fwd_adapter), GFP_KERNEL);
+       if (!fwd_adapter)
+               return ERR_PTR(-ENOMEM);
+
+       pool = find_first_zero_bit(&adapter->fwd_bitmask, 32);
+       adapter->num_rx_pools++;
+       set_bit(pool, &adapter->fwd_bitmask);
+       limit = find_last_bit(&adapter->fwd_bitmask, 32);
+
+       /* Enable VMDq flag so device will be set in VM mode */
+       adapter->flags |= IXGBE_FLAG_VMDQ_ENABLED | IXGBE_FLAG_SRIOV_ENABLED;
+       adapter->ring_feature[RING_F_VMDQ].limit = limit + 1;
+       adapter->ring_feature[RING_F_RSS].limit = vdev->num_tx_queues;
+
+       /* Force reinit of ring allocation with VMDQ enabled */
+       err = ixgbe_setup_tc(pdev, netdev_get_num_tc(pdev));
+       if (err)
+               goto fwd_add_err;
+       fwd_adapter->pool = pool;
+       fwd_adapter->real_adapter = adapter;
+       err = ixgbe_fwd_ring_up(vdev, fwd_adapter);
+       if (err)
+               goto fwd_add_err;
+       netif_tx_start_all_queues(vdev);
+       return fwd_adapter;
+fwd_add_err:
+       /* unwind counter and free adapter struct */
+       netdev_info(pdev,
+                   "%s: dfwd hardware acceleration failed\n", vdev->name);
+       clear_bit(pool, &adapter->fwd_bitmask);
+       adapter->num_rx_pools--;
+       kfree(fwd_adapter);
+       return ERR_PTR(err);
+}
+
+static void ixgbe_fwd_del(struct net_device *pdev, void *priv)
+{
+       struct ixgbe_fwd_adapter *fwd_adapter = priv;
+       struct ixgbe_adapter *adapter = fwd_adapter->real_adapter;
+       unsigned int limit;
+
+       clear_bit(fwd_adapter->pool, &adapter->fwd_bitmask);
+       adapter->num_rx_pools--;
+
+       limit = find_last_bit(&adapter->fwd_bitmask, 32);
+       adapter->ring_feature[RING_F_VMDQ].limit = limit + 1;
+       ixgbe_fwd_ring_down(fwd_adapter->netdev, fwd_adapter);
+       ixgbe_setup_tc(pdev, netdev_get_num_tc(pdev));
+       netdev_dbg(pdev, "pool %i:%i queues %i:%i VSI bitmask %lx\n",
+                  fwd_adapter->pool, adapter->num_rx_pools,
+                  fwd_adapter->rx_base_queue,
+                  fwd_adapter->rx_base_queue + adapter->num_rx_queues_per_pool,
+                  adapter->fwd_bitmask);
+       kfree(fwd_adapter);
+}
+
+static netdev_tx_t ixgbe_fwd_xmit(struct sk_buff *skb,
+                                 struct net_device *dev,
+                                 void *priv)
+{
+       struct ixgbe_fwd_adapter *fwd_adapter = priv;
+       unsigned int queue;
+       struct ixgbe_ring *tx_ring;
+
+       queue = skb->queue_mapping + fwd_adapter->tx_base_queue;
+       tx_ring = fwd_adapter->real_adapter->tx_ring[queue];
+
+       return __ixgbe_xmit_frame(skb, dev, tx_ring);
+}
+
 static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_open               = ixgbe_open,
        .ndo_stop               = ixgbe_close,
@@ -7349,6 +7683,9 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_fdb_add            = ixgbe_ndo_fdb_add,
        .ndo_bridge_setlink     = ixgbe_ndo_bridge_setlink,
        .ndo_bridge_getlink     = ixgbe_ndo_bridge_getlink,
+       .ndo_dfwd_add_station   = ixgbe_fwd_add,
+       .ndo_dfwd_del_station   = ixgbe_fwd_del,
+       .ndo_dfwd_start_xmit    = ixgbe_fwd_xmit,
 };
 
 /**
@@ -7362,19 +7699,16 @@ static const struct net_device_ops ixgbe_netdev_ops = {
  **/
 static inline int ixgbe_enumerate_functions(struct ixgbe_adapter *adapter)
 {
-       struct ixgbe_hw *hw = &adapter->hw;
        struct list_head *entry;
        int physfns = 0;
 
-       /* Some cards can not use the generic count PCIe functions method, and
-        * so must be hardcoded to the correct value.
+       /* Some cards can not use the generic count PCIe functions method,
+        * because they are behind a parent switch, so we hardcode these with
+        * the correct number of functions.
         */
-       switch (hw->device_id) {
-       case IXGBE_DEV_ID_82599_SFP_SF_QP:
-       case IXGBE_DEV_ID_82599_QSFP_SF_QP:
+       if (ixgbe_pcie_from_parent(&adapter->hw)) {
                physfns = 4;
-               break;
-       default:
+       } else {
                list_for_each(entry, &adapter->pdev->bus_list) {
                        struct pci_dev *pdev =
                                list_entry(entry, struct pci_dev, bus_list);
@@ -7653,7 +7987,8 @@ skip_sriov:
                           NETIF_F_TSO |
                           NETIF_F_TSO6 |
                           NETIF_F_RXHASH |
-                          NETIF_F_RXCSUM;
+                          NETIF_F_RXCSUM |
+                          NETIF_F_HW_L2FW_DOFFLOAD;
 
        netdev->hw_features = netdev->features;
 
@@ -7759,29 +8094,6 @@ skip_sriov:
        if (ixgbe_pcie_from_parent(hw))
                ixgbe_get_parent_bus_info(adapter);
 
-       /* print bus type/speed/width info */
-       e_dev_info("(PCI Express:%s:%s) %pM\n",
-                  (hw->bus.speed == ixgbe_bus_speed_8000 ? "8.0GT/s" :
-                   hw->bus.speed == ixgbe_bus_speed_5000 ? "5.0GT/s" :
-                   hw->bus.speed == ixgbe_bus_speed_2500 ? "2.5GT/s" :
-                   "Unknown"),
-                  (hw->bus.width == ixgbe_bus_width_pcie_x8 ? "Width x8" :
-                   hw->bus.width == ixgbe_bus_width_pcie_x4 ? "Width x4" :
-                   hw->bus.width == ixgbe_bus_width_pcie_x1 ? "Width x1" :
-                   "Unknown"),
-                  netdev->dev_addr);
-
-       err = ixgbe_read_pba_string_generic(hw, part_str, IXGBE_PBANUM_LENGTH);
-       if (err)
-               strncpy(part_str, "Unknown", IXGBE_PBANUM_LENGTH);
-       if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present)
-               e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n",
-                          hw->mac.type, hw->phy.type, hw->phy.sfp_type,
-                          part_str);
-       else
-               e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n",
-                          hw->mac.type, hw->phy.type, part_str);
-
        /* calculate the expected PCIe bandwidth required for optimal
         * performance. Note that some older parts will never have enough
         * bandwidth due to being older generation PCIe parts. We clamp these
@@ -7797,6 +8109,19 @@ skip_sriov:
        }
        ixgbe_check_minimum_link(adapter, expected_gts);
 
+       err = ixgbe_read_pba_string_generic(hw, part_str, IXGBE_PBANUM_LENGTH);
+       if (err)
+               strncpy(part_str, "Unknown", IXGBE_PBANUM_LENGTH);
+       if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present)
+               e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n",
+                          hw->mac.type, hw->phy.type, hw->phy.sfp_type,
+                          part_str);
+       else
+               e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n",
+                          hw->mac.type, hw->phy.type, part_str);
+
+       e_dev_info("%pM\n", netdev->dev_addr);
+
        /* reset the hardware with the new settings */
        err = hw->mac.ops.start_hw(hw);
        if (err == IXGBE_ERR_EEPROM_VERSION) {
index 24af12e..aae900a 100644 (file)
 #define IXGBE_SFF_QSFP_DEVICE_TECH     0x93
 
 /* Bitmasks */
-#define IXGBE_SFF_DA_PASSIVE_CABLE           0x4
-#define IXGBE_SFF_DA_ACTIVE_CABLE            0x8
-#define IXGBE_SFF_DA_SPEC_ACTIVE_LIMITING    0x4
-#define IXGBE_SFF_1GBASESX_CAPABLE           0x1
-#define IXGBE_SFF_1GBASELX_CAPABLE           0x2
-#define IXGBE_SFF_1GBASET_CAPABLE            0x8
-#define IXGBE_SFF_10GBASESR_CAPABLE          0x10
-#define IXGBE_SFF_10GBASELR_CAPABLE          0x20
-#define IXGBE_SFF_SOFT_RS_SELECT_MASK  0x8
-#define IXGBE_SFF_SOFT_RS_SELECT_10G   0x8
-#define IXGBE_SFF_SOFT_RS_SELECT_1G    0x0
-#define IXGBE_SFF_ADDRESSING_MODE           0x4
-#define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE       0x1
-#define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE      0x8
+#define IXGBE_SFF_DA_PASSIVE_CABLE             0x4
+#define IXGBE_SFF_DA_ACTIVE_CABLE              0x8
+#define IXGBE_SFF_DA_SPEC_ACTIVE_LIMITING      0x4
+#define IXGBE_SFF_1GBASESX_CAPABLE             0x1
+#define IXGBE_SFF_1GBASELX_CAPABLE             0x2
+#define IXGBE_SFF_1GBASET_CAPABLE              0x8
+#define IXGBE_SFF_10GBASESR_CAPABLE            0x10
+#define IXGBE_SFF_10GBASELR_CAPABLE            0x20
+#define IXGBE_SFF_SOFT_RS_SELECT_MASK          0x8
+#define IXGBE_SFF_SOFT_RS_SELECT_10G           0x8
+#define IXGBE_SFF_SOFT_RS_SELECT_1G            0x0
+#define IXGBE_SFF_ADDRESSING_MODE              0x4
+#define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE         0x1
+#define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE                0x8
 #define IXGBE_SFF_QSFP_CONNECTOR_NOT_SEPARABLE 0x23
 #define IXGBE_SFF_QSFP_TRANSMITER_850NM_VCSEL  0x0
-#define IXGBE_I2C_EEPROM_READ_MASK           0x100
-#define IXGBE_I2C_EEPROM_STATUS_MASK         0x3
-#define IXGBE_I2C_EEPROM_STATUS_NO_OPERATION 0x0
-#define IXGBE_I2C_EEPROM_STATUS_PASS         0x1
-#define IXGBE_I2C_EEPROM_STATUS_FAIL         0x2
-#define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS  0x3
+#define IXGBE_I2C_EEPROM_READ_MASK             0x100
+#define IXGBE_I2C_EEPROM_STATUS_MASK           0x3
+#define IXGBE_I2C_EEPROM_STATUS_NO_OPERATION   0x0
+#define IXGBE_I2C_EEPROM_STATUS_PASS           0x1
+#define IXGBE_I2C_EEPROM_STATUS_FAIL           0x2
+#define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS    0x3
 
 /* Flow control defines */
 #define IXGBE_TAF_SYM_PAUSE                  0x400
index 276d7b1..d6f0c0d 100644 (file)
@@ -129,10 +129,6 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
        if (!pre_existing_vfs && !adapter->num_vfs)
                return;
 
-       if (!pre_existing_vfs)
-               dev_warn(&adapter->pdev->dev,
-                        "Enabling SR-IOV VFs using the module parameter is deprecated - please use the pci sysfs interface.\n");
-
        /* If there are pre-existing VFs then we have to force
         * use of that many - over ride any module parameter value.
         * This may result from the user unloading the PF driver
@@ -223,17 +219,19 @@ int ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
        IXGBE_WRITE_FLUSH(hw);
 
        /* Disable VMDq flag so device will be set in VM mode */
-       if (adapter->ring_feature[RING_F_VMDQ].limit == 1)
+       if (adapter->ring_feature[RING_F_VMDQ].limit == 1) {
                adapter->flags &= ~IXGBE_FLAG_VMDQ_ENABLED;
-       adapter->ring_feature[RING_F_VMDQ].offset = 0;
+               adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED;
+               rss = min_t(int, IXGBE_MAX_RSS_INDICES, num_online_cpus());
+       } else {
+               rss = min_t(int, IXGBE_MAX_L2A_QUEUES, num_online_cpus());
+       }
 
-       rss = min_t(int, IXGBE_MAX_RSS_INDICES, num_online_cpus());
+       adapter->ring_feature[RING_F_VMDQ].offset = 0;
        adapter->ring_feature[RING_F_RSS].limit = rss;
 
        /* take a breather then clean up driver data */
        msleep(100);
-
-       adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED;
        return 0;
 }
 
@@ -298,13 +296,10 @@ static int ixgbe_pci_sriov_disable(struct pci_dev *dev)
        err = ixgbe_disable_sriov(adapter);
 
        /* Only reinit if no error and state changed */
-       if (!err && current_flags != adapter->flags) {
-               /* ixgbe_disable_sriov() doesn't clear VMDQ flag */
-               adapter->flags &= ~IXGBE_FLAG_VMDQ_ENABLED;
 #ifdef CONFIG_PCI_IOV
+       if (!err && current_flags != adapter->flags)
                ixgbe_sriov_reinit(adapter);
 #endif
-       }
 
        return err;
 }
@@ -558,7 +553,7 @@ static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter,
        struct ixgbe_hw *hw = &adapter->hw;
        int rar_entry = hw->mac.num_rar_entries - (vf + 1);
 
-       memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, 6);
+       memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, ETH_ALEN);
        hw->mac.ops.set_rar(hw, rar_entry, mac_addr, vf, IXGBE_RAH_AV);
 
        return 0;
@@ -621,16 +616,13 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
 
 int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask)
 {
-       unsigned char vf_mac_addr[6];
        struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
        unsigned int vfn = (event_mask & 0x3f);
 
        bool enable = ((event_mask & 0x10000000U) != 0);
 
-       if (enable) {
-               eth_zero_addr(vf_mac_addr);
-               memcpy(adapter->vfinfo[vfn].vf_mac_addresses, vf_mac_addr, 6);
-       }
+       if (enable)
+               eth_zero_addr(adapter->vfinfo[vfn].vf_mac_addresses);
 
        return 0;
 }
index 10775cb..7c19e96 100644 (file)
@@ -561,6 +561,10 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_RTTDQSEL    0x04904
 #define IXGBE_RTTDT1C     0x04908
 #define IXGBE_RTTDT1S     0x0490C
+#define IXGBE_RTTQCNCR    0x08B00
+#define IXGBE_RTTQCNTG    0x04A90
+#define IXGBE_RTTBCNRD    0x0498C
+#define IXGBE_RTTQCNRR    0x0498C
 #define IXGBE_RTTDTECC    0x04990
 #define IXGBE_RTTDTECC_NO_BCN   0x00000100
 #define IXGBE_RTTBCNRC    0x04984
@@ -570,6 +574,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_RTTBCNRC_RF_INT_MASK     \
        (IXGBE_RTTBCNRC_RF_DEC_MASK << IXGBE_RTTBCNRC_RF_INT_SHIFT)
 #define IXGBE_RTTBCNRM    0x04980
+#define IXGBE_RTTQCNRM    0x04980
 
 /* FCoE DMA Context Registers */
 #define IXGBE_FCPTRL    0x02410 /* FC User Desc. PTR Low */
index 389324f..24b80a6 100644 (file)
 #include "ixgbe.h"
 #include "ixgbe_phy.h"
 
-#define IXGBE_X540_MAX_TX_QUEUES 128
-#define IXGBE_X540_MAX_RX_QUEUES 128
-#define IXGBE_X540_RAR_ENTRIES   128
-#define IXGBE_X540_MC_TBL_SIZE   128
-#define IXGBE_X540_VFT_TBL_SIZE  128
-#define IXGBE_X540_RX_PB_SIZE   384
+#define IXGBE_X540_MAX_TX_QUEUES       128
+#define IXGBE_X540_MAX_RX_QUEUES       128
+#define IXGBE_X540_RAR_ENTRIES         128
+#define IXGBE_X540_MC_TBL_SIZE         128
+#define IXGBE_X540_VFT_TBL_SIZE                128
+#define IXGBE_X540_RX_PB_SIZE          384
 
 static s32 ixgbe_update_flash_X540(struct ixgbe_hw *hw);
 static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw);
index c9d0c12..54d9ace 100644 (file)
 
 struct ixgbe_stats {
        char stat_string[ETH_GSTRING_LEN];
-       int sizeof_stat;
-       int stat_offset;
-       int base_stat_offset;
-       int saved_reset_offset;
+       struct {
+               int sizeof_stat;
+               int stat_offset;
+               int base_stat_offset;
+               int saved_reset_offset;
+       };
 };
 
-#define IXGBEVF_STAT(m, b, r)  sizeof(((struct ixgbevf_adapter *)0)->m), \
-                           offsetof(struct ixgbevf_adapter, m),         \
-                           offsetof(struct ixgbevf_adapter, b),         \
-                           offsetof(struct ixgbevf_adapter, r)
+#define IXGBEVF_STAT(m, b, r) { \
+       .sizeof_stat = FIELD_SIZEOF(struct ixgbevf_adapter, m), \
+       .stat_offset = offsetof(struct ixgbevf_adapter, m), \
+       .base_stat_offset = offsetof(struct ixgbevf_adapter, b), \
+       .saved_reset_offset = offsetof(struct ixgbevf_adapter, r) \
+}
+
+#define IXGBEVF_ZSTAT(m) { \
+       .sizeof_stat = FIELD_SIZEOF(struct ixgbevf_adapter, m), \
+       .stat_offset = offsetof(struct ixgbevf_adapter, m), \
+       .base_stat_offset = -1, \
+       .saved_reset_offset = -1 \
+}
 
 static const struct ixgbe_stats ixgbe_gstrings_stats[] = {
        {"rx_packets", IXGBEVF_STAT(stats.vfgprc, stats.base_vfgprc,
@@ -65,15 +76,20 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = {
                                  stats.saved_reset_vfgorc)},
        {"tx_bytes", IXGBEVF_STAT(stats.vfgotc, stats.base_vfgotc,
                                  stats.saved_reset_vfgotc)},
-       {"tx_busy", IXGBEVF_STAT(tx_busy, zero_base, zero_base)},
+       {"tx_busy", IXGBEVF_ZSTAT(tx_busy)},
        {"multicast", IXGBEVF_STAT(stats.vfmprc, stats.base_vfmprc,
                                   stats.saved_reset_vfmprc)},
-       {"rx_csum_offload_good", IXGBEVF_STAT(hw_csum_rx_good, zero_base,
-                                             zero_base)},
-       {"rx_csum_offload_errors", IXGBEVF_STAT(hw_csum_rx_error, zero_base,
-                                               zero_base)},
-       {"tx_csum_offload_ctxt", IXGBEVF_STAT(hw_csum_tx_good, zero_base,
-                                             zero_base)},
+       {"rx_csum_offload_good", IXGBEVF_ZSTAT(hw_csum_rx_good)},
+       {"rx_csum_offload_errors", IXGBEVF_ZSTAT(hw_csum_rx_error)},
+       {"tx_csum_offload_ctxt", IXGBEVF_ZSTAT(hw_csum_tx_good)},
+#ifdef BP_EXTENDED_STATS
+       {"rx_bp_poll_yield", IXGBEVF_ZSTAT(bp_rx_yields)},
+       {"rx_bp_cleaned", IXGBEVF_ZSTAT(bp_rx_cleaned)},
+       {"rx_bp_misses", IXGBEVF_ZSTAT(bp_rx_missed)},
+       {"tx_bp_napi_yield", IXGBEVF_ZSTAT(bp_tx_yields)},
+       {"tx_bp_cleaned", IXGBEVF_ZSTAT(bp_tx_cleaned)},
+       {"tx_bp_misses", IXGBEVF_ZSTAT(bp_tx_missed)},
+#endif
 };
 
 #define IXGBE_QUEUE_STATS_LEN 0
@@ -140,58 +156,10 @@ static void ixgbevf_set_msglevel(struct net_device *netdev, u32 data)
 
 #define IXGBE_GET_STAT(_A_, _R_) (_A_->stats._R_)
 
-static char *ixgbevf_reg_names[] = {
-       "IXGBE_VFCTRL",
-       "IXGBE_VFSTATUS",
-       "IXGBE_VFLINKS",
-       "IXGBE_VFRXMEMWRAP",
-       "IXGBE_VFFRTIMER",
-       "IXGBE_VTEICR",
-       "IXGBE_VTEICS",
-       "IXGBE_VTEIMS",
-       "IXGBE_VTEIMC",
-       "IXGBE_VTEIAC",
-       "IXGBE_VTEIAM",
-       "IXGBE_VTEITR",
-       "IXGBE_VTIVAR",
-       "IXGBE_VTIVAR_MISC",
-       "IXGBE_VFRDBAL0",
-       "IXGBE_VFRDBAL1",
-       "IXGBE_VFRDBAH0",
-       "IXGBE_VFRDBAH1",
-       "IXGBE_VFRDLEN0",
-       "IXGBE_VFRDLEN1",
-       "IXGBE_VFRDH0",
-       "IXGBE_VFRDH1",
-       "IXGBE_VFRDT0",
-       "IXGBE_VFRDT1",
-       "IXGBE_VFRXDCTL0",
-       "IXGBE_VFRXDCTL1",
-       "IXGBE_VFSRRCTL0",
-       "IXGBE_VFSRRCTL1",
-       "IXGBE_VFPSRTYPE",
-       "IXGBE_VFTDBAL0",
-       "IXGBE_VFTDBAL1",
-       "IXGBE_VFTDBAH0",
-       "IXGBE_VFTDBAH1",
-       "IXGBE_VFTDLEN0",
-       "IXGBE_VFTDLEN1",
-       "IXGBE_VFTDH0",
-       "IXGBE_VFTDH1",
-       "IXGBE_VFTDT0",
-       "IXGBE_VFTDT1",
-       "IXGBE_VFTXDCTL0",
-       "IXGBE_VFTXDCTL1",
-       "IXGBE_VFTDWBAL0",
-       "IXGBE_VFTDWBAL1",
-       "IXGBE_VFTDWBAH0",
-       "IXGBE_VFTDWBAH1"
-};
-
-
 static int ixgbevf_get_regs_len(struct net_device *netdev)
 {
-       return (ARRAY_SIZE(ixgbevf_reg_names)) * sizeof(u32);
+#define IXGBE_REGS_LEN 45
+       return IXGBE_REGS_LEN * sizeof(u32);
 }
 
 static void ixgbevf_get_regs(struct net_device *netdev,
@@ -264,9 +232,6 @@ static void ixgbevf_get_regs(struct net_device *netdev,
                regs_buff[41 + i] = IXGBE_READ_REG(hw, IXGBE_VFTDWBAL(i));
        for (i = 0; i < 2; i++)
                regs_buff[43 + i] = IXGBE_READ_REG(hw, IXGBE_VFTDWBAH(i));
-
-       for (i = 0; i < ARRAY_SIZE(ixgbevf_reg_names); i++)
-               hw_dbg(hw, "%s\t%8.8x\n", ixgbevf_reg_names[i], regs_buff[i]);
 }
 
 static void ixgbevf_get_drvinfo(struct net_device *netdev,
@@ -441,22 +406,50 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev,
                                      struct ethtool_stats *stats, u64 *data)
 {
        struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+       char *base = (char *) adapter;
        int i;
+#ifdef BP_EXTENDED_STATS
+       u64 rx_yields = 0, rx_cleaned = 0, rx_missed = 0,
+           tx_yields = 0, tx_cleaned = 0, tx_missed = 0;
+
+       for (i = 0; i < adapter->num_rx_queues; i++) {
+               rx_yields += adapter->rx_ring[i].bp_yields;
+               rx_cleaned += adapter->rx_ring[i].bp_cleaned;
+               rx_yields += adapter->rx_ring[i].bp_yields;
+       }
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               tx_yields += adapter->tx_ring[i].bp_yields;
+               tx_cleaned += adapter->tx_ring[i].bp_cleaned;
+               tx_yields += adapter->tx_ring[i].bp_yields;
+       }
+
+       adapter->bp_rx_yields = rx_yields;
+       adapter->bp_rx_cleaned = rx_cleaned;
+       adapter->bp_rx_missed = rx_missed;
+
+       adapter->bp_tx_yields = tx_yields;
+       adapter->bp_tx_cleaned = tx_cleaned;
+       adapter->bp_tx_missed = tx_missed;
+#endif
 
        ixgbevf_update_stats(adapter);
        for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
-               char *p = (char *)adapter +
-                       ixgbe_gstrings_stats[i].stat_offset;
-               char *b = (char *)adapter +
-                       ixgbe_gstrings_stats[i].base_stat_offset;
-               char *r = (char *)adapter +
-                       ixgbe_gstrings_stats[i].saved_reset_offset;
-               data[i] = ((ixgbe_gstrings_stats[i].sizeof_stat ==
-                           sizeof(u64)) ? *(u64 *)p : *(u32 *)p) -
-                         ((ixgbe_gstrings_stats[i].sizeof_stat ==
-                           sizeof(u64)) ? *(u64 *)b : *(u32 *)b) +
-                         ((ixgbe_gstrings_stats[i].sizeof_stat ==
-                           sizeof(u64)) ? *(u64 *)r : *(u32 *)r);
+               char *p = base + ixgbe_gstrings_stats[i].stat_offset;
+               char *b = base + ixgbe_gstrings_stats[i].base_stat_offset;
+               char *r = base + ixgbe_gstrings_stats[i].saved_reset_offset;
+
+               if (ixgbe_gstrings_stats[i].sizeof_stat == sizeof(u64)) {
+                       if (ixgbe_gstrings_stats[i].base_stat_offset >= 0)
+                               data[i] = *(u64 *)p - *(u64 *)b + *(u64 *)r;
+                       else
+                               data[i] = *(u64 *)p;
+               } else {
+                       if (ixgbe_gstrings_stats[i].base_stat_offset >= 0)
+                               data[i] = *(u32 *)p - *(u32 *)b + *(u32 *)r;
+                       else
+                               data[i] = *(u32 *)p;
+               }
        }
 }
 
@@ -685,6 +678,85 @@ static int ixgbevf_nway_reset(struct net_device *netdev)
        return 0;
 }
 
+static int ixgbevf_get_coalesce(struct net_device *netdev,
+                               struct ethtool_coalesce *ec)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+
+       /* only valid if in constant ITR mode */
+       if (adapter->rx_itr_setting <= 1)
+               ec->rx_coalesce_usecs = adapter->rx_itr_setting;
+       else
+               ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2;
+
+       /* if in mixed tx/rx queues per vector mode, report only rx settings */
+       if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count)
+               return 0;
+
+       /* only valid if in constant ITR mode */
+       if (adapter->tx_itr_setting <= 1)
+               ec->tx_coalesce_usecs = adapter->tx_itr_setting;
+       else
+               ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2;
+
+       return 0;
+}
+
+static int ixgbevf_set_coalesce(struct net_device *netdev,
+                               struct ethtool_coalesce *ec)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+       struct ixgbevf_q_vector *q_vector;
+       int num_vectors, i;
+       u16 tx_itr_param, rx_itr_param;
+
+       /* don't accept tx specific changes if we've got mixed RxTx vectors */
+       if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count
+           && ec->tx_coalesce_usecs)
+               return -EINVAL;
+
+
+       if ((ec->rx_coalesce_usecs > (IXGBE_MAX_EITR >> 2)) ||
+           (ec->tx_coalesce_usecs > (IXGBE_MAX_EITR >> 2)))
+               return -EINVAL;
+
+       if (ec->rx_coalesce_usecs > 1)
+               adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2;
+       else
+               adapter->rx_itr_setting = ec->rx_coalesce_usecs;
+
+       if (adapter->rx_itr_setting == 1)
+               rx_itr_param = IXGBE_20K_ITR;
+       else
+               rx_itr_param = adapter->rx_itr_setting;
+
+
+       if (ec->tx_coalesce_usecs > 1)
+               adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2;
+       else
+               adapter->tx_itr_setting = ec->tx_coalesce_usecs;
+
+       if (adapter->tx_itr_setting == 1)
+               tx_itr_param = IXGBE_10K_ITR;
+       else
+               tx_itr_param = adapter->tx_itr_setting;
+
+       num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+
+       for (i = 0; i < num_vectors; i++) {
+               q_vector = adapter->q_vector[i];
+               if (q_vector->tx.count && !q_vector->rx.count)
+                       /* tx only */
+                       q_vector->itr = tx_itr_param;
+               else
+                       /* rx only or mixed */
+                       q_vector->itr = rx_itr_param;
+               ixgbevf_write_eitr(q_vector);
+       }
+
+       return 0;
+}
+
 static const struct ethtool_ops ixgbevf_ethtool_ops = {
        .get_settings           = ixgbevf_get_settings,
        .get_drvinfo            = ixgbevf_get_drvinfo,
@@ -700,6 +772,8 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = {
        .get_sset_count         = ixgbevf_get_sset_count,
        .get_strings            = ixgbevf_get_strings,
        .get_ethtool_stats      = ixgbevf_get_ethtool_stats,
+       .get_coalesce           = ixgbevf_get_coalesce,
+       .set_coalesce           = ixgbevf_set_coalesce,
 };
 
 void ixgbevf_set_ethtool_ops(struct net_device *netdev)
index fff0d98..8971e2d 100644 (file)
 
 #include "vf.h"
 
+#ifdef CONFIG_NET_RX_BUSY_POLL
+#include <net/busy_poll.h>
+#define BP_EXTENDED_STATS
+#endif
+
 /* wrapper around a pointer to a socket buffer,
  * so a DMA handle can be stored along with the buffer */
 struct ixgbevf_tx_buffer {
@@ -76,6 +81,11 @@ struct ixgbevf_ring {
        struct u64_stats_sync   syncp;
        u64 hw_csum_rx_error;
        u64 hw_csum_rx_good;
+#ifdef BP_EXTENDED_STATS
+       u64 bp_yields;
+       u64 bp_misses;
+       u64 bp_cleaned;
+#endif
 
        u16 head;
        u16 tail;
@@ -145,7 +155,118 @@ struct ixgbevf_q_vector {
        struct napi_struct napi;
        struct ixgbevf_ring_container rx, tx;
        char name[IFNAMSIZ + 9];
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       unsigned int state;
+#define IXGBEVF_QV_STATE_IDLE          0
+#define IXGBEVF_QV_STATE_NAPI          1    /* NAPI owns this QV */
+#define IXGBEVF_QV_STATE_POLL          2    /* poll owns this QV */
+#define IXGBEVF_QV_STATE_DISABLED      4    /* QV is disabled */
+#define IXGBEVF_QV_OWNED (IXGBEVF_QV_STATE_NAPI | IXGBEVF_QV_STATE_POLL)
+#define IXGBEVF_QV_LOCKED (IXGBEVF_QV_OWNED | IXGBEVF_QV_STATE_DISABLED)
+#define IXGBEVF_QV_STATE_NAPI_YIELD    8    /* NAPI yielded this QV */
+#define IXGBEVF_QV_STATE_POLL_YIELD    16   /* poll yielded this QV */
+#define IXGBEVF_QV_YIELD (IXGBEVF_QV_STATE_NAPI_YIELD | IXGBEVF_QV_STATE_POLL_YIELD)
+#define IXGBEVF_QV_USER_PEND (IXGBEVF_QV_STATE_POLL | IXGBEVF_QV_STATE_POLL_YIELD)
+       spinlock_t lock;
+#endif /* CONFIG_NET_RX_BUSY_POLL */
 };
+#ifdef CONFIG_NET_RX_BUSY_POLL
+static inline void ixgbevf_qv_init_lock(struct ixgbevf_q_vector *q_vector)
+{
+
+       spin_lock_init(&q_vector->lock);
+       q_vector->state = IXGBEVF_QV_STATE_IDLE;
+}
+
+/* called from the device poll routine to get ownership of a q_vector */
+static inline bool ixgbevf_qv_lock_napi(struct ixgbevf_q_vector *q_vector)
+{
+       int rc = true;
+       spin_lock_bh(&q_vector->lock);
+       if (q_vector->state & IXGBEVF_QV_LOCKED) {
+               WARN_ON(q_vector->state & IXGBEVF_QV_STATE_NAPI);
+               q_vector->state |= IXGBEVF_QV_STATE_NAPI_YIELD;
+               rc = false;
+#ifdef BP_EXTENDED_STATS
+               q_vector->tx.ring->bp_yields++;
+#endif
+       } else {
+               /* we don't care if someone yielded */
+               q_vector->state = IXGBEVF_QV_STATE_NAPI;
+       }
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+/* returns true is someone tried to get the qv while napi had it */
+static inline bool ixgbevf_qv_unlock_napi(struct ixgbevf_q_vector *q_vector)
+{
+       int rc = false;
+       spin_lock_bh(&q_vector->lock);
+       WARN_ON(q_vector->state & (IXGBEVF_QV_STATE_POLL |
+                                  IXGBEVF_QV_STATE_NAPI_YIELD));
+
+       if (q_vector->state & IXGBEVF_QV_STATE_POLL_YIELD)
+               rc = true;
+       /* reset state to idle, unless QV is disabled */
+       q_vector->state &= IXGBEVF_QV_STATE_DISABLED;
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+/* called from ixgbevf_low_latency_poll() */
+static inline bool ixgbevf_qv_lock_poll(struct ixgbevf_q_vector *q_vector)
+{
+       int rc = true;
+       spin_lock_bh(&q_vector->lock);
+       if ((q_vector->state & IXGBEVF_QV_LOCKED)) {
+               q_vector->state |= IXGBEVF_QV_STATE_POLL_YIELD;
+               rc = false;
+#ifdef BP_EXTENDED_STATS
+               q_vector->rx.ring->bp_yields++;
+#endif
+       } else {
+               /* preserve yield marks */
+               q_vector->state |= IXGBEVF_QV_STATE_POLL;
+       }
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+/* returns true if someone tried to get the qv while it was locked */
+static inline bool ixgbevf_qv_unlock_poll(struct ixgbevf_q_vector *q_vector)
+{
+       int rc = false;
+       spin_lock_bh(&q_vector->lock);
+       WARN_ON(q_vector->state & (IXGBEVF_QV_STATE_NAPI));
+
+       if (q_vector->state & IXGBEVF_QV_STATE_POLL_YIELD)
+               rc = true;
+       /* reset state to idle, unless QV is disabled */
+       q_vector->state &= IXGBEVF_QV_STATE_DISABLED;
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+/* true if a socket is polling, even if it did not get the lock */
+static inline bool ixgbevf_qv_busy_polling(struct ixgbevf_q_vector *q_vector)
+{
+       WARN_ON(!(q_vector->state & IXGBEVF_QV_OWNED));
+       return q_vector->state & IXGBEVF_QV_USER_PEND;
+}
+
+/* false if QV is currently owned */
+static inline bool ixgbevf_qv_disable(struct ixgbevf_q_vector *q_vector)
+{
+       int rc = true;
+       spin_lock_bh(&q_vector->lock);
+       if (q_vector->state & IXGBEVF_QV_OWNED)
+               rc = false;
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+#endif /* CONFIG_NET_RX_BUSY_POLL */
 
 /*
  * microsecond values for various ITR rates shifted by 2 to fit itr register
@@ -165,9 +286,13 @@ struct ixgbevf_q_vector {
        ((_eitr) ? (1000000000 / ((_eitr) * 256)) : 8)
 #define EITR_REG_TO_INTS_PER_SEC EITR_INTS_PER_SEC_TO_REG
 
-#define IXGBE_DESC_UNUSED(R) \
-       ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
-       (R)->next_to_clean - (R)->next_to_use - 1)
+static inline u16 ixgbevf_desc_unused(struct ixgbevf_ring *ring)
+{
+       u16 ntc = ring->next_to_clean;
+       u16 ntu = ring->next_to_use;
+
+       return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1;
+}
 
 #define IXGBEVF_RX_DESC(R, i)      \
        (&(((union ixgbe_adv_rx_desc *)((R)->desc))[i]))
@@ -240,7 +365,6 @@ struct ixgbevf_adapter {
        struct ixgbe_hw hw;
        u16 msg_enable;
        struct ixgbevf_hw_stats stats;
-       u64 zero_base;
        /* Interrupt Throttle Rate */
        u32 eitr_param;
 
@@ -249,6 +373,16 @@ struct ixgbevf_adapter {
        unsigned int tx_ring_count;
        unsigned int rx_ring_count;
 
+#ifdef BP_EXTENDED_STATS
+       u64 bp_rx_yields;
+       u64 bp_rx_cleaned;
+       u64 bp_rx_missed;
+
+       u64 bp_tx_yields;
+       u64 bp_tx_cleaned;
+       u64 bp_tx_missed;
+#endif
+
        u32 link_speed;
        bool link_up;
 
@@ -281,27 +415,25 @@ extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops;
 extern const char ixgbevf_driver_name[];
 extern const char ixgbevf_driver_version[];
 
-extern void ixgbevf_up(struct ixgbevf_adapter *adapter);
-extern void ixgbevf_down(struct ixgbevf_adapter *adapter);
-extern void ixgbevf_reinit_locked(struct ixgbevf_adapter *adapter);
-extern void ixgbevf_reset(struct ixgbevf_adapter *adapter);
-extern void ixgbevf_set_ethtool_ops(struct net_device *netdev);
-extern int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *,
-                                     struct ixgbevf_ring *);
-extern int ixgbevf_setup_tx_resources(struct ixgbevf_adapter *,
-                                     struct ixgbevf_ring *);
-extern void ixgbevf_free_rx_resources(struct ixgbevf_adapter *,
-                                     struct ixgbevf_ring *);
-extern void ixgbevf_free_tx_resources(struct ixgbevf_adapter *,
-                                     struct ixgbevf_ring *);
-extern void ixgbevf_update_stats(struct ixgbevf_adapter *adapter);
-extern int ethtool_ioctl(struct ifreq *ifr);
-
-extern void ixgbe_napi_add_all(struct ixgbevf_adapter *adapter);
-extern void ixgbe_napi_del_all(struct ixgbevf_adapter *adapter);
+void ixgbevf_up(struct ixgbevf_adapter *adapter);
+void ixgbevf_down(struct ixgbevf_adapter *adapter);
+void ixgbevf_reinit_locked(struct ixgbevf_adapter *adapter);
+void ixgbevf_reset(struct ixgbevf_adapter *adapter);
+void ixgbevf_set_ethtool_ops(struct net_device *netdev);
+int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *);
+int ixgbevf_setup_tx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *);
+void ixgbevf_free_rx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *);
+void ixgbevf_free_tx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *);
+void ixgbevf_update_stats(struct ixgbevf_adapter *adapter);
+int ethtool_ioctl(struct ifreq *ifr);
+
+extern void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector);
+
+void ixgbe_napi_add_all(struct ixgbevf_adapter *adapter);
+void ixgbe_napi_del_all(struct ixgbevf_adapter *adapter);
 
 #ifdef DEBUG
-extern char *ixgbevf_get_hw_dev_name(struct ixgbe_hw *hw);
+char *ixgbevf_get_hw_dev_name(struct ixgbe_hw *hw);
 #define hw_dbg(hw, format, arg...) \
        printk(KERN_DEBUG "%s: " format, ixgbevf_get_hw_dev_name(hw), ##arg)
 #else
index 59a62bb..038bfc8 100644 (file)
@@ -58,7 +58,7 @@ const char ixgbevf_driver_name[] = "ixgbevf";
 static const char ixgbevf_driver_string[] =
        "Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver";
 
-#define DRV_VERSION "2.7.12-k"
+#define DRV_VERSION "2.11.3-k"
 const char ixgbevf_driver_version[] = DRV_VERSION;
 static char ixgbevf_copyright[] =
        "Copyright (c) 2009 - 2012 Intel Corporation.";
@@ -251,7 +251,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
 
 #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
        if (unlikely(count && netif_carrier_ok(tx_ring->netdev) &&
-                    (IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) {
+                    (ixgbevf_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
                /* Make sure that anybody stopping the queue after this
                 * sees the new next_to_clean.
                 */
@@ -299,6 +299,30 @@ static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector,
                netif_rx(skb);
 }
 
+/**
+ * ixgbevf_rx_skb - Helper function to determine proper Rx method
+ * @q_vector: structure containing interrupt and ring information
+ * @skb: packet to send up
+ * @status: hardware indication of status of receive
+ * @rx_desc: rx descriptor
+ **/
+static void ixgbevf_rx_skb(struct ixgbevf_q_vector *q_vector,
+                          struct sk_buff *skb, u8 status,
+                          union ixgbe_adv_rx_desc *rx_desc)
+{
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       skb_mark_napi_id(skb, &q_vector->napi);
+
+       if (ixgbevf_qv_busy_polling(q_vector)) {
+               netif_receive_skb(skb);
+               /* exit early if we busy polled */
+               return;
+       }
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
+       ixgbevf_receive_skb(q_vector, skb, status, rx_desc);
+}
+
 /**
  * ixgbevf_rx_checksum - indicate in skb if hw indicated a good cksum
  * @ring: pointer to Rx descriptor ring structure
@@ -396,9 +420,9 @@ static inline void ixgbevf_irq_enable_queues(struct ixgbevf_adapter *adapter,
        IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, qmask);
 }
 
-static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
-                                struct ixgbevf_ring *rx_ring,
-                                int budget)
+static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
+                               struct ixgbevf_ring *rx_ring,
+                               int budget)
 {
        struct ixgbevf_adapter *adapter = q_vector->adapter;
        struct pci_dev *pdev = adapter->pdev;
@@ -473,15 +497,6 @@ static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
                total_rx_bytes += skb->len;
                total_rx_packets++;
 
-               /*
-                * Work around issue of some types of VM to VM loop back
-                * packets not getting split correctly
-                */
-               if (staterr & IXGBE_RXD_STAT_LB) {
-                       u32 header_fixup_len = skb_headlen(skb);
-                       if (header_fixup_len < 14)
-                               skb_push(skb, header_fixup_len);
-               }
                skb->protocol = eth_type_trans(skb, rx_ring->netdev);
 
                /* Workaround hardware that can't do proper VEPA multicast
@@ -494,7 +509,7 @@ static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
                        goto next_desc;
                }
 
-               ixgbevf_receive_skb(q_vector, skb, staterr, rx_desc);
+               ixgbevf_rx_skb(q_vector, skb, staterr, rx_desc);
 
 next_desc:
                rx_desc->wb.upper.status_error = 0;
@@ -514,7 +529,7 @@ next_desc:
        }
 
        rx_ring->next_to_clean = i;
-       cleaned_count = IXGBE_DESC_UNUSED(rx_ring);
+       cleaned_count = ixgbevf_desc_unused(rx_ring);
 
        if (cleaned_count)
                ixgbevf_alloc_rx_buffers(adapter, rx_ring, cleaned_count);
@@ -526,7 +541,7 @@ next_desc:
        q_vector->rx.total_packets += total_rx_packets;
        q_vector->rx.total_bytes += total_rx_bytes;
 
-       return !!budget;
+       return total_rx_packets;
 }
 
 /**
@@ -549,6 +564,11 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
        ixgbevf_for_each_ring(ring, q_vector->tx)
                clean_complete &= ixgbevf_clean_tx_irq(q_vector, ring);
 
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       if (!ixgbevf_qv_lock_napi(q_vector))
+               return budget;
+#endif
+
        /* attempt to distribute budget to each queue fairly, but don't allow
         * the budget to go below 1 because we'll exit polling */
        if (q_vector->rx.count > 1)
@@ -558,10 +578,15 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
 
        adapter->flags |= IXGBE_FLAG_IN_NETPOLL;
        ixgbevf_for_each_ring(ring, q_vector->rx)
-               clean_complete &= ixgbevf_clean_rx_irq(q_vector, ring,
-                                                      per_ring_budget);
+               clean_complete &= (ixgbevf_clean_rx_irq(q_vector, ring,
+                                                       per_ring_budget)
+                                  < per_ring_budget);
        adapter->flags &= ~IXGBE_FLAG_IN_NETPOLL;
 
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       ixgbevf_qv_unlock_napi(q_vector);
+#endif
+
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
                return budget;
@@ -580,7 +605,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
  * ixgbevf_write_eitr - write VTEITR register in hardware specific way
  * @q_vector: structure containing interrupt and ring information
  */
-static void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector)
+void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector)
 {
        struct ixgbevf_adapter *adapter = q_vector->adapter;
        struct ixgbe_hw *hw = &adapter->hw;
@@ -596,6 +621,40 @@ static void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector)
        IXGBE_WRITE_REG(hw, IXGBE_VTEITR(v_idx), itr_reg);
 }
 
+#ifdef CONFIG_NET_RX_BUSY_POLL
+/* must be called with local_bh_disable()d */
+static int ixgbevf_busy_poll_recv(struct napi_struct *napi)
+{
+       struct ixgbevf_q_vector *q_vector =
+                       container_of(napi, struct ixgbevf_q_vector, napi);
+       struct ixgbevf_adapter *adapter = q_vector->adapter;
+       struct ixgbevf_ring  *ring;
+       int found = 0;
+
+       if (test_bit(__IXGBEVF_DOWN, &adapter->state))
+               return LL_FLUSH_FAILED;
+
+       if (!ixgbevf_qv_lock_poll(q_vector))
+               return LL_FLUSH_BUSY;
+
+       ixgbevf_for_each_ring(ring, q_vector->rx) {
+               found = ixgbevf_clean_rx_irq(q_vector, ring, 4);
+#ifdef BP_EXTENDED_STATS
+               if (found)
+                       ring->bp_cleaned += found;
+               else
+                       ring->bp_misses++;
+#endif
+               if (found)
+                       break;
+       }
+
+       ixgbevf_qv_unlock_poll(q_vector);
+
+       return found;
+}
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
 /**
  * ixgbevf_configure_msix - Configure MSI-X hardware
  * @adapter: board private structure
@@ -756,37 +815,12 @@ static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector)
 static irqreturn_t ixgbevf_msix_other(int irq, void *data)
 {
        struct ixgbevf_adapter *adapter = data;
-       struct pci_dev *pdev = adapter->pdev;
        struct ixgbe_hw *hw = &adapter->hw;
-       u32 msg;
-       bool got_ack = false;
 
        hw->mac.get_link_status = 1;
-       if (!hw->mbx.ops.check_for_ack(hw))
-               got_ack = true;
-
-       if (!hw->mbx.ops.check_for_msg(hw)) {
-               hw->mbx.ops.read(hw, &msg, 1);
-
-               if ((msg & IXGBE_MBVFICR_VFREQ_MASK) == IXGBE_PF_CONTROL_MSG) {
-                       mod_timer(&adapter->watchdog_timer,
-                                 round_jiffies(jiffies + 1));
-                       adapter->link_up = false;
-               }
 
-               if (msg & IXGBE_VT_MSGTYPE_NACK)
-                       dev_info(&pdev->dev,
-                                "Last Request of type %2.2x to PF Nacked\n",
-                                msg & 0xFF);
-               hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFSTS;
-       }
-
-       /* checking for the ack clears the PFACK bit.  Place
-        * it back in the v2p_mailbox cache so that anyone
-        * polling for an ack will not miss it
-        */
-       if (got_ack)
-               hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFACK;
+       if (!test_bit(__IXGBEVF_DOWN, &adapter->state))
+               mod_timer(&adapter->watchdog_timer, jiffies);
 
        IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other);
 
@@ -1107,6 +1141,21 @@ static void ixgbevf_configure_srrctl(struct ixgbevf_adapter *adapter, int index)
        IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(index), srrctl);
 }
 
+static void ixgbevf_setup_psrtype(struct ixgbevf_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       /* PSRTYPE must be initialized in 82599 */
+       u32 psrtype = IXGBE_PSRTYPE_TCPHDR | IXGBE_PSRTYPE_UDPHDR |
+                     IXGBE_PSRTYPE_IPV4HDR | IXGBE_PSRTYPE_IPV6HDR |
+                     IXGBE_PSRTYPE_L2HDR;
+
+       if (adapter->num_rx_queues > 1)
+               psrtype |= 1 << 29;
+
+       IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, psrtype);
+}
+
 static void ixgbevf_set_rx_buffer_len(struct ixgbevf_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
@@ -1154,8 +1203,7 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter)
        int i, j;
        u32 rdlen;
 
-       /* PSRTYPE must be initialized in 82599 */
-       IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, 0);
+       ixgbevf_setup_psrtype(adapter);
 
        /* set_rx_buffer_len must be called before ring initialization */
        ixgbevf_set_rx_buffer_len(adapter);
@@ -1293,6 +1341,9 @@ static void ixgbevf_napi_enable_all(struct ixgbevf_adapter *adapter)
 
        for (q_idx = 0; q_idx < q_vectors; q_idx++) {
                q_vector = adapter->q_vector[q_idx];
+#ifdef CONFIG_NET_RX_BUSY_POLL
+               ixgbevf_qv_init_lock(adapter->q_vector[q_idx]);
+#endif
                napi_enable(&q_vector->napi);
        }
 }
@@ -1306,6 +1357,12 @@ static void ixgbevf_napi_disable_all(struct ixgbevf_adapter *adapter)
        for (q_idx = 0; q_idx < q_vectors; q_idx++) {
                q_vector = adapter->q_vector[q_idx];
                napi_disable(&q_vector->napi);
+#ifdef CONFIG_NET_RX_BUSY_POLL
+               while (!ixgbevf_qv_disable(adapter->q_vector[q_idx])) {
+                       pr_info("QV %d locked\n", q_idx);
+                       usleep_range(1000, 20000);
+               }
+#endif /* CONFIG_NET_RX_BUSY_POLL */
        }
 }
 
@@ -1323,31 +1380,55 @@ static void ixgbevf_configure(struct ixgbevf_adapter *adapter)
        for (i = 0; i < adapter->num_rx_queues; i++) {
                struct ixgbevf_ring *ring = &adapter->rx_ring[i];
                ixgbevf_alloc_rx_buffers(adapter, ring,
-                                        IXGBE_DESC_UNUSED(ring));
+                                        ixgbevf_desc_unused(ring));
        }
 }
 
-#define IXGBE_MAX_RX_DESC_POLL 10
-static inline void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter,
-                                               int rxr)
+#define IXGBEVF_MAX_RX_DESC_POLL 10
+static void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter,
+                                        int rxr)
 {
        struct ixgbe_hw *hw = &adapter->hw;
+       int wait_loop = IXGBEVF_MAX_RX_DESC_POLL;
+       u32 rxdctl;
        int j = adapter->rx_ring[rxr].reg_idx;
-       int k;
 
-       for (k = 0; k < IXGBE_MAX_RX_DESC_POLL; k++) {
-               if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(j)) & IXGBE_RXDCTL_ENABLE)
-                       break;
-               else
-                       msleep(1);
-       }
-       if (k >= IXGBE_MAX_RX_DESC_POLL) {
-               hw_dbg(hw, "RXDCTL.ENABLE on Rx queue %d "
-                      "not set within the polling period\n", rxr);
-       }
+       do {
+               usleep_range(1000, 2000);
+               rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(j));
+       } while (--wait_loop && !(rxdctl & IXGBE_RXDCTL_ENABLE));
+
+       if (!wait_loop)
+               hw_dbg(hw, "RXDCTL.ENABLE queue %d not set while polling\n",
+                      rxr);
+
+       ixgbevf_release_rx_desc(&adapter->hw, &adapter->rx_ring[rxr],
+                               (adapter->rx_ring[rxr].count - 1));
+}
+
+static void ixgbevf_disable_rx_queue(struct ixgbevf_adapter *adapter,
+                                    struct ixgbevf_ring *ring)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int wait_loop = IXGBEVF_MAX_RX_DESC_POLL;
+       u32 rxdctl;
+       u8 reg_idx = ring->reg_idx;
+
+       rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(reg_idx));
+       rxdctl &= ~IXGBE_RXDCTL_ENABLE;
+
+       /* write value back with RXDCTL.ENABLE bit cleared */
+       IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl);
 
-       ixgbevf_release_rx_desc(hw, &adapter->rx_ring[rxr],
-                               adapter->rx_ring[rxr].count - 1);
+       /* the hardware may take up to 100us to really disable the rx queue */
+       do {
+               udelay(10);
+               rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(reg_idx));
+       } while (--wait_loop && (rxdctl & IXGBE_RXDCTL_ENABLE));
+
+       if (!wait_loop)
+               hw_dbg(hw, "RXDCTL.ENABLE queue %d not cleared while polling\n",
+                      reg_idx);
 }
 
 static void ixgbevf_save_reset_stats(struct ixgbevf_adapter *adapter)
@@ -1545,8 +1626,6 @@ void ixgbevf_up(struct ixgbevf_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
 
-       ixgbevf_negotiate_api(adapter);
-
        ixgbevf_reset_queues(adapter);
 
        ixgbevf_configure(adapter);
@@ -1679,7 +1758,10 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter)
 
        /* signal that we are down to the interrupt handler */
        set_bit(__IXGBEVF_DOWN, &adapter->state);
-       /* disable receives */
+
+       /* disable all enabled rx queues */
+       for (i = 0; i < adapter->num_rx_queues; i++)
+               ixgbevf_disable_rx_queue(adapter, &adapter->rx_ring[i]);
 
        netif_tx_disable(netdev);
 
@@ -1733,10 +1815,12 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter)
        struct ixgbe_hw *hw = &adapter->hw;
        struct net_device *netdev = adapter->netdev;
 
-       if (hw->mac.ops.reset_hw(hw))
+       if (hw->mac.ops.reset_hw(hw)) {
                hw_dbg(hw, "PF still resetting\n");
-       else
+       } else {
                hw->mac.ops.init_hw(hw);
+               ixgbevf_negotiate_api(adapter);
+       }
 
        if (is_valid_ether_addr(adapter->hw.mac.addr)) {
                memcpy(netdev->dev_addr, adapter->hw.mac.addr,
@@ -1929,6 +2013,9 @@ static int ixgbevf_alloc_q_vectors(struct ixgbevf_adapter *adapter)
                q_vector->v_idx = q_idx;
                netif_napi_add(adapter->netdev, &q_vector->napi,
                               ixgbevf_poll, 64);
+#ifdef CONFIG_NET_RX_BUSY_POLL
+               napi_hash_add(&q_vector->napi);
+#endif
                adapter->q_vector[q_idx] = q_vector;
        }
 
@@ -1938,6 +2025,9 @@ err_out:
        while (q_idx) {
                q_idx--;
                q_vector = adapter->q_vector[q_idx];
+#ifdef CONFIG_NET_RX_BUSY_POLL
+               napi_hash_del(&q_vector->napi);
+#endif
                netif_napi_del(&q_vector->napi);
                kfree(q_vector);
                adapter->q_vector[q_idx] = NULL;
@@ -1961,6 +2051,9 @@ static void ixgbevf_free_q_vectors(struct ixgbevf_adapter *adapter)
                struct ixgbevf_q_vector *q_vector = adapter->q_vector[q_idx];
 
                adapter->q_vector[q_idx] = NULL;
+#ifdef CONFIG_NET_RX_BUSY_POLL
+               napi_hash_del(&q_vector->napi);
+#endif
                netif_napi_del(&q_vector->napi);
                kfree(q_vector);
        }
@@ -2072,6 +2165,9 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
        hw->mac.max_tx_queues = 2;
        hw->mac.max_rx_queues = 2;
 
+       /* lock to protect mailbox accesses */
+       spin_lock_init(&adapter->mbx_lock);
+
        err = hw->mac.ops.reset_hw(hw);
        if (err) {
                dev_info(&pdev->dev,
@@ -2082,6 +2178,7 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
                        pr_err("init_shared_code failed: %d\n", err);
                        goto out;
                }
+               ixgbevf_negotiate_api(adapter);
                err = hw->mac.ops.get_mac_addr(hw, hw->mac.addr);
                if (err)
                        dev_info(&pdev->dev, "Error reading MAC address\n");
@@ -2097,9 +2194,6 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
                memcpy(hw->mac.addr, netdev->dev_addr, netdev->addr_len);
        }
 
-       /* lock to protect mailbox accesses */
-       spin_lock_init(&adapter->mbx_lock);
-
        /* Enable dynamic interrupt throttling rates */
        adapter->rx_itr_setting = 1;
        adapter->tx_itr_setting = 1;
@@ -2620,8 +2714,6 @@ static int ixgbevf_open(struct net_device *netdev)
                }
        }
 
-       ixgbevf_negotiate_api(adapter);
-
        /* setup queue reg_idx and Rx queue count */
        err = ixgbevf_setup_queues(adapter);
        if (err)
@@ -3010,7 +3102,7 @@ static int __ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size)
 
        /* We need to check again in a case another CPU has just
         * made room available. */
-       if (likely(IXGBE_DESC_UNUSED(tx_ring) < size))
+       if (likely(ixgbevf_desc_unused(tx_ring) < size))
                return -EBUSY;
 
        /* A reprieve! - use start_queue because it doesn't call schedule */
@@ -3021,7 +3113,7 @@ static int __ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size)
 
 static int ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size)
 {
-       if (likely(IXGBE_DESC_UNUSED(tx_ring) >= size))
+       if (likely(ixgbevf_desc_unused(tx_ring) >= size))
                return 0;
        return __ixgbevf_maybe_stop_tx(tx_ring, size);
 }
@@ -3216,6 +3308,8 @@ static int ixgbevf_resume(struct pci_dev *pdev)
        }
        pci_set_master(pdev);
 
+       ixgbevf_reset(adapter);
+
        rtnl_lock();
        err = ixgbevf_init_interrupt_scheme(adapter);
        rtnl_unlock();
@@ -3224,8 +3318,6 @@ static int ixgbevf_resume(struct pci_dev *pdev)
                return err;
        }
 
-       ixgbevf_reset(adapter);
-
        if (netif_running(netdev)) {
                err = ixgbevf_open(netdev);
                if (err)
@@ -3293,6 +3385,9 @@ static const struct net_device_ops ixgbevf_netdev_ops = {
        .ndo_tx_timeout         = ixgbevf_tx_timeout,
        .ndo_vlan_rx_add_vid    = ixgbevf_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = ixgbevf_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_RX_BUSY_POLL
+       .ndo_busy_poll          = ixgbevf_busy_poll_recv,
+#endif
 };
 
 static void ixgbevf_assign_netdev_ops(struct net_device *dev)
index 387b526..4d44d64 100644 (file)
@@ -242,7 +242,7 @@ static s32 ixgbevf_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr)
        msgbuf[0] |= index << IXGBE_VT_MSGINFO_SHIFT;
        msgbuf[0] |= IXGBE_VF_SET_MACVLAN;
        if (addr)
-               memcpy(msg_addr, addr, 6);
+               memcpy(msg_addr, addr, ETH_ALEN);
        ret_val = mbx->ops.write_posted(hw, msgbuf, 3);
 
        if (!ret_val)
@@ -275,7 +275,7 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr,
 
        memset(msgbuf, 0, sizeof(msgbuf));
        msgbuf[0] = IXGBE_VF_SET_MAC_ADDR;
-       memcpy(msg_addr, addr, 6);
+       memcpy(msg_addr, addr, ETH_ALEN);
        ret_val = mbx->ops.write_posted(hw, msgbuf, 3);
 
        if (!ret_val)
index 23de82a..f5685c0 100644 (file)
@@ -309,7 +309,7 @@ static void
 jme_load_macaddr(struct net_device *netdev)
 {
        struct jme_adapter *jme = netdev_priv(netdev);
-       unsigned char macaddr[6];
+       unsigned char macaddr[ETH_ALEN];
        u32 val;
 
        spin_lock_bh(&jme->macaddr_lock);
@@ -321,7 +321,7 @@ jme_load_macaddr(struct net_device *netdev)
        val = jread32(jme, JME_RXUMA_HI);
        macaddr[4] = (val >>  0) & 0xFF;
        macaddr[5] = (val >>  8) & 0xFF;
-       memcpy(netdev->dev_addr, macaddr, 6);
+       memcpy(netdev->dev_addr, macaddr, ETH_ALEN);
        spin_unlock_bh(&jme->macaddr_lock);
 }
 
@@ -3192,7 +3192,6 @@ jme_init_one(struct pci_dev *pdev,
 err_out_unmap:
        iounmap(jme->regs);
 err_out_free_netdev:
-       pci_set_drvdata(pdev, NULL);
        free_netdev(netdev);
 err_out_release_regions:
        pci_release_regions(pdev);
@@ -3210,7 +3209,6 @@ jme_remove_one(struct pci_dev *pdev)
 
        unregister_netdev(netdev);
        iounmap(jme->regs);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(netdev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
index 3efc897..58cd67c 100644 (file)
@@ -28,7 +28,6 @@
 
 #define DRV_NAME       "jme"
 #define DRV_VERSION    "1.0.8"
-#define PFX            DRV_NAME ": "
 
 #define PCI_DEVICE_ID_JMICRON_JMC250   0x0250
 #define PCI_DEVICE_ID_JMICRON_JMC260   0x0260
index a36fa80..4a5e3b0 100644 (file)
@@ -1110,7 +1110,7 @@ static int korina_probe(struct platform_device *pdev)
        lp = netdev_priv(dev);
 
        bif->dev = dev;
-       memcpy(dev->dev_addr, bif->mac, 6);
+       memcpy(dev->dev_addr, bif->mac, ETH_ALEN);
 
        lp->rx_irq = platform_get_irq_byname(pdev, "korina_rx");
        lp->tx_irq = platform_get_irq_byname(pdev, "korina_tx");
index 7fb5677..00cd36e 100644 (file)
@@ -1131,15 +1131,13 @@ static void mib_counters_update(struct mv643xx_eth_private *mp)
        p->rx_discard += rdlp(mp, RX_DISCARD_FRAME_CNT);
        p->rx_overrun += rdlp(mp, RX_OVERRUN_FRAME_CNT);
        spin_unlock_bh(&mp->mib_counters_lock);
-
-       mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
 }
 
 static void mib_counters_timer_wrapper(unsigned long _mp)
 {
        struct mv643xx_eth_private *mp = (void *)_mp;
-
        mib_counters_update(mp);
+       mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ);
 }
 
 
@@ -2237,6 +2235,7 @@ static int mv643xx_eth_open(struct net_device *dev)
                mp->int_mask |= INT_TX_END_0 << i;
        }
 
+       add_timer(&mp->mib_counters_timer);
        port_start(mp);
 
        wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX);
@@ -2514,7 +2513,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
 
        mac_addr = of_get_mac_address(pnp);
        if (mac_addr)
-               memcpy(ppd.mac_addr, mac_addr, 6);
+               memcpy(ppd.mac_addr, mac_addr, ETH_ALEN);
 
        mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size);
        mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr);
@@ -2534,6 +2533,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
        if (!ppdev)
                return -ENOMEM;
        ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+       ppdev->dev.of_node = pnp;
 
        ret = platform_device_add_resources(ppdev, &res, 1);
        if (ret)
@@ -2696,7 +2696,7 @@ static void set_params(struct mv643xx_eth_private *mp,
        struct net_device *dev = mp->dev;
 
        if (is_valid_ether_addr(pd->mac_addr))
-               memcpy(dev->dev_addr, pd->mac_addr, 6);
+               memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
        else
                uc_addr_get(mp, dev->dev_addr);
 
@@ -2890,6 +2890,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
                                         PHY_INTERFACE_MODE_GMII);
                if (!mp->phy)
                        err = -ENODEV;
+               phy_addr_set(mp, mp->phy->addr);
        } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
                mp->phy = phy_scan(mp, pd->phy_addr);
 
@@ -2916,7 +2917,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        mp->mib_counters_timer.data = (unsigned long)mp;
        mp->mib_counters_timer.function = mib_counters_timer_wrapper;
        mp->mib_counters_timer.expires = jiffies + 30 * HZ;
-       add_timer(&mp->mib_counters_timer);
 
        spin_lock_init(&mp->mib_counters_lock);
 
index e2f6626..7354960 100644 (file)
@@ -4,11 +4,9 @@
  * Since the MDIO interface of Marvell network interfaces is shared
  * between all network interfaces, having a single driver allows to
  * handle concurrent accesses properly (you may have four Ethernet
- * ports, but they in fact share the same SMI interface to access the
- * MDIO bus). Moreover, this MDIO interface code is similar between
- * the mv643xx_eth driver and the mvneta driver. For now, it is only
- * used by the mvneta driver, but it could later be used by the
- * mv643xx_eth driver as well.
+ * ports, but they in fact share the same SMI interface to access
+ * the MDIO bus). This driver is currently used by the mvneta and
+ * mv643xx_eth drivers.
  *
  * Copyright (C) 2012 Marvell
  *
 #define  MVMDIO_ERR_INT_SMI_DONE          0x00000010
 #define MVMDIO_ERR_INT_MASK               0x0080
 
+/*
+ * SMI Timeout measurements:
+ * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt)
+ * - Armada 370       (Globalscale Mirabox):   41us to 43us (Polled)
+ */
+#define MVMDIO_SMI_TIMEOUT                1000 /* 1000us = 1ms */
+#define MVMDIO_SMI_POLL_INTERVAL_MIN      45
+#define MVMDIO_SMI_POLL_INTERVAL_MAX      55
+
 struct orion_mdio_dev {
        struct mutex lock;
        void __iomem *regs;
@@ -68,77 +75,68 @@ static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev)
 static int orion_mdio_wait_ready(struct mii_bus *bus)
 {
        struct orion_mdio_dev *dev = bus->priv;
-       int count;
-
-       if (dev->err_interrupt <= 0) {
-               count = 0;
-               while (1) {
-                       if (orion_mdio_smi_is_done(dev))
-                               break;
-
-                       if (count > 100) {
-                               dev_err(bus->parent,
-                                       "Timeout: SMI busy for too long\n");
-                               return -ETIMEDOUT;
-                       }
-
-                       udelay(10);
-                       count++;
-               }
-       } else {
-               if (!orion_mdio_smi_is_done(dev)) {
+       unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT);
+       unsigned long end = jiffies + timeout;
+       int timedout = 0;
+
+       while (1) {
+               if (orion_mdio_smi_is_done(dev))
+                       return 0;
+               else if (timedout)
+                       break;
+
+               if (dev->err_interrupt <= 0) {
+                       usleep_range(MVMDIO_SMI_POLL_INTERVAL_MIN,
+                                    MVMDIO_SMI_POLL_INTERVAL_MAX);
+
+                       if (time_is_before_jiffies(end))
+                               ++timedout;
+               } else {
                        wait_event_timeout(dev->smi_busy_wait,
-                               orion_mdio_smi_is_done(dev),
-                               msecs_to_jiffies(100));
-                       if (!orion_mdio_smi_is_done(dev))
-                               return -ETIMEDOUT;
-               }
+                                          orion_mdio_smi_is_done(dev),
+                                          timeout);
+
+                       ++timedout;
+               }
        }
 
-       return 0;
+       dev_err(bus->parent, "Timeout: SMI busy for too long\n");
+       return  -ETIMEDOUT;
 }
 
 static int orion_mdio_read(struct mii_bus *bus, int mii_id,
                           int regnum)
 {
        struct orion_mdio_dev *dev = bus->priv;
-       int count;
        u32 val;
        int ret;
 
        mutex_lock(&dev->lock);
 
        ret = orion_mdio_wait_ready(bus);
-       if (ret < 0) {
-               mutex_unlock(&dev->lock);
-               return ret;
-       }
+       if (ret < 0)
+               goto out;
 
        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
                MVMDIO_SMI_READ_OPERATION),
               dev->regs);
 
-       /* Wait for the value to become available */
-       count = 0;
-       while (1) {
-               val = readl(dev->regs);
-               if (val & MVMDIO_SMI_READ_VALID)
-                       break;
-
-               if (count > 100) {
-                       dev_err(bus->parent, "Timeout when reading PHY\n");
-                       mutex_unlock(&dev->lock);
-                       return -ETIMEDOUT;
-               }
+       ret = orion_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
 
-               udelay(10);
-               count++;
+       val = readl(dev->regs);
+       if (!(val & MVMDIO_SMI_READ_VALID)) {
+               dev_err(bus->parent, "SMI bus read not valid\n");
+               ret = -ENODEV;
+               goto out;
        }
 
+       ret = val & 0xFFFF;
+out:
        mutex_unlock(&dev->lock);
-
-       return val & 0xFFFF;
+       return ret;
 }
 
 static int orion_mdio_write(struct mii_bus *bus, int mii_id,
@@ -150,10 +148,8 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id,
        mutex_lock(&dev->lock);
 
        ret = orion_mdio_wait_ready(bus);
-       if (ret < 0) {
-               mutex_unlock(&dev->lock);
-               return ret;
-       }
+       if (ret < 0)
+               goto out;
 
        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
@@ -161,9 +157,9 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id,
                (value << MVMDIO_SMI_DATA_SHIFT)),
               dev->regs);
 
+out:
        mutex_unlock(&dev->lock);
-
-       return 0;
+       return ret;
 }
 
 static int orion_mdio_reset(struct mii_bus *bus)
index e35bac7..7d99e69 100644 (file)
@@ -2811,7 +2811,7 @@ static int mvneta_probe(struct platform_device *pdev)
        }
 
        dt_mac_addr = of_get_mac_address(dn);
-       if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) {
+       if (dt_mac_addr) {
                mac_from = "device tree";
                memcpy(dev->dev_addr, dt_mac_addr, ETH_ALEN);
        } else {
index 1a9c4f6..5978461 100644 (file)
@@ -3086,13 +3086,16 @@ static struct sk_buff *skge_rx_get(struct net_device *dev,
                                               PCI_DMA_FROMDEVICE);
                skge_rx_reuse(e, skge->rx_buf_size);
        } else {
+               struct skge_element ee;
                struct sk_buff *nskb;
 
                nskb = netdev_alloc_skb_ip_align(dev, skge->rx_buf_size);
                if (!nskb)
                        goto resubmit;
 
-               skb = e->skb;
+               ee = *e;
+
+               skb = ee.skb;
                prefetch(skb->data);
 
                if (skge_rx_setup(skge, e, nskb, skge->rx_buf_size) < 0) {
@@ -3101,8 +3104,8 @@ static struct sk_buff *skge_rx_get(struct net_device *dev,
                }
 
                pci_unmap_single(skge->hw->pdev,
-                                dma_unmap_addr(e, mapaddr),
-                                dma_unmap_len(e, maplen),
+                                dma_unmap_addr(&ee, mapaddr),
+                                dma_unmap_len(&ee, maplen),
                                 PCI_DMA_FROMDEVICE);
        }
 
@@ -4043,7 +4046,6 @@ err_out_free_regions:
        pci_release_regions(pdev);
 err_out_disable_pdev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 err_out:
        return err;
 }
@@ -4087,7 +4089,6 @@ static void skge_remove(struct pci_dev *pdev)
 
        iounmap(hw->regs);
        kfree(hw);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM_SLEEP
index e09a8c6..a7df981 100644 (file)
@@ -5081,7 +5081,6 @@ err_out_free_regions:
 err_out_disable:
        pci_disable_device(pdev);
 err_out:
-       pci_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -5124,8 +5123,6 @@ static void sky2_remove(struct pci_dev *pdev)
 
        iounmap(hw->regs);
        kfree(hw);
-
-       pci_set_drvdata(pdev, NULL);
 }
 
 static int sky2_suspend(struct device *dev)
index ea20182..1e9970d 100644 (file)
@@ -1539,11 +1539,6 @@ out:
        return ret;
 }
 
-static int calculate_transition(u16 oper_vlan, u16 admin_vlan)
-{
-       return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT));
-}
-
 static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
                                            int slave, int port)
 {
@@ -1553,7 +1548,6 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
        struct mlx4_dev *dev = &(priv->dev);
        int err;
        int admin_vlan_ix = NO_INDX;
-       enum mlx4_vlan_transition vlan_trans;
 
        vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
        vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
@@ -1563,12 +1557,8 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
            vp_oper->state.link_state == vp_admin->link_state)
                return 0;
 
-       vlan_trans = calculate_transition(vp_oper->state.default_vlan,
-                                         vp_admin->default_vlan);
-
        if (!(priv->mfunc.master.slave_state[slave].active &&
-             dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP &&
-             vlan_trans == MLX4_VLAN_TRANSITION_VST_VST)) {
+             dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP)) {
                /* even if the UPDATE_QP command isn't supported, we still want
                 * to set this VF link according to the admin directive
                 */
@@ -1586,15 +1576,19 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
                return -ENOMEM;
 
        if (vp_oper->state.default_vlan != vp_admin->default_vlan) {
-               err = __mlx4_register_vlan(&priv->dev, port,
-                                          vp_admin->default_vlan,
-                                          &admin_vlan_ix);
-               if (err) {
-                       kfree(work);
-                       mlx4_warn((&priv->dev),
-                                 "No vlan resources slave %d, port %d\n",
-                                 slave, port);
-                       return err;
+               if (MLX4_VGT != vp_admin->default_vlan) {
+                       err = __mlx4_register_vlan(&priv->dev, port,
+                                                  vp_admin->default_vlan,
+                                                  &admin_vlan_ix);
+                       if (err) {
+                               kfree(work);
+                               mlx4_warn((&priv->dev),
+                                         "No vlan resources slave %d, port %d\n",
+                                         slave, port);
+                               return err;
+                       }
+               } else {
+                       admin_vlan_ix = NO_INDX;
                }
                work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN;
                mlx4_dbg((&(priv->dev)),
@@ -1687,11 +1681,11 @@ static void mlx4_master_deactivate_admin_state(struct mlx4_priv *priv, int slave
                vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
                if (NO_INDX != vp_oper->vlan_idx) {
                        __mlx4_unregister_vlan(&priv->dev,
-                                              port, vp_oper->vlan_idx);
+                                              port, vp_oper->state.default_vlan);
                        vp_oper->vlan_idx = NO_INDX;
                }
                if (NO_INDX != vp_oper->mac_idx) {
-                       __mlx4_unregister_mac(&priv->dev, port, vp_oper->mac_idx);
+                       __mlx4_unregister_mac(&priv->dev, port, vp_oper->state.mac);
                        vp_oper->mac_idx = NO_INDX;
                }
        }
@@ -1718,6 +1712,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
        if (cmd == MLX4_COMM_CMD_RESET) {
                mlx4_warn(dev, "Received reset from slave:%d\n", slave);
                slave_state[slave].active = false;
+               slave_state[slave].old_vlan_api = false;
                mlx4_master_deactivate_admin_state(priv, slave);
                for (i = 0; i < MLX4_EVENT_TYPES_NUM; ++i) {
                                slave_state[slave].event_eq[i].eqn = -1;
@@ -2198,6 +2193,8 @@ struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev)
                return ERR_PTR(-ENOMEM);
        }
 
+       memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE);
+
        return mailbox;
 }
 EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox);
@@ -2253,7 +2250,6 @@ EXPORT_SYMBOL_GPL(mlx4_set_vf_mac);
 int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
-       struct mlx4_vport_oper_state *vf_oper;
        struct mlx4_vport_state *vf_admin;
        int slave;
 
@@ -2269,7 +2265,6 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
                return -EINVAL;
 
        vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
-       vf_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
 
        if ((0 == vlan) && (0 == qos))
                vf_admin->default_vlan = MLX4_VGT;
index 004e423..22fcbe7 100644 (file)
@@ -128,8 +128,6 @@ int mlx4_cq_modify(struct mlx4_dev *dev, struct mlx4_cq *cq,
                return PTR_ERR(mailbox);
 
        cq_context = mailbox->buf;
-       memset(cq_context, 0, sizeof *cq_context);
-
        cq_context->cq_max_count = cpu_to_be16(count);
        cq_context->cq_period    = cpu_to_be16(period);
 
@@ -153,8 +151,6 @@ int mlx4_cq_resize(struct mlx4_dev *dev, struct mlx4_cq *cq,
                return PTR_ERR(mailbox);
 
        cq_context = mailbox->buf;
-       memset(cq_context, 0, sizeof *cq_context);
-
        cq_context->logsize_usrpage = cpu_to_be32(ilog2(entries) << 24);
        cq_context->log_page_size   = mtt->page_shift - 12;
        mtt_addr = mlx4_mtt_addr(dev, mtt);
@@ -274,8 +270,6 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
        }
 
        cq_context = mailbox->buf;
-       memset(cq_context, 0, sizeof *cq_context);
-
        cq_context->flags           = cpu_to_be32(!!collapsed << 18);
        if (timestamp_en)
                cq_context->flags  |= cpu_to_be32(1 << 19);
index 3e2d504..3a098cc 100644 (file)
@@ -44,12 +44,23 @@ static void mlx4_en_cq_event(struct mlx4_cq *cq, enum mlx4_event event)
 
 
 int mlx4_en_create_cq(struct mlx4_en_priv *priv,
-                     struct mlx4_en_cq *cq,
-                     int entries, int ring, enum cq_type mode)
+                     struct mlx4_en_cq **pcq,
+                     int entries, int ring, enum cq_type mode,
+                     int node)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_cq *cq;
        int err;
 
+       cq = kzalloc_node(sizeof(*cq), GFP_KERNEL, node);
+       if (!cq) {
+               cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+               if (!cq) {
+                       en_err(priv, "Failed to allocate CQ structure\n");
+                       return -ENOMEM;
+               }
+       }
+
        cq->size = entries;
        cq->buf_size = cq->size * mdev->dev->caps.cqe_size;
 
@@ -57,17 +68,30 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
        cq->is_tx = mode;
        spin_lock_init(&cq->lock);
 
+       /* Allocate HW buffers on provided NUMA node.
+        * dev->numa_node is used in mtt range allocation flow.
+        */
+       set_dev_node(&mdev->dev->pdev->dev, node);
        err = mlx4_alloc_hwq_res(mdev->dev, &cq->wqres,
                                cq->buf_size, 2 * PAGE_SIZE);
+       set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node);
        if (err)
-               return err;
+               goto err_cq;
 
        err = mlx4_en_map_buffer(&cq->wqres.buf);
        if (err)
-               mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
-       else
-               cq->buf = (struct mlx4_cqe *) cq->wqres.buf.direct.buf;
+               goto err_res;
 
+       cq->buf = (struct mlx4_cqe *)cq->wqres.buf.direct.buf;
+       *pcq = cq;
+
+       return 0;
+
+err_res:
+       mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
+err_cq:
+       kfree(cq);
+       *pcq = NULL;
        return err;
 }
 
@@ -117,12 +141,12 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
                struct mlx4_en_cq *rx_cq;
 
                cq_idx = cq_idx % priv->rx_ring_num;
-               rx_cq = &priv->rx_cq[cq_idx];
+               rx_cq = priv->rx_cq[cq_idx];
                cq->vector = rx_cq->vector;
        }
 
        if (!cq->is_tx)
-               cq->size = priv->rx_ring[cq->ring].actual_size;
+               cq->size = priv->rx_ring[cq->ring]->actual_size;
 
        if ((cq->is_tx && priv->hwtstamp_config.tx_type) ||
            (!cq->is_tx && priv->hwtstamp_config.rx_filter))
@@ -146,9 +170,10 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
        return 0;
 }
 
-void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
+void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_cq *cq = *pcq;
 
        mlx4_en_unmap_buffer(&cq->wqres.buf);
        mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
@@ -157,6 +182,8 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
        cq->vector = 0;
        cq->buf_size = 0;
        cq->buf = NULL;
+       kfree(cq);
+       *pcq = NULL;
 }
 
 void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
index 0c75098..0596f9f 100644 (file)
@@ -51,10 +51,10 @@ static int mlx4_en_moderation_update(struct mlx4_en_priv *priv)
        int err = 0;
 
        for (i = 0; i < priv->tx_ring_num; i++) {
-               priv->tx_cq[i].moder_cnt = priv->tx_frames;
-               priv->tx_cq[i].moder_time = priv->tx_usecs;
+               priv->tx_cq[i]->moder_cnt = priv->tx_frames;
+               priv->tx_cq[i]->moder_time = priv->tx_usecs;
                if (priv->port_up) {
-                       err = mlx4_en_set_cq_moder(priv, &priv->tx_cq[i]);
+                       err = mlx4_en_set_cq_moder(priv, priv->tx_cq[i]);
                        if (err)
                                return err;
                }
@@ -64,11 +64,11 @@ static int mlx4_en_moderation_update(struct mlx4_en_priv *priv)
                return 0;
 
        for (i = 0; i < priv->rx_ring_num; i++) {
-               priv->rx_cq[i].moder_cnt = priv->rx_frames;
-               priv->rx_cq[i].moder_time = priv->rx_usecs;
+               priv->rx_cq[i]->moder_cnt = priv->rx_frames;
+               priv->rx_cq[i]->moder_time = priv->rx_usecs;
                priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
                if (priv->port_up) {
-                       err = mlx4_en_set_cq_moder(priv, &priv->rx_cq[i]);
+                       err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]);
                        if (err)
                                return err;
                }
@@ -274,16 +274,16 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev,
                }
        }
        for (i = 0; i < priv->tx_ring_num; i++) {
-               data[index++] = priv->tx_ring[i].packets;
-               data[index++] = priv->tx_ring[i].bytes;
+               data[index++] = priv->tx_ring[i]->packets;
+               data[index++] = priv->tx_ring[i]->bytes;
        }
        for (i = 0; i < priv->rx_ring_num; i++) {
-               data[index++] = priv->rx_ring[i].packets;
-               data[index++] = priv->rx_ring[i].bytes;
+               data[index++] = priv->rx_ring[i]->packets;
+               data[index++] = priv->rx_ring[i]->bytes;
 #ifdef CONFIG_NET_RX_BUSY_POLL
-               data[index++] = priv->rx_ring[i].yields;
-               data[index++] = priv->rx_ring[i].misses;
-               data[index++] = priv->rx_ring[i].cleaned;
+               data[index++] = priv->rx_ring[i]->yields;
+               data[index++] = priv->rx_ring[i]->misses;
+               data[index++] = priv->rx_ring[i]->cleaned;
 #endif
        }
        spin_unlock_bh(&priv->stats_lock);
@@ -510,9 +510,9 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
        tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE);
        tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE);
 
-       if (rx_size == (priv->port_up ? priv->rx_ring[0].actual_size :
-                                       priv->rx_ring[0].size) &&
-           tx_size == priv->tx_ring[0].size)
+       if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size :
+                                       priv->rx_ring[0]->size) &&
+           tx_size == priv->tx_ring[0]->size)
                return 0;
 
        mutex_lock(&mdev->state_lock);
@@ -553,8 +553,8 @@ static void mlx4_en_get_ringparam(struct net_device *dev,
        param->rx_max_pending = MLX4_EN_MAX_RX_SIZE;
        param->tx_max_pending = MLX4_EN_MAX_TX_SIZE;
        param->rx_pending = priv->port_up ?
-               priv->rx_ring[0].actual_size : priv->rx_ring[0].size;
-       param->tx_pending = priv->tx_ring[0].size;
+               priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size;
+       param->tx_pending = priv->tx_ring[0]->size;
 }
 
 static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev)
index a071cda..0d087b0 100644 (file)
@@ -264,6 +264,10 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
        mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH)
                mdev->port_cnt++;
 
+       /* Initialize time stamp mechanism */
+       if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
+               mlx4_en_init_timestamp(mdev);
+
        mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) {
                if (!dev->caps.comp_pool) {
                        mdev->profile.prof[i].rx_ring_num =
@@ -301,10 +305,6 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
                        mdev->pndev[i] = NULL;
        }
 
-       /* Initialize time stamp mechanism */
-       if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
-               mlx4_en_init_timestamp(mdev);
-
        return mdev;
 
 err_mr:
index fa37b7a..e72d8a1 100644 (file)
@@ -75,7 +75,7 @@ static int mlx4_en_low_latency_recv(struct napi_struct *napi)
        struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi);
        struct net_device *dev = cq->dev;
        struct mlx4_en_priv *priv = netdev_priv(dev);
-       struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring];
+       struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring];
        int done;
 
        if (!priv->port_up)
@@ -102,6 +102,7 @@ struct mlx4_en_filter {
        struct list_head next;
        struct work_struct work;
 
+       u8     ip_proto;
        __be32 src_ip;
        __be32 dst_ip;
        __be16 src_port;
@@ -120,14 +121,26 @@ struct mlx4_en_filter {
 
 static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv);
 
+static enum mlx4_net_trans_rule_id mlx4_ip_proto_to_trans_rule_id(u8 ip_proto)
+{
+       switch (ip_proto) {
+       case IPPROTO_UDP:
+               return MLX4_NET_TRANS_RULE_ID_UDP;
+       case IPPROTO_TCP:
+               return MLX4_NET_TRANS_RULE_ID_TCP;
+       default:
+               return -EPROTONOSUPPORT;
+       }
+};
+
 static void mlx4_en_filter_work(struct work_struct *work)
 {
        struct mlx4_en_filter *filter = container_of(work,
                                                     struct mlx4_en_filter,
                                                     work);
        struct mlx4_en_priv *priv = filter->priv;
-       struct mlx4_spec_list spec_tcp = {
-               .id = MLX4_NET_TRANS_RULE_ID_TCP,
+       struct mlx4_spec_list spec_tcp_udp = {
+               .id = mlx4_ip_proto_to_trans_rule_id(filter->ip_proto),
                {
                        .tcp_udp = {
                                .dst_port = filter->dst_port,
@@ -163,9 +176,14 @@ static void mlx4_en_filter_work(struct work_struct *work)
        int rc;
        __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
 
+       if (spec_tcp_udp.id < 0) {
+               en_warn(priv, "RFS: ignoring unsupported ip protocol (%d)\n",
+                       filter->ip_proto);
+               goto ignore;
+       }
        list_add_tail(&spec_eth.list, &rule.list);
        list_add_tail(&spec_ip.list, &rule.list);
-       list_add_tail(&spec_tcp.list, &rule.list);
+       list_add_tail(&spec_tcp_udp.list, &rule.list);
 
        rule.qpn = priv->rss_map.qps[filter->rxq_index].qpn;
        memcpy(spec_eth.eth.dst_mac, priv->dev->dev_addr, ETH_ALEN);
@@ -183,6 +201,7 @@ static void mlx4_en_filter_work(struct work_struct *work)
        if (rc)
                en_err(priv, "Error attaching flow. err = %d\n", rc);
 
+ignore:
        mlx4_en_filter_rfs_expire(priv);
 
        filter->activated = 1;
@@ -206,8 +225,8 @@ filter_hash_bucket(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip,
 
 static struct mlx4_en_filter *
 mlx4_en_filter_alloc(struct mlx4_en_priv *priv, int rxq_index, __be32 src_ip,
-                    __be32 dst_ip, __be16 src_port, __be16 dst_port,
-                    u32 flow_id)
+                    __be32 dst_ip, u8 ip_proto, __be16 src_port,
+                    __be16 dst_port, u32 flow_id)
 {
        struct mlx4_en_filter *filter = NULL;
 
@@ -221,6 +240,7 @@ mlx4_en_filter_alloc(struct mlx4_en_priv *priv, int rxq_index, __be32 src_ip,
 
        filter->src_ip = src_ip;
        filter->dst_ip = dst_ip;
+       filter->ip_proto = ip_proto;
        filter->src_port = src_port;
        filter->dst_port = dst_port;
 
@@ -252,7 +272,7 @@ static void mlx4_en_filter_free(struct mlx4_en_filter *filter)
 
 static inline struct mlx4_en_filter *
 mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip,
-                   __be16 src_port, __be16 dst_port)
+                   u8 ip_proto, __be16 src_port, __be16 dst_port)
 {
        struct mlx4_en_filter *filter;
        struct mlx4_en_filter *ret = NULL;
@@ -263,6 +283,7 @@ mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip,
                             filter_chain) {
                if (filter->src_ip == src_ip &&
                    filter->dst_ip == dst_ip &&
+                   filter->ip_proto == ip_proto &&
                    filter->src_port == src_port &&
                    filter->dst_port == dst_port) {
                        ret = filter;
@@ -281,6 +302,7 @@ mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
        struct mlx4_en_filter *filter;
        const struct iphdr *ip;
        const __be16 *ports;
+       u8 ip_proto;
        __be32 src_ip;
        __be32 dst_ip;
        __be16 src_port;
@@ -295,18 +317,19 @@ mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
        if (ip_is_fragment(ip))
                return -EPROTONOSUPPORT;
 
+       if ((ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP))
+               return -EPROTONOSUPPORT;
        ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
 
+       ip_proto = ip->protocol;
        src_ip = ip->saddr;
        dst_ip = ip->daddr;
        src_port = ports[0];
        dst_port = ports[1];
 
-       if (ip->protocol != IPPROTO_TCP)
-               return -EPROTONOSUPPORT;
-
        spin_lock_bh(&priv->filters_lock);
-       filter = mlx4_en_filter_find(priv, src_ip, dst_ip, src_port, dst_port);
+       filter = mlx4_en_filter_find(priv, src_ip, dst_ip, ip_proto,
+                                    src_port, dst_port);
        if (filter) {
                if (filter->rxq_index == rxq_index)
                        goto out;
@@ -314,7 +337,7 @@ mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
                filter->rxq_index = rxq_index;
        } else {
                filter = mlx4_en_filter_alloc(priv, rxq_index,
-                                             src_ip, dst_ip,
+                                             src_ip, dst_ip, ip_proto,
                                              src_port, dst_port, flow_id);
                if (!filter) {
                        ret = -ENOMEM;
@@ -332,8 +355,7 @@ err:
        return ret;
 }
 
-void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv,
-                            struct mlx4_en_rx_ring *rx_ring)
+void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv)
 {
        struct mlx4_en_filter *filter, *tmp;
        LIST_HEAD(del_list);
@@ -417,7 +439,6 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
        int err;
-       int idx;
 
        en_dbg(HW, priv, "Killing VID:%d\n", vid);
 
@@ -425,10 +446,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
 
        /* Remove VID from port VLAN filter */
        mutex_lock(&mdev->state_lock);
-       if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx))
-               mlx4_unregister_vlan(mdev->dev, priv->port, idx);
-       else
-               en_dbg(HW, priv, "could not find vid %d in cache\n", vid);
+       mlx4_unregister_vlan(mdev->dev, priv->port, vid);
 
        if (mdev->device_up && priv->port_up) {
                err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
@@ -1223,7 +1241,7 @@ static void mlx4_en_netpoll(struct net_device *dev)
        int i;
 
        for (i = 0; i < priv->rx_ring_num; i++) {
-               cq = &priv->rx_cq[i];
+               cq = priv->rx_cq[i];
                spin_lock_irqsave(&cq->lock, flags);
                napi_synchronize(&cq->napi);
                mlx4_en_process_rx_cq(dev, cq, 0);
@@ -1245,8 +1263,8 @@ static void mlx4_en_tx_timeout(struct net_device *dev)
                if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i)))
                        continue;
                en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n",
-                       i, priv->tx_ring[i].qpn, priv->tx_ring[i].cqn,
-                       priv->tx_ring[i].cons, priv->tx_ring[i].prod);
+                       i, priv->tx_ring[i]->qpn, priv->tx_ring[i]->cqn,
+                       priv->tx_ring[i]->cons, priv->tx_ring[i]->prod);
        }
 
        priv->port_stats.tx_timeout++;
@@ -1286,7 +1304,7 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
 
        /* Setup cq moderation params */
        for (i = 0; i < priv->rx_ring_num; i++) {
-               cq = &priv->rx_cq[i];
+               cq = priv->rx_cq[i];
                cq->moder_cnt = priv->rx_frames;
                cq->moder_time = priv->rx_usecs;
                priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
@@ -1295,7 +1313,7 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
        }
 
        for (i = 0; i < priv->tx_ring_num; i++) {
-               cq = &priv->tx_cq[i];
+               cq = priv->tx_cq[i];
                cq->moder_cnt = priv->tx_frames;
                cq->moder_time = priv->tx_usecs;
        }
@@ -1329,8 +1347,8 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv)
 
        for (ring = 0; ring < priv->rx_ring_num; ring++) {
                spin_lock_bh(&priv->stats_lock);
-               rx_packets = priv->rx_ring[ring].packets;
-               rx_bytes = priv->rx_ring[ring].bytes;
+               rx_packets = priv->rx_ring[ring]->packets;
+               rx_bytes = priv->rx_ring[ring]->bytes;
                spin_unlock_bh(&priv->stats_lock);
 
                rx_pkt_diff = ((unsigned long) (rx_packets -
@@ -1359,7 +1377,7 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv)
 
                if (moder_time != priv->last_moder_time[ring]) {
                        priv->last_moder_time[ring] = moder_time;
-                       cq = &priv->rx_cq[ring];
+                       cq = priv->rx_cq[ring];
                        cq->moder_time = moder_time;
                        cq->moder_cnt = priv->rx_frames;
                        err = mlx4_en_set_cq_moder(priv, cq);
@@ -1482,7 +1500,7 @@ int mlx4_en_start_port(struct net_device *dev)
                return err;
        }
        for (i = 0; i < priv->rx_ring_num; i++) {
-               cq = &priv->rx_cq[i];
+               cq = priv->rx_cq[i];
 
                mlx4_en_cq_init_lock(cq);
 
@@ -1500,7 +1518,7 @@ int mlx4_en_start_port(struct net_device *dev)
                        goto cq_err;
                }
                mlx4_en_arm_cq(priv, cq);
-               priv->rx_ring[i].cqn = cq->mcq.cqn;
+               priv->rx_ring[i]->cqn = cq->mcq.cqn;
                ++rx_index;
        }
 
@@ -1526,7 +1544,7 @@ int mlx4_en_start_port(struct net_device *dev)
        /* Configure tx cq's and rings */
        for (i = 0; i < priv->tx_ring_num; i++) {
                /* Configure cq */
-               cq = &priv->tx_cq[i];
+               cq = priv->tx_cq[i];
                err = mlx4_en_activate_cq(priv, cq, i);
                if (err) {
                        en_err(priv, "Failed allocating Tx CQ\n");
@@ -1542,7 +1560,7 @@ int mlx4_en_start_port(struct net_device *dev)
                cq->buf->wqe_index = cpu_to_be16(0xffff);
 
                /* Configure ring */
-               tx_ring = &priv->tx_ring[i];
+               tx_ring = priv->tx_ring[i];
                err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn,
                        i / priv->num_tx_rings_p_up);
                if (err) {
@@ -1612,8 +1630,8 @@ int mlx4_en_start_port(struct net_device *dev)
 
 tx_err:
        while (tx_index--) {
-               mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[tx_index]);
-               mlx4_en_deactivate_cq(priv, &priv->tx_cq[tx_index]);
+               mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[tx_index]);
+               mlx4_en_deactivate_cq(priv, priv->tx_cq[tx_index]);
        }
        mlx4_en_destroy_drop_qp(priv);
 rss_err:
@@ -1622,9 +1640,9 @@ mac_err:
        mlx4_en_put_qp(priv);
 cq_err:
        while (rx_index--)
-               mlx4_en_deactivate_cq(priv, &priv->rx_cq[rx_index]);
+               mlx4_en_deactivate_cq(priv, priv->rx_cq[rx_index]);
        for (i = 0; i < priv->rx_ring_num; i++)
-               mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]);
+               mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
 
        return err; /* need to close devices */
 }
@@ -1720,25 +1738,25 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
 
        /* Free TX Rings */
        for (i = 0; i < priv->tx_ring_num; i++) {
-               mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[i]);
-               mlx4_en_deactivate_cq(priv, &priv->tx_cq[i]);
+               mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[i]);
+               mlx4_en_deactivate_cq(priv, priv->tx_cq[i]);
        }
        msleep(10);
 
        for (i = 0; i < priv->tx_ring_num; i++)
-               mlx4_en_free_tx_buf(dev, &priv->tx_ring[i]);
+               mlx4_en_free_tx_buf(dev, priv->tx_ring[i]);
 
        /* Free RSS qps */
        mlx4_en_release_rss_steer(priv);
 
        /* Unregister Mac address for the port */
        mlx4_en_put_qp(priv);
-       if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN))
+       if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN))
                mdev->mac_removed[priv->port] = 1;
 
        /* Free RX Rings */
        for (i = 0; i < priv->rx_ring_num; i++) {
-               struct mlx4_en_cq *cq = &priv->rx_cq[i];
+               struct mlx4_en_cq *cq = priv->rx_cq[i];
 
                local_bh_disable();
                while (!mlx4_en_cq_lock_napi(cq)) {
@@ -1749,7 +1767,7 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
 
                while (test_bit(NAPI_STATE_SCHED, &cq->napi.state))
                        msleep(1);
-               mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]);
+               mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
                mlx4_en_deactivate_cq(priv, cq);
        }
 }
@@ -1787,15 +1805,15 @@ static void mlx4_en_clear_stats(struct net_device *dev)
        memset(&priv->port_stats, 0, sizeof(priv->port_stats));
 
        for (i = 0; i < priv->tx_ring_num; i++) {
-               priv->tx_ring[i].bytes = 0;
-               priv->tx_ring[i].packets = 0;
-               priv->tx_ring[i].tx_csum = 0;
+               priv->tx_ring[i]->bytes = 0;
+               priv->tx_ring[i]->packets = 0;
+               priv->tx_ring[i]->tx_csum = 0;
        }
        for (i = 0; i < priv->rx_ring_num; i++) {
-               priv->rx_ring[i].bytes = 0;
-               priv->rx_ring[i].packets = 0;
-               priv->rx_ring[i].csum_ok = 0;
-               priv->rx_ring[i].csum_none = 0;
+               priv->rx_ring[i]->bytes = 0;
+               priv->rx_ring[i]->packets = 0;
+               priv->rx_ring[i]->csum_ok = 0;
+               priv->rx_ring[i]->csum_none = 0;
        }
 }
 
@@ -1852,17 +1870,17 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv)
 #endif
 
        for (i = 0; i < priv->tx_ring_num; i++) {
-               if (priv->tx_ring[i].tx_info)
+               if (priv->tx_ring && priv->tx_ring[i])
                        mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]);
-               if (priv->tx_cq[i].buf)
+               if (priv->tx_cq && priv->tx_cq[i])
                        mlx4_en_destroy_cq(priv, &priv->tx_cq[i]);
        }
 
        for (i = 0; i < priv->rx_ring_num; i++) {
-               if (priv->rx_ring[i].rx_info)
+               if (priv->rx_ring[i])
                        mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i],
                                priv->prof->rx_ring_size, priv->stride);
-               if (priv->rx_cq[i].buf)
+               if (priv->rx_cq[i])
                        mlx4_en_destroy_cq(priv, &priv->rx_cq[i]);
        }
 
@@ -1877,6 +1895,7 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
        struct mlx4_en_port_profile *prof = priv->prof;
        int i;
        int err;
+       int node;
 
        err = mlx4_qp_reserve_range(priv->mdev->dev, priv->tx_ring_num, 256, &priv->base_tx_qpn);
        if (err) {
@@ -1886,23 +1905,26 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
 
        /* Create tx Rings */
        for (i = 0; i < priv->tx_ring_num; i++) {
+               node = cpu_to_node(i % num_online_cpus());
                if (mlx4_en_create_cq(priv, &priv->tx_cq[i],
-                                     prof->tx_ring_size, i, TX))
+                                     prof->tx_ring_size, i, TX, node))
                        goto err;
 
                if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[i], priv->base_tx_qpn + i,
-                                          prof->tx_ring_size, TXBB_SIZE))
+                                          prof->tx_ring_size, TXBB_SIZE, node))
                        goto err;
        }
 
        /* Create rx Rings */
        for (i = 0; i < priv->rx_ring_num; i++) {
+               node = cpu_to_node(i % num_online_cpus());
                if (mlx4_en_create_cq(priv, &priv->rx_cq[i],
-                                     prof->rx_ring_size, i, RX))
+                                     prof->rx_ring_size, i, RX, node))
                        goto err;
 
                if (mlx4_en_create_rx_ring(priv, &priv->rx_ring[i],
-                                          prof->rx_ring_size, priv->stride))
+                                          prof->rx_ring_size, priv->stride,
+                                          node))
                        goto err;
        }
 
@@ -1918,6 +1940,20 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
 
 err:
        en_err(priv, "Failed to allocate NIC resources\n");
+       for (i = 0; i < priv->rx_ring_num; i++) {
+               if (priv->rx_ring[i])
+                       mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i],
+                                               prof->rx_ring_size,
+                                               priv->stride);
+               if (priv->rx_cq[i])
+                       mlx4_en_destroy_cq(priv, &priv->rx_cq[i]);
+       }
+       for (i = 0; i < priv->tx_ring_num; i++) {
+               if (priv->tx_ring[i])
+                       mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]);
+               if (priv->tx_cq[i])
+                       mlx4_en_destroy_cq(priv, &priv->tx_cq[i]);
+       }
        return -ENOMEM;
 }
 
@@ -2211,13 +2247,13 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        priv->num_tx_rings_p_up = mdev->profile.num_tx_rings_p_up;
        priv->tx_ring_num = prof->tx_ring_num;
 
-       priv->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring) * MAX_TX_RINGS,
+       priv->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS,
                                GFP_KERNEL);
        if (!priv->tx_ring) {
                err = -ENOMEM;
                goto out;
        }
-       priv->tx_cq = kzalloc(sizeof(struct mlx4_en_cq) * MAX_TX_RINGS,
+       priv->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS,
                              GFP_KERNEL);
        if (!priv->tx_cq) {
                err = -ENOMEM;
index 3317914..dae1a1f 100644 (file)
@@ -56,7 +56,6 @@ int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv)
                return PTR_ERR(mailbox);
 
        filter = mailbox->buf;
-       memset(filter, 0, sizeof(*filter));
        for (i = VLAN_FLTR_SIZE - 1; i >= 0; i--) {
                entry = 0;
                for (j = 0; j < 32; j++)
@@ -81,7 +80,6 @@ int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port)
        mailbox = mlx4_alloc_cmd_mailbox(mdev->dev);
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
-       memset(mailbox->buf, 0, sizeof(*qport_context));
        err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, port, 0,
                           MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B,
                           MLX4_CMD_WRAPPED);
@@ -127,7 +125,6 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset)
        mailbox = mlx4_alloc_cmd_mailbox(mdev->dev);
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
-       memset(mailbox->buf, 0, sizeof(*mlx4_en_stats));
        err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, in_mod, 0,
                           MLX4_CMD_DUMP_ETH_STATS, MLX4_CMD_TIME_CLASS_B,
                           MLX4_CMD_WRAPPED);
@@ -143,18 +140,18 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset)
        priv->port_stats.rx_chksum_good = 0;
        priv->port_stats.rx_chksum_none = 0;
        for (i = 0; i < priv->rx_ring_num; i++) {
-               stats->rx_packets += priv->rx_ring[i].packets;
-               stats->rx_bytes += priv->rx_ring[i].bytes;
-               priv->port_stats.rx_chksum_good += priv->rx_ring[i].csum_ok;
-               priv->port_stats.rx_chksum_none += priv->rx_ring[i].csum_none;
+               stats->rx_packets += priv->rx_ring[i]->packets;
+               stats->rx_bytes += priv->rx_ring[i]->bytes;
+               priv->port_stats.rx_chksum_good += priv->rx_ring[i]->csum_ok;
+               priv->port_stats.rx_chksum_none += priv->rx_ring[i]->csum_none;
        }
        stats->tx_packets = 0;
        stats->tx_bytes = 0;
        priv->port_stats.tx_chksum_offload = 0;
        for (i = 0; i < priv->tx_ring_num; i++) {
-               stats->tx_packets += priv->tx_ring[i].packets;
-               stats->tx_bytes += priv->tx_ring[i].bytes;
-               priv->port_stats.tx_chksum_offload += priv->tx_ring[i].tx_csum;
+               stats->tx_packets += priv->tx_ring[i]->packets;
+               stats->tx_bytes += priv->tx_ring[i]->bytes;
+               priv->port_stats.tx_chksum_offload += priv->tx_ring[i]->tx_csum;
        }
 
        stats->rx_errors = be64_to_cpu(mlx4_en_stats->PCS) +
index dec455c..07a1d0f 100644 (file)
@@ -70,14 +70,15 @@ static int mlx4_alloc_pages(struct mlx4_en_priv *priv,
                put_page(page);
                return -ENOMEM;
        }
-       page_alloc->size = PAGE_SIZE << order;
+       page_alloc->page_size = PAGE_SIZE << order;
        page_alloc->page = page;
        page_alloc->dma = dma;
-       page_alloc->offset = frag_info->frag_align;
+       page_alloc->page_offset = frag_info->frag_align;
        /* Not doing get_page() for each frag is a big win
         * on asymetric workloads.
         */
-       atomic_set(&page->_count, page_alloc->size / frag_info->frag_stride);
+       atomic_set(&page->_count,
+                  page_alloc->page_size / frag_info->frag_stride);
        return 0;
 }
 
@@ -96,16 +97,19 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv,
        for (i = 0; i < priv->num_frags; i++) {
                frag_info = &priv->frag_info[i];
                page_alloc[i] = ring_alloc[i];
-               page_alloc[i].offset += frag_info->frag_stride;
-               if (page_alloc[i].offset + frag_info->frag_stride <= ring_alloc[i].size)
+               page_alloc[i].page_offset += frag_info->frag_stride;
+
+               if (page_alloc[i].page_offset + frag_info->frag_stride <=
+                   ring_alloc[i].page_size)
                        continue;
+
                if (mlx4_alloc_pages(priv, &page_alloc[i], frag_info, gfp))
                        goto out;
        }
 
        for (i = 0; i < priv->num_frags; i++) {
                frags[i] = ring_alloc[i];
-               dma = ring_alloc[i].dma + ring_alloc[i].offset;
+               dma = ring_alloc[i].dma + ring_alloc[i].page_offset;
                ring_alloc[i] = page_alloc[i];
                rx_desc->data[i].addr = cpu_to_be64(dma);
        }
@@ -117,7 +121,7 @@ out:
                frag_info = &priv->frag_info[i];
                if (page_alloc[i].page != ring_alloc[i].page) {
                        dma_unmap_page(priv->ddev, page_alloc[i].dma,
-                               page_alloc[i].size, PCI_DMA_FROMDEVICE);
+                               page_alloc[i].page_size, PCI_DMA_FROMDEVICE);
                        page = page_alloc[i].page;
                        atomic_set(&page->_count, 1);
                        put_page(page);
@@ -131,10 +135,12 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
                              int i)
 {
        const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
+       u32 next_frag_end = frags[i].page_offset + 2 * frag_info->frag_stride;
 
-       if (frags[i].offset + frag_info->frag_stride > frags[i].size)
-               dma_unmap_page(priv->ddev, frags[i].dma, frags[i].size,
-                                        PCI_DMA_FROMDEVICE);
+
+       if (next_frag_end > frags[i].page_size)
+               dma_unmap_page(priv->ddev, frags[i].dma, frags[i].page_size,
+                              PCI_DMA_FROMDEVICE);
 
        if (frags[i].page)
                put_page(frags[i].page);
@@ -161,7 +167,7 @@ out:
 
                page_alloc = &ring->page_alloc[i];
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                              page_alloc->size, PCI_DMA_FROMDEVICE);
+                              page_alloc->page_size, PCI_DMA_FROMDEVICE);
                page = page_alloc->page;
                atomic_set(&page->_count, 1);
                put_page(page);
@@ -184,10 +190,11 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv,
                       i, page_count(page_alloc->page));
 
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                               page_alloc->size, PCI_DMA_FROMDEVICE);
-               while (page_alloc->offset + frag_info->frag_stride < page_alloc->size) {
+                               page_alloc->page_size, PCI_DMA_FROMDEVICE);
+               while (page_alloc->page_offset + frag_info->frag_stride <
+                      page_alloc->page_size) {
                        put_page(page_alloc->page);
-                       page_alloc->offset += frag_info->frag_stride;
+                       page_alloc->page_offset += frag_info->frag_stride;
                }
                page_alloc->page = NULL;
        }
@@ -257,7 +264,7 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv)
 
        for (buf_ind = 0; buf_ind < priv->prof->rx_ring_size; buf_ind++) {
                for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) {
-                       ring = &priv->rx_ring[ring_ind];
+                       ring = priv->rx_ring[ring_ind];
 
                        if (mlx4_en_prepare_rx_desc(priv, ring,
                                                    ring->actual_size,
@@ -282,7 +289,7 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv)
 
 reduce_rings:
        for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) {
-               ring = &priv->rx_ring[ring_ind];
+               ring = priv->rx_ring[ring_ind];
                while (ring->actual_size > new_size) {
                        ring->actual_size--;
                        ring->prod--;
@@ -312,12 +319,23 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv,
 }
 
 int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
-                          struct mlx4_en_rx_ring *ring, u32 size, u16 stride)
+                          struct mlx4_en_rx_ring **pring,
+                          u32 size, u16 stride, int node)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_rx_ring *ring;
        int err = -ENOMEM;
        int tmp;
 
+       ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node);
+       if (!ring) {
+               ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+               if (!ring) {
+                       en_err(priv, "Failed to allocate RX ring structure\n");
+                       return -ENOMEM;
+               }
+       }
+
        ring->prod = 0;
        ring->cons = 0;
        ring->size = size;
@@ -328,17 +346,25 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
 
        tmp = size * roundup_pow_of_two(MLX4_EN_MAX_RX_FRAGS *
                                        sizeof(struct mlx4_en_rx_alloc));
-       ring->rx_info = vmalloc(tmp);
-       if (!ring->rx_info)
-               return -ENOMEM;
+       ring->rx_info = vmalloc_node(tmp, node);
+       if (!ring->rx_info) {
+               ring->rx_info = vmalloc(tmp);
+               if (!ring->rx_info) {
+                       err = -ENOMEM;
+                       goto err_ring;
+               }
+       }
 
        en_dbg(DRV, priv, "Allocated rx_info ring at addr:%p size:%d\n",
                 ring->rx_info, tmp);
 
+       /* Allocate HW buffers on provided NUMA node */
+       set_dev_node(&mdev->dev->pdev->dev, node);
        err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres,
                                 ring->buf_size, 2 * PAGE_SIZE);
+       set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node);
        if (err)
-               goto err_ring;
+               goto err_info;
 
        err = mlx4_en_map_buffer(&ring->wqres.buf);
        if (err) {
@@ -349,13 +375,18 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
 
        ring->hwtstamp_rx_filter = priv->hwtstamp_config.rx_filter;
 
+       *pring = ring;
        return 0;
 
 err_hwq:
        mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size);
-err_ring:
+err_info:
        vfree(ring->rx_info);
        ring->rx_info = NULL;
+err_ring:
+       kfree(ring);
+       *pring = NULL;
+
        return err;
 }
 
@@ -369,12 +400,12 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv)
                                        DS_SIZE * priv->num_frags);
 
        for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) {
-               ring = &priv->rx_ring[ring_ind];
+               ring = priv->rx_ring[ring_ind];
 
                ring->prod = 0;
                ring->cons = 0;
                ring->actual_size = 0;
-               ring->cqn = priv->rx_cq[ring_ind].mcq.cqn;
+               ring->cqn = priv->rx_cq[ring_ind]->mcq.cqn;
 
                ring->stride = stride;
                if (ring->stride <= TXBB_SIZE)
@@ -405,7 +436,7 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv)
                goto err_buffers;
 
        for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) {
-               ring = &priv->rx_ring[ring_ind];
+               ring = priv->rx_ring[ring_ind];
 
                ring->size_mask = ring->actual_size - 1;
                mlx4_en_update_rx_prod_db(ring);
@@ -415,30 +446,34 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv)
 
 err_buffers:
        for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++)
-               mlx4_en_free_rx_buf(priv, &priv->rx_ring[ring_ind]);
+               mlx4_en_free_rx_buf(priv, priv->rx_ring[ring_ind]);
 
        ring_ind = priv->rx_ring_num - 1;
 err_allocator:
        while (ring_ind >= 0) {
-               if (priv->rx_ring[ring_ind].stride <= TXBB_SIZE)
-                       priv->rx_ring[ring_ind].buf -= TXBB_SIZE;
-               mlx4_en_destroy_allocator(priv, &priv->rx_ring[ring_ind]);
+               if (priv->rx_ring[ring_ind]->stride <= TXBB_SIZE)
+                       priv->rx_ring[ring_ind]->buf -= TXBB_SIZE;
+               mlx4_en_destroy_allocator(priv, priv->rx_ring[ring_ind]);
                ring_ind--;
        }
        return err;
 }
 
 void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
-                            struct mlx4_en_rx_ring *ring, u32 size, u16 stride)
+                            struct mlx4_en_rx_ring **pring,
+                            u32 size, u16 stride)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_rx_ring *ring = *pring;
 
        mlx4_en_unmap_buffer(&ring->wqres.buf);
        mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE);
        vfree(ring->rx_info);
        ring->rx_info = NULL;
+       kfree(ring);
+       *pring = NULL;
 #ifdef CONFIG_RFS_ACCEL
-       mlx4_en_cleanup_filters(priv, ring);
+       mlx4_en_cleanup_filters(priv);
 #endif
 }
 
@@ -478,7 +513,7 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
                /* Save page reference in skb */
                __skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page);
                skb_frag_size_set(&skb_frags_rx[nr], frag_info->frag_size);
-               skb_frags_rx[nr].page_offset = frags[nr].offset;
+               skb_frags_rx[nr].page_offset = frags[nr].page_offset;
                skb->truesize += frag_info->frag_stride;
                frags[nr].page = NULL;
        }
@@ -517,7 +552,7 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv,
 
        /* Get pointer to first fragment so we could copy the headers into the
         * (linear part of the) skb */
-       va = page_address(frags[0].page) + frags[0].offset;
+       va = page_address(frags[0].page) + frags[0].page_offset;
 
        if (length <= SMALL_PACKET_SIZE) {
                /* We are copying all relevant data to the skb - temporarily
@@ -585,7 +620,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
        struct mlx4_cqe *cqe;
-       struct mlx4_en_rx_ring *ring = &priv->rx_ring[cq->ring];
+       struct mlx4_en_rx_ring *ring = priv->rx_ring[cq->ring];
        struct mlx4_en_rx_alloc *frags;
        struct mlx4_en_rx_desc *rx_desc;
        struct sk_buff *skb;
@@ -645,7 +680,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                        dma_sync_single_for_cpu(priv->ddev, dma, sizeof(*ethh),
                                                DMA_FROM_DEVICE);
                        ethh = (struct ethhdr *)(page_address(frags[0].page) +
-                                                frags[0].offset);
+                                                frags[0].page_offset);
 
                        if (is_multicast_ether_addr(ethh->h_dest)) {
                                struct mlx4_mac_entry *entry;
@@ -984,7 +1019,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv)
 
        for (i = 0; i < priv->rx_ring_num; i++) {
                qpn = rss_map->base_qpn + i;
-               err = mlx4_en_config_rss_qp(priv, qpn, &priv->rx_ring[i],
+               err = mlx4_en_config_rss_qp(priv, qpn, priv->rx_ring[i],
                                            &rss_map->state[i],
                                            &rss_map->qps[i]);
                if (err)
@@ -1001,7 +1036,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv)
        }
        rss_map->indir_qp.event = mlx4_en_sqp_event;
        mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn,
-                               priv->rx_ring[0].cqn, -1, &context);
+                               priv->rx_ring[0]->cqn, -1, &context);
 
        if (!priv->prof->rss_rings || priv->prof->rss_rings > priv->rx_ring_num)
                rss_rings = priv->rx_ring_num;
index 2448f0d..4062669 100644 (file)
@@ -156,7 +156,7 @@ retry_tx:
                 * since we turned the carrier off */
                msleep(200);
                for (i = 0; i < priv->tx_ring_num && carrier_ok; i++) {
-                       tx_ring = &priv->tx_ring[i];
+                       tx_ring = priv->tx_ring[i];
                        if (tx_ring->prod != (tx_ring->cons + tx_ring->last_nr_txbb))
                                goto retry_tx;
                }
index 0698c82..f54ebd5 100644 (file)
@@ -54,13 +54,23 @@ module_param_named(inline_thold, inline_thold, int, 0444);
 MODULE_PARM_DESC(inline_thold, "threshold for using inline data");
 
 int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
-                          struct mlx4_en_tx_ring *ring, int qpn, u32 size,
-                          u16 stride)
+                          struct mlx4_en_tx_ring **pring, int qpn, u32 size,
+                          u16 stride, int node)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_tx_ring *ring;
        int tmp;
        int err;
 
+       ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node);
+       if (!ring) {
+               ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+               if (!ring) {
+                       en_err(priv, "Failed allocating TX ring\n");
+                       return -ENOMEM;
+               }
+       }
+
        ring->size = size;
        ring->size_mask = size - 1;
        ring->stride = stride;
@@ -68,22 +78,33 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
        inline_thold = min(inline_thold, MAX_INLINE);
 
        tmp = size * sizeof(struct mlx4_en_tx_info);
-       ring->tx_info = vmalloc(tmp);
-       if (!ring->tx_info)
-               return -ENOMEM;
+       ring->tx_info = vmalloc_node(tmp, node);
+       if (!ring->tx_info) {
+               ring->tx_info = vmalloc(tmp);
+               if (!ring->tx_info) {
+                       err = -ENOMEM;
+                       goto err_ring;
+               }
+       }
 
        en_dbg(DRV, priv, "Allocated tx_info ring at addr:%p size:%d\n",
                 ring->tx_info, tmp);
 
-       ring->bounce_buf = kmalloc(MAX_DESC_SIZE, GFP_KERNEL);
+       ring->bounce_buf = kmalloc_node(MAX_DESC_SIZE, GFP_KERNEL, node);
        if (!ring->bounce_buf) {
-               err = -ENOMEM;
-               goto err_tx;
+               ring->bounce_buf = kmalloc(MAX_DESC_SIZE, GFP_KERNEL);
+               if (!ring->bounce_buf) {
+                       err = -ENOMEM;
+                       goto err_info;
+               }
        }
        ring->buf_size = ALIGN(size * ring->stride, MLX4_EN_PAGE_SIZE);
 
+       /* Allocate HW buffers on provided NUMA node */
+       set_dev_node(&mdev->dev->pdev->dev, node);
        err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size,
                                 2 * PAGE_SIZE);
+       set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node);
        if (err) {
                en_err(priv, "Failed allocating hwq resources\n");
                goto err_bounce;
@@ -109,7 +130,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
        }
        ring->qp.event = mlx4_en_sqp_event;
 
-       err = mlx4_bf_alloc(mdev->dev, &ring->bf);
+       err = mlx4_bf_alloc(mdev->dev, &ring->bf, node);
        if (err) {
                en_dbg(DRV, priv, "working without blueflame (%d)", err);
                ring->bf.uar = &mdev->priv_uar;
@@ -120,6 +141,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
 
        ring->hwtstamp_tx_type = priv->hwtstamp_config.tx_type;
 
+       *pring = ring;
        return 0;
 
 err_map:
@@ -129,16 +151,20 @@ err_hwq_res:
 err_bounce:
        kfree(ring->bounce_buf);
        ring->bounce_buf = NULL;
-err_tx:
+err_info:
        vfree(ring->tx_info);
        ring->tx_info = NULL;
+err_ring:
+       kfree(ring);
+       *pring = NULL;
        return err;
 }
 
 void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv,
-                            struct mlx4_en_tx_ring *ring)
+                            struct mlx4_en_tx_ring **pring)
 {
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_tx_ring *ring = *pring;
        en_dbg(DRV, priv, "Destroying tx ring, qpn: %d\n", ring->qpn);
 
        if (ring->bf_enabled)
@@ -151,6 +177,8 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv,
        ring->bounce_buf = NULL;
        vfree(ring->tx_info);
        ring->tx_info = NULL;
+       kfree(ring);
+       *pring = NULL;
 }
 
 int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
@@ -330,7 +358,7 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_cq *mcq = &cq->mcq;
-       struct mlx4_en_tx_ring *ring = &priv->tx_ring[cq->ring];
+       struct mlx4_en_tx_ring *ring = priv->tx_ring[cq->ring];
        struct mlx4_cqe *cqe;
        u16 index;
        u16 new_index, ring_index, stamp_index;
@@ -622,7 +650,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        tx_ind = skb->queue_mapping;
-       ring = &priv->tx_ring[tx_ind];
+       ring = priv->tx_ring[tx_ind];
        if (vlan_tx_tag_present(skb))
                vlan_tag = vlan_tx_tag_get(skb);
 
index 0416c5b..c9cdb2a 100644 (file)
@@ -936,7 +936,6 @@ static int mlx4_create_eq(struct mlx4_dev *dev, int nent,
        if (err)
                goto err_out_free_mtt;
 
-       memset(eq_context, 0, sizeof *eq_context);
        eq_context->flags         = cpu_to_be32(MLX4_EQ_STATUS_OK   |
                                                MLX4_EQ_STATE_ARMED);
        eq_context->log_eq_size   = ilog2(eq->nent);
index 0d63daa..fda2667 100644 (file)
@@ -159,8 +159,6 @@ int mlx4_MOD_STAT_CFG(struct mlx4_dev *dev, struct mlx4_mod_stat_cfg *cfg)
                return PTR_ERR(mailbox);
        inbox = mailbox->buf;
 
-       memset(inbox, 0, MOD_STAT_CFG_IN_SIZE);
-
        MLX4_PUT(inbox, cfg->log_pg_sz, MOD_STAT_CFG_PG_SZ_OFFSET);
        MLX4_PUT(inbox, cfg->log_pg_sz_m, MOD_STAT_CFG_PG_SZ_M_OFFSET);
 
@@ -177,6 +175,7 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
                                struct mlx4_cmd_mailbox *outbox,
                                struct mlx4_cmd_info *cmd)
 {
+       struct mlx4_priv *priv = mlx4_priv(dev);
        u8      field;
        u32     size;
        int     err = 0;
@@ -185,18 +184,26 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
 #define QUERY_FUNC_CAP_NUM_PORTS_OFFSET                0x1
 #define QUERY_FUNC_CAP_PF_BHVR_OFFSET          0x4
 #define QUERY_FUNC_CAP_FMR_OFFSET              0x8
-#define QUERY_FUNC_CAP_QP_QUOTA_OFFSET         0x10
-#define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET         0x14
-#define QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET                0x18
-#define QUERY_FUNC_CAP_MPT_QUOTA_OFFSET                0x20
-#define QUERY_FUNC_CAP_MTT_QUOTA_OFFSET                0x24
-#define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET                0x28
+#define QUERY_FUNC_CAP_QP_QUOTA_OFFSET_DEP     0x10
+#define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET_DEP     0x14
+#define QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET_DEP    0x18
+#define QUERY_FUNC_CAP_MPT_QUOTA_OFFSET_DEP    0x20
+#define QUERY_FUNC_CAP_MTT_QUOTA_OFFSET_DEP    0x24
+#define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP    0x28
 #define QUERY_FUNC_CAP_MAX_EQ_OFFSET           0x2c
 #define QUERY_FUNC_CAP_RESERVED_EQ_OFFSET      0x30
 
+#define QUERY_FUNC_CAP_QP_QUOTA_OFFSET         0x50
+#define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET         0x54
+#define QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET                0x58
+#define QUERY_FUNC_CAP_MPT_QUOTA_OFFSET                0x60
+#define QUERY_FUNC_CAP_MTT_QUOTA_OFFSET                0x64
+#define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET                0x68
+
 #define QUERY_FUNC_CAP_FMR_FLAG                        0x80
 #define QUERY_FUNC_CAP_FLAG_RDMA               0x40
 #define QUERY_FUNC_CAP_FLAG_ETH                        0x80
+#define QUERY_FUNC_CAP_FLAG_QUOTAS             0x10
 
 /* when opcode modifier = 1 */
 #define QUERY_FUNC_CAP_PHYS_PORT_OFFSET                0x3
@@ -237,8 +244,9 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP1_PROXY);
 
        } else if (vhcr->op_modifier == 0) {
-               /* enable rdma and ethernet interfaces */
-               field = (QUERY_FUNC_CAP_FLAG_ETH | QUERY_FUNC_CAP_FLAG_RDMA);
+               /* enable rdma and ethernet interfaces, and new quota locations */
+               field = (QUERY_FUNC_CAP_FLAG_ETH | QUERY_FUNC_CAP_FLAG_RDMA |
+                        QUERY_FUNC_CAP_FLAG_QUOTAS);
                MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FLAGS_OFFSET);
 
                field = dev->caps.num_ports;
@@ -250,14 +258,20 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
                field = 0; /* protected FMR support not available as yet */
                MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FMR_OFFSET);
 
-               size = dev->caps.num_qps;
+               size = priv->mfunc.master.res_tracker.res_alloc[RES_QP].quota[slave];
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP_QUOTA_OFFSET);
+               size = dev->caps.num_qps;
+               MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP_QUOTA_OFFSET_DEP);
 
-               size = dev->caps.num_srqs;
+               size = priv->mfunc.master.res_tracker.res_alloc[RES_SRQ].quota[slave];
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET);
+               size = dev->caps.num_srqs;
+               MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET_DEP);
 
-               size = dev->caps.num_cqs;
+               size = priv->mfunc.master.res_tracker.res_alloc[RES_CQ].quota[slave];
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET);
+               size = dev->caps.num_cqs;
+               MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET_DEP);
 
                size = dev->caps.num_eqs;
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MAX_EQ_OFFSET);
@@ -265,14 +279,19 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
                size = dev->caps.reserved_eqs;
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_RESERVED_EQ_OFFSET);
 
-               size = dev->caps.num_mpts;
+               size = priv->mfunc.master.res_tracker.res_alloc[RES_MPT].quota[slave];
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET);
+               size = dev->caps.num_mpts;
+               MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET_DEP);
 
-               size = dev->caps.num_mtts;
+               size = priv->mfunc.master.res_tracker.res_alloc[RES_MTT].quota[slave];
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET);
+               size = dev->caps.num_mtts;
+               MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET_DEP);
 
                size = dev->caps.num_mgms + dev->caps.num_amgms;
                MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET);
+               MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP);
 
        } else
                err = -EINVAL;
@@ -287,7 +306,7 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port,
        u32                     *outbox;
        u8                      field, op_modifier;
        u32                     size;
-       int                     err = 0;
+       int                     err = 0, quotas = 0;
 
        op_modifier = !!gen_or_port; /* 0 = general, 1 = logical port */
 
@@ -311,6 +330,7 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port,
                        goto out;
                }
                func_cap->flags = field;
+               quotas = !!(func_cap->flags & QUERY_FUNC_CAP_FLAG_QUOTAS);
 
                MLX4_GET(field, outbox, QUERY_FUNC_CAP_NUM_PORTS_OFFSET);
                func_cap->num_ports = field;
@@ -318,29 +338,50 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port,
                MLX4_GET(size, outbox, QUERY_FUNC_CAP_PF_BHVR_OFFSET);
                func_cap->pf_context_behaviour = size;
 
-               MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_QUOTA_OFFSET);
-               func_cap->qp_quota = size & 0xFFFFFF;
+               if (quotas) {
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_QUOTA_OFFSET);
+                       func_cap->qp_quota = size & 0xFFFFFF;
 
-               MLX4_GET(size, outbox, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET);
-               func_cap->srq_quota = size & 0xFFFFFF;
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET);
+                       func_cap->srq_quota = size & 0xFFFFFF;
 
-               MLX4_GET(size, outbox, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET);
-               func_cap->cq_quota = size & 0xFFFFFF;
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET);
+                       func_cap->cq_quota = size & 0xFFFFFF;
 
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET);
+                       func_cap->mpt_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET);
+                       func_cap->mtt_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET);
+                       func_cap->mcg_quota = size & 0xFFFFFF;
+
+               } else {
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_QUOTA_OFFSET_DEP);
+                       func_cap->qp_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET_DEP);
+                       func_cap->srq_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET_DEP);
+                       func_cap->cq_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET_DEP);
+                       func_cap->mpt_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET_DEP);
+                       func_cap->mtt_quota = size & 0xFFFFFF;
+
+                       MLX4_GET(size, outbox, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP);
+                       func_cap->mcg_quota = size & 0xFFFFFF;
+               }
                MLX4_GET(size, outbox, QUERY_FUNC_CAP_MAX_EQ_OFFSET);
                func_cap->max_eq = size & 0xFFFFFF;
 
                MLX4_GET(size, outbox, QUERY_FUNC_CAP_RESERVED_EQ_OFFSET);
                func_cap->reserved_eq = size & 0xFFFFFF;
 
-               MLX4_GET(size, outbox, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET);
-               func_cap->mpt_quota = size & 0xFFFFFF;
-
-               MLX4_GET(size, outbox, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET);
-               func_cap->mtt_quota = size & 0xFFFFFF;
-
-               MLX4_GET(size, outbox, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET);
-               func_cap->mcg_quota = size & 0xFFFFFF;
                goto out;
        }
 
@@ -652,7 +693,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
                 QUERY_DEV_CAP_RSVD_LKEY_OFFSET);
        MLX4_GET(field, outbox, QUERY_DEV_CAP_FW_REASSIGN_MAC);
        if (field & 1<<6)
-               dev_cap->flags2 |= MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN;
+               dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN;
        MLX4_GET(dev_cap->max_icm_sz, outbox,
                 QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET);
        if (dev_cap->flags & MLX4_DEV_CAP_FLAG_COUNTERS)
@@ -924,7 +965,6 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt)
        mailbox = mlx4_alloc_cmd_mailbox(dev);
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
-       memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE);
        pages = mailbox->buf;
 
        for (mlx4_icm_first(icm, &iter);
@@ -1273,8 +1313,6 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param)
                return PTR_ERR(mailbox);
        inbox = mailbox->buf;
 
-       memset(inbox, 0, INIT_HCA_IN_SIZE);
-
        *((u8 *) mailbox->buf + INIT_HCA_VERSION_OFFSET) = INIT_HCA_VERSION;
 
        *((u8 *) mailbox->buf + INIT_HCA_CACHELINE_SZ_OFFSET) =
@@ -1573,8 +1611,6 @@ int mlx4_INIT_PORT(struct mlx4_dev *dev, int port)
                        return PTR_ERR(mailbox);
                inbox = mailbox->buf;
 
-               memset(inbox, 0, INIT_PORT_IN_SIZE);
-
                flags = 0;
                flags |= (dev->caps.vl_cap[port] & 0xf) << INIT_PORT_VL_SHIFT;
                flags |= (dev->caps.port_width_cap[port] & 0xf) << INIT_PORT_PORT_WIDTH_SHIFT;
@@ -1713,7 +1749,6 @@ void mlx4_opreq_action(struct work_struct *work)
        u32 *outbox;
        u32 modifier;
        u16 token;
-       u16 type_m;
        u16 type;
        int err;
        u32 num_qps;
@@ -1746,7 +1781,6 @@ void mlx4_opreq_action(struct work_struct *work)
                MLX4_GET(modifier, outbox, GET_OP_REQ_MODIFIER_OFFSET);
                MLX4_GET(token, outbox, GET_OP_REQ_TOKEN_OFFSET);
                MLX4_GET(type, outbox, GET_OP_REQ_TYPE_OFFSET);
-               type_m = type >> 12;
                type &= 0xfff;
 
                switch (type) {
index 31d0264..5fbf492 100644 (file)
@@ -93,13 +93,17 @@ void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent)
        kfree(icm);
 }
 
-static int mlx4_alloc_icm_pages(struct scatterlist *mem, int order, gfp_t gfp_mask)
+static int mlx4_alloc_icm_pages(struct scatterlist *mem, int order,
+                               gfp_t gfp_mask, int node)
 {
        struct page *page;
 
-       page = alloc_pages(gfp_mask, order);
-       if (!page)
-               return -ENOMEM;
+       page = alloc_pages_node(node, gfp_mask, order);
+       if (!page) {
+               page = alloc_pages(gfp_mask, order);
+               if (!page)
+                       return -ENOMEM;
+       }
 
        sg_set_page(mem, page, PAGE_SIZE << order, 0);
        return 0;
@@ -130,9 +134,15 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
        /* We use sg_set_buf for coherent allocs, which assumes low memory */
        BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM));
 
-       icm = kmalloc(sizeof *icm, gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
-       if (!icm)
-               return NULL;
+       icm = kmalloc_node(sizeof(*icm),
+                          gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN),
+                          dev->numa_node);
+       if (!icm) {
+               icm = kmalloc(sizeof(*icm),
+                             gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
+               if (!icm)
+                       return NULL;
+       }
 
        icm->refcount = 0;
        INIT_LIST_HEAD(&icm->chunk_list);
@@ -141,10 +151,17 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
 
        while (npages > 0) {
                if (!chunk) {
-                       chunk = kmalloc(sizeof *chunk,
-                                       gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
-                       if (!chunk)
-                               goto fail;
+                       chunk = kmalloc_node(sizeof(*chunk),
+                                            gfp_mask & ~(__GFP_HIGHMEM |
+                                                         __GFP_NOWARN),
+                                            dev->numa_node);
+                       if (!chunk) {
+                               chunk = kmalloc(sizeof(*chunk),
+                                               gfp_mask & ~(__GFP_HIGHMEM |
+                                                            __GFP_NOWARN));
+                               if (!chunk)
+                                       goto fail;
+                       }
 
                        sg_init_table(chunk->mem, MLX4_ICM_CHUNK_LEN);
                        chunk->npages = 0;
@@ -161,7 +178,8 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
                                                      cur_order, gfp_mask);
                else
                        ret = mlx4_alloc_icm_pages(&chunk->mem[chunk->npages],
-                                                  cur_order, gfp_mask);
+                                                  cur_order, gfp_mask,
+                                                  dev->numa_node);
 
                if (ret) {
                        if (--cur_order < 0)
index 60c9f4f..5789ea2 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/io-mapping.h>
 #include <linux/delay.h>
 #include <linux/netdevice.h>
+#include <linux/kmod.h>
 
 #include <linux/mlx4/device.h>
 #include <linux/mlx4/doorbell.h>
@@ -561,13 +562,17 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        }
 
        dev->caps.num_ports             = func_cap.num_ports;
-       dev->caps.num_qps               = func_cap.qp_quota;
-       dev->caps.num_srqs              = func_cap.srq_quota;
-       dev->caps.num_cqs               = func_cap.cq_quota;
-       dev->caps.num_eqs               = func_cap.max_eq;
-       dev->caps.reserved_eqs          = func_cap.reserved_eq;
-       dev->caps.num_mpts              = func_cap.mpt_quota;
-       dev->caps.num_mtts              = func_cap.mtt_quota;
+       dev->quotas.qp                  = func_cap.qp_quota;
+       dev->quotas.srq                 = func_cap.srq_quota;
+       dev->quotas.cq                  = func_cap.cq_quota;
+       dev->quotas.mpt                 = func_cap.mpt_quota;
+       dev->quotas.mtt                 = func_cap.mtt_quota;
+       dev->caps.num_qps               = 1 << hca_param.log_num_qps;
+       dev->caps.num_srqs              = 1 << hca_param.log_num_srqs;
+       dev->caps.num_cqs               = 1 << hca_param.log_num_cqs;
+       dev->caps.num_mpts              = 1 << hca_param.log_mpt_sz;
+       dev->caps.num_eqs               = func_cap.max_eq;
+       dev->caps.reserved_eqs          = func_cap.reserved_eq;
        dev->caps.num_pds               = MLX4_NUM_PDS;
        dev->caps.num_mgms              = 0;
        dev->caps.num_amgms             = 0;
@@ -650,6 +655,27 @@ err_mem:
        return err;
 }
 
+static void mlx4_request_modules(struct mlx4_dev *dev)
+{
+       int port;
+       int has_ib_port = false;
+       int has_eth_port = false;
+#define EN_DRV_NAME    "mlx4_en"
+#define IB_DRV_NAME    "mlx4_ib"
+
+       for (port = 1; port <= dev->caps.num_ports; port++) {
+               if (dev->caps.port_type[port] == MLX4_PORT_TYPE_IB)
+                       has_ib_port = true;
+               else if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH)
+                       has_eth_port = true;
+       }
+
+       if (has_ib_port)
+               request_module_nowait(IB_DRV_NAME);
+       if (has_eth_port)
+               request_module_nowait(EN_DRV_NAME);
+}
+
 /*
  * Change the port configuration of the device.
  * Every user of this function must hold the port mutex.
@@ -681,6 +707,11 @@ int mlx4_change_port_types(struct mlx4_dev *dev,
                }
                mlx4_set_port_mask(dev);
                err = mlx4_register_device(dev);
+               if (err) {
+                       mlx4_err(dev, "Failed to register device\n");
+                       goto out;
+               }
+               mlx4_request_modules(dev);
        }
 
 out:
@@ -2075,9 +2106,15 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        "aborting.\n");
                return err;
        }
-       if (num_vfs > MLX4_MAX_NUM_VF) {
-               printk(KERN_ERR "There are more VF's (%d) than allowed(%d)\n",
-                      num_vfs, MLX4_MAX_NUM_VF);
+
+       /* Due to requirement that all VFs and the PF are *guaranteed* 2 MACS
+        * per port, we must limit the number of VFs to 63 (since their are
+        * 128 MACs)
+        */
+       if (num_vfs >= MLX4_MAX_NUM_VF) {
+               dev_err(&pdev->dev,
+                       "Requested more VF's (%d) than allowed (%d)\n",
+                       num_vfs, MLX4_MAX_NUM_VF - 1);
                return -EINVAL;
        }
 
@@ -2154,6 +2191,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
        mutex_init(&priv->bf_mutex);
 
        dev->rev_id = pdev->revision;
+       dev->numa_node = dev_to_node(&pdev->dev);
        /* Detect if this device is a virtual function */
        if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
                /* When acting as pf, we normally skip vfs unless explicitly
@@ -2295,6 +2333,8 @@ slave_start:
        if (err)
                goto err_steer;
 
+       mlx4_init_quotas(dev);
+
        for (port = 1; port <= dev->caps.num_ports; port++) {
                err = mlx4_init_port_info(dev, port);
                if (err)
@@ -2305,6 +2345,8 @@ slave_start:
        if (err)
                goto err_port;
 
+       mlx4_request_modules(dev);
+
        mlx4_sense_init(dev);
        mlx4_start_sense(dev);
 
index 55f6245..acf9d5f 100644 (file)
@@ -506,7 +506,6 @@ static int remove_promisc_qp(struct mlx4_dev *dev, u8 port,
                goto out_list;
        }
        mgm = mailbox->buf;
-       memset(mgm, 0, sizeof *mgm);
        members_count = 0;
        list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list)
                mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK);
@@ -645,7 +644,7 @@ static const u8 __promisc_mode[] = {
 int mlx4_map_sw_to_hw_steering_mode(struct mlx4_dev *dev,
                                    enum mlx4_net_trans_promisc_mode flow_type)
 {
-       if (flow_type >= MLX4_FS_MODE_NUM || flow_type < 0) {
+       if (flow_type >= MLX4_FS_MODE_NUM) {
                mlx4_err(dev, "Invalid flow type. type = %d\n", flow_type);
                return -EINVAL;
        }
@@ -681,7 +680,7 @@ const u16 __sw_id_hw[] = {
 int mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev,
                                  enum mlx4_net_trans_rule_id id)
 {
-       if (id >= MLX4_NET_TRANS_RULE_NUM || id < 0) {
+       if (id >= MLX4_NET_TRANS_RULE_NUM) {
                mlx4_err(dev, "Invalid network rule id. id = %d\n", id);
                return -EINVAL;
        }
@@ -706,7 +705,7 @@ static const int __rule_hw_sz[] = {
 int mlx4_hw_rule_sz(struct mlx4_dev *dev,
               enum mlx4_net_trans_rule_id id)
 {
-       if (id >= MLX4_NET_TRANS_RULE_NUM || id < 0) {
+       if (id >= MLX4_NET_TRANS_RULE_NUM) {
                mlx4_err(dev, "Invalid network rule id. id = %d\n", id);
                return -EINVAL;
        }
@@ -857,7 +856,6 @@ int mlx4_flow_attach(struct mlx4_dev *dev,
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
 
-       memset(mailbox->buf, 0, sizeof(struct mlx4_net_trans_rule_hw_ctrl));
        trans_rule_ctrl_to_hw(rule, mailbox->buf);
 
        size += sizeof(struct mlx4_net_trans_rule_hw_ctrl);
index 348bb8c..e582a41 100644 (file)
@@ -455,6 +455,7 @@ struct mlx4_slave_state {
        u8 last_cmd;
        u8 init_port_mask;
        bool active;
+       bool old_vlan_api;
        u8 function;
        dma_addr_t vhcr_dma;
        u16 mtu[MLX4_MAX_PORTS + 1];
@@ -503,12 +504,28 @@ struct slave_list {
        struct list_head res_list[MLX4_NUM_OF_RESOURCE_TYPE];
 };
 
+struct resource_allocator {
+       spinlock_t alloc_lock; /* protect quotas */
+       union {
+               int res_reserved;
+               int res_port_rsvd[MLX4_MAX_PORTS];
+       };
+       union {
+               int res_free;
+               int res_port_free[MLX4_MAX_PORTS];
+       };
+       int *quota;
+       int *allocated;
+       int *guaranteed;
+};
+
 struct mlx4_resource_tracker {
        spinlock_t lock;
        /* tree for each resources */
        struct rb_root res_tree[MLX4_NUM_OF_RESOURCE_TYPE];
        /* num_of_slave's lists, one per slave */
        struct slave_list *slave_list;
+       struct resource_allocator res_alloc[MLX4_NUM_OF_RESOURCE_TYPE];
 };
 
 #define SLAVE_EVENT_EQ_SIZE    128
@@ -1111,7 +1128,7 @@ int mlx4_change_port_types(struct mlx4_dev *dev,
 
 void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table);
 void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table);
-void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index);
+void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan);
 int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
 
 int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz);
@@ -1252,4 +1269,6 @@ static inline spinlock_t *mlx4_tlock(struct mlx4_dev *dev)
 
 void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work);
 
+void mlx4_init_quotas(struct mlx4_dev *dev);
+
 #endif /* MLX4_H */
index 5e0aa56..f3758de 100644 (file)
@@ -237,8 +237,8 @@ struct mlx4_en_tx_desc {
 struct mlx4_en_rx_alloc {
        struct page     *page;
        dma_addr_t      dma;
-       u32             offset;
-       u32             size;
+       u32             page_offset;
+       u32             page_size;
 };
 
 struct mlx4_en_tx_ring {
@@ -530,10 +530,10 @@ struct mlx4_en_priv {
        u16 num_frags;
        u16 log_rx_info;
 
-       struct mlx4_en_tx_ring *tx_ring;
-       struct mlx4_en_rx_ring rx_ring[MAX_RX_RINGS];
-       struct mlx4_en_cq *tx_cq;
-       struct mlx4_en_cq rx_cq[MAX_RX_RINGS];
+       struct mlx4_en_tx_ring **tx_ring;
+       struct mlx4_en_rx_ring *rx_ring[MAX_RX_RINGS];
+       struct mlx4_en_cq **tx_cq;
+       struct mlx4_en_cq *rx_cq[MAX_RX_RINGS];
        struct mlx4_qp drop_qp;
        struct work_struct rx_mode_task;
        struct work_struct watchdog_task;
@@ -626,7 +626,7 @@ static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
        if ((cq->state & MLX4_CQ_LOCKED)) {
                struct net_device *dev = cq->dev;
                struct mlx4_en_priv *priv = netdev_priv(dev);
-               struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring];
+               struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring];
 
                cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD;
                rc = false;
@@ -704,9 +704,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach);
 void mlx4_en_free_resources(struct mlx4_en_priv *priv);
 int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
 
-int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
-                     int entries, int ring, enum cq_type mode);
-void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
+int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq,
+                     int entries, int ring, enum cq_type mode, int node);
+void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq);
 int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
                        int cq_idx);
 void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq);
@@ -717,9 +717,11 @@ void mlx4_en_tx_irq(struct mlx4_cq *mcq);
 u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb);
 netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev);
 
-int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring,
-                          int qpn, u32 size, u16 stride);
-void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring);
+int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
+                          struct mlx4_en_tx_ring **pring,
+                          int qpn, u32 size, u16 stride, int node);
+void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv,
+                            struct mlx4_en_tx_ring **pring);
 int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
                             struct mlx4_en_tx_ring *ring,
                             int cq, int user_prio);
@@ -727,10 +729,10 @@ void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv,
                                struct mlx4_en_tx_ring *ring);
 
 int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
-                          struct mlx4_en_rx_ring *ring,
-                          u32 size, u16 stride);
+                          struct mlx4_en_rx_ring **pring,
+                          u32 size, u16 stride, int node);
 void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
-                            struct mlx4_en_rx_ring *ring,
+                            struct mlx4_en_rx_ring **pring,
                             u32 size, u16 stride);
 int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv);
 void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv,
@@ -768,8 +770,7 @@ extern const struct dcbnl_rtnl_ops mlx4_en_dcbnl_pfc_ops;
 int mlx4_en_setup_tc(struct net_device *dev, u8 up);
 
 #ifdef CONFIG_RFS_ACCEL
-void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv,
-                            struct mlx4_en_rx_ring *rx_ring);
+void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv);
 #endif
 
 #define MLX4_EN_NUM_SELF_TEST  5
index f91719a..b3ee9ba 100644 (file)
@@ -480,9 +480,6 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr)
                goto err_table;
        }
        mpt_entry = mailbox->buf;
-
-       memset(mpt_entry, 0, sizeof *mpt_entry);
-
        mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO         |
                                       MLX4_MPT_FLAG_REGION      |
                                       mr->access);
@@ -695,8 +692,6 @@ int mlx4_mw_enable(struct mlx4_dev *dev, struct mlx4_mw *mw)
        }
        mpt_entry = mailbox->buf;
 
-       memset(mpt_entry, 0, sizeof(*mpt_entry));
-
        /* Note that the MLX4_MPT_FLAG_REGION bit in mpt_entry->flags is turned
         * off, thus creating a memory window and not a memory region.
         */
@@ -755,14 +750,14 @@ int mlx4_init_mr_table(struct mlx4_dev *dev)
        struct mlx4_mr_table *mr_table = &priv->mr_table;
        int err;
 
-       if (!is_power_of_2(dev->caps.num_mpts))
-               return -EINVAL;
-
        /* Nothing to do for slaves - all MR handling is forwarded
        * to the master */
        if (mlx4_is_slave(dev))
                return 0;
 
+       if (!is_power_of_2(dev->caps.num_mpts))
+               return -EINVAL;
+
        err = mlx4_bitmap_init(&mr_table->mpt_bitmap, dev->caps.num_mpts,
                               ~0, dev->caps.reserved_mrws, 0);
        if (err)
index 00f223a..84cfb40 100644 (file)
@@ -168,7 +168,7 @@ void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar)
 }
 EXPORT_SYMBOL_GPL(mlx4_uar_free);
 
-int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf)
+int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf, int node)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
        struct mlx4_uar *uar;
@@ -186,10 +186,13 @@ int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf)
                        err = -ENOMEM;
                        goto out;
                }
-               uar = kmalloc(sizeof *uar, GFP_KERNEL);
+               uar = kmalloc_node(sizeof(*uar), GFP_KERNEL, node);
                if (!uar) {
-                       err = -ENOMEM;
-                       goto out;
+                       uar = kmalloc(sizeof(*uar), GFP_KERNEL);
+                       if (!uar) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
                }
                err = mlx4_uar_alloc(dev, uar);
                if (err)
index 946e0af..97d342f 100644 (file)
@@ -178,13 +178,24 @@ EXPORT_SYMBOL_GPL(__mlx4_register_mac);
 int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
 {
        u64 out_param = 0;
-       int err;
+       int err = -EINVAL;
 
        if (mlx4_is_mfunc(dev)) {
-               set_param_l(&out_param, port);
-               err = mlx4_cmd_imm(dev, mac, &out_param, RES_MAC,
-                                  RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES,
-                                  MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
+               if (!(dev->flags & MLX4_FLAG_OLD_REG_MAC)) {
+                       err = mlx4_cmd_imm(dev, mac, &out_param,
+                                          ((u32) port) << 8 | (u32) RES_MAC,
+                                          RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES,
+                                          MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
+               }
+               if (err && err == -EINVAL && mlx4_is_slave(dev)) {
+                       /* retry using old REG_MAC format */
+                       set_param_l(&out_param, port);
+                       err = mlx4_cmd_imm(dev, mac, &out_param, RES_MAC,
+                                          RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES,
+                                          MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
+                       if (!err)
+                               dev->flags |= MLX4_FLAG_OLD_REG_MAC;
+               }
                if (err)
                        return err;
 
@@ -231,10 +242,18 @@ void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
        u64 out_param = 0;
 
        if (mlx4_is_mfunc(dev)) {
-               set_param_l(&out_param, port);
-               (void) mlx4_cmd_imm(dev, mac, &out_param, RES_MAC,
-                                   RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES,
-                                   MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
+               if (!(dev->flags & MLX4_FLAG_OLD_REG_MAC)) {
+                       (void) mlx4_cmd_imm(dev, mac, &out_param,
+                                           ((u32) port) << 8 | (u32) RES_MAC,
+                                           RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES,
+                                           MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
+               } else {
+                       /* use old unregister mac format */
+                       set_param_l(&out_param, port);
+                       (void) mlx4_cmd_imm(dev, mac, &out_param, RES_MAC,
+                                           RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES,
+                                           MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
+               }
                return;
        }
        __mlx4_unregister_mac(dev, port, mac);
@@ -284,7 +303,7 @@ static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port,
        memcpy(mailbox->buf, entries, MLX4_VLAN_TABLE_SIZE);
        in_mod = MLX4_SET_PORT_VLAN_TABLE << 8 | port;
        err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
-                      MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
+                      MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
 
        mlx4_free_cmd_mailbox(dev, mailbox);
 
@@ -370,9 +389,12 @@ int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index)
        u64 out_param = 0;
        int err;
 
+       if (vlan > 4095)
+               return -EINVAL;
+
        if (mlx4_is_mfunc(dev)) {
-               set_param_l(&out_param, port);
-               err = mlx4_cmd_imm(dev, vlan, &out_param, RES_VLAN,
+               err = mlx4_cmd_imm(dev, vlan, &out_param,
+                                  ((u32) port) << 8 | (u32) RES_VLAN,
                                   RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES,
                                   MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
                if (!err)
@@ -384,23 +406,26 @@ int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index)
 }
 EXPORT_SYMBOL_GPL(mlx4_register_vlan);
 
-void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index)
+void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
 {
        struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
+       int index;
 
-       if (index < MLX4_VLAN_REGULAR) {
-               mlx4_warn(dev, "Trying to free special vlan index %d\n", index);
-               return;
+       mutex_lock(&table->mutex);
+       if (mlx4_find_cached_vlan(dev, port, vlan, &index)) {
+               mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan);
+               goto out;
        }
 
-       mutex_lock(&table->mutex);
-       if (!table->refs[index]) {
-               mlx4_warn(dev, "No vlan entry for index %d\n", index);
+       if (index < MLX4_VLAN_REGULAR) {
+               mlx4_warn(dev, "Trying to free special vlan index %d\n", index);
                goto out;
        }
+
        if (--table->refs[index]) {
-               mlx4_dbg(dev, "Have more references for index %d,"
-                        "no need to modify vlan table\n", index);
+               mlx4_dbg(dev, "Have %d more references for index %d,"
+                        "no need to modify vlan table\n", table->refs[index],
+                        index);
                goto out;
        }
        table->entries[index] = 0;
@@ -410,23 +435,19 @@ out:
        mutex_unlock(&table->mutex);
 }
 
-void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index)
+void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
 {
-       u64 in_param = 0;
-       int err;
+       u64 out_param = 0;
 
        if (mlx4_is_mfunc(dev)) {
-               set_param_l(&in_param, port);
-               err = mlx4_cmd(dev, in_param, RES_VLAN, RES_OP_RESERVE_AND_MAP,
-                              MLX4_CMD_FREE_RES, MLX4_CMD_TIME_CLASS_A,
-                              MLX4_CMD_WRAPPED);
-               if (!err)
-                       mlx4_warn(dev, "Failed freeing vlan at index:%d\n",
-                                       index);
-
+               (void) mlx4_cmd_imm(dev, vlan, &out_param,
+                                   ((u32) port) << 8 | (u32) RES_VLAN,
+                                   RES_OP_RESERVE_AND_MAP,
+                                   MLX4_CMD_FREE_RES, MLX4_CMD_TIME_CLASS_A,
+                                   MLX4_CMD_WRAPPED);
                return;
        }
-       __mlx4_unregister_vlan(dev, port, index);
+       __mlx4_unregister_vlan(dev, port, vlan);
 }
 EXPORT_SYMBOL_GPL(mlx4_unregister_vlan);
 
@@ -448,8 +469,6 @@ int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
 
        inbuf = inmailbox->buf;
        outbuf = outmailbox->buf;
-       memset(inbuf, 0, 256);
-       memset(outbuf, 0, 256);
        inbuf[0] = 1;
        inbuf[1] = 1;
        inbuf[2] = 1;
@@ -632,8 +651,6 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz)
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
 
-       memset(mailbox->buf, 0, 256);
-
        ((__be32 *) mailbox->buf)[1] = dev->caps.ib_port_def_cap[port];
 
        if (pkey_tbl_sz >= 0 && mlx4_is_master(dev)) {
@@ -671,8 +688,6 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu,
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
        context = mailbox->buf;
-       memset(context, 0, sizeof *context);
-
        context->flags = SET_PORT_GEN_ALL_VALID;
        context->mtu = cpu_to_be16(mtu);
        context->pptx = (pptx * (!pfctx)) << 7;
@@ -706,8 +721,6 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
        context = mailbox->buf;
-       memset(context, 0, sizeof *context);
-
        context->base_qpn = cpu_to_be32(base_qpn);
        context->n_mac = dev->caps.log_num_macs;
        context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT |
@@ -740,8 +753,6 @@ int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc)
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
        context = mailbox->buf;
-       memset(context, 0, sizeof *context);
-
        for (i = 0; i < MLX4_NUM_UP; i += 2)
                context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1];
 
@@ -767,7 +778,6 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
        if (IS_ERR(mailbox))
                return PTR_ERR(mailbox);
        context = mailbox->buf;
-       memset(context, 0, sizeof *context);
 
        for (i = 0; i < MLX4_NUM_TC; i++) {
                struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i];
index e891b05..2715e61 100644 (file)
@@ -480,8 +480,7 @@ int mlx4_init_qp_table(struct mlx4_dev *dev)
        */
 
        err = mlx4_bitmap_init(&qp_table->bitmap, dev->caps.num_qps,
-                              (1 << 23) - 1, dev->phys_caps.base_sqpn + 8 +
-                              16 * MLX4_MFUNC_MAX * !!mlx4_is_master(dev),
+                              (1 << 23) - 1, mlx4_num_reserved_sqps(dev),
                               reserved_from_top);
        if (err)
                return err;
index dd68763..2f3f2bc 100644 (file)
@@ -55,6 +55,14 @@ struct mac_res {
        u8 port;
 };
 
+struct vlan_res {
+       struct list_head list;
+       u16 vlan;
+       int ref_count;
+       int vlan_index;
+       u8 port;
+};
+
 struct res_common {
        struct list_head        list;
        struct rb_node          node;
@@ -102,7 +110,14 @@ struct res_qp {
        int                     local_qpn;
        atomic_t                ref_count;
        u32                     qpc_flags;
+       /* saved qp params before VST enforcement in order to restore on VGT */
        u8                      sched_queue;
+       __be32                  param3;
+       u8                      vlan_control;
+       u8                      fvl_rx;
+       u8                      pri_path_fl;
+       u8                      vlan_index;
+       u8                      feup;
 };
 
 enum res_mtt_states {
@@ -266,6 +281,7 @@ static const char *ResourceType(enum mlx4_resource rt)
        case RES_MPT: return "RES_MPT";
        case RES_MTT: return "RES_MTT";
        case RES_MAC: return  "RES_MAC";
+       case RES_VLAN: return  "RES_VLAN";
        case RES_EQ: return "RES_EQ";
        case RES_COUNTER: return "RES_COUNTER";
        case RES_FS_RULE: return "RES_FS_RULE";
@@ -274,10 +290,139 @@ static const char *ResourceType(enum mlx4_resource rt)
        };
 }
 
+static void rem_slave_vlans(struct mlx4_dev *dev, int slave);
+static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
+                                     enum mlx4_resource res_type, int count,
+                                     int port)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct resource_allocator *res_alloc =
+               &priv->mfunc.master.res_tracker.res_alloc[res_type];
+       int err = -EINVAL;
+       int allocated, free, reserved, guaranteed, from_free;
+
+       if (slave > dev->num_vfs)
+               return -EINVAL;
+
+       spin_lock(&res_alloc->alloc_lock);
+       allocated = (port > 0) ?
+               res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] :
+               res_alloc->allocated[slave];
+       free = (port > 0) ? res_alloc->res_port_free[port - 1] :
+               res_alloc->res_free;
+       reserved = (port > 0) ? res_alloc->res_port_rsvd[port - 1] :
+               res_alloc->res_reserved;
+       guaranteed = res_alloc->guaranteed[slave];
+
+       if (allocated + count > res_alloc->quota[slave])
+               goto out;
+
+       if (allocated + count <= guaranteed) {
+               err = 0;
+       } else {
+               /* portion may need to be obtained from free area */
+               if (guaranteed - allocated > 0)
+                       from_free = count - (guaranteed - allocated);
+               else
+                       from_free = count;
+
+               if (free - from_free > reserved)
+                       err = 0;
+       }
+
+       if (!err) {
+               /* grant the request */
+               if (port > 0) {
+                       res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] += count;
+                       res_alloc->res_port_free[port - 1] -= count;
+               } else {
+                       res_alloc->allocated[slave] += count;
+                       res_alloc->res_free -= count;
+               }
+       }
+
+out:
+       spin_unlock(&res_alloc->alloc_lock);
+       return err;
+}
+
+static inline void mlx4_release_resource(struct mlx4_dev *dev, int slave,
+                                   enum mlx4_resource res_type, int count,
+                                   int port)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct resource_allocator *res_alloc =
+               &priv->mfunc.master.res_tracker.res_alloc[res_type];
+
+       if (slave > dev->num_vfs)
+               return;
+
+       spin_lock(&res_alloc->alloc_lock);
+       if (port > 0) {
+               res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] -= count;
+               res_alloc->res_port_free[port - 1] += count;
+       } else {
+               res_alloc->allocated[slave] -= count;
+               res_alloc->res_free += count;
+       }
+
+       spin_unlock(&res_alloc->alloc_lock);
+       return;
+}
+
+static inline void initialize_res_quotas(struct mlx4_dev *dev,
+                                        struct resource_allocator *res_alloc,
+                                        enum mlx4_resource res_type,
+                                        int vf, int num_instances)
+{
+       res_alloc->guaranteed[vf] = num_instances / (2 * (dev->num_vfs + 1));
+       res_alloc->quota[vf] = (num_instances / 2) + res_alloc->guaranteed[vf];
+       if (vf == mlx4_master_func_num(dev)) {
+               res_alloc->res_free = num_instances;
+               if (res_type == RES_MTT) {
+                       /* reserved mtts will be taken out of the PF allocation */
+                       res_alloc->res_free += dev->caps.reserved_mtts;
+                       res_alloc->guaranteed[vf] += dev->caps.reserved_mtts;
+                       res_alloc->quota[vf] += dev->caps.reserved_mtts;
+               }
+       }
+}
+
+void mlx4_init_quotas(struct mlx4_dev *dev)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       int pf;
+
+       /* quotas for VFs are initialized in mlx4_slave_cap */
+       if (mlx4_is_slave(dev))
+               return;
+
+       if (!mlx4_is_mfunc(dev)) {
+               dev->quotas.qp = dev->caps.num_qps - dev->caps.reserved_qps -
+                       mlx4_num_reserved_sqps(dev);
+               dev->quotas.cq = dev->caps.num_cqs - dev->caps.reserved_cqs;
+               dev->quotas.srq = dev->caps.num_srqs - dev->caps.reserved_srqs;
+               dev->quotas.mtt = dev->caps.num_mtts - dev->caps.reserved_mtts;
+               dev->quotas.mpt = dev->caps.num_mpts - dev->caps.reserved_mrws;
+               return;
+       }
+
+       pf = mlx4_master_func_num(dev);
+       dev->quotas.qp =
+               priv->mfunc.master.res_tracker.res_alloc[RES_QP].quota[pf];
+       dev->quotas.cq =
+               priv->mfunc.master.res_tracker.res_alloc[RES_CQ].quota[pf];
+       dev->quotas.srq =
+               priv->mfunc.master.res_tracker.res_alloc[RES_SRQ].quota[pf];
+       dev->quotas.mtt =
+               priv->mfunc.master.res_tracker.res_alloc[RES_MTT].quota[pf];
+       dev->quotas.mpt =
+               priv->mfunc.master.res_tracker.res_alloc[RES_MPT].quota[pf];
+}
 int mlx4_init_resource_tracker(struct mlx4_dev *dev)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
-       int i;
+       int i, j;
        int t;
 
        priv->mfunc.master.res_tracker.slave_list =
@@ -298,8 +443,105 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev)
        for (i = 0 ; i < MLX4_NUM_OF_RESOURCE_TYPE; i++)
                priv->mfunc.master.res_tracker.res_tree[i] = RB_ROOT;
 
+       for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) {
+               struct resource_allocator *res_alloc =
+                       &priv->mfunc.master.res_tracker.res_alloc[i];
+               res_alloc->quota = kmalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL);
+               res_alloc->guaranteed = kmalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL);
+               if (i == RES_MAC || i == RES_VLAN)
+                       res_alloc->allocated = kzalloc(MLX4_MAX_PORTS *
+                                                      (dev->num_vfs + 1) * sizeof(int),
+                                                       GFP_KERNEL);
+               else
+                       res_alloc->allocated = kzalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL);
+
+               if (!res_alloc->quota || !res_alloc->guaranteed ||
+                   !res_alloc->allocated)
+                       goto no_mem_err;
+
+               spin_lock_init(&res_alloc->alloc_lock);
+               for (t = 0; t < dev->num_vfs + 1; t++) {
+                       switch (i) {
+                       case RES_QP:
+                               initialize_res_quotas(dev, res_alloc, RES_QP,
+                                                     t, dev->caps.num_qps -
+                                                     dev->caps.reserved_qps -
+                                                     mlx4_num_reserved_sqps(dev));
+                               break;
+                       case RES_CQ:
+                               initialize_res_quotas(dev, res_alloc, RES_CQ,
+                                                     t, dev->caps.num_cqs -
+                                                     dev->caps.reserved_cqs);
+                               break;
+                       case RES_SRQ:
+                               initialize_res_quotas(dev, res_alloc, RES_SRQ,
+                                                     t, dev->caps.num_srqs -
+                                                     dev->caps.reserved_srqs);
+                               break;
+                       case RES_MPT:
+                               initialize_res_quotas(dev, res_alloc, RES_MPT,
+                                                     t, dev->caps.num_mpts -
+                                                     dev->caps.reserved_mrws);
+                               break;
+                       case RES_MTT:
+                               initialize_res_quotas(dev, res_alloc, RES_MTT,
+                                                     t, dev->caps.num_mtts -
+                                                     dev->caps.reserved_mtts);
+                               break;
+                       case RES_MAC:
+                               if (t == mlx4_master_func_num(dev)) {
+                                       res_alloc->quota[t] = MLX4_MAX_MAC_NUM;
+                                       res_alloc->guaranteed[t] = 2;
+                                       for (j = 0; j < MLX4_MAX_PORTS; j++)
+                                               res_alloc->res_port_free[j] = MLX4_MAX_MAC_NUM;
+                               } else {
+                                       res_alloc->quota[t] = MLX4_MAX_MAC_NUM;
+                                       res_alloc->guaranteed[t] = 2;
+                               }
+                               break;
+                       case RES_VLAN:
+                               if (t == mlx4_master_func_num(dev)) {
+                                       res_alloc->quota[t] = MLX4_MAX_VLAN_NUM;
+                                       res_alloc->guaranteed[t] = MLX4_MAX_VLAN_NUM / 2;
+                                       for (j = 0; j < MLX4_MAX_PORTS; j++)
+                                               res_alloc->res_port_free[j] =
+                                                       res_alloc->quota[t];
+                               } else {
+                                       res_alloc->quota[t] = MLX4_MAX_VLAN_NUM / 2;
+                                       res_alloc->guaranteed[t] = 0;
+                               }
+                               break;
+                       case RES_COUNTER:
+                               res_alloc->quota[t] = dev->caps.max_counters;
+                               res_alloc->guaranteed[t] = 0;
+                               if (t == mlx4_master_func_num(dev))
+                                       res_alloc->res_free = res_alloc->quota[t];
+                               break;
+                       default:
+                               break;
+                       }
+                       if (i == RES_MAC || i == RES_VLAN) {
+                               for (j = 0; j < MLX4_MAX_PORTS; j++)
+                                       res_alloc->res_port_rsvd[j] +=
+                                               res_alloc->guaranteed[t];
+                       } else {
+                               res_alloc->res_reserved += res_alloc->guaranteed[t];
+                       }
+               }
+       }
        spin_lock_init(&priv->mfunc.master.res_tracker.lock);
-       return 0 ;
+       return 0;
+
+no_mem_err:
+       for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) {
+               kfree(priv->mfunc.master.res_tracker.res_alloc[i].allocated);
+               priv->mfunc.master.res_tracker.res_alloc[i].allocated = NULL;
+               kfree(priv->mfunc.master.res_tracker.res_alloc[i].guaranteed);
+               priv->mfunc.master.res_tracker.res_alloc[i].guaranteed = NULL;
+               kfree(priv->mfunc.master.res_tracker.res_alloc[i].quota);
+               priv->mfunc.master.res_tracker.res_alloc[i].quota = NULL;
+       }
+       return -ENOMEM;
 }
 
 void mlx4_free_resource_tracker(struct mlx4_dev *dev,
@@ -309,13 +551,28 @@ void mlx4_free_resource_tracker(struct mlx4_dev *dev,
        int i;
 
        if (priv->mfunc.master.res_tracker.slave_list) {
-               if (type != RES_TR_FREE_STRUCTS_ONLY)
-                       for (i = 0 ; i < dev->num_slaves; i++)
+               if (type != RES_TR_FREE_STRUCTS_ONLY) {
+                       for (i = 0; i < dev->num_slaves; i++) {
                                if (type == RES_TR_FREE_ALL ||
                                    dev->caps.function != i)
                                        mlx4_delete_all_resources_for_slave(dev, i);
+                       }
+                       /* free master's vlans */
+                       i = dev->caps.function;
+                       mutex_lock(&priv->mfunc.master.res_tracker.slave_list[i].mutex);
+                       rem_slave_vlans(dev, i);
+                       mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[i].mutex);
+               }
 
                if (type != RES_TR_FREE_SLAVES_ONLY) {
+                       for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) {
+                               kfree(priv->mfunc.master.res_tracker.res_alloc[i].allocated);
+                               priv->mfunc.master.res_tracker.res_alloc[i].allocated = NULL;
+                               kfree(priv->mfunc.master.res_tracker.res_alloc[i].guaranteed);
+                               priv->mfunc.master.res_tracker.res_alloc[i].guaranteed = NULL;
+                               kfree(priv->mfunc.master.res_tracker.res_alloc[i].quota);
+                               priv->mfunc.master.res_tracker.res_alloc[i].quota = NULL;
+                       }
                        kfree(priv->mfunc.master.res_tracker.slave_list);
                        priv->mfunc.master.res_tracker.slave_list = NULL;
                }
@@ -1229,12 +1486,19 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        case RES_OP_RESERVE:
                count = get_param_l(&in_param);
                align = get_param_h(&in_param);
-               err = __mlx4_qp_reserve_range(dev, count, align, &base);
+               err = mlx4_grant_resource(dev, slave, RES_QP, count, 0);
                if (err)
                        return err;
 
+               err = __mlx4_qp_reserve_range(dev, count, align, &base);
+               if (err) {
+                       mlx4_release_resource(dev, slave, RES_QP, count, 0);
+                       return err;
+               }
+
                err = add_res_range(dev, slave, base, count, RES_QP, 0);
                if (err) {
+                       mlx4_release_resource(dev, slave, RES_QP, count, 0);
                        __mlx4_qp_release_range(dev, base, count);
                        return err;
                }
@@ -1282,15 +1546,24 @@ static int mtt_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                return err;
 
        order = get_param_l(&in_param);
+
+       err = mlx4_grant_resource(dev, slave, RES_MTT, 1 << order, 0);
+       if (err)
+               return err;
+
        base = __mlx4_alloc_mtt_range(dev, order);
-       if (base == -1)
+       if (base == -1) {
+               mlx4_release_resource(dev, slave, RES_MTT, 1 << order, 0);
                return -ENOMEM;
+       }
 
        err = add_res_range(dev, slave, base, 1, RES_MTT, order);
-       if (err)
+       if (err) {
+               mlx4_release_resource(dev, slave, RES_MTT, 1 << order, 0);
                __mlx4_free_mtt_range(dev, base, order);
-       else
+       } else {
                set_param_l(out_param, base);
+       }
 
        return err;
 }
@@ -1305,13 +1578,20 @@ static int mpt_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 
        switch (op) {
        case RES_OP_RESERVE:
+               err = mlx4_grant_resource(dev, slave, RES_MPT, 1, 0);
+               if (err)
+                       break;
+
                index = __mlx4_mpt_reserve(dev);
-               if (index == -1)
+               if (index == -1) {
+                       mlx4_release_resource(dev, slave, RES_MPT, 1, 0);
                        break;
+               }
                id = index & mpt_mask(dev);
 
                err = add_res_range(dev, slave, id, 1, RES_MPT, index);
                if (err) {
+                       mlx4_release_resource(dev, slave, RES_MPT, 1, 0);
                        __mlx4_mpt_release(dev, index);
                        break;
                }
@@ -1345,12 +1625,19 @@ static int cq_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 
        switch (op) {
        case RES_OP_RESERVE_AND_MAP:
-               err = __mlx4_cq_alloc_icm(dev, &cqn);
+               err = mlx4_grant_resource(dev, slave, RES_CQ, 1, 0);
                if (err)
                        break;
 
+               err = __mlx4_cq_alloc_icm(dev, &cqn);
+               if (err) {
+                       mlx4_release_resource(dev, slave, RES_CQ, 1, 0);
+                       break;
+               }
+
                err = add_res_range(dev, slave, cqn, 1, RES_CQ, 0);
                if (err) {
+                       mlx4_release_resource(dev, slave, RES_CQ, 1, 0);
                        __mlx4_cq_free_icm(dev, cqn);
                        break;
                }
@@ -1373,12 +1660,19 @@ static int srq_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 
        switch (op) {
        case RES_OP_RESERVE_AND_MAP:
-               err = __mlx4_srq_alloc_icm(dev, &srqn);
+               err = mlx4_grant_resource(dev, slave, RES_SRQ, 1, 0);
                if (err)
                        break;
 
+               err = __mlx4_srq_alloc_icm(dev, &srqn);
+               if (err) {
+                       mlx4_release_resource(dev, slave, RES_SRQ, 1, 0);
+                       break;
+               }
+
                err = add_res_range(dev, slave, srqn, 1, RES_SRQ, 0);
                if (err) {
+                       mlx4_release_resource(dev, slave, RES_SRQ, 1, 0);
                        __mlx4_srq_free_icm(dev, srqn);
                        break;
                }
@@ -1399,9 +1693,13 @@ static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port)
        struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
        struct mac_res *res;
 
+       if (mlx4_grant_resource(dev, slave, RES_MAC, 1, port))
+               return -EINVAL;
        res = kzalloc(sizeof *res, GFP_KERNEL);
-       if (!res)
+       if (!res) {
+               mlx4_release_resource(dev, slave, RES_MAC, 1, port);
                return -ENOMEM;
+       }
        res->mac = mac;
        res->port = (u8) port;
        list_add_tail(&res->list,
@@ -1421,6 +1719,7 @@ static void mac_del_from_slave(struct mlx4_dev *dev, int slave, u64 mac,
        list_for_each_entry_safe(res, tmp, mac_list, list) {
                if (res->mac == mac && res->port == (u8) port) {
                        list_del(&res->list);
+                       mlx4_release_resource(dev, slave, RES_MAC, 1, port);
                        kfree(res);
                        break;
                }
@@ -1438,12 +1737,13 @@ static void rem_slave_macs(struct mlx4_dev *dev, int slave)
        list_for_each_entry_safe(res, tmp, mac_list, list) {
                list_del(&res->list);
                __mlx4_unregister_mac(dev, res->port, res->mac);
+               mlx4_release_resource(dev, slave, RES_MAC, 1, res->port);
                kfree(res);
        }
 }
 
 static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                        u64 in_param, u64 *out_param)
+                        u64 in_param, u64 *out_param, int in_port)
 {
        int err = -EINVAL;
        int port;
@@ -1452,7 +1752,7 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        if (op != RES_OP_RESERVE_AND_MAP)
                return err;
 
-       port = get_param_l(out_param);
+       port = !in_port ? get_param_l(out_param) : in_port;
        mac = in_param;
 
        err = __mlx4_register_mac(dev, port, mac);
@@ -1469,12 +1769,114 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        return err;
 }
 
-static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                        u64 in_param, u64 *out_param)
+static int vlan_add_to_slave(struct mlx4_dev *dev, int slave, u16 vlan,
+                            int port, int vlan_index)
 {
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *vlan_list =
+               &tracker->slave_list[slave].res_list[RES_VLAN];
+       struct vlan_res *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, vlan_list, list) {
+               if (res->vlan == vlan && res->port == (u8) port) {
+                       /* vlan found. update ref count */
+                       ++res->ref_count;
+                       return 0;
+               }
+       }
+
+       if (mlx4_grant_resource(dev, slave, RES_VLAN, 1, port))
+               return -EINVAL;
+       res = kzalloc(sizeof(*res), GFP_KERNEL);
+       if (!res) {
+               mlx4_release_resource(dev, slave, RES_VLAN, 1, port);
+               return -ENOMEM;
+       }
+       res->vlan = vlan;
+       res->port = (u8) port;
+       res->vlan_index = vlan_index;
+       res->ref_count = 1;
+       list_add_tail(&res->list,
+                     &tracker->slave_list[slave].res_list[RES_VLAN]);
        return 0;
 }
 
+
+static void vlan_del_from_slave(struct mlx4_dev *dev, int slave, u16 vlan,
+                               int port)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *vlan_list =
+               &tracker->slave_list[slave].res_list[RES_VLAN];
+       struct vlan_res *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, vlan_list, list) {
+               if (res->vlan == vlan && res->port == (u8) port) {
+                       if (!--res->ref_count) {
+                               list_del(&res->list);
+                               mlx4_release_resource(dev, slave, RES_VLAN,
+                                                     1, port);
+                               kfree(res);
+                       }
+                       break;
+               }
+       }
+}
+
+static void rem_slave_vlans(struct mlx4_dev *dev, int slave)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+       struct list_head *vlan_list =
+               &tracker->slave_list[slave].res_list[RES_VLAN];
+       struct vlan_res *res, *tmp;
+       int i;
+
+       list_for_each_entry_safe(res, tmp, vlan_list, list) {
+               list_del(&res->list);
+               /* dereference the vlan the num times the slave referenced it */
+               for (i = 0; i < res->ref_count; i++)
+                       __mlx4_unregister_vlan(dev, res->port, res->vlan);
+               mlx4_release_resource(dev, slave, RES_VLAN, 1, res->port);
+               kfree(res);
+       }
+}
+
+static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
+                         u64 in_param, u64 *out_param, int in_port)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state;
+       int err;
+       u16 vlan;
+       int vlan_index;
+       int port;
+
+       port = !in_port ? get_param_l(out_param) : in_port;
+
+       if (!port || op != RES_OP_RESERVE_AND_MAP)
+               return -EINVAL;
+
+       /* upstream kernels had NOP for reg/unreg vlan. Continue this. */
+       if (!in_port && port > 0 && port <= dev->caps.num_ports) {
+               slave_state[slave].old_vlan_api = true;
+               return 0;
+       }
+
+       vlan = (u16) in_param;
+
+       err = __mlx4_register_vlan(dev, port, vlan, &vlan_index);
+       if (!err) {
+               set_param_l(out_param, (u32) vlan_index);
+               err = vlan_add_to_slave(dev, slave, vlan, port, vlan_index);
+               if (err)
+                       __mlx4_unregister_vlan(dev, port, vlan);
+       }
+       return err;
+}
+
 static int counter_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                             u64 in_param, u64 *out_param)
 {
@@ -1484,15 +1886,23 @@ static int counter_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        if (op != RES_OP_RESERVE)
                return -EINVAL;
 
-       err = __mlx4_counter_alloc(dev, &index);
+       err = mlx4_grant_resource(dev, slave, RES_COUNTER, 1, 0);
        if (err)
                return err;
 
+       err = __mlx4_counter_alloc(dev, &index);
+       if (err) {
+               mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0);
+               return err;
+       }
+
        err = add_res_range(dev, slave, index, 1, RES_COUNTER, 0);
-       if (err)
+       if (err) {
                __mlx4_counter_free(dev, index);
-       else
+               mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0);
+       } else {
                set_param_l(out_param, index);
+       }
 
        return err;
 }
@@ -1528,7 +1938,7 @@ int mlx4_ALLOC_RES_wrapper(struct mlx4_dev *dev, int slave,
        int err;
        int alop = vhcr->op_modifier;
 
-       switch (vhcr->in_modifier) {
+       switch (vhcr->in_modifier & 0xFF) {
        case RES_QP:
                err = qp_alloc_res(dev, slave, vhcr->op_modifier, alop,
                                   vhcr->in_param, &vhcr->out_param);
@@ -1556,12 +1966,14 @@ int mlx4_ALLOC_RES_wrapper(struct mlx4_dev *dev, int slave,
 
        case RES_MAC:
                err = mac_alloc_res(dev, slave, vhcr->op_modifier, alop,
-                                   vhcr->in_param, &vhcr->out_param);
+                                   vhcr->in_param, &vhcr->out_param,
+                                   (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_VLAN:
                err = vlan_alloc_res(dev, slave, vhcr->op_modifier, alop,
-                                   vhcr->in_param, &vhcr->out_param);
+                                    vhcr->in_param, &vhcr->out_param,
+                                    (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_COUNTER:
@@ -1597,6 +2009,7 @@ static int qp_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                err = rem_res_range(dev, slave, base, count, RES_QP, 0);
                if (err)
                        break;
+               mlx4_release_resource(dev, slave, RES_QP, count, 0);
                __mlx4_qp_release_range(dev, base, count);
                break;
        case RES_OP_MAP_ICM:
@@ -1634,8 +2047,10 @@ static int mtt_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
        base = get_param_l(&in_param);
        order = get_param_h(&in_param);
        err = rem_res_range(dev, slave, base, 1, RES_MTT, order);
-       if (!err)
+       if (!err) {
+               mlx4_release_resource(dev, slave, RES_MTT, 1 << order, 0);
                __mlx4_free_mtt_range(dev, base, order);
+       }
        return err;
 }
 
@@ -1660,6 +2075,7 @@ static int mpt_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                err = rem_res_range(dev, slave, id, 1, RES_MPT, 0);
                if (err)
                        break;
+               mlx4_release_resource(dev, slave, RES_MPT, 1, 0);
                __mlx4_mpt_release(dev, index);
                break;
        case RES_OP_MAP_ICM:
@@ -1694,6 +2110,7 @@ static int cq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                if (err)
                        break;
 
+               mlx4_release_resource(dev, slave, RES_CQ, 1, 0);
                __mlx4_cq_free_icm(dev, cqn);
                break;
 
@@ -1718,6 +2135,7 @@ static int srq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                if (err)
                        break;
 
+               mlx4_release_resource(dev, slave, RES_SRQ, 1, 0);
                __mlx4_srq_free_icm(dev, srqn);
                break;
 
@@ -1730,14 +2148,14 @@ static int srq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 }
 
 static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                           u64 in_param, u64 *out_param)
+                           u64 in_param, u64 *out_param, int in_port)
 {
        int port;
        int err = 0;
 
        switch (op) {
        case RES_OP_RESERVE_AND_MAP:
-               port = get_param_l(out_param);
+               port = !in_port ? get_param_l(out_param) : in_port;
                mac_del_from_slave(dev, slave, in_param, port);
                __mlx4_unregister_mac(dev, port, in_param);
                break;
@@ -1751,9 +2169,27 @@ static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 }
 
 static int vlan_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
-                           u64 in_param, u64 *out_param)
+                           u64 in_param, u64 *out_param, int port)
 {
-       return 0;
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state;
+       int err = 0;
+
+       switch (op) {
+       case RES_OP_RESERVE_AND_MAP:
+               if (slave_state[slave].old_vlan_api)
+                       return 0;
+               if (!port)
+                       return -EINVAL;
+               vlan_del_from_slave(dev, slave, in_param, port);
+               __mlx4_unregister_vlan(dev, port, in_param);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+
+       return err;
 }
 
 static int counter_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
@@ -1771,6 +2207,7 @@ static int counter_free_res(struct mlx4_dev *dev, int slave, int op, int cmd,
                return err;
 
        __mlx4_counter_free(dev, index);
+       mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0);
 
        return err;
 }
@@ -1803,7 +2240,7 @@ int mlx4_FREE_RES_wrapper(struct mlx4_dev *dev, int slave,
        int err = -EINVAL;
        int alop = vhcr->op_modifier;
 
-       switch (vhcr->in_modifier) {
+       switch (vhcr->in_modifier & 0xFF) {
        case RES_QP:
                err = qp_free_res(dev, slave, vhcr->op_modifier, alop,
                                  vhcr->in_param);
@@ -1831,12 +2268,14 @@ int mlx4_FREE_RES_wrapper(struct mlx4_dev *dev, int slave,
 
        case RES_MAC:
                err = mac_free_res(dev, slave, vhcr->op_modifier, alop,
-                                  vhcr->in_param, &vhcr->out_param);
+                                  vhcr->in_param, &vhcr->out_param,
+                                  (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_VLAN:
                err = vlan_free_res(dev, slave, vhcr->op_modifier, alop,
-                                  vhcr->in_param, &vhcr->out_param);
+                                   vhcr->in_param, &vhcr->out_param,
+                                   (vhcr->in_modifier >> 8) & 0xFF);
                break;
 
        case RES_COUNTER:
@@ -2136,6 +2575,12 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave,
                return err;
        qp->local_qpn = local_qpn;
        qp->sched_queue = 0;
+       qp->param3 = 0;
+       qp->vlan_control = 0;
+       qp->fvl_rx = 0;
+       qp->pri_path_fl = 0;
+       qp->vlan_index = 0;
+       qp->feup = 0;
        qp->qpc_flags = be32_to_cpu(qpc->flags);
 
        err = get_res(dev, slave, mtt_base, RES_MTT, &mtt);
@@ -2862,6 +3307,12 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
        int qpn = vhcr->in_modifier & 0x7fffff;
        struct res_qp *qp;
        u8 orig_sched_queue;
+       __be32  orig_param3 = qpc->param3;
+       u8 orig_vlan_control = qpc->pri_path.vlan_control;
+       u8 orig_fvl_rx = qpc->pri_path.fvl_rx;
+       u8 orig_pri_path_fl = qpc->pri_path.fl;
+       u8 orig_vlan_index = qpc->pri_path.vlan_index;
+       u8 orig_feup = qpc->pri_path.feup;
 
        err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave);
        if (err)
@@ -2889,9 +3340,15 @@ out:
         * essentially the QOS value provided by the VF. This will be useful
         * if we allow dynamic changes from VST back to VGT
         */
-       if (!err)
+       if (!err) {
                qp->sched_queue = orig_sched_queue;
-
+               qp->param3      = orig_param3;
+               qp->vlan_control = orig_vlan_control;
+               qp->fvl_rx      =  orig_fvl_rx;
+               qp->pri_path_fl = orig_pri_path_fl;
+               qp->vlan_index  = orig_vlan_index;
+               qp->feup        = orig_feup;
+       }
        put_res(dev, slave, qpn, RES_QP);
        return err;
 }
@@ -3498,6 +3955,11 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave)
                                                 &tracker->res_tree[RES_QP]);
                                        list_del(&qp->com.list);
                                        spin_unlock_irq(mlx4_tlock(dev));
+                                       if (!valid_reserved(dev, slave, qpn)) {
+                                               __mlx4_qp_release_range(dev, qpn, 1);
+                                               mlx4_release_resource(dev, slave,
+                                                                     RES_QP, 1, 0);
+                                       }
                                        kfree(qp);
                                        state = 0;
                                        break;
@@ -3569,6 +4031,8 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave)
                                                 &tracker->res_tree[RES_SRQ]);
                                        list_del(&srq->com.list);
                                        spin_unlock_irq(mlx4_tlock(dev));
+                                       mlx4_release_resource(dev, slave,
+                                                             RES_SRQ, 1, 0);
                                        kfree(srq);
                                        state = 0;
                                        break;
@@ -3635,6 +4099,8 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave)
                                                 &tracker->res_tree[RES_CQ]);
                                        list_del(&cq->com.list);
                                        spin_unlock_irq(mlx4_tlock(dev));
+                                       mlx4_release_resource(dev, slave,
+                                                             RES_CQ, 1, 0);
                                        kfree(cq);
                                        state = 0;
                                        break;
@@ -3698,6 +4164,8 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave)
                                                 &tracker->res_tree[RES_MPT]);
                                        list_del(&mpt->com.list);
                                        spin_unlock_irq(mlx4_tlock(dev));
+                                       mlx4_release_resource(dev, slave,
+                                                             RES_MPT, 1, 0);
                                        kfree(mpt);
                                        state = 0;
                                        break;
@@ -3767,6 +4235,8 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave)
                                                 &tracker->res_tree[RES_MTT]);
                                        list_del(&mtt->com.list);
                                        spin_unlock_irq(mlx4_tlock(dev));
+                                       mlx4_release_resource(dev, slave, RES_MTT,
+                                                             1 << mtt->order, 0);
                                        kfree(mtt);
                                        state = 0;
                                        break;
@@ -3925,6 +4395,7 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave)
                        list_del(&counter->com.list);
                        kfree(counter);
                        __mlx4_counter_free(dev, index);
+                       mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0);
                }
        }
        spin_unlock_irq(mlx4_tlock(dev));
@@ -3964,7 +4435,7 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave)
        struct mlx4_priv *priv = mlx4_priv(dev);
 
        mutex_lock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex);
-       /*VLAN*/
+       rem_slave_vlans(dev, slave);
        rem_slave_macs(dev, slave);
        rem_slave_fs_rule(dev, slave);
        rem_slave_qps(dev, slave);
@@ -3991,13 +4462,20 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
                &tracker->slave_list[work->slave].res_list[RES_QP];
        struct res_qp *qp;
        struct res_qp *tmp;
-       u64 qp_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) |
+       u64 qp_path_mask_vlan_ctrl =
+                      ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) |
                       (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P) |
                       (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED) |
                       (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED) |
                       (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P) |
-                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED) |
-                      (1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED));
+
+       u64 qp_path_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_FVL) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_CV) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_HIDE_CQE_VLAN) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_FEUP) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_FVL_RX) |
                       (1ULL << MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE));
 
        int err;
@@ -4029,9 +4507,7 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
                        MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED;
 
        upd_context = mailbox->buf;
-       upd_context->primary_addr_path_mask = cpu_to_be64(qp_mask);
-       upd_context->qp_context.pri_path.vlan_control = vlan_control;
-       upd_context->qp_context.pri_path.vlan_index = work->vlan_ix;
+       upd_context->qp_mask = cpu_to_be64(MLX4_UPD_QP_MASK_VSD);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(qp, tmp, qp_list, com.list) {
@@ -4049,10 +4525,35 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
                                spin_lock_irq(mlx4_tlock(dev));
                                continue;
                        }
-                       upd_context->qp_context.pri_path.sched_queue =
-                               qp->sched_queue & 0xC7;
-                       upd_context->qp_context.pri_path.sched_queue |=
-                               ((work->qos & 0x7) << 3);
+                       if (MLX4_QP_ST_RC == ((qp->qpc_flags >> 16) & 0xff))
+                               upd_context->primary_addr_path_mask = cpu_to_be64(qp_path_mask);
+                       else
+                               upd_context->primary_addr_path_mask =
+                                       cpu_to_be64(qp_path_mask | qp_path_mask_vlan_ctrl);
+                       if (work->vlan_id == MLX4_VGT) {
+                               upd_context->qp_context.param3 = qp->param3;
+                               upd_context->qp_context.pri_path.vlan_control = qp->vlan_control;
+                               upd_context->qp_context.pri_path.fvl_rx = qp->fvl_rx;
+                               upd_context->qp_context.pri_path.vlan_index = qp->vlan_index;
+                               upd_context->qp_context.pri_path.fl = qp->pri_path_fl;
+                               upd_context->qp_context.pri_path.feup = qp->feup;
+                               upd_context->qp_context.pri_path.sched_queue =
+                                       qp->sched_queue;
+                       } else {
+                               upd_context->qp_context.param3 = qp->param3 & ~cpu_to_be32(MLX4_STRIP_VLAN);
+                               upd_context->qp_context.pri_path.vlan_control = vlan_control;
+                               upd_context->qp_context.pri_path.vlan_index = work->vlan_ix;
+                               upd_context->qp_context.pri_path.fvl_rx =
+                                       qp->fvl_rx | MLX4_FVL_RX_FORCE_ETH_VLAN;
+                               upd_context->qp_context.pri_path.fl =
+                                       qp->pri_path_fl | MLX4_FL_CV | MLX4_FL_ETH_HIDE_CQE_VLAN;
+                               upd_context->qp_context.pri_path.feup =
+                                       qp->feup | MLX4_FEUP_FORCE_ETH_UP | MLX4_FVL_FORCE_ETH_VLAN;
+                               upd_context->qp_context.pri_path.sched_queue =
+                                       qp->sched_queue & 0xC7;
+                               upd_context->qp_context.pri_path.sched_queue |=
+                                       ((work->qos & 0x7) << 3);
+                       }
 
                        err = mlx4_cmd(dev, mailbox->dma,
                                       qp->local_qpn & 0xffffff,
@@ -4081,7 +4582,7 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
        if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN && !errors &&
            NO_INDX != work->orig_vlan_ix)
                __mlx4_unregister_vlan(&work->priv->dev, work->port,
-                                      work->orig_vlan_ix);
+                                      work->orig_vlan_id);
 out:
        kfree(work);
        return;
index 79fd269..8fdf237 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 
 #include <linux/mlx4/cmd.h>
+#include <linux/mlx4/srq.h>
 #include <linux/export.h>
 #include <linux/gfp.h>
 
@@ -188,8 +189,6 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd,
        }
 
        srq_context = mailbox->buf;
-       memset(srq_context, 0, sizeof *srq_context);
-
        srq_context->state_logsize_srqn = cpu_to_be32((ilog2(srq->max) << 24) |
                                                      srq->srqn);
        srq_context->logstride          = srq->wqe_shift - 4;
index 5472cbd..6ca3073 100644 (file)
@@ -180,28 +180,32 @@ static int verify_block_sig(struct mlx5_cmd_prot_block *block)
        return 0;
 }
 
-static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token)
+static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token,
+                          int csum)
 {
        block->token = token;
-       block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 2);
-       block->sig = ~xor8_buf(block, sizeof(*block) - 1);
+       if (csum) {
+               block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) -
+                                           sizeof(block->data) - 2);
+               block->sig = ~xor8_buf(block, sizeof(*block) - 1);
+       }
 }
 
-static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token)
+static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token, int csum)
 {
        struct mlx5_cmd_mailbox *next = msg->next;
 
        while (next) {
-               calc_block_sig(next->buf, token);
+               calc_block_sig(next->buf, token, csum);
                next = next->next;
        }
 }
 
-static void set_signature(struct mlx5_cmd_work_ent *ent)
+static void set_signature(struct mlx5_cmd_work_ent *ent, int csum)
 {
        ent->lay->sig = ~xor8_buf(ent->lay, sizeof(*ent->lay));
-       calc_chain_sig(ent->in, ent->token);
-       calc_chain_sig(ent->out, ent->token);
+       calc_chain_sig(ent->in, ent->token, csum);
+       calc_chain_sig(ent->out, ent->token, csum);
 }
 
 static void poll_timeout(struct mlx5_cmd_work_ent *ent)
@@ -539,8 +543,7 @@ static void cmd_work_handler(struct work_struct *work)
        lay->type = MLX5_PCI_CMD_XPORT;
        lay->token = ent->token;
        lay->status_own = CMD_OWNER_HW;
-       if (!cmd->checksum_disabled)
-               set_signature(ent);
+       set_signature(ent, !cmd->checksum_disabled);
        dump_command(dev, ent, 1);
        ktime_get_ts(&ent->ts1);
 
@@ -773,8 +776,6 @@ static int mlx5_copy_from_msg(void *to, struct mlx5_cmd_msg *from, int size)
 
                copy = min_t(int, size, MLX5_CMD_DATA_BLOCK_SIZE);
                block = next->buf;
-               if (xor8_buf(block, sizeof(*block)) != 0xff)
-                       return -EINVAL;
 
                memcpy(to, block->data, copy);
                to += copy;
@@ -1361,6 +1362,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev)
                goto err_map;
        }
 
+       cmd->checksum_disabled = 1;
        cmd->max_reg_cmds = (1 << cmd->log_sz) - 1;
        cmd->bitmask = (1 << cmd->max_reg_cmds) - 1;
 
@@ -1510,7 +1512,7 @@ int mlx5_cmd_status_to_err(struct mlx5_outbox_hdr *hdr)
        case MLX5_CMD_STAT_BAD_SYS_STATE_ERR:           return -EIO;
        case MLX5_CMD_STAT_BAD_RES_ERR:                 return -EINVAL;
        case MLX5_CMD_STAT_RES_BUSY:                    return -EBUSY;
-       case MLX5_CMD_STAT_LIM_ERR:                     return -EINVAL;
+       case MLX5_CMD_STAT_LIM_ERR:                     return -ENOMEM;
        case MLX5_CMD_STAT_BAD_RES_STATE_ERR:           return -EINVAL;
        case MLX5_CMD_STAT_IX_ERR:                      return -EINVAL;
        case MLX5_CMD_STAT_NO_RES_ERR:                  return -EAGAIN;
index 443cc4d..2231d93 100644 (file)
@@ -366,9 +366,11 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
                goto err_in;
        }
 
+       snprintf(eq->name, MLX5_MAX_EQ_NAME, "%s@pci:%s",
+                name, pci_name(dev->pdev));
        eq->eqn = out.eq_number;
        err = request_irq(table->msix_arr[vecidx].vector, mlx5_msix_handler, 0,
-                         name, eq);
+                         eq->name, eq);
        if (err)
                goto err_eq;
 
index b47739b..bc0f5fb 100644 (file)
@@ -165,9 +165,7 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
        struct mlx5_cmd_set_hca_cap_mbox_in *set_ctx = NULL;
        struct mlx5_cmd_query_hca_cap_mbox_in query_ctx;
        struct mlx5_cmd_set_hca_cap_mbox_out set_out;
-       struct mlx5_profile *prof = dev->profile;
        u64 flags;
-       int csum = 1;
        int err;
 
        memset(&query_ctx, 0, sizeof(query_ctx));
@@ -197,20 +195,14 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
        memcpy(&set_ctx->hca_cap, &query_out->hca_cap,
               sizeof(set_ctx->hca_cap));
 
-       if (prof->mask & MLX5_PROF_MASK_CMDIF_CSUM) {
-               csum = !!prof->cmdif_csum;
-               flags = be64_to_cpu(set_ctx->hca_cap.flags);
-               if (csum)
-                       flags |= MLX5_DEV_CAP_FLAG_CMDIF_CSUM;
-               else
-                       flags &= ~MLX5_DEV_CAP_FLAG_CMDIF_CSUM;
-
-               set_ctx->hca_cap.flags = cpu_to_be64(flags);
-       }
-
        if (dev->profile->mask & MLX5_PROF_MASK_QP_SIZE)
                set_ctx->hca_cap.log_max_qp = dev->profile->log_max_qp;
 
+       flags = be64_to_cpu(query_out->hca_cap.flags);
+       /* disable checksum */
+       flags &= ~MLX5_DEV_CAP_FLAG_CMDIF_CSUM;
+
+       set_ctx->hca_cap.flags = cpu_to_be64(flags);
        memset(&set_out, 0, sizeof(set_out));
        set_ctx->hca_cap.log_uar_page_sz = cpu_to_be16(PAGE_SHIFT - 12);
        set_ctx->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_SET_HCA_CAP);
@@ -225,9 +217,6 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
        if (err)
                goto query_ex;
 
-       if (!csum)
-               dev->cmd.checksum_disabled = 1;
-
 query_ex:
        kfree(query_out);
        kfree(set_ctx);
index 3a2408d..7b12acf 100644 (file)
@@ -90,6 +90,10 @@ struct mlx5_manage_pages_outbox {
        __be64                  pas[0];
 };
 
+enum {
+       MAX_RECLAIM_TIME_MSECS  = 5000,
+};
+
 static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u16 func_id)
 {
        struct rb_root *root = &dev->priv.page_root;
@@ -279,6 +283,9 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        int err;
        int i;
 
+       if (nclaimed)
+               *nclaimed = 0;
+
        memset(&in, 0, sizeof(in));
        outlen = sizeof(*out) + npages * sizeof(out->pas[0]);
        out = mlx5_vzalloc(outlen);
@@ -388,20 +395,25 @@ static int optimal_reclaimed_pages(void)
 
 int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
 {
-       unsigned long end = jiffies + msecs_to_jiffies(5000);
+       unsigned long end = jiffies + msecs_to_jiffies(MAX_RECLAIM_TIME_MSECS);
        struct fw_page *fwp;
        struct rb_node *p;
+       int nclaimed = 0;
        int err;
 
        do {
                p = rb_first(&dev->priv.page_root);
                if (p) {
                        fwp = rb_entry(p, struct fw_page, rb_node);
-                       err = reclaim_pages(dev, fwp->func_id, optimal_reclaimed_pages(), NULL);
+                       err = reclaim_pages(dev, fwp->func_id,
+                                           optimal_reclaimed_pages(),
+                                           &nclaimed);
                        if (err) {
                                mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", err);
                                return err;
                        }
+                       if (nclaimed)
+                               end = jiffies + msecs_to_jiffies(MAX_RECLAIM_TIME_MSECS);
                }
                if (time_after(jiffies, end)) {
                        mlx5_core_warn(dev, "FW did not return all pages. giving up...\n");
index 075f4e2..c83d16d 100644 (file)
@@ -1248,7 +1248,7 @@ static void ks_set_mac(struct ks_net *ks, u8 *data)
        w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
        ks_wrreg16(ks, KS_MARL, w);
 
-       memcpy(ks->mac_addr, data, 6);
+       memcpy(ks->mac_addr, data, ETH_ALEN);
 
        if (ks->enabled)
                ks_start_rx(ks);
@@ -1651,7 +1651,7 @@ static int ks8851_probe(struct platform_device *pdev)
        }
        netdev_info(netdev, "Mac address is: %pM\n", ks->mac_addr);
 
-       memcpy(netdev->dev_addr, ks->mac_addr, 6);
+       memcpy(netdev->dev_addr, ks->mac_addr, ETH_ALEN);
 
        ks_set_mac(ks, netdev->dev_addr);
 
index 8ebc352..ddd252a 100644 (file)
@@ -7150,8 +7150,6 @@ static void pcidev_exit(struct pci_dev *pdev)
        struct platform_info *info = pci_get_drvdata(pdev);
        struct dev_info *hw_priv = &info->dev_info;
 
-       pci_set_drvdata(pdev, NULL);
-
        release_mem_region(pci_resource_start(pdev, 0),
                pci_resource_len(pdev, 0));
        for (i = 0; i < hw_priv->hw.dev_count; i++) {
@@ -7227,7 +7225,7 @@ static int pcidev_suspend(struct pci_dev *pdev, pm_message_t state)
 
 static char pcidev_name[] = "ksz884xp";
 
-static struct pci_device_id pcidev_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(pcidev_table) = {
        { PCI_VENDOR_ID_MICREL_KS, 0x8841,
                PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
        { PCI_VENDOR_ID_MICREL_KS, 0x8842,
index 83c2091..cbd0133 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/of_irq.h>
 #include <linux/crc32.h>
 #include <linux/crc32c.h>
-#include <linux/dma-mapping.h>
 
 #include "moxart_ether.h"
 
@@ -448,7 +447,8 @@ static int moxart_mac_probe(struct platform_device *pdev)
        irq = irq_of_parse_and_map(node, 0);
        if (irq <= 0) {
                netdev_err(ndev, "irq_of_parse_and_map failed\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto irq_map_fail;
        }
 
        priv = netdev_priv(ndev);
@@ -472,24 +472,32 @@ static int moxart_mac_probe(struct platform_device *pdev)
        priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE *
                                                TX_DESC_NUM, &priv->tx_base,
                                                GFP_DMA | GFP_KERNEL);
-       if (priv->tx_desc_base == NULL)
+       if (priv->tx_desc_base == NULL) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        priv->rx_desc_base = dma_alloc_coherent(NULL, RX_REG_DESC_SIZE *
                                                RX_DESC_NUM, &priv->rx_base,
                                                GFP_DMA | GFP_KERNEL);
-       if (priv->rx_desc_base == NULL)
+       if (priv->rx_desc_base == NULL) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
                                    GFP_ATOMIC);
-       if (!priv->tx_buf_base)
+       if (!priv->tx_buf_base) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
                                    GFP_ATOMIC);
-       if (!priv->rx_buf_base)
+       if (!priv->rx_buf_base) {
+               ret = -ENOMEM;
                goto init_fail;
+       }
 
        platform_set_drvdata(pdev, ndev);
 
@@ -522,7 +530,8 @@ static int moxart_mac_probe(struct platform_device *pdev)
 init_fail:
        netdev_err(ndev, "init failed\n");
        moxart_mac_free_memory(ndev);
-
+irq_map_fail:
+       free_netdev(ndev);
        return ret;
 }
 
@@ -543,7 +552,7 @@ static const struct of_device_id moxart_mac_match[] = {
        { }
 };
 
-struct __initdata platform_driver moxart_mac_driver = {
+static struct platform_driver moxart_mac_driver = {
        .probe  = moxart_mac_probe,
        .remove = moxart_remove,
        .driver = {
index 149355b..68026f7 100644 (file)
@@ -934,7 +934,7 @@ static inline void myri10ge_ss_init_lock(struct myri10ge_slice_state *ss)
 
 static inline bool myri10ge_ss_lock_napi(struct myri10ge_slice_state *ss)
 {
-       int rc = true;
+       bool rc = true;
        spin_lock(&ss->lock);
        if ((ss->state & SLICE_LOCKED)) {
                WARN_ON((ss->state & SLICE_STATE_NAPI));
@@ -957,7 +957,7 @@ static inline void myri10ge_ss_unlock_napi(struct myri10ge_slice_state *ss)
 
 static inline bool myri10ge_ss_lock_poll(struct myri10ge_slice_state *ss)
 {
-       int rc = true;
+       bool rc = true;
        spin_lock_bh(&ss->lock);
        if ((ss->state & SLICE_LOCKED)) {
                ss->state |= SLICE_STATE_POLL_YIELD;
@@ -3164,7 +3164,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev)
 
        /* Walk the multicast list, and add each address */
        netdev_for_each_mc_addr(ha, dev) {
-               memcpy(data, &ha->addr, 6);
+               memcpy(data, &ha->addr, ETH_ALEN);
                cmd.data0 = ntohl(data[0]);
                cmd.data1 = ntohl(data[1]);
                err = myri10ge_send_cmd(mgp, MXGEFW_JOIN_MULTICAST_GROUP,
@@ -3207,7 +3207,7 @@ static int myri10ge_set_mac_address(struct net_device *dev, void *addr)
        }
 
        /* change the dev structure */
-       memcpy(dev->dev_addr, sa->sa_data, 6);
+       memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
        return 0;
 }
 
@@ -4208,7 +4208,6 @@ static void myri10ge_remove(struct pci_dev *pdev)
        set_fw_name(mgp, NULL, false);
        free_netdev(netdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #define PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E     0x0008
index 7a5e295..64ec2a4 100644 (file)
@@ -970,7 +970,6 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
 
  err_ioremap:
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
 
  err_pci_request_regions:
        free_netdev(dev);
@@ -3220,7 +3219,6 @@ static void natsemi_remove1(struct pci_dev *pdev)
        pci_release_regions (pdev);
        iounmap(ioaddr);
        free_netdev (dev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
index 51b0094..9eeddbd 100644 (file)
@@ -8185,7 +8185,6 @@ mem_alloc_failed:
        free_shared_mem(sp);
        pci_disable_device(pdev);
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 
        return ret;
@@ -8221,7 +8220,6 @@ static void s2io_rem_nic(struct pci_dev *pdev)
        iounmap(sp->bar0);
        iounmap(sp->bar1);
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
        pci_disable_device(pdev);
 }
index 5a20eaf..8614eeb 100644 (file)
@@ -4739,7 +4739,6 @@ _exit6:
 _exit5:
        vxge_device_unregister(hldev);
 _exit4:
-       pci_set_drvdata(pdev, NULL);
        vxge_hw_device_terminate(hldev);
        pci_disable_sriov(pdev);
 _exit3:
@@ -4782,7 +4781,6 @@ static void vxge_remove(struct pci_dev *pdev)
                vxge_free_mac_add_list(&vdev->vpaths[i]);
 
        vxge_device_unregister(hldev);
-       pci_set_drvdata(pdev, NULL);
        /* Do not call pci_disable_sriov here, as it will break child devices */
        vxge_hw_device_terminate(hldev);
        iounmap(vdev->bar0);
index 622aa75..1b326cb 100644 (file)
@@ -1545,7 +1545,7 @@ static int octeon_mgmt_probe(struct platform_device *pdev)
 
        mac = of_get_mac_address(pdev->dev.of_node);
 
-       if (mac && is_valid_ether_addr(mac))
+       if (mac)
                memcpy(netdev->dev_addr, mac, ETH_ALEN);
        else
                eth_hw_addr_random(netdev);
index 6797b10..2a90030 100644 (file)
@@ -653,38 +653,38 @@ struct pch_gbe_adapter {
 extern const char pch_driver_version[];
 
 /* pch_gbe_main.c */
-extern int pch_gbe_up(struct pch_gbe_adapter *adapter);
-extern void pch_gbe_down(struct pch_gbe_adapter *adapter);
-extern void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter);
-extern void pch_gbe_reset(struct pch_gbe_adapter *adapter);
-extern int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter,
-                                      struct pch_gbe_tx_ring *txdr);
-extern int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter,
-                                      struct pch_gbe_rx_ring *rxdr);
-extern void pch_gbe_free_tx_resources(struct pch_gbe_adapter *adapter,
-                                      struct pch_gbe_tx_ring *tx_ring);
-extern void pch_gbe_free_rx_resources(struct pch_gbe_adapter *adapter,
-                                      struct pch_gbe_rx_ring *rx_ring);
-extern void pch_gbe_update_stats(struct pch_gbe_adapter *adapter);
-extern u32 pch_ch_control_read(struct pci_dev *pdev);
-extern void pch_ch_control_write(struct pci_dev *pdev, u32 val);
-extern u32 pch_ch_event_read(struct pci_dev *pdev);
-extern void pch_ch_event_write(struct pci_dev *pdev, u32 val);
-extern u32 pch_src_uuid_lo_read(struct pci_dev *pdev);
-extern u32 pch_src_uuid_hi_read(struct pci_dev *pdev);
-extern u64 pch_rx_snap_read(struct pci_dev *pdev);
-extern u64 pch_tx_snap_read(struct pci_dev *pdev);
-extern int pch_set_station_address(u8 *addr, struct pci_dev *pdev);
+int pch_gbe_up(struct pch_gbe_adapter *adapter);
+void pch_gbe_down(struct pch_gbe_adapter *adapter);
+void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter);
+void pch_gbe_reset(struct pch_gbe_adapter *adapter);
+int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter,
+                              struct pch_gbe_tx_ring *txdr);
+int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter,
+                              struct pch_gbe_rx_ring *rxdr);
+void pch_gbe_free_tx_resources(struct pch_gbe_adapter *adapter,
+                              struct pch_gbe_tx_ring *tx_ring);
+void pch_gbe_free_rx_resources(struct pch_gbe_adapter *adapter,
+                              struct pch_gbe_rx_ring *rx_ring);
+void pch_gbe_update_stats(struct pch_gbe_adapter *adapter);
+u32 pch_ch_control_read(struct pci_dev *pdev);
+void pch_ch_control_write(struct pci_dev *pdev, u32 val);
+u32 pch_ch_event_read(struct pci_dev *pdev);
+void pch_ch_event_write(struct pci_dev *pdev, u32 val);
+u32 pch_src_uuid_lo_read(struct pci_dev *pdev);
+u32 pch_src_uuid_hi_read(struct pci_dev *pdev);
+u64 pch_rx_snap_read(struct pci_dev *pdev);
+u64 pch_tx_snap_read(struct pci_dev *pdev);
+int pch_set_station_address(u8 *addr, struct pci_dev *pdev);
 
 /* pch_gbe_param.c */
-extern void pch_gbe_check_options(struct pch_gbe_adapter *adapter);
+void pch_gbe_check_options(struct pch_gbe_adapter *adapter);
 
 /* pch_gbe_ethtool.c */
-extern void pch_gbe_set_ethtool_ops(struct net_device *netdev);
+void pch_gbe_set_ethtool_ops(struct net_device *netdev);
 
 /* pch_gbe_mac.c */
-extern s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw);
-extern s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw);
-extern u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw,
-                                 u32 addr, u32 dir, u32 reg, u16 data);
+s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw);
+s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw);
+u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
+                         u16 data);
 #endif /* _PCH_GBE_H_ */
index cac33e5..b6bdeb3 100644 (file)
@@ -1910,7 +1910,6 @@ static void hamachi_remove_one(struct pci_dev *pdev)
                iounmap(hmp->base);
                free_netdev(dev);
                pci_release_regions(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index d28593b..07a890e 100644 (file)
@@ -513,7 +513,6 @@ err_out_unmap_rx:
 err_out_unmap_tx:
         pci_free_consistent(pdev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma);
 err_out_cleardev:
-       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
@@ -1392,7 +1391,6 @@ static void yellowfin_remove_one(struct pci_dev *pdev)
        pci_release_regions (pdev);
 
        free_netdev (dev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 
index 5b65356..dbaa49e 100644 (file)
@@ -1870,7 +1870,6 @@ static void pasemi_mac_remove(struct pci_dev *pdev)
        pasemi_dma_free_chan(&mac->tx->chan);
        pasemi_dma_free_chan(&mac->rx->chan);
 
-       pci_set_drvdata(pdev, NULL);
        free_netdev(netdev);
 }
 
index 32675e1..9adcdbb 100644 (file)
@@ -53,8 +53,8 @@
 
 #define _NETXEN_NIC_LINUX_MAJOR 4
 #define _NETXEN_NIC_LINUX_MINOR 0
-#define _NETXEN_NIC_LINUX_SUBVERSION 81
-#define NETXEN_NIC_LINUX_VERSIONID  "4.0.81"
+#define _NETXEN_NIC_LINUX_SUBVERSION 82
+#define NETXEN_NIC_LINUX_VERSIONID  "4.0.82"
 
 #define NETXEN_VERSION_CODE(a, b, c)   (((a) << 24) + ((b) << 16) + (c))
 #define _major(v)      (((v) >> 24) & 0xff)
@@ -1883,9 +1883,8 @@ static inline u32 netxen_tx_avail(struct nx_host_tx_ring *tx_ring)
 
 int netxen_get_flash_mac_addr(struct netxen_adapter *adapter, u64 *mac);
 int netxen_p3_get_mac_addr(struct netxen_adapter *adapter, u64 *mac);
-extern void netxen_change_ringparam(struct netxen_adapter *adapter);
-extern int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr,
-                               int *valp);
+void netxen_change_ringparam(struct netxen_adapter *adapter);
+int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp);
 
 extern const struct ethtool_ops netxen_nic_ethtool_ops;
 
index 32c7906..0c64c82 100644 (file)
@@ -958,6 +958,7 @@ enum {
 #define NETXEN_PEG_HALT_STATUS2        (NETXEN_CAM_RAM(0xac))
 #define NX_CRB_DEV_REF_COUNT           (NETXEN_CAM_RAM(0x138))
 #define NX_CRB_DEV_STATE               (NETXEN_CAM_RAM(0x140))
+#define NETXEN_ULA_KEY                 (NETXEN_CAM_RAM(0x178))
 
 /* MiniDIMM related macros */
 #define NETXEN_DIMM_CAPABILITY         (NETXEN_CAM_RAM(0x258))
index 8375cbd..67efe75 100644 (file)
@@ -648,7 +648,7 @@ nx_p3_sre_macaddr_change(struct netxen_adapter *adapter, u8 *addr, unsigned op)
 
        mac_req = (nx_mac_req_t *)&req.words[0];
        mac_req->op = op;
-       memcpy(mac_req->mac_addr, addr, 6);
+       memcpy(mac_req->mac_addr, addr, ETH_ALEN);
 
        return netxen_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
 }
index cbd75f9..3bec8cf 100644 (file)
@@ -1415,6 +1415,32 @@ netxen_setup_netdev(struct netxen_adapter *adapter,
        return 0;
 }
 
+#define NETXEN_ULA_ADAPTER_KEY         (0xdaddad01)
+#define NETXEN_NON_ULA_ADAPTER_KEY     (0xdaddad00)
+
+static void netxen_read_ula_info(struct netxen_adapter *adapter)
+{
+       u32 temp;
+
+       /* Print ULA info only once for an adapter */
+       if (adapter->portnum != 0)
+               return;
+
+       temp = NXRD32(adapter, NETXEN_ULA_KEY);
+       switch (temp) {
+       case NETXEN_ULA_ADAPTER_KEY:
+               dev_info(&adapter->pdev->dev, "ULA adapter");
+               break;
+       case NETXEN_NON_ULA_ADAPTER_KEY:
+               dev_info(&adapter->pdev->dev, "non ULA adapter");
+               break;
+       default:
+               break;
+       }
+
+       return;
+}
+
 #ifdef CONFIG_PCIEAER
 static void netxen_mask_aer_correctable(struct netxen_adapter *adapter)
 {
@@ -1561,6 +1587,8 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_out_disable_msi;
        }
 
+       netxen_read_ula_info(adapter);
+
        err = netxen_setup_netdev(adapter, netdev);
        if (err)
                goto err_out_disable_msi;
@@ -1602,7 +1630,6 @@ err_out_free_res:
        pci_release_regions(pdev);
 
 err_out_disable_pdev:
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
        return err;
 }
@@ -1661,7 +1688,6 @@ static void netxen_nic_remove(struct pci_dev *pdev)
 
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        free_netdev(netdev);
 }
index 91a8fcd..0758b94 100644 (file)
@@ -3916,7 +3916,6 @@ err_out_free_regions:
        pci_release_regions(pdev);
 err_out_disable_pdev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 err_out:
        return err;
 }
@@ -3939,7 +3938,6 @@ static void ql3xxx_remove(struct pci_dev *pdev)
 
        iounmap(qdev->mem_map_registers);
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(ndev);
 }
 
index 81bf836..631ea0a 100644 (file)
@@ -38,8 +38,8 @@
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 3
-#define _QLCNIC_LINUX_SUBVERSION 50
-#define QLCNIC_LINUX_VERSIONID  "5.3.50"
+#define _QLCNIC_LINUX_SUBVERSION 52
+#define QLCNIC_LINUX_VERSIONID  "5.3.52"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
 #define TX_STOP_THRESH         ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \
                                                        + MGMT_CMD_DESC_RESV)
 #define QLCNIC_MAX_TX_TIMEOUTS 2
-#define QLCNIC_MAX_TX_RINGS    8
-#define QLCNIC_MAX_SDS_RINGS   8
+
+/* Driver will use 1 Tx ring in INT-x/MSI/SRIOV mode. */
+#define QLCNIC_SINGLE_RING             1
+#define QLCNIC_DEF_SDS_RINGS           4
+#define QLCNIC_DEF_TX_RINGS            4
+#define QLCNIC_MAX_VNIC_TX_RINGS       4
+#define QLCNIC_MAX_VNIC_SDS_RINGS      4
+
+enum qlcnic_queue_type {
+       QLCNIC_TX_QUEUE = 1,
+       QLCNIC_RX_QUEUE,
+};
+
+/* Operational mode for driver */
+#define QLCNIC_VNIC_MODE       0xFF
+#define QLCNIC_DEFAULT_MODE    0x0
 
 /*
  * Following are the states of the Phantom. Phantom will set them and
@@ -533,6 +547,14 @@ struct qlcnic_host_sds_ring {
        char name[IFNAMSIZ + 12];
 } ____cacheline_internodealigned_in_smp;
 
+struct qlcnic_tx_queue_stats {
+       u64 xmit_on;
+       u64 xmit_off;
+       u64 xmit_called;
+       u64 xmit_finished;
+       u64 tx_bytes;
+};
+
 struct qlcnic_host_tx_ring {
        int irq;
        void __iomem *crb_intr_mask;
@@ -544,10 +566,7 @@ struct qlcnic_host_tx_ring {
        u32 sw_consumer;
        u32 num_desc;
 
-       u64 xmit_on;
-       u64 xmit_off;
-       u64 xmit_called;
-       u64 xmit_finished;
+       struct qlcnic_tx_queue_stats tx_stats;
 
        void __iomem *crb_cmd_producer;
        struct cmd_desc_type0 *desc_head;
@@ -940,8 +959,6 @@ struct qlcnic_ipaddr {
 #define QLCNIC_BEACON_EANBLE           0xC
 #define QLCNIC_BEACON_DISABLE          0xD
 
-#define QLCNIC_DEF_NUM_STS_DESC_RINGS  4
-#define QLCNIC_DEF_NUM_TX_RINGS                4
 #define QLCNIC_MSIX_TBL_SPACE          8192
 #define QLCNIC_PCI_REG_MSIX_TBL        0x44
 #define QLCNIC_MSIX_TBL_PGSIZE         4096
@@ -961,8 +978,7 @@ struct qlcnic_ipaddr {
 #define __QLCNIC_SRIOV_CAPABLE         11
 #define __QLCNIC_MBX_POLL_ENABLE       12
 #define __QLCNIC_DIAG_MODE             13
-#define __QLCNIC_DCB_STATE             14
-#define __QLCNIC_DCB_IN_AEN            15
+#define __QLCNIC_MAINTENANCE_MODE      16
 
 #define QLCNIC_INTERRUPT_TEST          1
 #define QLCNIC_LOOPBACK_TEST           2
@@ -1013,7 +1029,6 @@ struct qlcnic_adapter {
        unsigned long state;
        u32 flags;
 
-       int max_drv_tx_rings;
        u16 num_txd;
        u16 num_rxd;
        u16 num_jumbo_rxd;
@@ -1021,7 +1036,13 @@ struct qlcnic_adapter {
        u16 max_jumbo_rxd;
 
        u8 max_rds_rings;
-       u8 max_sds_rings;
+
+       u8 max_sds_rings; /* max sds rings supported by adapter */
+       u8 max_tx_rings;  /* max tx rings supported by adapter */
+
+       u8 drv_tx_rings;  /* max tx rings supported by driver */
+       u8 drv_sds_rings; /* max sds rings supported by driver */
+
        u8 rx_csum;
        u8 portnum;
 
@@ -1199,6 +1220,7 @@ struct qlcnic_npar_info {
        u8      promisc_mode;
        u8      offload_flags;
        u8      pci_func;
+       u8      mac[ETH_ALEN];
 };
 
 struct qlcnic_eswitch {
@@ -1543,12 +1565,13 @@ int qlcnic_loopback_test(struct net_device *, u8);
 
 /* Functions from qlcnic_main.c */
 int qlcnic_reset_context(struct qlcnic_adapter *);
-void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings);
-int qlcnic_diag_alloc_res(struct net_device *netdev, int test);
-netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
-int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, int);
-int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32);
-int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *, u32 txq);
+void qlcnic_diag_free_res(struct net_device *netdev, int);
+int qlcnic_diag_alloc_res(struct net_device *netdev, int);
+netdev_tx_t qlcnic_xmit_frame(struct sk_buff *, struct net_device *);
+void qlcnic_set_tx_ring_count(struct qlcnic_adapter *, u8);
+void qlcnic_set_sds_ring_count(struct qlcnic_adapter *, u8);
+int qlcnic_setup_rings(struct qlcnic_adapter *, u8, u8);
+int qlcnic_validate_rings(struct qlcnic_adapter *, __u32, int);
 void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
 void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *);
 int qlcnic_enable_msix(struct qlcnic_adapter *, u32);
@@ -1641,19 +1664,18 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring)
 static inline int qlcnic_set_real_num_queues(struct qlcnic_adapter *adapter,
                                             struct net_device *netdev)
 {
-       int err, tx_q;
-
-       tx_q = adapter->max_drv_tx_rings;
+       int err;
 
-       netdev->num_tx_queues = tx_q;
-       netdev->real_num_tx_queues = tx_q;
+       netdev->num_tx_queues = adapter->drv_tx_rings;
+       netdev->real_num_tx_queues = adapter->drv_tx_rings;
 
-       err = netif_set_real_num_tx_queues(netdev, tx_q);
+       err = netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings);
        if (err)
                dev_err(&adapter->pdev->dev, "failed to set %d Tx queues\n",
-                       tx_q);
+                       adapter->drv_tx_rings);
        else
-               dev_info(&adapter->pdev->dev, "set %d Tx queues\n", tx_q);
+               dev_info(&adapter->pdev->dev, "Set %d Tx queues\n",
+                        adapter->drv_tx_rings);
 
        return err;
 }
@@ -1695,7 +1717,7 @@ struct qlcnic_hardware_ops {
        int (*write_reg) (struct qlcnic_adapter *, ulong, u32);
        void (*get_ocm_win) (struct qlcnic_hardware_context *);
        int (*get_mac_address) (struct qlcnic_adapter *, u8 *, u8);
-       int (*setup_intr) (struct qlcnic_adapter *, u8, int);
+       int (*setup_intr) (struct qlcnic_adapter *);
        int (*alloc_mbx_args)(struct qlcnic_cmd_args *,
                              struct qlcnic_adapter *, u32);
        int (*mbx_cmd) (struct qlcnic_adapter *, struct qlcnic_cmd_args *);
@@ -1766,10 +1788,9 @@ static inline int qlcnic_get_mac_address(struct qlcnic_adapter *adapter,
        return adapter->ahw->hw_ops->get_mac_address(adapter, mac, function);
 }
 
-static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter,
-                                   u8 num_intr, int txq)
+static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter)
 {
-       return adapter->ahw->hw_ops->setup_intr(adapter, num_intr, txq);
+       return adapter->ahw->hw_ops->setup_intr(adapter);
 }
 
 static inline int qlcnic_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
@@ -2005,7 +2026,7 @@ static inline bool qlcnic_check_multi_tx(struct qlcnic_adapter *adapter)
 static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter)
 {
        test_and_clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
-       adapter->max_drv_tx_rings = 1;
+       adapter->drv_tx_rings = QLCNIC_SINGLE_RING;
 }
 
 /* When operating in a muti tx mode, driver needs to write 0x1
@@ -2115,98 +2136,4 @@ static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter)
 
        return status;
 }
-
-static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->get_hw_capability)
-               return dcb->ops->get_hw_capability(adapter);
-
-       return 0;
-}
-
-static inline void qlcnic_dcb_free(struct qlcnic_adapter *adapter)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->free)
-               dcb->ops->free(adapter);
-}
-
-static inline int qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->attach)
-               return dcb->ops->attach(adapter);
-
-       return 0;
-}
-
-static inline int
-qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, char *buf)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->query_hw_capability)
-               return dcb->ops->query_hw_capability(adapter, buf);
-
-       return 0;
-}
-
-static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->get_info)
-               dcb->ops->get_info(adapter);
-}
-
-static inline int
-qlcnic_dcb_query_cee_param(struct qlcnic_adapter *adapter, char *buf, u8 type)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->query_cee_param)
-               return dcb->ops->query_cee_param(adapter, buf, type);
-
-       return 0;
-}
-
-static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->get_cee_cfg)
-               return dcb->ops->get_cee_cfg(adapter);
-
-       return 0;
-}
-
-static inline void
-qlcnic_dcb_register_aen(struct qlcnic_adapter *adapter, u8 flag)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->register_aen)
-               dcb->ops->register_aen(adapter, flag);
-}
-
-static inline void qlcnic_dcb_handle_aen(struct qlcnic_adapter *adapter,
-                                        void *msg)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->handle_aen)
-               dcb->ops->handle_aen(adapter, msg);
-}
-
-static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_adapter *adapter)
-{
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (dcb && dcb->ops->init_dcbnl_ops)
-               dcb->ops->init_dcbnl_ops(adapter);
-}
 #endif                         /* __QLCNIC_H_ */
index 3ca00e0..09810dd 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/interrupt.h>
 #include <linux/aer.h>
 
-#define QLCNIC_MAX_TX_QUEUES           1
 #define RSS_HASHTYPE_IP_TCP            0x3
 #define QLC_83XX_FW_MBX_CMD            0
 
@@ -268,20 +267,18 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *adapter, ulong addr,
        }
 }
 
-int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq)
+int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter)
 {
        int err, i, num_msix;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
 
-       if (!num_intr)
-               num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS;
-       num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(),
-                                             num_intr));
+       num_msix = adapter->drv_sds_rings;
+
        /* account for AEN interrupt MSI-X based interrupts */
        num_msix += 1;
 
        if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
-               num_msix += adapter->max_drv_tx_rings;
+               num_msix += adapter->drv_tx_rings;
 
        err = qlcnic_enable_msix(adapter, num_msix);
        if (err == -ENOMEM)
@@ -325,7 +322,8 @@ inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter)
 
 inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter)
 {
-       writel(1, adapter->tgt_mask_reg);
+       if (adapter->tgt_mask_reg)
+               writel(1, adapter->tgt_mask_reg);
 }
 
 /* Enable MSI-x and INT-x interrupts */
@@ -498,8 +496,11 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter)
                num_msix = 0;
 
        msleep(20);
-       synchronize_irq(adapter->msix_entries[num_msix].vector);
-       free_irq(adapter->msix_entries[num_msix].vector, adapter);
+
+       if (adapter->msix_entries) {
+               synchronize_irq(adapter->msix_entries[num_msix].vector);
+               free_irq(adapter->msix_entries[num_msix].vector, adapter);
+       }
 }
 
 int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)
@@ -760,6 +761,9 @@ int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *adapter,
        int cmd_type, err, opcode;
        unsigned long timeout;
 
+       if (!mbx)
+               return -EIO;
+
        opcode = LSW(cmd->req.arg[0]);
        cmd_type = cmd->type;
        err = mbx->ops->enqueue_cmd(adapter, cmd, &timeout);
@@ -902,7 +906,7 @@ void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
                         QLCNIC_MBX_RSP(event[0]));
                break;
        case QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT:
-               qlcnic_dcb_handle_aen(adapter, (void *)&event[1]);
+               qlcnic_dcb_aen_handler(adapter->dcb, (void *)&event[1]);
                break;
        default:
                dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n",
@@ -979,14 +983,14 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
 
        sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
        context_id = recv_ctx->context_id;
-       num_sds = (adapter->max_sds_rings - QLCNIC_MAX_RING_SETS);
+       num_sds = adapter->drv_sds_rings - QLCNIC_MAX_SDS_RINGS;
        ahw->hw_ops->alloc_mbx_args(&cmd, adapter,
                                    QLCNIC_CMD_ADD_RCV_RINGS);
        cmd.req.arg[1] = 0 | (num_sds << 8) | (context_id << 16);
 
        /* set up status rings, mbx 2-81 */
        index = 2;
-       for (i = 8; i < adapter->max_sds_rings; i++) {
+       for (i = 8; i < adapter->drv_sds_rings; i++) {
                memset(&sds_mbx, 0, sds_mbx_size);
                sds = &recv_ctx->sds_rings[i];
                sds->consumer = 0;
@@ -1021,7 +1025,7 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
        mbx_out = (struct qlcnic_add_rings_mbx_out *)&cmd.rsp.arg[1];
        index = 0;
        /* status descriptor ring */
-       for (i = 8; i < adapter->max_sds_rings; i++) {
+       for (i = 8; i < adapter->drv_sds_rings; i++) {
                sds = &recv_ctx->sds_rings[i];
                sds->crb_sts_consumer = ahw->pci_base0 +
                                        mbx_out->host_csmr[index];
@@ -1079,10 +1083,10 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        num_rds = adapter->max_rds_rings;
 
-       if (adapter->max_sds_rings <= QLCNIC_MAX_RING_SETS)
-               num_sds = adapter->max_sds_rings;
+       if (adapter->drv_sds_rings <= QLCNIC_MAX_SDS_RINGS)
+               num_sds = adapter->drv_sds_rings;
        else
-               num_sds = QLCNIC_MAX_RING_SETS;
+               num_sds = QLCNIC_MAX_SDS_RINGS;
 
        sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
        rds_mbx_size = sizeof(struct qlcnic_rds_mbx);
@@ -1183,7 +1187,7 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
                sds->crb_intr_mask = ahw->pci_base0 + intr_mask;
        }
 
-       if (adapter->max_sds_rings > QLCNIC_MAX_RING_SETS)
+       if (adapter->drv_sds_rings > QLCNIC_MAX_SDS_RINGS)
                err = qlcnic_83xx_add_rings(adapter);
 out:
        qlcnic_free_mbx_args(&cmd);
@@ -1239,9 +1243,9 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
        mbx.size = tx->num_desc;
        if (adapter->flags & QLCNIC_MSIX_ENABLED) {
                if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
-                       msix_vector = adapter->max_sds_rings + ring;
+                       msix_vector = adapter->drv_sds_rings + ring;
                else
-                       msix_vector = adapter->max_sds_rings - 1;
+                       msix_vector = adapter->drv_sds_rings - 1;
                msix_id = ahw->intr_tbl[msix_vector].id;
        } else {
                msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID);
@@ -1264,7 +1268,8 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
                qlcnic_pf_set_interface_id_create_tx_ctx(adapter, &temp);
 
        cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT;
-       cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES | temp;
+       cmd.req.arg[5] = QLCNIC_SINGLE_RING | temp;
+
        buf = &cmd.req.arg[6];
        memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx));
        /* send the mailbox command*/
@@ -1279,7 +1284,7 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
        tx->ctx_id = mbx_out->ctx_id;
        if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
-               intr_mask = ahw->intr_tbl[adapter->max_sds_rings + ring].src;
+               intr_mask = ahw->intr_tbl[adapter->drv_sds_rings + ring].src;
                tx->crb_intr_mask = ahw->pci_base0 + intr_mask;
        }
        dev_info(&adapter->pdev->dev, "Tx Context[0x%x] Created, state:0x%x\n",
@@ -1290,7 +1295,7 @@ out:
 }
 
 static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
-                                     int num_sds_ring)
+                                     u8 num_sds_ring)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_host_sds_ring *sds_ring;
@@ -1306,7 +1311,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
 
        qlcnic_detach(adapter);
 
-       adapter->max_sds_rings = 1;
+       adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
        adapter->ahw->diag_test = test;
        adapter->ahw->linkup = 0;
 
@@ -1320,7 +1325,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
        if (ret) {
                qlcnic_detach(adapter);
                if (adapter_state == QLCNIC_ADAPTER_UP_MAGIC) {
-                       adapter->max_sds_rings = num_sds_ring;
+                       adapter->drv_sds_rings = num_sds_ring;
                        qlcnic_attach(adapter);
                }
                netif_device_attach(netdev);
@@ -1333,7 +1338,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
        }
 
        if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
-               for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                        sds_ring = &adapter->recv_ctx->sds_rings[ring];
                        qlcnic_83xx_enable_intr(adapter, sds_ring);
                }
@@ -1354,7 +1359,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
 }
 
 static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
-                                       int max_sds_rings)
+                                     u8 drv_sds_rings)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_host_sds_ring *sds_ring;
@@ -1362,7 +1367,7 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
 
        clear_bit(__QLCNIC_DEV_UP, &adapter->state);
        if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
-               for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                        sds_ring = &adapter->recv_ctx->sds_rings[ring];
                        qlcnic_83xx_disable_intr(adapter, sds_ring);
                        if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
@@ -1386,7 +1391,7 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
                }
        }
        adapter->ahw->diag_test = 0;
-       adapter->max_sds_rings = max_sds_rings;
+       adapter->drv_sds_rings = drv_sds_rings;
 
        if (qlcnic_attach(adapter))
                goto out;
@@ -1648,7 +1653,9 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_hardware_context *ahw = adapter->ahw;
-       int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings;
+       u8 drv_sds_rings = adapter->drv_sds_rings;
+       u8 drv_tx_rings = adapter->drv_tx_rings;
+       int ret = 0, loop = 0;
 
        if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
                netdev_warn(netdev,
@@ -1670,7 +1677,7 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
                    mode == QLCNIC_ILB_MODE ? "internal" : "external");
 
        ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST,
-                                        max_sds_rings);
+                                        drv_sds_rings);
        if (ret)
                goto fail_diag_alloc;
 
@@ -1708,10 +1715,11 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
        qlcnic_83xx_clear_lb_mode(adapter, mode);
 
 free_diag_res:
-       qlcnic_83xx_diag_free_res(netdev, max_sds_rings);
+       qlcnic_83xx_diag_free_res(netdev, drv_sds_rings);
 
 fail_diag_alloc:
-       adapter->max_sds_rings = max_sds_rings;
+       adapter->drv_sds_rings = drv_sds_rings;
+       adapter->drv_tx_rings = drv_tx_rings;
        qlcnic_release_diag_lock(adapter);
        return ret;
 }
@@ -2276,9 +2284,9 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter,
                temp = (cmd.rsp.arg[8] & 0x7FFE0000) >> 17;
                npar_info->max_linkspeed_reg_offset = temp;
        }
-       if (npar_info->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS)
-               memcpy(ahw->extra_capability, &cmd.rsp.arg[16],
-                      sizeof(ahw->extra_capability));
+
+       memcpy(ahw->extra_capability, &cmd.rsp.arg[16],
+              sizeof(ahw->extra_capability));
 
 out:
        qlcnic_free_mbx_args(&cmd);
@@ -2321,19 +2329,7 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,
                        i++;
                        memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2);
                        i = i + 3;
-                       if (ahw->op_mode == QLCNIC_MGMT_FUNC)
-                               dev_info(dev, "id = %d active = %d type = %d\n"
-                                        "\tport = %d min bw = %d max bw = %d\n"
-                                        "\tmac_addr =  %pM\n", pci_info->id,
-                                        pci_info->active, pci_info->type,
-                                        pci_info->default_port,
-                                        pci_info->tx_min_bw,
-                                        pci_info->tx_max_bw, pci_info->mac);
                }
-               if (ahw->op_mode == QLCNIC_MGMT_FUNC)
-                       dev_info(dev, "Max functions = %d, active functions = %d\n",
-                                ahw->max_pci_func, ahw->act_pci_func);
-
        } else {
                dev_err(dev, "Failed to get PCI Info, error = %d\n", err);
                err = -EIO;
@@ -3061,11 +3057,14 @@ int qlcnic_83xx_get_settings(struct qlcnic_adapter *adapter,
        int status = 0;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
 
-       /* Get port configuration info */
-       status = qlcnic_83xx_get_port_info(adapter);
-       /* Get Link Status related info */
-       config = qlcnic_83xx_test_link(adapter);
-       ahw->module_type = QLC_83XX_SFP_MODULE_TYPE(config);
+       if (!test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) {
+               /* Get port configuration info */
+               status = qlcnic_83xx_get_port_info(adapter);
+               /* Get Link Status related info */
+               config = qlcnic_83xx_test_link(adapter);
+               ahw->module_type = QLC_83XX_SFP_MODULE_TYPE(config);
+       }
+
        /* hard code until there is a way to get it from flash */
        ahw->board_type = QLCNIC_BRDTYPE_83XX_10G;
 
@@ -3279,12 +3278,12 @@ int qlcnic_83xx_reg_test(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter)
+inline int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter)
 {
        return (ARRAY_SIZE(qlcnic_83xx_ext_reg_tbl) *
-               sizeof(adapter->ahw->ext_reg_tbl)) +
-               (ARRAY_SIZE(qlcnic_83xx_reg_tbl) +
-               sizeof(adapter->ahw->reg_tbl));
+               sizeof(*adapter->ahw->ext_reg_tbl)) +
+               (ARRAY_SIZE(qlcnic_83xx_reg_tbl) *
+               sizeof(*adapter->ahw->reg_tbl));
 }
 
 int qlcnic_83xx_get_registers(struct qlcnic_adapter *adapter, u32 *regs_buff)
@@ -3305,10 +3304,11 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        struct qlcnic_cmd_args cmd;
+       u8 val, drv_sds_rings = adapter->drv_sds_rings;
+       u8 drv_tx_rings = adapter->drv_tx_rings;
        u32 data;
        u16 intrpt_id, id;
-       u8 val;
-       int ret, max_sds_rings = adapter->max_sds_rings;
+       int ret;
 
        if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
                netdev_info(netdev, "Device is resetting\n");
@@ -3321,7 +3321,7 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
        }
 
        ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST,
-                                        max_sds_rings);
+                                        drv_sds_rings);
        if (ret)
                goto fail_diag_irq;
 
@@ -3358,10 +3358,11 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
 
 done:
        qlcnic_free_mbx_args(&cmd);
-       qlcnic_83xx_diag_free_res(netdev, max_sds_rings);
+       qlcnic_83xx_diag_free_res(netdev, drv_sds_rings);
 
 fail_diag_irq:
-       adapter->max_sds_rings = max_sds_rings;
+       adapter->drv_sds_rings = drv_sds_rings;
+       adapter->drv_tx_rings = drv_tx_rings;
        qlcnic_release_diag_lock(adapter);
        return ret;
 }
@@ -3381,10 +3382,21 @@ void qlcnic_83xx_get_pauseparam(struct qlcnic_adapter *adapter,
        }
        config = ahw->port_config;
        if (config & QLC_83XX_CFG_STD_PAUSE) {
-               if (config & QLC_83XX_CFG_STD_TX_PAUSE)
+               switch (MSW(config)) {
+               case QLC_83XX_TX_PAUSE:
+                       pause->tx_pause = 1;
+                       break;
+               case QLC_83XX_RX_PAUSE:
+                       pause->rx_pause = 1;
+                       break;
+               case QLC_83XX_TX_RX_PAUSE:
+               default:
+                       /* Backward compatibility for existing
+                        * flash definitions
+                        */
                        pause->tx_pause = 1;
-               if (config & QLC_83XX_CFG_STD_RX_PAUSE)
                        pause->rx_pause = 1;
+               }
        }
 
        if (QLC_83XX_AUTONEG(config))
@@ -3427,7 +3439,8 @@ int qlcnic_83xx_set_pauseparam(struct qlcnic_adapter *adapter,
                ahw->port_config &= ~QLC_83XX_CFG_STD_RX_PAUSE;
                ahw->port_config |= QLC_83XX_CFG_STD_TX_PAUSE;
        } else if (!pause->rx_pause && !pause->tx_pause) {
-               ahw->port_config &= ~QLC_83XX_CFG_STD_TX_RX_PAUSE;
+               ahw->port_config &= ~(QLC_83XX_CFG_STD_TX_RX_PAUSE |
+                                     QLC_83XX_CFG_STD_PAUSE);
        }
        status = qlcnic_83xx_set_port_config(adapter);
        if (status) {
@@ -3503,7 +3516,7 @@ int qlcnic_83xx_resume(struct qlcnic_adapter *adapter)
        if (err)
                return err;
 
-       if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) {
+       if (ahw->nic_mode == QLCNIC_VNIC_MODE) {
                if (ahw->op_mode == QLCNIC_MGMT_FUNC) {
                        qlcnic_83xx_set_vnic_opmode(adapter);
                } else {
@@ -3530,6 +3543,9 @@ void qlcnic_83xx_reinit_mbx_work(struct qlcnic_mailbox *mbx)
 
 void qlcnic_83xx_free_mailbox(struct qlcnic_mailbox *mbx)
 {
+       if (!mbx)
+               return;
+
        destroy_workqueue(mbx->work_q);
        kfree(mbx);
 }
@@ -3650,6 +3666,9 @@ void qlcnic_83xx_detach_mailbox_work(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_mailbox *mbx = adapter->ahw->mailbox;
 
+       if (!mbx)
+               return;
+
        clear_bit(QLC_83XX_MBX_READY, &mbx->status);
        complete(&mbx->completion);
        cancel_work_sync(&mbx->work);
index 533e150..4cae6ca 100644 (file)
@@ -61,7 +61,6 @@
 #define QLC_83XX_HOST_SDS_MBX_IDX              8
 
 #define QLCNIC_HOST_RDS_MBX_IDX                        88
-#define QLCNIC_MAX_RING_SETS                   8
 
 /* Pause control registers */
 #define QLC_83XX_SRE_SHIM_REG          0x0D200284
@@ -183,8 +182,8 @@ struct qlcnic_rcv_mbx_out {
        u8      num_pci_func;
        u8      state;
 #endif
-       u32     host_csmr[QLCNIC_MAX_RING_SETS];
-       struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
+       u32     host_csmr[QLCNIC_MAX_SDS_RINGS];
+       struct __host_producer_mbx host_prod[QLCNIC_MAX_SDS_RINGS];
 } __packed;
 
 struct qlcnic_add_rings_mbx_out {
@@ -197,8 +196,8 @@ struct qlcnic_add_rings_mbx_out {
        u8      sts_num;
        u8      rcv_num;
 #endif
-       u32  host_csmr[QLCNIC_MAX_RING_SETS];
-       struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
+       u32  host_csmr[QLCNIC_MAX_SDS_RINGS];
+       struct __host_producer_mbx host_prod[QLCNIC_MAX_SDS_RINGS];
 } __packed;
 
 /* Transmit context mailbox inbox registers
@@ -363,6 +362,9 @@ enum qlcnic_83xx_states {
 #define QLC_83XX_LINK_EEE(data)                ((data) & BIT_13)
 #define QLC_83XX_DCBX(data)                    (((data) >> 28) & 7)
 #define QLC_83XX_AUTONEG(data)                 ((data) & BIT_15)
+#define QLC_83XX_TX_PAUSE                      0x10
+#define QLC_83XX_RX_PAUSE                      0x20
+#define QLC_83XX_TX_RX_PAUSE                   0x30
 #define QLC_83XX_CFG_STD_PAUSE                 (1 << 5)
 #define QLC_83XX_CFG_STD_TX_PAUSE              (1 << 20)
 #define QLC_83XX_CFG_STD_RX_PAUSE              (2 << 20)
@@ -412,8 +414,6 @@ enum qlcnic_83xx_states {
 #define QLC_83XX_GET_VLAN_ALIGN_CAPABILITY(val)        (val & 0x4000)
 #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val)        (val & 0x20000)
 #define QLC_83XX_ESWITCH_CAPABILITY                    BIT_23
-#define QLC_83XX_VIRTUAL_NIC_MODE                      0xFF
-#define QLC_83XX_DEFAULT_MODE                          0x0
 #define QLC_83XX_SRIOV_MODE                            0x1
 #define QLCNIC_BRDTYPE_83XX_10G                        0x0083
 
@@ -521,7 +521,7 @@ enum qlc_83xx_ext_regs {
 /* 83xx funcitons */
 int qlcnic_83xx_get_fw_version(struct qlcnic_adapter *);
 int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *, struct qlcnic_cmd_args *);
-int qlcnic_83xx_setup_intr(struct qlcnic_adapter *, u8, int);
+int qlcnic_83xx_setup_intr(struct qlcnic_adapter *);
 void qlcnic_83xx_get_func_no(struct qlcnic_adapter *);
 int qlcnic_83xx_cam_lock(struct qlcnic_adapter *);
 void qlcnic_83xx_cam_unlock(struct qlcnic_adapter *);
@@ -626,7 +626,7 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *);
 int qlcnic_83xx_get_vnic_vport_info(struct qlcnic_adapter *,
                                    struct qlcnic_info *, u8);
 int qlcnic_83xx_get_vnic_pf_info(struct qlcnic_adapter *, struct qlcnic_info *);
-int qlcnic_83xx_enable_port_eswitch(struct qlcnic_adapter *, int);
+int qlcnic_83xx_set_port_eswitch_status(struct qlcnic_adapter *, int, int *);
 
 void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *);
 void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data);
index f09e787..89208e5 100644 (file)
@@ -636,7 +636,7 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
        if (adapter->portnum == 0)
                qlcnic_set_drv_version(adapter);
 
-       qlcnic_dcb_get_info(adapter);
+       qlcnic_dcb_get_info(adapter->dcb);
        qlcnic_83xx_idc_attach_driver(adapter);
 
        return 0;
@@ -818,6 +818,7 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter)
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        struct qlcnic_mailbox *mbx = ahw->mailbox;
        int ret = 0;
+       u32 owner;
        u32 val;
 
        /* Perform NIC configuration based ready state entry actions */
@@ -846,6 +847,10 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter)
                        clear_bit(QLC_83XX_MBX_READY, &mbx->status);
                        set_bit(__QLCNIC_RESETTING, &adapter->state);
                        qlcnic_83xx_idc_enter_need_reset_state(adapter, 1);
+               }  else {
+                       owner = qlcnic_83xx_idc_find_reset_owner_id(adapter);
+                       if (ahw->pci_func == owner)
+                               qlcnic_dump_fw(adapter);
                }
                return -EIO;
        }
@@ -897,7 +902,7 @@ static int qlcnic_83xx_idc_need_reset_state(struct qlcnic_adapter *adapter)
                qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1);
                set_bit(__QLCNIC_RESETTING, &adapter->state);
                clear_bit(QLC_83XX_MBX_READY, &mbx->status);
-               if (adapter->ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE)
+               if (adapter->ahw->nic_mode == QLCNIC_VNIC_MODE)
                        qlcnic_83xx_disable_vnic_mode(adapter, 1);
 
                if (qlcnic_check_diag_status(adapter)) {
@@ -1058,6 +1063,12 @@ void qlcnic_83xx_idc_poll_dev_state(struct work_struct *work)
        adapter->ahw->idc.prev_state = adapter->ahw->idc.curr_state;
        qlcnic_83xx_periodic_tasks(adapter);
 
+       /* Do not reschedule if firmaware is in hanged state and auto
+        * recovery is disabled
+        */
+       if ((adapter->flags & QLCNIC_FW_HANG) && !qlcnic_auto_fw_reset)
+               return;
+
        /* Re-schedule the function */
        if (test_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status))
                qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state,
@@ -2022,6 +2033,8 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
        ahw->max_mac_filters = nic_info.max_mac_filters;
        ahw->max_mtu = nic_info.max_mtu;
 
+       adapter->max_tx_rings = ahw->max_tx_ques;
+       adapter->max_sds_rings = ahw->max_rx_ques;
        /* eSwitch capability indicates vNIC mode.
         * vNIC and SRIOV are mutually exclusive operational modes.
         * If SR-IOV capability is detected, SR-IOV physical function
@@ -2034,7 +2047,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
                return QLC_83XX_DEFAULT_OPMODE;
 
        if (ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY)
-               return QLC_83XX_VIRTUAL_NIC_MODE;
+               return QLCNIC_VNIC_MODE;
 
        return QLC_83XX_DEFAULT_OPMODE;
 }
@@ -2048,15 +2061,20 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
        if (ret == -EIO)
                return -EIO;
 
-       if (ret == QLC_83XX_VIRTUAL_NIC_MODE) {
-               ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE;
+       if (ret == QLCNIC_VNIC_MODE) {
+               ahw->nic_mode = QLCNIC_VNIC_MODE;
+
                if (qlcnic_83xx_config_vnic_opmode(adapter))
                        return -EIO;
 
+               adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS;
+               adapter->max_tx_rings = QLCNIC_MAX_VNIC_TX_RINGS;
        } else if (ret == QLC_83XX_DEFAULT_OPMODE) {
-               ahw->nic_mode = QLC_83XX_DEFAULT_MODE;
+               ahw->nic_mode = QLCNIC_DEFAULT_MODE;
                adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;
                ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
+               adapter->max_sds_rings = ahw->max_rx_ques;
+               adapter->max_tx_rings = ahw->max_tx_ques;
        } else {
                return -EIO;
        }
@@ -2159,13 +2177,34 @@ static int qlcnic_83xx_get_fw_info(struct qlcnic_adapter *adapter)
        return err;
 }
 
+static void qlcnic_83xx_init_rings(struct qlcnic_adapter *adapter)
+{
+       u8 rx_cnt = QLCNIC_DEF_SDS_RINGS;
+       u8 tx_cnt = QLCNIC_DEF_TX_RINGS;
+
+       adapter->max_tx_rings = QLCNIC_MAX_TX_RINGS;
+       adapter->max_sds_rings = QLCNIC_MAX_SDS_RINGS;
+
+       if (!adapter->ahw->msix_supported) {
+               rx_cnt = QLCNIC_SINGLE_RING;
+               tx_cnt = QLCNIC_SINGLE_RING;
+       }
+
+       /* compute and set drv sds rings */
+       qlcnic_set_tx_ring_count(adapter, tx_cnt);
+       qlcnic_set_sds_ring_count(adapter, rx_cnt);
+}
 
 int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlcnic_dcb *dcb;
        int err = 0;
 
        ahw->msix_supported = !!qlcnic_use_msi_x;
+
+       qlcnic_83xx_init_rings(adapter);
+
        err = qlcnic_83xx_init_mailbox_work(adapter);
        if (err)
                goto exit;
@@ -2178,22 +2217,26 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
                        return err;
        }
 
+       if (qlcnic_83xx_read_flash_descriptor_table(adapter) ||
+           qlcnic_83xx_read_flash_mfg_id(adapter)) {
+               dev_err(&adapter->pdev->dev, "Failed reading flash mfg id\n");
+               err = -ENOTRECOVERABLE;
+               goto detach_mbx;
+       }
+
        err = qlcnic_83xx_check_hw_status(adapter);
        if (err)
                goto detach_mbx;
 
-       if (!qlcnic_83xx_read_flash_descriptor_table(adapter))
-               qlcnic_83xx_read_flash_mfg_id(adapter);
-
        err = qlcnic_83xx_get_fw_info(adapter);
        if (err)
                goto detach_mbx;
 
        err = qlcnic_83xx_idc_init(adapter);
        if (err)
-               goto clear_fw_info;
+               goto detach_mbx;
 
-       err = qlcnic_setup_intr(adapter, 0, 0);
+       err = qlcnic_setup_intr(adapter);
        if (err) {
                dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n");
                goto disable_intr;
@@ -2215,13 +2258,16 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
        if (err)
                goto disable_mbx_intr;
 
+
        /* Perform operating mode specific initialization */
        err = adapter->nic_ops->init_driver(adapter);
        if (err)
                goto disable_mbx_intr;
 
-       if (adapter->dcb && qlcnic_dcb_attach(adapter))
-               qlcnic_clear_dcb_ops(adapter);
+       dcb = adapter->dcb;
+
+       if (dcb && qlcnic_dcb_attach(dcb))
+               qlcnic_clear_dcb_ops(dcb);
 
        /* Periodically monitor device status */
        qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work);
@@ -2233,12 +2279,10 @@ disable_mbx_intr:
 disable_intr:
        qlcnic_teardown_intr(adapter);
 
-clear_fw_info:
-       kfree(ahw->fw_info);
-
 detach_mbx:
        qlcnic_83xx_detach_mailbox_work(adapter);
        qlcnic_83xx_free_mailbox(ahw->mailbox);
+       ahw->mailbox = NULL;
 exit:
        return err;
 }
@@ -2251,7 +2295,7 @@ void qlcnic_83xx_aer_stop_poll_work(struct qlcnic_adapter *adapter)
        clear_bit(QLC_83XX_MBX_READY, &idc->status);
        cancel_delayed_work_sync(&adapter->fw_work);
 
-       if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE)
+       if (ahw->nic_mode == QLCNIC_VNIC_MODE)
                qlcnic_83xx_disable_vnic_mode(adapter, 1);
 
        qlcnic_83xx_idc_detach_driver(adapter);
index 0248a4c..734d286 100644 (file)
@@ -94,13 +94,29 @@ qlcnic_83xx_config_vnic_buff_descriptors(struct qlcnic_adapter *adapter)
  **/
 static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter)
 {
-       int err = -EIO;
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct device *dev = &adapter->pdev->dev;
+       struct qlcnic_npar_info *npar;
+       int i, err = -EIO;
 
        qlcnic_83xx_get_minidump_template(adapter);
+
        if (!(adapter->flags & QLCNIC_ADAPTER_INITIALIZED)) {
                if (qlcnic_init_pci_info(adapter))
                        return err;
 
+               npar = adapter->npars;
+
+               for (i = 0; i < ahw->act_pci_func; i++, npar++) {
+                       dev_info(dev, "id:%d active:%d type:%d port:%d min_bw:%d max_bw:%d mac_addr:%pM\n",
+                                npar->pci_func, npar->active, npar->type,
+                                npar->phy_port, npar->min_bw, npar->max_bw,
+                                npar->mac);
+               }
+
+               dev_info(dev, "Max functions = %d, active functions = %d\n",
+                        ahw->max_pci_func, ahw->act_pci_func);
+
                if (qlcnic_83xx_set_vnic_opmode(adapter))
                        return err;
 
@@ -115,12 +131,12 @@ static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter)
                return err;
 
        qlcnic_83xx_config_vnic_buff_descriptors(adapter);
-       adapter->ahw->msix_supported = !!qlcnic_use_msi_x;
+       ahw->msix_supported = qlcnic_use_msi_x ? 1 : 0;
        adapter->flags |= QLCNIC_ADAPTER_INITIALIZED;
        qlcnic_83xx_enable_vnic_mode(adapter, 1);
 
-       dev_info(&adapter->pdev->dev, "HAL Version: %d, Management function\n",
-                adapter->ahw->fw_hal_version);
+       dev_info(dev, "HAL Version: %d, Management function\n",
+                ahw->fw_hal_version);
 
        return 0;
 }
@@ -240,8 +256,8 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter,
-                                            int func, int *port_id)
+int qlcnic_83xx_set_port_eswitch_status(struct qlcnic_adapter *adapter,
+                                       int func, int *port_id)
 {
        struct qlcnic_info nic_info;
        int err = 0;
@@ -257,23 +273,8 @@ static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter,
        else
                err = -EIO;
 
-       return err;
-}
-
-int qlcnic_83xx_enable_port_eswitch(struct qlcnic_adapter *adapter, int func)
-{
-       int id, err = 0;
-
-       err = qlcnic_83xx_get_eswitch_port_info(adapter, func, &id);
-       if (err)
-               return err;
-
-       if (!(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) {
-               if (!qlcnic_enable_eswitch(adapter, id, 1))
-                       adapter->eswitch[id].flags |= QLCNIC_SWITCH_ENABLE;
-               else
-                       err = -EIO;
-       }
+       if (!err)
+               adapter->eswitch[*port_id].flags |= QLCNIC_SWITCH_ENABLE;
 
        return err;
 }
index 86850dd..859cb16 100644 (file)
@@ -270,7 +270,7 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
        int err;
 
        nrds_rings = adapter->max_rds_rings;
-       nsds_rings = adapter->max_sds_rings;
+       nsds_rings = adapter->drv_sds_rings;
 
        rq_size = SIZEOF_HOSTRQ_RX(struct qlcnic_hostrq_rx_ctx, nrds_rings,
                                   nsds_rings);
@@ -475,7 +475,7 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
 
        if (qlcnic_check_multi_tx(adapter) &&
            !adapter->ahw->diag_test) {
-               temp_nsds_rings = adapter->max_sds_rings;
+               temp_nsds_rings = adapter->drv_sds_rings;
                index = temp_nsds_rings + ring;
                msix_id = ahw->intr_tbl[index].id;
                prq->msi_index = cpu_to_le16(msix_id);
@@ -512,7 +512,7 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
                if (qlcnic_check_multi_tx(adapter) &&
                    !adapter->ahw->diag_test &&
                    (adapter->flags & QLCNIC_MSIX_ENABLED)) {
-                       index = adapter->max_sds_rings + ring;
+                       index = adapter->drv_sds_rings + ring;
                        intr_mask = ahw->intr_tbl[index].src;
                        tx_ring->crb_intr_mask = ahw->pci_base0 + intr_mask;
                }
@@ -582,7 +582,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
 
        recv_ctx = adapter->recv_ctx;
 
-       for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                tx_ring = &adapter->tx_ring[ring];
                ptr = (__le32 *)dma_alloc_coherent(&pdev->dev, sizeof(u32),
                                                   &tx_ring->hw_cons_phys_addr,
@@ -616,7 +616,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
 
        }
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
 
                addr = dma_alloc_coherent(&adapter->pdev->dev,
@@ -664,7 +664,7 @@ int qlcnic_fw_create_ctx(struct qlcnic_adapter *dev)
        if (err)
                goto err_out;
 
-       for (ring = 0; ring < dev->max_drv_tx_rings; ring++) {
+       for (ring = 0; ring < dev->drv_tx_rings; ring++) {
                err = qlcnic_fw_cmd_create_tx_ctx(dev,
                                                  &dev->tx_ring[ring],
                                                  ring);
@@ -703,7 +703,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
 
        if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) {
                qlcnic_fw_cmd_del_rx_ctx(adapter);
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++)
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++)
                        qlcnic_fw_cmd_del_tx_ctx(adapter,
                                                 &adapter->tx_ring[ring]);
 
@@ -733,7 +733,7 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
 
        recv_ctx = adapter->recv_ctx;
 
-       for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                tx_ring = &adapter->tx_ring[ring];
                if (tx_ring->hw_consumer != NULL) {
                        dma_free_coherent(&adapter->pdev->dev, sizeof(u32),
@@ -764,7 +764,7 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
                }
        }
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
 
                if (sds_ring->desc_head != NULL) {
@@ -895,6 +895,8 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
                npar_info->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques);
                npar_info->capabilities = le32_to_cpu(nic_info->capabilities);
                npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
+               adapter->max_tx_rings = npar_info->max_tx_ques;
+               adapter->max_sds_rings = npar_info->max_rx_ques;
        }
 
        qlcnic_free_mbx_args(&cmd);
index d62d5ce..86bca7c 100644 (file)
@@ -57,22 +57,22 @@ static const struct dcbnl_rtnl_ops qlcnic_dcbnl_ops;
 static void qlcnic_dcb_aen_work(struct work_struct *);
 static void qlcnic_dcb_data_cee_param_map(struct qlcnic_adapter *);
 
-static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_adapter *);
-static void __qlcnic_dcb_free(struct qlcnic_adapter *);
-static int __qlcnic_dcb_attach(struct qlcnic_adapter *);
-static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *);
-static void __qlcnic_dcb_get_info(struct qlcnic_adapter *);
-
-static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *);
-static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8);
-static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *);
-static void qlcnic_82xx_dcb_handle_aen(struct qlcnic_adapter *, void *);
-
-static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *);
-static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8);
-static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *);
-static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *, bool);
-static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *, void *);
+static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_dcb *);
+static void __qlcnic_dcb_free(struct qlcnic_dcb *);
+static int __qlcnic_dcb_attach(struct qlcnic_dcb *);
+static int __qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *, char *);
+static void __qlcnic_dcb_get_info(struct qlcnic_dcb *);
+
+static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_dcb *);
+static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_dcb *, char *, u8);
+static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_dcb *);
+static void qlcnic_82xx_dcb_aen_handler(struct qlcnic_dcb *, void *);
+
+static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_dcb *);
+static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_dcb *, char *, u8);
+static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_dcb *);
+static int qlcnic_83xx_dcb_register_aen(struct qlcnic_dcb *, bool);
+static void qlcnic_83xx_dcb_aen_handler(struct qlcnic_dcb *, void *);
 
 struct qlcnic_dcb_capability {
        bool    tsa_capability;
@@ -180,7 +180,7 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
        .query_cee_param        = qlcnic_83xx_dcb_query_cee_param,
        .get_cee_cfg            = qlcnic_83xx_dcb_get_cee_cfg,
        .register_aen           = qlcnic_83xx_dcb_register_aen,
-       .handle_aen             = qlcnic_83xx_dcb_handle_aen,
+       .aen_handler            = qlcnic_83xx_dcb_aen_handler,
 };
 
 static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
@@ -193,7 +193,7 @@ static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
        .get_hw_capability      = qlcnic_82xx_dcb_get_hw_capability,
        .query_cee_param        = qlcnic_82xx_dcb_query_cee_param,
        .get_cee_cfg            = qlcnic_82xx_dcb_get_cee_cfg,
-       .handle_aen             = qlcnic_82xx_dcb_handle_aen,
+       .aen_handler            = qlcnic_82xx_dcb_aen_handler,
 };
 
 static u8 qlcnic_dcb_get_num_app(struct qlcnic_adapter *adapter, u32 val)
@@ -242,10 +242,10 @@ static int qlcnic_dcb_prio_count(u8 up_tc_map)
        return j;
 }
 
-static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_adapter *adapter)
+static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_dcb *dcb)
 {
-       if (test_bit(__QLCNIC_DCB_STATE, &adapter->state))
-               adapter->netdev->dcbnl_ops = &qlcnic_dcbnl_ops;
+       if (test_bit(QLCNIC_DCB_STATE, &dcb->state))
+               dcb->adapter->netdev->dcbnl_ops = &qlcnic_dcbnl_ops;
 }
 
 static void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter)
@@ -256,7 +256,7 @@ static void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter)
                adapter->dcb->ops = &qlcnic_83xx_dcb_ops;
 }
 
-int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_dcb *dcb;
 
@@ -267,20 +267,22 @@ int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
        adapter->dcb = dcb;
        dcb->adapter = adapter;
        qlcnic_set_dcb_ops(adapter);
+       dcb->state = 0;
 
        return 0;
 }
 
-static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
+static void __qlcnic_dcb_free(struct qlcnic_dcb *dcb)
 {
-       struct qlcnic_dcb *dcb = adapter->dcb;
+       struct qlcnic_adapter *adapter;
 
        if (!dcb)
                return;
 
-       qlcnic_dcb_register_aen(adapter, 0);
+       adapter = dcb->adapter;
+       qlcnic_dcb_register_aen(dcb, 0);
 
-       while (test_bit(__QLCNIC_DCB_IN_AEN, &adapter->state))
+       while (test_bit(QLCNIC_DCB_AEN_MODE, &dcb->state))
                usleep_range(10000, 11000);
 
        cancel_delayed_work_sync(&dcb->aen_work);
@@ -298,23 +300,22 @@ static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
        adapter->dcb = NULL;
 }
 
-static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
+static void __qlcnic_dcb_get_info(struct qlcnic_dcb *dcb)
 {
-       qlcnic_dcb_get_hw_capability(adapter);
-       qlcnic_dcb_get_cee_cfg(adapter);
-       qlcnic_dcb_register_aen(adapter, 1);
+       qlcnic_dcb_get_hw_capability(dcb);
+       qlcnic_dcb_get_cee_cfg(dcb);
+       qlcnic_dcb_register_aen(dcb, 1);
 }
 
-static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
+static int __qlcnic_dcb_attach(struct qlcnic_dcb *dcb)
 {
-       struct qlcnic_dcb *dcb = adapter->dcb;
        int err = 0;
 
        INIT_DELAYED_WORK(&dcb->aen_work, qlcnic_dcb_aen_work);
 
        dcb->wq = create_singlethread_workqueue("qlcnic-dcb");
        if (!dcb->wq) {
-               dev_err(&adapter->pdev->dev,
+               dev_err(&dcb->adapter->pdev->dev,
                        "DCB workqueue allocation failed. DCB will be disabled\n");
                return -1;
        }
@@ -331,7 +332,7 @@ static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
                goto out_free_cfg;
        }
 
-       qlcnic_dcb_get_info(adapter);
+       qlcnic_dcb_get_info(dcb);
 
        return 0;
 out_free_cfg:
@@ -345,9 +346,9 @@ out_free_wq:
        return err;
 }
 
-static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter,
-                                           char *buf)
+static int __qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *dcb, char *buf)
 {
+       struct qlcnic_adapter *adapter = dcb->adapter;
        struct qlcnic_cmd_args cmd;
        u32 mbx_out;
        int err;
@@ -371,15 +372,15 @@ static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter,
        return err;
 }
 
-static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val)
+static int __qlcnic_dcb_get_capability(struct qlcnic_dcb *dcb, u32 *val)
 {
-       struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
+       struct qlcnic_dcb_capability *cap = &dcb->cfg->capability;
        u32 mbx_out;
        int err;
 
        memset(cap, 0, sizeof(struct qlcnic_dcb_capability));
 
-       err = qlcnic_dcb_query_hw_capability(adapter, (char *)val);
+       err = qlcnic_dcb_query_hw_capability(dcb, (char *)val);
        if (err)
                return err;
 
@@ -397,21 +398,21 @@ static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val)
        if (cap->max_num_tc > QLC_DCB_MAX_TC ||
            cap->max_ets_tc > cap->max_num_tc ||
            cap->max_pfc_tc > cap->max_num_tc) {
-               dev_err(&adapter->pdev->dev, "Invalid DCB configuration\n");
+               dev_err(&dcb->adapter->pdev->dev, "Invalid DCB configuration\n");
                return -EINVAL;
        }
 
        return err;
 }
 
-static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_dcb *dcb)
 {
-       struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+       struct qlcnic_dcb_cfg *cfg = dcb->cfg;
        struct qlcnic_dcb_capability *cap;
        u32 mbx_out;
        int err;
 
-       err = __qlcnic_dcb_get_capability(adapter, &mbx_out);
+       err = __qlcnic_dcb_get_capability(dcb, &mbx_out);
        if (err)
                return err;
 
@@ -419,15 +420,16 @@ static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
        cap->dcb_capability = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_LLD_MANAGED;
 
        if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability)
-               set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+               set_bit(QLCNIC_DCB_STATE, &dcb->state);
 
        return err;
 }
 
-static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *adapter,
+static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_dcb *dcb,
                                           char *buf, u8 type)
 {
        u16 size = sizeof(struct qlcnic_82xx_dcb_param_mbx_le);
+       struct qlcnic_adapter *adapter = dcb->adapter;
        struct qlcnic_82xx_dcb_param_mbx_le *prsp_le;
        struct device *dev = &adapter->pdev->dev;
        dma_addr_t cardrsp_phys_addr;
@@ -447,8 +449,7 @@ static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *adapter,
                return -EINVAL;
        }
 
-       addr = dma_alloc_coherent(&adapter->pdev->dev, size, &cardrsp_phys_addr,
-                                 GFP_KERNEL);
+       addr = dma_alloc_coherent(dev, size, &cardrsp_phys_addr, GFP_KERNEL);
        if (addr == NULL)
                return -ENOMEM;
 
@@ -488,72 +489,67 @@ out:
        qlcnic_free_mbx_args(&cmd);
 
 out_free_rsp:
-       dma_free_coherent(&adapter->pdev->dev, size, addr, cardrsp_phys_addr);
+       dma_free_coherent(dev, size, addr, cardrsp_phys_addr);
 
        return err;
 }
 
-static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
+static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_dcb *dcb)
 {
        struct qlcnic_dcb_mbx_params *mbx;
        int err;
 
-       mbx = adapter->dcb->param;
+       mbx = dcb->param;
        if (!mbx)
                return 0;
 
-       err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[0],
+       err = qlcnic_dcb_query_cee_param(dcb, (char *)&mbx->type[0],
                                         QLC_DCB_LOCAL_PARAM_FWID);
        if (err)
                return err;
 
-       err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[1],
+       err = qlcnic_dcb_query_cee_param(dcb, (char *)&mbx->type[1],
                                         QLC_DCB_OPER_PARAM_FWID);
        if (err)
                return err;
 
-       err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[2],
+       err = qlcnic_dcb_query_cee_param(dcb, (char *)&mbx->type[2],
                                         QLC_DCB_PEER_PARAM_FWID);
        if (err)
                return err;
 
        mbx->prio_tc_map = QLC_82XX_DCB_PRIO_TC_MAP;
 
-       qlcnic_dcb_data_cee_param_map(adapter);
+       qlcnic_dcb_data_cee_param_map(dcb->adapter);
 
        return err;
 }
 
 static void qlcnic_dcb_aen_work(struct work_struct *work)
 {
-       struct qlcnic_adapter *adapter;
        struct qlcnic_dcb *dcb;
 
        dcb = container_of(work, struct qlcnic_dcb, aen_work.work);
-       adapter = dcb->adapter;
 
-       qlcnic_dcb_get_cee_cfg(adapter);
-       clear_bit(__QLCNIC_DCB_IN_AEN, &adapter->state);
+       qlcnic_dcb_get_cee_cfg(dcb);
+       clear_bit(QLCNIC_DCB_AEN_MODE, &dcb->state);
 }
 
-static void qlcnic_82xx_dcb_handle_aen(struct qlcnic_adapter *adapter,
-                                      void *data)
+static void qlcnic_82xx_dcb_aen_handler(struct qlcnic_dcb *dcb, void *data)
 {
-       struct qlcnic_dcb *dcb = adapter->dcb;
-
-       if (test_and_set_bit(__QLCNIC_DCB_IN_AEN, &adapter->state))
+       if (test_and_set_bit(QLCNIC_DCB_AEN_MODE, &dcb->state))
                return;
 
        queue_delayed_work(dcb->wq, &dcb->aen_work, 0);
 }
 
-static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_dcb *dcb)
 {
-       struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
+       struct qlcnic_dcb_capability *cap = &dcb->cfg->capability;
        u32 mbx_out;
        int err;
 
-       err = __qlcnic_dcb_get_capability(adapter, &mbx_out);
+       err = __qlcnic_dcb_get_capability(dcb, &mbx_out);
        if (err)
                return err;
 
@@ -565,14 +561,15 @@ static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
                cap->dcb_capability |= DCB_CAP_DCBX_LLD_MANAGED;
 
        if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability)
-               set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+               set_bit(QLCNIC_DCB_STATE, &dcb->state);
 
        return err;
 }
 
-static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *adapter,
+static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_dcb *dcb,
                                           char *buf, u8 idx)
 {
+       struct qlcnic_adapter *adapter = dcb->adapter;
        struct qlcnic_dcb_mbx_params mbx_out;
        int err, i, j, k, max_app, size;
        struct qlcnic_dcb_param *each;
@@ -632,24 +629,23 @@ out:
        return err;
 }
 
-static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter)
+static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_dcb *dcb)
 {
-       struct qlcnic_dcb *dcb = adapter->dcb;
        int err;
 
-       err = qlcnic_dcb_query_cee_param(adapter, (char *)dcb->param, 0);
+       err = qlcnic_dcb_query_cee_param(dcb, (char *)dcb->param, 0);
        if (err)
                return err;
 
-       qlcnic_dcb_data_cee_param_map(adapter);
+       qlcnic_dcb_data_cee_param_map(dcb->adapter);
 
        return err;
 }
 
-static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *adapter,
-                                       bool flag)
+static int qlcnic_83xx_dcb_register_aen(struct qlcnic_dcb *dcb, bool flag)
 {
        u8 val = (flag ? QLCNIC_CMD_INIT_NIC_FUNC : QLCNIC_CMD_STOP_NIC_FUNC);
+       struct qlcnic_adapter *adapter = dcb->adapter;
        struct qlcnic_cmd_args cmd;
        int err;
 
@@ -669,19 +665,17 @@ static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *adapter,
        return err;
 }
 
-static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *adapter,
-                                      void *data)
+static void qlcnic_83xx_dcb_aen_handler(struct qlcnic_dcb *dcb, void *data)
 {
-       struct qlcnic_dcb *dcb = adapter->dcb;
        u32 *val = data;
 
-       if (test_and_set_bit(__QLCNIC_DCB_IN_AEN, &adapter->state))
+       if (test_and_set_bit(QLCNIC_DCB_AEN_MODE, &dcb->state))
                return;
 
        if (*val & BIT_8)
-               set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+               set_bit(QLCNIC_DCB_STATE, &dcb->state);
        else
-               clear_bit(__QLCNIC_DCB_STATE, &adapter->state);
+               clear_bit(QLCNIC_DCB_STATE, &dcb->state);
 
        queue_delayed_work(dcb->wq, &dcb->aen_work, 0);
 }
@@ -814,12 +808,12 @@ static u8 qlcnic_dcb_get_state(struct net_device *netdev)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
 
-       return test_bit(__QLCNIC_DCB_STATE, &adapter->state);
+       return test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state);
 }
 
 static void qlcnic_dcb_get_perm_hw_addr(struct net_device *netdev, u8 *addr)
 {
-       memcpy(addr, netdev->dev_addr, netdev->addr_len);
+       memcpy(addr, netdev->perm_addr, netdev->addr_len);
 }
 
 static void
@@ -834,7 +828,7 @@ qlcnic_dcb_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, u8 *prio,
        type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
        *prio = *pgid = *bw_per = *up_tc_map = 0;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) ||
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state) ||
            !type->tc_param_valid)
                return;
 
@@ -870,7 +864,7 @@ static void qlcnic_dcb_get_pg_bwg_cfg_tx(struct net_device *netdev, int pgid,
        *bw_pct = 0;
        type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) ||
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state) ||
            !type->tc_param_valid)
                return;
 
@@ -896,7 +890,7 @@ static void qlcnic_dcb_get_pfc_cfg(struct net_device *netdev, int prio,
        *setting = 0;
        type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) ||
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state) ||
            !type->pfc_mode_enable)
                return;
 
@@ -915,7 +909,7 @@ static u8 qlcnic_dcb_get_capability(struct net_device *netdev, int capid,
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        switch (capid) {
@@ -944,7 +938,7 @@ static int qlcnic_dcb_get_num_tcs(struct net_device *netdev, int attr, u8 *num)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return -EINVAL;
 
        switch (attr) {
@@ -967,7 +961,7 @@ static u8 qlcnic_dcb_get_app(struct net_device *netdev, u8 idtype, u16 id)
                                .protocol = id,
                             };
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        return dcb_getapp(netdev, &app);
@@ -978,7 +972,7 @@ static u8 qlcnic_dcb_get_pfc_state(struct net_device *netdev)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_dcb *dcb = adapter->dcb;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &dcb->state))
                return 0;
 
        return dcb->cfg->type[QLC_DCB_OPER_IDX].pfc_mode_enable;
@@ -989,7 +983,7 @@ static u8 qlcnic_dcb_get_dcbx(struct net_device *netdev)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        return cfg->capability.dcb_capability;
@@ -1000,7 +994,7 @@ static u8 qlcnic_dcb_get_feat_cfg(struct net_device *netdev, int fid, u8 *flag)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_dcb_cee *type;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 1;
 
        type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX];
@@ -1055,7 +1049,7 @@ static int qlcnic_dcb_peer_app_info(struct net_device *netdev,
 
        *app_count = 0;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX];
@@ -1076,7 +1070,7 @@ static int qlcnic_dcb_peer_app_table(struct net_device *netdev,
        struct qlcnic_dcb_app *app;
        int i, j;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX];
@@ -1101,7 +1095,7 @@ static int qlcnic_dcb_cee_peer_get_pg(struct net_device *netdev,
        struct qlcnic_dcb_cee *peer;
        u8 i, j, k, map;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX];
@@ -1136,7 +1130,7 @@ static int qlcnic_dcb_cee_peer_get_pfc(struct net_device *netdev,
 
        pfc->pfc_en = 0;
 
-       if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state))
+       if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state))
                return 0;
 
        peer = &cfg->type[QLC_DCB_PEER_IDX];
index b87ce9f..c04ae0c 100644 (file)
@@ -8,26 +8,29 @@
 #ifndef __QLCNIC_DCBX_H
 #define __QLCNIC_DCBX_H
 
-void qlcnic_clear_dcb_ops(struct qlcnic_adapter *);
+#define QLCNIC_DCB_STATE       0
+#define QLCNIC_DCB_AEN_MODE    1
 
 #ifdef CONFIG_QLCNIC_DCB
-int __qlcnic_register_dcb(struct qlcnic_adapter *);
+int qlcnic_register_dcb(struct qlcnic_adapter *);
 #else
-static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+static inline int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
 { return 0; }
 #endif
 
+struct qlcnic_dcb;
+
 struct qlcnic_dcb_ops {
-       void (*init_dcbnl_ops) (struct qlcnic_adapter *);
-       void (*free) (struct qlcnic_adapter *);
-       int (*attach) (struct qlcnic_adapter *);
-       int (*query_hw_capability) (struct qlcnic_adapter *, char *);
-       int (*get_hw_capability) (struct qlcnic_adapter *);
-       void (*get_info) (struct qlcnic_adapter *);
-       int (*query_cee_param) (struct qlcnic_adapter *, char *, u8);
-       int (*get_cee_cfg) (struct qlcnic_adapter *);
-       int (*register_aen) (struct qlcnic_adapter *, bool);
-       void (*handle_aen) (struct qlcnic_adapter *, void *);
+       int (*query_hw_capability) (struct qlcnic_dcb *, char *);
+       int (*get_hw_capability) (struct qlcnic_dcb *);
+       int (*query_cee_param) (struct qlcnic_dcb *, char *, u8);
+       void (*init_dcbnl_ops) (struct qlcnic_dcb *);
+       int (*register_aen) (struct qlcnic_dcb *, bool);
+       void (*aen_handler) (struct qlcnic_dcb *, void *);
+       int (*get_cee_cfg) (struct qlcnic_dcb *);
+       void (*get_info) (struct qlcnic_dcb *);
+       int (*attach) (struct qlcnic_dcb *);
+       void (*free) (struct qlcnic_dcb *);
 };
 
 struct qlcnic_dcb {
@@ -37,5 +40,85 @@ struct qlcnic_dcb {
        struct workqueue_struct         *wq;
        struct qlcnic_dcb_ops           *ops;
        struct qlcnic_dcb_cfg           *cfg;
+       unsigned long                   state;
 };
+
+static inline void qlcnic_clear_dcb_ops(struct qlcnic_dcb *dcb)
+{
+       kfree(dcb);
+       dcb = NULL;
+}
+
+static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_dcb *dcb)
+{
+       if (dcb && dcb->ops->get_hw_capability)
+               return dcb->ops->get_hw_capability(dcb);
+
+       return 0;
+}
+
+static inline void qlcnic_dcb_free(struct qlcnic_dcb *dcb)
+{
+       if (dcb && dcb->ops->free)
+               dcb->ops->free(dcb);
+}
+
+static inline int qlcnic_dcb_attach(struct qlcnic_dcb *dcb)
+{
+       if (dcb && dcb->ops->attach)
+               return dcb->ops->attach(dcb);
+
+       return 0;
+}
+
+static inline int
+qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *dcb, char *buf)
+{
+       if (dcb && dcb->ops->query_hw_capability)
+               return dcb->ops->query_hw_capability(dcb, buf);
+
+       return 0;
+}
+
+static inline void qlcnic_dcb_get_info(struct qlcnic_dcb *dcb)
+{
+       if (dcb && dcb->ops->get_info)
+               dcb->ops->get_info(dcb);
+}
+
+static inline int
+qlcnic_dcb_query_cee_param(struct qlcnic_dcb *dcb, char *buf, u8 type)
+{
+       if (dcb && dcb->ops->query_cee_param)
+               return dcb->ops->query_cee_param(dcb, buf, type);
+
+       return 0;
+}
+
+static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_dcb *dcb)
+{
+       if (dcb && dcb->ops->get_cee_cfg)
+               return dcb->ops->get_cee_cfg(dcb);
+
+       return 0;
+}
+
+static inline void
+qlcnic_dcb_register_aen(struct qlcnic_dcb *dcb, u8 flag)
+{
+       if (dcb && dcb->ops->register_aen)
+               dcb->ops->register_aen(dcb, flag);
+}
+
+static inline void qlcnic_dcb_aen_handler(struct qlcnic_dcb *dcb, void *msg)
+{
+       if (dcb && dcb->ops->aen_handler)
+               dcb->ops->aen_handler(dcb, msg);
+}
+
+static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_dcb *dcb)
+{
+       if (dcb && dcb->ops->init_dcbnl_ops)
+               dcb->ops->init_dcbnl_ops(dcb);
+}
 #endif
index 4d7ad00..b36c02f 100644 (file)
@@ -27,43 +27,36 @@ static const u32 qlcnic_fw_dump_level[] = {
 };
 
 static const struct qlcnic_stats qlcnic_gstrings_stats[] = {
+       {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)},
+       {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)},
        {"xmit_called", QLC_SIZEOF(stats.xmitcalled),
-               QLC_OFF(stats.xmitcalled)},
+        QLC_OFF(stats.xmitcalled)},
        {"xmit_finished", QLC_SIZEOF(stats.xmitfinished),
-               QLC_OFF(stats.xmitfinished)},
-       {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)},
+        QLC_OFF(stats.xmitfinished)},
+       {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error),
+        QLC_OFF(stats.tx_dma_map_error)},
+       {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)},
        {"tx_dropped", QLC_SIZEOF(stats.txdropped), QLC_OFF(stats.txdropped)},
-       {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)},
+       {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error),
+        QLC_OFF(stats.rx_dma_map_error)},
        {"rx_pkts", QLC_SIZEOF(stats.rx_pkts), QLC_OFF(stats.rx_pkts)},
-       {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)},
        {"rx_bytes", QLC_SIZEOF(stats.rxbytes), QLC_OFF(stats.rxbytes)},
-       {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)},
+       {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)},
+       {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)},
+       {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)},
+       {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)},
        {"lrobytes", QLC_SIZEOF(stats.lrobytes), QLC_OFF(stats.lrobytes)},
        {"lso_frames", QLC_SIZEOF(stats.lso_frames), QLC_OFF(stats.lso_frames)},
-       {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)},
-       {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)},
        {"skb_alloc_failure", QLC_SIZEOF(stats.skb_alloc_failure),
         QLC_OFF(stats.skb_alloc_failure)},
-       {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)},
-       {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error),
-                                        QLC_OFF(stats.rx_dma_map_error)},
-       {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error),
-                                        QLC_OFF(stats.tx_dma_map_error)},
        {"mac_filter_limit_overrun", QLC_SIZEOF(stats.mac_filter_limit_overrun),
-                               QLC_OFF(stats.mac_filter_limit_overrun)},
+        QLC_OFF(stats.mac_filter_limit_overrun)},
        {"spurious intr", QLC_SIZEOF(stats.spurious_intr),
         QLC_OFF(stats.spurious_intr)},
 
 };
 
 static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
-       "rx unicast frames",
-       "rx multicast frames",
-       "rx broadcast frames",
-       "rx dropped frames",
-       "rx errors",
-       "rx local frames",
-       "rx numbytes",
        "tx unicast frames",
        "tx multicast frames",
        "tx broadcast frames",
@@ -71,6 +64,13 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
        "tx errors",
        "tx local frames",
        "tx numbytes",
+       "rx unicast frames",
+       "rx multicast frames",
+       "rx broadcast frames",
+       "rx dropped frames",
+       "rx errors",
+       "rx local frames",
+       "rx numbytes",
 };
 
 static const char qlcnic_83xx_tx_stats_strings[][ETH_GSTRING_LEN] = {
@@ -126,13 +126,16 @@ static const char qlcnic_83xx_mac_stats_strings[][ETH_GSTRING_LEN] = {
 
 #define QLCNIC_STATS_LEN       ARRAY_SIZE(qlcnic_gstrings_stats)
 
-static const char qlcnic_tx_ring_stats_strings[][ETH_GSTRING_LEN] = {
+static const char qlcnic_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {
        "xmit_on",
        "xmit_off",
        "xmit_called",
        "xmit_finished",
+       "tx_bytes",
 };
 
+#define QLCNIC_TX_STATS_LEN    ARRAY_SIZE(qlcnic_tx_queue_stats_strings)
+
 static const char qlcnic_83xx_rx_stats_strings[][ETH_GSTRING_LEN] = {
        "ctx_rx_bytes",
        "ctx_rx_pkts",
@@ -187,8 +190,8 @@ static int qlcnic_dev_statistics_len(struct qlcnic_adapter *adapter)
                return -1;
 }
 
-#define QLCNIC_RING_REGS_COUNT 20
-#define QLCNIC_RING_REGS_LEN   (QLCNIC_RING_REGS_COUNT * sizeof(u32))
+#define        QLCNIC_TX_INTR_NOT_CONFIGURED   0X78563412
+
 #define QLCNIC_MAX_EEPROM_LEN   1024
 
 static const u32 diag_registers[] = {
@@ -219,7 +222,15 @@ static const u32 ext_diag_registers[] = {
 };
 
 #define QLCNIC_MGMT_API_VERSION        2
-#define QLCNIC_ETHTOOL_REGS_VER        3
+#define QLCNIC_ETHTOOL_REGS_VER        4
+
+static inline int qlcnic_get_ring_regs_len(struct qlcnic_adapter *adapter)
+{
+       int ring_regs_cnt = (adapter->drv_tx_rings * 5) +
+                           (adapter->max_rds_rings * 2) +
+                           (adapter->drv_sds_rings * 3) + 5;
+       return ring_regs_cnt * sizeof(u32);
+}
 
 static int qlcnic_get_regs_len(struct net_device *dev)
 {
@@ -231,7 +242,9 @@ static int qlcnic_get_regs_len(struct net_device *dev)
        else
                len = sizeof(ext_diag_registers) + sizeof(diag_registers);
 
-       return QLCNIC_RING_REGS_LEN + len + QLCNIC_DEV_INFO_SIZE + 1;
+       len += ((QLCNIC_DEV_INFO_SIZE + 2) * sizeof(u32));
+       len += qlcnic_get_ring_regs_len(adapter);
+       return len;
 }
 
 static int qlcnic_get_eeprom_len(struct net_device *dev)
@@ -493,6 +506,8 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
        struct qlcnic_host_sds_ring *sds_ring;
+       struct qlcnic_host_rds_ring *rds_rings;
+       struct qlcnic_host_tx_ring *tx_ring;
        u32 *regs_buff = p;
        int ring, i = 0;
 
@@ -512,21 +527,35 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
        if (!test_bit(__QLCNIC_DEV_UP, &adapter->state))
                return;
 
-       regs_buff[i++] = 0xFFEFCDAB; /* Marker btw regs and ring count*/
-
-       regs_buff[i++] = 1; /* No. of tx ring */
-       regs_buff[i++] = le32_to_cpu(*(adapter->tx_ring->hw_consumer));
-       regs_buff[i++] = readl(adapter->tx_ring->crb_cmd_producer);
-
-       regs_buff[i++] = 2; /* No. of rx ring */
-       regs_buff[i++] = readl(recv_ctx->rds_rings[0].crb_rcv_producer);
-       regs_buff[i++] = readl(recv_ctx->rds_rings[1].crb_rcv_producer);
+       /* Marker btw regs and TX ring count */
+       regs_buff[i++] = 0xFFEFCDAB;
+
+       regs_buff[i++] = adapter->drv_tx_rings; /* No. of TX ring */
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
+               tx_ring = &adapter->tx_ring[ring];
+               regs_buff[i++] = le32_to_cpu(*(tx_ring->hw_consumer));
+               regs_buff[i++] = tx_ring->sw_consumer;
+               regs_buff[i++] = readl(tx_ring->crb_cmd_producer);
+               regs_buff[i++] = tx_ring->producer;
+               if (tx_ring->crb_intr_mask)
+                       regs_buff[i++] = readl(tx_ring->crb_intr_mask);
+               else
+                       regs_buff[i++] = QLCNIC_TX_INTR_NOT_CONFIGURED;
+       }
 
-       regs_buff[i++] = adapter->max_sds_rings;
+       regs_buff[i++] = adapter->max_rds_rings; /* No. of RX ring */
+       for (ring = 0; ring < adapter->max_rds_rings; ring++) {
+               rds_rings = &recv_ctx->rds_rings[ring];
+               regs_buff[i++] = readl(rds_rings->crb_rcv_producer);
+               regs_buff[i++] = rds_rings->producer;
+       }
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       regs_buff[i++] = adapter->drv_sds_rings; /* No. of SDS ring */
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &(recv_ctx->sds_rings[ring]);
                regs_buff[i++] = readl(sds_ring->crb_sts_consumer);
+               regs_buff[i++] = sds_ring->consumer;
+               regs_buff[i++] = readl(sds_ring->crb_intr_mask);
        }
 }
 
@@ -635,46 +664,88 @@ qlcnic_set_ringparam(struct net_device *dev,
        return qlcnic_reset_context(adapter);
 }
 
+static int qlcnic_validate_ring_count(struct qlcnic_adapter *adapter,
+                                     u8 rx_ring, u8 tx_ring)
+{
+       if (rx_ring != 0) {
+               if (rx_ring > adapter->max_sds_rings) {
+                       netdev_err(adapter->netdev, "Invalid ring count, SDS ring count %d should not be greater than max %d driver sds rings.\n",
+                                  rx_ring, adapter->max_sds_rings);
+                       return -EINVAL;
+               }
+       }
+
+        if (tx_ring != 0) {
+               if (qlcnic_82xx_check(adapter) &&
+                   (tx_ring > adapter->max_tx_rings)) {
+                       netdev_err(adapter->netdev,
+                                  "Invalid ring count, Tx ring count %d should not be greater than max %d driver Tx rings.\n",
+                                  tx_ring, adapter->max_tx_rings);
+                       return -EINVAL;
+               }
+
+               if (qlcnic_83xx_check(adapter) &&
+                   (tx_ring > QLCNIC_SINGLE_RING)) {
+                       netdev_err(adapter->netdev,
+                                  "Invalid ring count, Tx ring count %d should not be greater than %d driver Tx rings.\n",
+                                  tx_ring, QLCNIC_SINGLE_RING);
+                        return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static void qlcnic_get_channels(struct net_device *dev,
                struct ethtool_channels *channel)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
-       int min;
-
-       min = min_t(int, adapter->ahw->max_rx_ques, num_online_cpus());
-       channel->max_rx = rounddown_pow_of_two(min);
-       channel->max_tx = min_t(int, QLCNIC_MAX_TX_RINGS, num_online_cpus());
 
-       channel->rx_count = adapter->max_sds_rings;
-       channel->tx_count = adapter->max_drv_tx_rings;
+       channel->max_rx = adapter->max_sds_rings;
+       channel->max_tx = adapter->max_tx_rings;
+       channel->rx_count = adapter->drv_sds_rings;
+       channel->tx_count = adapter->drv_tx_rings;
 }
 
 static int qlcnic_set_channels(struct net_device *dev,
-               struct ethtool_channels *channel)
+                              struct ethtool_channels *channel)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        int err;
-       int txq = 0;
 
        if (channel->other_count || channel->combined_count)
                return -EINVAL;
 
+       err = qlcnic_validate_ring_count(adapter, channel->rx_count,
+                                        channel->tx_count);
+       if (err)
+               return err;
+
        if (channel->rx_count) {
-               err = qlcnic_validate_max_rss(adapter, channel->rx_count);
-               if (err)
+               err = qlcnic_validate_rings(adapter, channel->rx_count,
+                                           QLCNIC_RX_QUEUE);
+               if (err) {
+                       netdev_err(dev, "Unable to configure %u SDS rings\n",
+                                  channel->rx_count);
                        return err;
+               }
        }
 
        if (channel->tx_count) {
-               err = qlcnic_validate_max_tx_rings(adapter, channel->tx_count);
-               if (err)
+               err = qlcnic_validate_rings(adapter, channel->tx_count,
+                                           QLCNIC_TX_QUEUE);
+               if (err) {
+                       netdev_err(dev, "Unable to configure %u Tx rings\n",
+                                  channel->tx_count);
                        return err;
-               txq = channel->tx_count;
+               }
        }
 
-       err = qlcnic_set_max_rss(adapter, channel->rx_count, txq);
-       netdev_info(dev, "allocated 0x%x sds rings and  0x%x tx rings\n",
-                   adapter->max_sds_rings, adapter->max_drv_tx_rings);
+       err = qlcnic_setup_rings(adapter, channel->rx_count,
+                                channel->tx_count);
+       netdev_info(dev, "Allocated %d SDS rings and %d Tx rings\n",
+                   adapter->drv_sds_rings, adapter->drv_tx_rings);
+
        return err;
 }
 
@@ -876,7 +947,7 @@ static int qlcnic_irq_test(struct net_device *netdev)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        struct qlcnic_cmd_args cmd;
-       int ret, max_sds_rings = adapter->max_sds_rings;
+       int ret, drv_sds_rings = adapter->drv_sds_rings;
 
        if (qlcnic_83xx_check(adapter))
                return qlcnic_83xx_interrupt_test(netdev);
@@ -905,10 +976,10 @@ done:
        qlcnic_free_mbx_args(&cmd);
 
 free_diag_res:
-       qlcnic_diag_free_res(netdev, max_sds_rings);
+       qlcnic_diag_free_res(netdev, drv_sds_rings);
 
 clear_diag_irq:
-       adapter->max_sds_rings = max_sds_rings;
+       adapter->drv_sds_rings = drv_sds_rings;
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
 
        return ret;
@@ -984,8 +1055,8 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode)
 int qlcnic_loopback_test(struct net_device *netdev, u8 mode)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
-       int max_drv_tx_rings = adapter->max_drv_tx_rings;
-       int max_sds_rings = adapter->max_sds_rings;
+       int drv_tx_rings = adapter->drv_tx_rings;
+       int drv_sds_rings = adapter->drv_sds_rings;
        struct qlcnic_host_sds_ring *sds_ring;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        int loop = 0;
@@ -1040,11 +1111,11 @@ int qlcnic_loopback_test(struct net_device *netdev, u8 mode)
        qlcnic_clear_lb_mode(adapter, mode);
 
  free_res:
-       qlcnic_diag_free_res(netdev, max_sds_rings);
+       qlcnic_diag_free_res(netdev, drv_sds_rings);
 
  clear_it:
-       adapter->max_sds_rings = max_sds_rings;
-       adapter->max_drv_tx_rings = max_drv_tx_rings;
+       adapter->drv_sds_rings = drv_sds_rings;
+       adapter->drv_tx_rings = drv_tx_rings;
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
        return ret;
 }
@@ -1097,11 +1168,11 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
                       QLCNIC_TEST_LEN * ETH_GSTRING_LEN);
                break;
        case ETH_SS_STATS:
-               num_stats = ARRAY_SIZE(qlcnic_tx_ring_stats_strings);
-               for (i = 0; i < adapter->max_drv_tx_rings; i++) {
+               num_stats = ARRAY_SIZE(qlcnic_tx_queue_stats_strings);
+               for (i = 0; i < adapter->drv_tx_rings; i++) {
                        for (index = 0; index < num_stats; index++) {
-                               sprintf(data, "tx_ring_%d %s", i,
-                                       qlcnic_tx_ring_stats_strings[index]);
+                               sprintf(data, "tx_queue_%d %s", i,
+                                       qlcnic_tx_queue_stats_strings[index]);
                                data += ETH_GSTRING_LEN;
                        }
                }
@@ -1199,6 +1270,36 @@ static u64 *qlcnic_fill_stats(u64 *data, void *stats, int type)
        return data;
 }
 
+static void qlcnic_update_stats(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_host_tx_ring *tx_ring;
+       int ring;
+
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
+               tx_ring = &adapter->tx_ring[ring];
+               adapter->stats.xmit_on += tx_ring->tx_stats.xmit_on;
+               adapter->stats.xmit_off += tx_ring->tx_stats.xmit_off;
+               adapter->stats.xmitcalled += tx_ring->tx_stats.xmit_called;
+               adapter->stats.xmitfinished += tx_ring->tx_stats.xmit_finished;
+               adapter->stats.txbytes += tx_ring->tx_stats.tx_bytes;
+       }
+}
+
+static u64 *qlcnic_fill_tx_queue_stats(u64 *data, void *stats)
+{
+       struct qlcnic_host_tx_ring *tx_ring;
+
+       tx_ring = (struct qlcnic_host_tx_ring *)stats;
+
+       *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_on);
+       *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_off);
+       *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_called);
+       *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_finished);
+       *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.tx_bytes);
+
+       return data;
+}
+
 static void qlcnic_get_ethtool_stats(struct net_device *dev,
                                     struct ethtool_stats *stats, u64 *data)
 {
@@ -1206,19 +1307,20 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,
        struct qlcnic_host_tx_ring *tx_ring;
        struct qlcnic_esw_statistics port_stats;
        struct qlcnic_mac_statistics mac_stats;
-       int index, ret, length, size, ring;
+       int index, ret, length, size, tx_size, ring;
        char *p;
 
-       memset(data, 0, adapter->max_drv_tx_rings * 4 * sizeof(u64));
-       for (ring = 0, index = 0; ring < adapter->max_drv_tx_rings; ring++) {
+       tx_size = adapter->drv_tx_rings * QLCNIC_TX_STATS_LEN;
+
+       memset(data, 0, tx_size * sizeof(u64));
+       for (ring = 0, index = 0; ring < adapter->drv_tx_rings; ring++) {
                if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
                        tx_ring = &adapter->tx_ring[ring];
-                       *data++ = tx_ring->xmit_on;
-                       *data++ = tx_ring->xmit_off;
-                       *data++ = tx_ring->xmit_called;
-                       *data++ = tx_ring->xmit_finished;
+                       data = qlcnic_fill_tx_queue_stats(data, tx_ring);
+                       qlcnic_update_stats(adapter);
                }
        }
+
        memset(data, 0, stats->n_stats * sizeof(u64));
        length = QLCNIC_STATS_LEN;
        for (index = 0; index < length; index++) {
@@ -1260,7 +1362,7 @@ static int qlcnic_set_led(struct net_device *dev,
                          enum ethtool_phys_id_state state)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
-       int max_sds_rings = adapter->max_sds_rings;
+       int drv_sds_rings = adapter->drv_sds_rings;
        int err = -EIO, active = 1;
 
        if (qlcnic_83xx_check(adapter))
@@ -1318,7 +1420,7 @@ static int qlcnic_set_led(struct net_device *dev,
        }
 
        if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
-               qlcnic_diag_free_res(dev, max_sds_rings);
+               qlcnic_diag_free_res(dev, drv_sds_rings);
 
        if (!active || err)
                clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
@@ -1659,7 +1761,6 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
        struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
        bool valid_mask = false;
        int i, ret = 0;
-       u32 state;
 
        switch (val->flag) {
        case QLCNIC_FORCE_FW_DUMP_KEY:
@@ -1712,9 +1813,8 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
 
        case QLCNIC_SET_QUIESCENT:
        case QLCNIC_RESET_QUIESCENT:
-               state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
-               if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD))
-                       netdev_info(netdev, "Device in FAILED state\n");
+               if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state))
+                       netdev_info(netdev, "Device is in non-operational state\n");
                break;
 
        default:
@@ -1794,3 +1894,11 @@ const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops = {
        .set_msglevel           = qlcnic_set_msglevel,
        .get_msglevel           = qlcnic_get_msglevel,
 };
+
+const struct ethtool_ops qlcnic_ethtool_failed_ops = {
+       .get_settings           = qlcnic_get_settings,
+       .get_drvinfo            = qlcnic_get_drvinfo,
+       .set_msglevel           = qlcnic_set_msglevel,
+       .get_msglevel           = qlcnic_get_msglevel,
+       .set_dump               = qlcnic_set_dump,
+};
index f8adc7b..6f7f60c 100644 (file)
@@ -445,7 +445,7 @@ int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
 
        mac_req = (struct qlcnic_mac_req *)&req.words[0];
        mac_req->op = op;
-       memcpy(mac_req->mac_addr, addr, 6);
+       memcpy(mac_req->mac_addr, addr, ETH_ALEN);
 
        vlan_req = (struct qlcnic_vlan_req *)&req.words[1];
        vlan_req->vlan_id = cpu_to_le16(vlan_id);
@@ -785,8 +785,6 @@ void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter)
 
 #define QLCNIC_ENABLE_IPV4_LRO         1
 #define QLCNIC_ENABLE_IPV6_LRO         2
-#define QLCNIC_NO_DEST_IPV4_CHECK      (1 << 8)
-#define QLCNIC_NO_DEST_IPV6_CHECK      (2 << 8)
 
 int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
 {
@@ -806,11 +804,10 @@ int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
 
        word = 0;
        if (enable) {
-               word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK;
+               word = QLCNIC_ENABLE_IPV4_LRO;
                if (adapter->ahw->extra_capability[0] &
                    QLCNIC_FW_CAP2_HW_LRO_IPV6)
-                       word |= QLCNIC_ENABLE_IPV6_LRO |
-                               QLCNIC_NO_DEST_IPV6_CHECK;
+                       word |= QLCNIC_ENABLE_IPV6_LRO;
        }
 
        req.words[0] = cpu_to_le64(word);
index 272c356..13303e7 100644 (file)
@@ -146,6 +146,12 @@ struct qlcnic_mailbox_metadata {
 #define QLCNIC_MBX_PORT_RSP_OK 0x1a
 #define QLCNIC_MBX_ASYNC_EVENT BIT_15
 
+/* Set HW Tx ring limit for 82xx adapter. */
+#define QLCNIC_MAX_HW_TX_RINGS         8
+#define QLCNIC_MAX_HW_VNIC_TX_RINGS    4
+#define QLCNIC_MAX_TX_RINGS            8
+#define QLCNIC_MAX_SDS_RINGS           8
+
 struct qlcnic_pci_info;
 struct qlcnic_info;
 struct qlcnic_cmd_args;
@@ -176,7 +182,7 @@ int qlcnic_82xx_set_lb_mode(struct qlcnic_adapter *, u8);
 void qlcnic_82xx_write_crb(struct qlcnic_adapter *, char *, loff_t, size_t);
 void qlcnic_82xx_read_crb(struct qlcnic_adapter *, char *, loff_t, size_t);
 void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *, u32);
-int qlcnic_82xx_setup_intr(struct qlcnic_adapter *, u8, int);
+int qlcnic_82xx_setup_intr(struct qlcnic_adapter *);
 irqreturn_t qlcnic_82xx_clear_legacy_intr(struct qlcnic_adapter *);
 int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
                          struct qlcnic_cmd_args *);
index 66c26cf..e9c21e5 100644 (file)
@@ -236,7 +236,7 @@ int qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter)
                spin_lock_init(&rds_ring->lock);
        }
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                sds_ring->irq = adapter->msix_entries[ring].vector;
                sds_ring->adapter = adapter;
index 11b4bb8..0149c94 100644 (file)
@@ -581,10 +581,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
                        goto drop_packet;
        }
 
-       if (qlcnic_check_multi_tx(adapter))
-               tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)];
-       else
-               tx_ring = &adapter->tx_ring[0];
+       tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)];
        num_txd = tx_ring->num_desc;
 
        frag_count = skb_shinfo(skb)->nr_frags + 1;
@@ -607,8 +604,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
                if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
                        netif_tx_start_queue(tx_ring->txq);
                } else {
-                       adapter->stats.xmit_off++;
-                       tx_ring->xmit_off++;
+                       tx_ring->tx_stats.xmit_off++;
                        return NETDEV_TX_BUSY;
                }
        }
@@ -669,9 +665,8 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
        if (adapter->drv_mac_learn)
                qlcnic_send_filter(adapter, first_desc, skb);
 
-       adapter->stats.txbytes += skb->len;
-       adapter->stats.xmitcalled++;
-       tx_ring->xmit_called++;
+       tx_ring->tx_stats.tx_bytes += skb->len;
+       tx_ring->tx_stats.xmit_called++;
 
        qlcnic_update_cmd_producer(tx_ring);
 
@@ -789,6 +784,9 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
        struct net_device *netdev = adapter->netdev;
        struct qlcnic_skb_frag *frag;
 
+       if (!spin_trylock(&adapter->tx_clean_lock))
+               return 1;
+
        sw_consumer = tx_ring->sw_consumer;
        hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
 
@@ -805,8 +803,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
                                               PCI_DMA_TODEVICE);
                                frag->dma = 0ULL;
                        }
-                       adapter->stats.xmitfinished++;
-                       tx_ring->xmit_finished++;
+                       tx_ring->tx_stats.xmit_finished++;
                        dev_kfree_skb_any(buffer->skb);
                        buffer->skb = NULL;
                }
@@ -823,8 +820,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
                    netif_carrier_ok(netdev)) {
                        if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
                                netif_tx_wake_queue(tx_ring->txq);
-                               adapter->stats.xmit_on++;
-                               tx_ring->xmit_on++;
+                               tx_ring->tx_stats.xmit_on++;
                        }
                }
                adapter->tx_timeo_cnt = 0;
@@ -844,6 +840,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
         */
        hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
        done = (sw_consumer == hw_consumer);
+       spin_unlock(&adapter->tx_clean_lock);
 
        return done;
 }
@@ -1011,7 +1008,7 @@ static void qlcnic_handle_fw_message(int desc_cnt, int index,
                }
                break;
        case QLCNIC_C2H_OPCODE_GET_DCB_AEN:
-               qlcnic_dcb_handle_aen(adapter, (void *)&msg);
+               qlcnic_dcb_aen_handler(adapter->dcb, (void *)&msg);
                break;
        default:
                break;
@@ -1463,18 +1460,18 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
        struct qlcnic_host_tx_ring *tx_ring;
 
-       if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings))
+       if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
                return -ENOMEM;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                if (qlcnic_check_multi_tx(adapter) &&
                    !adapter->ahw->diag_test &&
-                   (adapter->max_drv_tx_rings > 1)) {
+                   (adapter->drv_tx_rings > QLCNIC_SINGLE_RING)) {
                        netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll,
                                       NAPI_POLL_WEIGHT);
                } else {
-                       if (ring == (adapter->max_sds_rings - 1))
+                       if (ring == (adapter->drv_sds_rings - 1))
                                netif_napi_add(netdev, &sds_ring->napi,
                                               qlcnic_poll,
                                               NAPI_POLL_WEIGHT);
@@ -1491,7 +1488,7 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
        }
 
        if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        netif_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll,
                                       NAPI_POLL_WEIGHT);
@@ -1508,7 +1505,7 @@ void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter)
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
        struct qlcnic_host_tx_ring *tx_ring;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                netif_napi_del(&sds_ring->napi);
        }
@@ -1516,7 +1513,7 @@ void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter)
        qlcnic_free_sds_rings(adapter->recv_ctx);
 
        if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        netif_napi_del(&tx_ring->napi);
                }
@@ -1535,7 +1532,7 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
        if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
                return;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                napi_enable(&sds_ring->napi);
                qlcnic_enable_int(sds_ring);
@@ -1544,8 +1541,8 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
        if (qlcnic_check_multi_tx(adapter) &&
            (adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !adapter->ahw->diag_test &&
-           (adapter->max_drv_tx_rings > 1)) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+           (adapter->drv_tx_rings > QLCNIC_SINGLE_RING)) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        napi_enable(&tx_ring->napi);
                        qlcnic_enable_tx_intr(adapter, tx_ring);
@@ -1563,7 +1560,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
        if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
                return;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                qlcnic_disable_int(sds_ring);
                napi_synchronize(&sds_ring->napi);
@@ -1573,7 +1570,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
        if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !adapter->ahw->diag_test &&
            qlcnic_check_multi_tx(adapter)) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        qlcnic_disable_tx_int(adapter, tx_ring);
                        napi_synchronize(&tx_ring->napi);
@@ -1911,7 +1908,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
        if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
                return;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                napi_enable(&sds_ring->napi);
                if (adapter->flags & QLCNIC_MSIX_ENABLED)
@@ -1920,7 +1917,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
 
        if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        napi_enable(&tx_ring->napi);
                        qlcnic_83xx_enable_tx_intr(adapter, tx_ring);
@@ -1938,7 +1935,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
        if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
                return;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                if (adapter->flags & QLCNIC_MSIX_ENABLED)
                        qlcnic_83xx_disable_intr(adapter, sds_ring);
@@ -1948,7 +1945,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
 
        if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        qlcnic_83xx_disable_tx_intr(adapter, tx_ring);
                        napi_synchronize(&tx_ring->napi);
@@ -1965,10 +1962,10 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
        struct qlcnic_host_tx_ring *tx_ring;
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 
-       if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings))
+       if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
                return -ENOMEM;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                if (adapter->flags & QLCNIC_MSIX_ENABLED) {
                        if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
@@ -1994,7 +1991,7 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
 
        if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        netif_napi_add(netdev, &tx_ring->napi,
                                       qlcnic_83xx_msix_tx_poll,
@@ -2012,7 +2009,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
        struct qlcnic_host_tx_ring *tx_ring;
 
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                netif_napi_del(&sds_ring->napi);
        }
@@ -2021,7 +2018,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
 
        if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
            !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        netif_napi_del(&tx_ring->napi);
                }
index c4c5023..05c1eef 100644 (file)
@@ -431,6 +431,9 @@ static void qlcnic_82xx_cancel_idc_work(struct qlcnic_adapter *adapter)
        while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
                usleep_range(10000, 11000);
 
+       if (!adapter->fw_work.work.func)
+               return;
+
        cancel_delayed_work_sync(&adapter->fw_work);
 }
 
@@ -545,36 +548,75 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
        .io_resume                      = qlcnic_82xx_io_resume,
 };
 
-static void qlcnic_get_multiq_capability(struct qlcnic_adapter *adapter)
+static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
-       int num_tx_q;
 
-       if (ahw->msix_supported &&
+       if (qlcnic_82xx_check(adapter) &&
            (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_MULTI_TX)) {
-               num_tx_q = min_t(int, QLCNIC_DEF_NUM_TX_RINGS,
-                                num_online_cpus());
-               if (num_tx_q > 1) {
-                       test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE,
-                                        &adapter->state);
-                       adapter->max_drv_tx_rings = num_tx_q;
-               }
+               test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
+               return 0;
        } else {
-               adapter->max_drv_tx_rings = 1;
+               return 1;
        }
 }
 
+static int qlcnic_max_rings(struct qlcnic_adapter *adapter, u8 ring_cnt,
+                           int queue_type)
+{
+       int num_rings, max_rings = QLCNIC_MAX_SDS_RINGS;
+
+       if (queue_type == QLCNIC_RX_QUEUE)
+               max_rings = adapter->max_sds_rings;
+       else if (queue_type == QLCNIC_TX_QUEUE)
+               max_rings = adapter->max_tx_rings;
+
+       num_rings = rounddown_pow_of_two(min_t(int, num_online_cpus(),
+                                             max_rings));
+
+       if (ring_cnt > num_rings)
+               return num_rings;
+       else
+               return ring_cnt;
+}
+
+void qlcnic_set_tx_ring_count(struct qlcnic_adapter *adapter, u8 tx_cnt)
+{
+       /* 83xx adapter does not have max_tx_rings intialized in probe */
+       if (adapter->max_tx_rings)
+               adapter->drv_tx_rings = qlcnic_max_rings(adapter, tx_cnt,
+                                                        QLCNIC_TX_QUEUE);
+       else
+               adapter->drv_tx_rings = tx_cnt;
+
+       dev_info(&adapter->pdev->dev, "Set %d Tx rings\n",
+                adapter->drv_tx_rings);
+}
+
+void qlcnic_set_sds_ring_count(struct qlcnic_adapter *adapter, u8 rx_cnt)
+{
+       /* 83xx adapter does not have max_sds_rings intialized in probe */
+       if (adapter->max_sds_rings)
+               adapter->drv_sds_rings = qlcnic_max_rings(adapter, rx_cnt,
+                                                         QLCNIC_RX_QUEUE);
+       else
+               adapter->drv_sds_rings = rx_cnt;
+
+       dev_info(&adapter->pdev->dev, "Set %d SDS rings\n",
+                adapter->drv_sds_rings);
+}
+
 int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
 {
        struct pci_dev *pdev = adapter->pdev;
-       int max_tx_rings, max_sds_rings, tx_vector;
+       int drv_tx_rings, drv_sds_rings, tx_vector;
        int err = -1, i;
 
        if (adapter->flags & QLCNIC_TX_INTR_SHARED) {
-               max_tx_rings = 0;
+               drv_tx_rings = 0;
                tx_vector = 0;
        } else {
-               max_tx_rings = adapter->max_drv_tx_rings;
+               drv_tx_rings = adapter->drv_tx_rings;
                tx_vector = 1;
        }
 
@@ -586,7 +628,7 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
                        return -ENOMEM;
        }
 
-       adapter->max_sds_rings = 1;
+       adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
        adapter->flags &= ~(QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED);
 
        if (adapter->ahw->msix_supported) {
@@ -599,18 +641,18 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
                        if (qlcnic_83xx_check(adapter)) {
                                adapter->ahw->num_msix = num_msix;
                                /* subtract mail box and tx ring vectors */
-                               adapter->max_sds_rings = num_msix -
-                                                        max_tx_rings - 1;
+                               adapter->drv_sds_rings = num_msix -
+                                                        drv_tx_rings - 1;
                        } else {
                                adapter->ahw->num_msix = num_msix;
                                if (qlcnic_check_multi_tx(adapter) &&
                                    !adapter->ahw->diag_test &&
-                                   (adapter->max_drv_tx_rings > 1))
-                                       max_sds_rings = num_msix - max_tx_rings;
+                                   (adapter->drv_tx_rings > 1))
+                                       drv_sds_rings = num_msix - drv_tx_rings;
                                else
-                                       max_sds_rings = num_msix;
+                                       drv_sds_rings = num_msix;
 
-                               adapter->max_sds_rings = max_sds_rings;
+                               adapter->drv_sds_rings = drv_sds_rings;
                        }
                        dev_info(&pdev->dev, "using msi-x interrupts\n");
                        return err;
@@ -621,13 +663,13 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
                        if (qlcnic_83xx_check(adapter)) {
                                if (err < (QLC_83XX_MINIMUM_VECTOR - tx_vector))
                                        return err;
-                               err -= (max_tx_rings + 1);
+                               err -= drv_tx_rings + 1;
                                num_msix = rounddown_pow_of_two(err);
-                               num_msix += (max_tx_rings + 1);
+                               num_msix += drv_tx_rings + 1;
                        } else {
                                num_msix = rounddown_pow_of_two(err);
                                if (qlcnic_check_multi_tx(adapter))
-                                       num_msix += max_tx_rings;
+                                       num_msix += drv_tx_rings;
                        }
 
                        if (num_msix) {
@@ -680,25 +722,14 @@ static int qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
        return err;
 }
 
-int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq)
+int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter)
 {
-       struct qlcnic_hardware_context *ahw = adapter->ahw;
        int num_msix, err = 0;
 
-       if (!num_intr)
-               num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS;
+       num_msix = adapter->drv_sds_rings;
 
-       if (ahw->msix_supported) {
-               num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(),
-                                               num_intr));
-               if (qlcnic_check_multi_tx(adapter)) {
-                       if (txq)
-                               adapter->max_drv_tx_rings = txq;
-                       num_msix += adapter->max_drv_tx_rings;
-               }
-       } else {
-               num_msix = 1;
-       }
+       if (qlcnic_check_multi_tx(adapter))
+               num_msix += adapter->drv_tx_rings;
 
        err = qlcnic_enable_msix(adapter, num_msix);
        if (err == -ENOMEM)
@@ -816,7 +847,7 @@ static bool qlcnic_port_eswitch_cfg_capability(struct qlcnic_adapter *adapter)
 int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_pci_info *pci_info;
-       int i, ret = 0, j = 0;
+       int i, id = 0, ret = 0, j = 0;
        u16 act_pci_func;
        u8 pfn;
 
@@ -857,7 +888,8 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
                        continue;
 
                if (qlcnic_port_eswitch_cfg_capability(adapter)) {
-                       if (!qlcnic_83xx_enable_port_eswitch(adapter, pfn))
+                       if (!qlcnic_83xx_set_port_eswitch_status(adapter, pfn,
+                                                                &id))
                                adapter->npars[j].eswitch_status = true;
                        else
                                continue;
@@ -872,15 +904,16 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
                adapter->npars[j].min_bw = pci_info[i].tx_min_bw;
                adapter->npars[j].max_bw = pci_info[i].tx_max_bw;
 
+               memcpy(&adapter->npars[j].mac, &pci_info[i].mac, ETH_ALEN);
                j++;
        }
 
-       if (qlcnic_82xx_check(adapter)) {
+       /* Update eSwitch status for adapters without per port eSwitch
+        * configuration capability
+        */
+       if (!qlcnic_port_eswitch_cfg_capability(adapter)) {
                for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
                        adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
-       } else if (!qlcnic_port_eswitch_cfg_capability(adapter)) {
-               for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
-                       qlcnic_enable_eswitch(adapter, i, 1);
        }
 
        kfree(pci_info);
@@ -1128,18 +1161,25 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
                if (err == -EIO)
                        return err;
                adapter->ahw->extra_capability[0] = temp;
+       } else {
+               adapter->ahw->extra_capability[0] = 0;
        }
+
        adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
        adapter->ahw->max_mtu = nic_info.max_mtu;
 
-       /* Disable NPAR for 83XX */
-       if (qlcnic_83xx_check(adapter))
-               return err;
-
-       if (adapter->ahw->capabilities & BIT_6)
+       if (adapter->ahw->capabilities & BIT_6) {
                adapter->flags |= QLCNIC_ESWITCH_ENABLED;
-       else
+               adapter->ahw->nic_mode = QLCNIC_VNIC_MODE;
+               adapter->max_tx_rings = QLCNIC_MAX_HW_VNIC_TX_RINGS;
+               adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS;
+
+               dev_info(&adapter->pdev->dev, "vNIC mode enabled.\n");
+       } else {
+               adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE;
+               adapter->max_tx_rings = QLCNIC_MAX_HW_TX_RINGS;
                adapter->flags &= ~QLCNIC_ESWITCH_ENABLED;
+       }
 
        return err;
 }
@@ -1287,6 +1327,8 @@ qlcnic_check_eswitch_mode(struct qlcnic_adapter *adapter)
                                "HAL Version: %d, Privileged function\n",
                                 adapter->ahw->fw_hal_version);
                }
+       } else {
+               adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE;
        }
 
        adapter->flags |= QLCNIC_ADAPTER_INITIALIZED;
@@ -1546,7 +1588,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
                if (qlcnic_82xx_check(adapter) ||
                    (qlcnic_83xx_check(adapter) &&
                     (adapter->flags & QLCNIC_MSIX_ENABLED))) {
-                       num_sds_rings = adapter->max_sds_rings;
+                       num_sds_rings = adapter->drv_sds_rings;
                        for (ring = 0; ring < num_sds_rings; ring++) {
                                sds_ring = &recv_ctx->sds_rings[ring];
                                if (qlcnic_82xx_check(adapter) &&
@@ -1580,7 +1622,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
                     (adapter->flags & QLCNIC_MSIX_ENABLED) &&
                     !(adapter->flags & QLCNIC_TX_INTR_SHARED))) {
                        handler = qlcnic_msix_tx_intr;
-                       for (ring = 0; ring < adapter->max_drv_tx_rings;
+                       for (ring = 0; ring < adapter->drv_tx_rings;
                             ring++) {
                                tx_ring = &adapter->tx_ring[ring];
                                snprintf(tx_ring->name, sizeof(tx_ring->name),
@@ -1608,7 +1650,7 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter)
                if (qlcnic_82xx_check(adapter) ||
                    (qlcnic_83xx_check(adapter) &&
                     (adapter->flags & QLCNIC_MSIX_ENABLED))) {
-                       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+                       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                                sds_ring = &recv_ctx->sds_rings[ring];
                                free_irq(sds_ring->irq, sds_ring);
                        }
@@ -1617,7 +1659,7 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter)
                     !(adapter->flags & QLCNIC_TX_INTR_SHARED)) ||
                    (qlcnic_82xx_check(adapter) &&
                     qlcnic_check_multi_tx(adapter))) {
-                       for (ring = 0; ring < adapter->max_drv_tx_rings;
+                       for (ring = 0; ring < adapter->drv_tx_rings;
                             ring++) {
                                tx_ring = &adapter->tx_ring[ring];
                                if (tx_ring->irq)
@@ -1671,7 +1713,7 @@ int __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
 
        adapter->ahw->linkup = 0;
 
-       if (adapter->max_sds_rings > 1)
+       if (adapter->drv_sds_rings > 1)
                qlcnic_config_rss(adapter, 1);
 
        qlcnic_config_intr_coalesce(adapter);
@@ -1713,6 +1755,7 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
        if (qlcnic_sriov_vf_check(adapter))
                qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
        smp_mb();
+       spin_lock(&adapter->tx_clean_lock);
        netif_carrier_off(netdev);
        adapter->ahw->linkup = 0;
        netif_tx_disable(netdev);
@@ -1731,8 +1774,9 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
 
        qlcnic_reset_rx_buffers_list(adapter);
 
-       for (ring = 0; ring < adapter->max_drv_tx_rings; ring++)
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++)
                qlcnic_release_tx_buffers(adapter, &adapter->tx_ring[ring]);
+       spin_unlock(&adapter->tx_clean_lock);
 }
 
 /* Usage: During suspend and firmware recovery module */
@@ -1808,16 +1852,16 @@ void qlcnic_detach(struct qlcnic_adapter *adapter)
        adapter->is_up = 0;
 }
 
-void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings)
+void qlcnic_diag_free_res(struct net_device *netdev, int drv_sds_rings)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_host_sds_ring *sds_ring;
-       int max_tx_rings = adapter->max_drv_tx_rings;
+       int drv_tx_rings = adapter->drv_tx_rings;
        int ring;
 
        clear_bit(__QLCNIC_DEV_UP, &adapter->state);
        if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
-               for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                        sds_ring = &adapter->recv_ctx->sds_rings[ring];
                        qlcnic_disable_int(sds_ring);
                }
@@ -1828,8 +1872,8 @@ void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings)
        qlcnic_detach(adapter);
 
        adapter->ahw->diag_test = 0;
-       adapter->max_sds_rings = max_sds_rings;
-       adapter->max_drv_tx_rings = max_tx_rings;
+       adapter->drv_sds_rings = drv_sds_rings;
+       adapter->drv_tx_rings = drv_tx_rings;
 
        if (qlcnic_attach(adapter))
                goto out;
@@ -1895,10 +1939,10 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
 
        qlcnic_detach(adapter);
 
-       adapter->max_sds_rings = 1;
+       adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
+       adapter->drv_tx_rings = QLCNIC_SINGLE_RING;
        adapter->ahw->diag_test = test;
        adapter->ahw->linkup = 0;
-       adapter->max_drv_tx_rings = 1;
 
        ret = qlcnic_attach(adapter);
        if (ret) {
@@ -1919,7 +1963,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
        }
 
        if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
-               for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                        sds_ring = &adapter->recv_ctx->sds_rings[ring];
                        qlcnic_enable_int(sds_ring);
                }
@@ -2066,7 +2110,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
                return err;
        }
 
-       qlcnic_dcb_init_dcbnl_ops(adapter);
+       qlcnic_dcb_init_dcbnl_ops(adapter->dcb);
 
        return 0;
 }
@@ -2092,7 +2136,7 @@ void qlcnic_free_tx_rings(struct qlcnic_adapter *adapter)
        int ring;
        struct qlcnic_host_tx_ring *tx_ring;
 
-       for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                tx_ring = &adapter->tx_ring[ring];
                if (tx_ring && tx_ring->cmd_buf_arr != NULL) {
                        vfree(tx_ring->cmd_buf_arr);
@@ -2110,14 +2154,14 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
        struct qlcnic_host_tx_ring *tx_ring;
        struct qlcnic_cmd_buffer *cmd_buf_arr;
 
-       tx_ring = kcalloc(adapter->max_drv_tx_rings,
+       tx_ring = kcalloc(adapter->drv_tx_rings,
                          sizeof(struct qlcnic_host_tx_ring), GFP_KERNEL);
        if (tx_ring == NULL)
                return -ENOMEM;
 
        adapter->tx_ring = tx_ring;
 
-       for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                tx_ring = &adapter->tx_ring[ring];
                tx_ring->num_desc = adapter->num_txd;
                tx_ring->txq = netdev_get_tx_queue(netdev, ring);
@@ -2132,11 +2176,11 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
 
        if (qlcnic_83xx_check(adapter) ||
            (qlcnic_82xx_check(adapter) && qlcnic_check_multi_tx(adapter))) {
-               for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
                        tx_ring = &adapter->tx_ring[ring];
                        tx_ring->adapter = adapter;
                        if (adapter->flags & QLCNIC_MSIX_ENABLED) {
-                               index = adapter->max_sds_rings + ring;
+                               index = adapter->drv_sds_rings + ring;
                                vector = adapter->msix_entries[index].vector;
                                tx_ring->irq = vector;
                        }
@@ -2156,22 +2200,10 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
        else if (qlcnic_83xx_check(adapter))
                fw_cmd = QLCNIC_CMD_83XX_SET_DRV_VER;
 
-       if ((ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) &&
-           (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER))
+       if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER)
                qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
 }
 
-static int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
-{
-       return __qlcnic_register_dcb(adapter);
-}
-
-void qlcnic_clear_dcb_ops(struct qlcnic_adapter *adapter)
-{
-       kfree(adapter->dcb);
-       adapter->dcb = NULL;
-}
-
 static int
 qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
@@ -2180,6 +2212,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct qlcnic_hardware_context *ahw;
        int err, pci_using_dac = -1;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
+       struct qlcnic_dcb *dcb;
 
        if (pdev->is_virtfn)
                return -ENODEV;
@@ -2254,7 +2287,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        err = qlcnic_alloc_adapter_resources(adapter);
        if (err)
-               goto err_out_free_netdev;
+               goto err_out_free_wq;
 
        adapter->dev_rst_time = jiffies;
        adapter->ahw->revision_id = pdev->revision;
@@ -2266,6 +2299,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        rwlock_init(&adapter->ahw->crb_lock);
        mutex_init(&adapter->ahw->mem_lock);
 
+       spin_lock_init(&adapter->tx_clean_lock);
        INIT_LIST_HEAD(&adapter->mac_list);
 
        qlcnic_register_dcb(adapter);
@@ -2275,42 +2309,56 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                adapter->portnum = adapter->ahw->pci_func;
                err = qlcnic_start_firmware(adapter);
                if (err) {
-                       dev_err(&pdev->dev, "Loading fw failed.Please Reboot\n");
-                       goto err_out_free_hw;
+                       dev_err(&pdev->dev, "Loading fw failed.Please Reboot\n"
+                               "\t\tIf reboot doesn't help, try flashing the card\n");
+                       goto err_out_maintenance_mode;
                }
 
-               qlcnic_get_multiq_capability(adapter);
-
-               if ((adapter->ahw->act_pci_func > 2) &&
-                   qlcnic_check_multi_tx(adapter)) {
-                       adapter->max_drv_tx_rings = QLCNIC_DEF_NUM_TX_RINGS;
-                       dev_info(&adapter->pdev->dev,
-                                "vNIC mode enabled, Set max TX rings = %d\n",
-                                adapter->max_drv_tx_rings);
+               /* compute and set default and max tx/sds rings */
+               if (adapter->ahw->msix_supported) {
+                       if (qlcnic_check_multi_tx_capability(adapter) == 1)
+                               qlcnic_set_tx_ring_count(adapter,
+                                                        QLCNIC_SINGLE_RING);
+                       else
+                               qlcnic_set_tx_ring_count(adapter,
+                                                        QLCNIC_DEF_TX_RINGS);
+                       qlcnic_set_sds_ring_count(adapter,
+                                                 QLCNIC_DEF_SDS_RINGS);
+               } else {
+                       qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING);
+                       qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING);
                }
 
-               if (!qlcnic_check_multi_tx(adapter)) {
-                       clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
-                       adapter->max_drv_tx_rings = 1;
-               }
                err = qlcnic_setup_idc_param(adapter);
                if (err)
                        goto err_out_free_hw;
 
                adapter->flags |= QLCNIC_NEED_FLR;
 
-               if (adapter->dcb && qlcnic_dcb_attach(adapter))
-                       qlcnic_clear_dcb_ops(adapter);
+               dcb = adapter->dcb;
 
+               if (dcb && qlcnic_dcb_attach(dcb))
+                       qlcnic_clear_dcb_ops(dcb);
        } else if (qlcnic_83xx_check(adapter)) {
-               adapter->max_drv_tx_rings = 1;
                qlcnic_83xx_check_vf(adapter, ent);
                adapter->portnum = adapter->ahw->pci_func;
                err = qlcnic_83xx_init(adapter, pci_using_dac);
                if (err) {
-                       dev_err(&pdev->dev, "%s: failed\n", __func__);
-                       goto err_out_free_hw;
+                       switch (err) {
+                       case -ENOTRECOVERABLE:
+                               dev_err(&pdev->dev, "Adapter initialization failed due to a faulty hardware. Please reboot\n");
+                               dev_err(&pdev->dev, "If reboot doesn't help, please replace the adapter with new one and return the faulty adapter for repair\n");
+                               goto err_out_free_hw;
+                       case -ENOMEM:
+                               dev_err(&pdev->dev, "Adapter initialization failed. Please reboot\n");
+                               goto err_out_free_hw;
+                       default:
+                               dev_err(&pdev->dev, "Adapter initialization failed. A reboot may be required to recover from this failure\n");
+                               dev_err(&pdev->dev, "If reboot does not help to recover from this failure, try a flash update of the adapter\n");
+                               goto err_out_maintenance_mode;
+                       }
                }
+
                if (qlcnic_sriov_vf_check(adapter))
                        return 0;
        } else {
@@ -2338,7 +2386,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                         "Device does not support MSI interrupts\n");
 
        if (qlcnic_82xx_check(adapter)) {
-               err = qlcnic_setup_intr(adapter, 0, 0);
+               err = qlcnic_setup_intr(adapter);
                if (err) {
                        dev_err(&pdev->dev, "Failed to setup interrupt\n");
                        goto err_out_disable_msi;
@@ -2392,6 +2440,9 @@ err_out_disable_msi:
 err_out_free_hw:
        qlcnic_free_adapter_resources(adapter);
 
+err_out_free_wq:
+       destroy_workqueue(adapter->qlcnic_wq);
+
 err_out_free_netdev:
        free_netdev(netdev);
 
@@ -2405,9 +2456,32 @@ err_out_free_res:
        pci_release_regions(pdev);
 
 err_out_disable_pdev:
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
        return err;
+
+err_out_maintenance_mode:
+       set_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state);
+       netdev->netdev_ops = &qlcnic_netdev_failed_ops;
+       SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops);
+       ahw->port_type = QLCNIC_XGBE;
+
+       if (qlcnic_83xx_check(adapter))
+               adapter->tgt_status_reg = NULL;
+       else
+               ahw->board_type = QLCNIC_BRDTYPE_P3P_10G_SFP_PLUS;
+
+       err = register_netdev(netdev);
+
+       if (err) {
+               dev_err(&pdev->dev, "Failed to register net device\n");
+               qlcnic_clr_all_drv_state(adapter, 0);
+               goto err_out_free_hw;
+       }
+
+       pci_set_drvdata(pdev, adapter);
+       qlcnic_add_sysfs(adapter);
+
+       return 0;
 }
 
 static void qlcnic_remove(struct pci_dev *pdev)
@@ -2426,7 +2500,7 @@ static void qlcnic_remove(struct pci_dev *pdev)
        qlcnic_cancel_idc_work(adapter);
        ahw = adapter->ahw;
 
-       qlcnic_dcb_free(adapter);
+       qlcnic_dcb_free(adapter->dcb);
 
        unregister_netdev(netdev);
        qlcnic_sriov_cleanup(adapter);
@@ -2465,7 +2539,6 @@ static void qlcnic_remove(struct pci_dev *pdev)
        pci_disable_pcie_error_reporting(pdev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        if (adapter->qlcnic_wq) {
                destroy_workqueue(adapter->qlcnic_wq);
@@ -2520,6 +2593,13 @@ static int qlcnic_open(struct net_device *netdev)
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        int err;
 
+       if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) {
+               netdev_err(netdev, "%s: Device is in non-operational state\n",
+                          __func__);
+
+               return -EIO;
+       }
+
        netif_carrier_off(netdev);
 
        err = qlcnic_attach(adapter);
@@ -2677,24 +2757,21 @@ static void qlcnic_tx_timeout(struct net_device *netdev)
                                                      QLCNIC_FORCE_FW_DUMP_KEY);
        } else {
                netdev_info(netdev, "Tx timeout, reset adapter context.\n");
-               if (qlcnic_82xx_check(adapter)) {
-                       for (ring = 0; ring < adapter->max_drv_tx_rings;
-                            ring++) {
-                               tx_ring = &adapter->tx_ring[ring];
-                               dev_info(&netdev->dev, "ring=%d\n", ring);
-                               dev_info(&netdev->dev, "crb_intr_mask=%d\n",
-                                        readl(tx_ring->crb_intr_mask));
-                               dev_info(&netdev->dev, "producer=%d\n",
-                                        readl(tx_ring->crb_cmd_producer));
-                               dev_info(&netdev->dev, "sw_consumer = %d\n",
-                                        tx_ring->sw_consumer);
-                               dev_info(&netdev->dev, "hw_consumer = %d\n",
-                                        le32_to_cpu(*(tx_ring->hw_consumer)));
-                               dev_info(&netdev->dev, "xmit-on=%llu\n",
-                                        tx_ring->xmit_on);
-                               dev_info(&netdev->dev, "xmit-off=%llu\n",
-                                        tx_ring->xmit_off);
-                       }
+               for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
+                       tx_ring = &adapter->tx_ring[ring];
+                       netdev_info(netdev, "Tx ring=%d\n", ring);
+                       netdev_info(netdev,
+                                   "crb_intr_mask=%d, producer=%d, sw_consumer=%d, hw_consumer=%d\n",
+                                   readl(tx_ring->crb_intr_mask),
+                                   readl(tx_ring->crb_cmd_producer),
+                                   tx_ring->sw_consumer,
+                                   le32_to_cpu(*(tx_ring->hw_consumer)));
+                       netdev_info(netdev,
+                                   "xmit_finished=%llu, xmit_called=%llu, xmit_on=%llu, xmit_off=%llu\n",
+                                   tx_ring->tx_stats.xmit_finished,
+                                   tx_ring->tx_stats.xmit_called,
+                                   tx_ring->tx_stats.xmit_on,
+                                   tx_ring->tx_stats.xmit_off);
                }
                adapter->ahw->reset_context = 1;
        }
@@ -2808,7 +2885,7 @@ static void qlcnic_poll_controller(struct net_device *netdev)
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 
        disable_irq(adapter->irq);
-       for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+       for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
                qlcnic_intr(adapter->irq, sds_ring);
        }
@@ -3229,6 +3306,14 @@ void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *adapter, u32 key)
 
        state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
 
+       if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) {
+               netdev_err(adapter->netdev, "%s: Device is in non-operational state\n",
+                          __func__);
+               qlcnic_api_unlock(adapter);
+
+               return;
+       }
+
        if (state == QLCNIC_DEV_READY) {
                QLC_SHARED_REG_WR32(adapter, QLCNIC_CRB_DEV_STATE,
                                    QLCNIC_DEV_NEED_RESET);
@@ -3289,7 +3374,7 @@ qlcnic_attach_work(struct work_struct *work)
                return;
        }
 attach:
-       qlcnic_dcb_get_info(adapter);
+       qlcnic_dcb_get_info(adapter->dcb);
 
        if (netif_running(netdev)) {
                if (qlcnic_up(adapter, netdev))
@@ -3314,6 +3399,8 @@ done:
 static int
 qlcnic_check_health(struct qlcnic_adapter *adapter)
 {
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlcnic_fw_dump *fw_dump = &ahw->fw_dump;
        u32 state = 0, heartbeat;
        u32 peg_status;
        int err = 0;
@@ -3338,7 +3425,7 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
                if (adapter->need_fw_reset)
                        goto detach;
 
-               if (adapter->ahw->reset_context && qlcnic_auto_fw_reset)
+               if (ahw->reset_context && qlcnic_auto_fw_reset)
                        qlcnic_reset_hw_context(adapter);
 
                return 0;
@@ -3381,6 +3468,9 @@ detach:
 
                qlcnic_schedule_work(adapter, qlcnic_detach_work, 0);
                QLCDB(adapter, DRV, "fw recovery scheduled.\n");
+       } else if (!qlcnic_auto_fw_reset && fw_dump->enable &&
+                  adapter->flags & QLCNIC_FW_RESET_OWNER) {
+               qlcnic_dump_fw(adapter);
        }
 
        return 1;
@@ -3462,7 +3552,7 @@ static int qlcnic_attach_func(struct pci_dev *pdev)
        qlcnic_clr_drv_state(adapter);
        kfree(adapter->msix_entries);
        adapter->msix_entries = NULL;
-       err = qlcnic_setup_intr(adapter, 0, 0);
+       err = qlcnic_setup_intr(adapter);
 
        if (err) {
                kfree(adapter->msix_entries);
@@ -3607,136 +3697,90 @@ qlcnicvf_start_firmware(struct qlcnic_adapter *adapter)
        return err;
 }
 
-int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *adapter, u32 txq)
+int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
+                         int queue_type)
 {
        struct net_device *netdev = adapter->netdev;
-       u8 max_hw = QLCNIC_MAX_TX_RINGS;
-       u32 max_allowed;
+       u8 max_hw_rings = 0;
+       char buf[8];
+       int cur_rings;
 
-       if (!qlcnic_82xx_check(adapter)) {
-               netdev_err(netdev, "No Multi TX-Q support\n");
-               return -EINVAL;
+       if (queue_type == QLCNIC_RX_QUEUE) {
+               max_hw_rings = adapter->max_sds_rings;
+               cur_rings = adapter->drv_sds_rings;
+               strcpy(buf, "SDS");
+       } else if (queue_type == QLCNIC_TX_QUEUE) {
+               max_hw_rings = adapter->max_tx_rings;
+               cur_rings = adapter->drv_tx_rings;
+               strcpy(buf, "Tx");
        }
 
        if (!qlcnic_use_msi_x && !qlcnic_use_msi) {
-               netdev_err(netdev, "No Multi TX-Q support in INT-x mode\n");
+               netdev_err(netdev, "No RSS/TSS support in INT-x mode\n");
                return -EINVAL;
        }
 
-       if (!qlcnic_check_multi_tx(adapter)) {
-               netdev_err(netdev, "No Multi TX-Q support\n");
+       if (adapter->flags & QLCNIC_MSI_ENABLED) {
+               netdev_err(netdev, "No RSS/TSS support in MSI mode\n");
                return -EINVAL;
        }
 
-       if (txq > QLCNIC_MAX_TX_RINGS) {
-               netdev_err(netdev, "Invalid ring count\n");
+       if (ring_cnt < 2) {
+               netdev_err(netdev,
+                          "%s rings value should not be lower than 2\n", buf);
                return -EINVAL;
        }
 
-       max_allowed = rounddown_pow_of_two(min_t(int, max_hw,
-                                                num_online_cpus()));
-       if ((txq > max_allowed) || !is_power_of_2(txq)) {
-               if (!is_power_of_2(txq))
-                       netdev_err(netdev,
-                                  "TX queue should be a power of 2\n");
-               if (txq > num_online_cpus())
-                       netdev_err(netdev,
-                                  "Tx queue should not be higher than [%u], number of online CPUs in the system\n",
-                                  num_online_cpus());
-               netdev_err(netdev, "Unable to configure %u Tx rings\n", txq);
+       if (!is_power_of_2(ring_cnt)) {
+               netdev_err(netdev, "%s rings value should be a power of 2\n",
+                          buf);
                return -EINVAL;
        }
 
-       return 0;
-}
-
-int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter,
-                               __u32 val)
-{
-       struct net_device *netdev = adapter->netdev;
-       u8 max_hw = adapter->ahw->max_rx_ques;
-       u32 max_allowed;
-
-       if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x &&
-           !qlcnic_use_msi) {
-               netdev_err(netdev, "No RSS support in INT-x mode\n");
-               return -EINVAL;
+       if (qlcnic_82xx_check(adapter) && (queue_type == QLCNIC_TX_QUEUE) &&
+           !qlcnic_check_multi_tx(adapter)) {
+                       netdev_err(netdev, "No Multi Tx queue support\n");
+                       return -EINVAL;
        }
 
-       if (val > QLCNIC_MAX_SDS_RINGS) {
-               netdev_err(netdev, "RSS value should not be higher than %u\n",
-                          QLCNIC_MAX_SDS_RINGS);
+       if (ring_cnt > num_online_cpus()) {
+               netdev_err(netdev,
+                          "%s value[%u] should not be higher than, number of online CPUs\n",
+                          buf, num_online_cpus());
                return -EINVAL;
        }
 
-       max_allowed = rounddown_pow_of_two(min_t(int, max_hw,
-                                                num_online_cpus()));
-       if ((val > max_allowed) || (val < 2) || !is_power_of_2(val)) {
-               if (!is_power_of_2(val))
-                       netdev_err(netdev, "RSS value should be a power of 2\n");
-
-               if (val < 2)
-                       netdev_err(netdev, "RSS value should not be lower than 2\n");
-
-               if (val > max_hw)
-                       netdev_err(netdev,
-                                  "RSS value should not be higher than[%u], the max RSS rings supported by the adapter\n",
-                                  max_hw);
-
-               if (val > num_online_cpus())
-                       netdev_err(netdev,
-                                  "RSS value should not be higher than[%u], number of online CPUs in the system\n",
-                                  num_online_cpus());
-
-               netdev_err(netdev, "Unable to configure %u RSS rings\n", val);
-
-               return -EINVAL;
-       }
        return 0;
 }
 
-int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, int txq)
+int qlcnic_setup_rings(struct qlcnic_adapter *adapter, u8 rx_cnt, u8 tx_cnt)
 {
-       int err;
        struct net_device *netdev = adapter->netdev;
-       int num_msix;
+       int err;
 
        if (test_bit(__QLCNIC_RESETTING, &adapter->state))
                return -EBUSY;
 
-       if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x &&
-           !qlcnic_use_msi) {
-               netdev_err(netdev, "No RSS support in INT-x mode\n");
-               return -EINVAL;
-       }
-
        netif_device_detach(netdev);
        if (netif_running(netdev))
                __qlcnic_down(adapter, netdev);
 
        qlcnic_detach(adapter);
 
-       if (qlcnic_82xx_check(adapter)) {
-               if (txq != 0)
-                       adapter->max_drv_tx_rings = txq;
-
-               if (qlcnic_check_multi_tx(adapter) &&
-                   (txq > adapter->max_drv_tx_rings))
-                       num_msix = adapter->max_drv_tx_rings;
-               else
-                       num_msix = data;
-       }
-
        if (qlcnic_83xx_check(adapter)) {
                qlcnic_83xx_free_mbx_intr(adapter);
                qlcnic_83xx_enable_mbx_poll(adapter);
        }
 
-       netif_set_real_num_tx_queues(netdev, adapter->max_drv_tx_rings);
-
        qlcnic_teardown_intr(adapter);
 
-       err = qlcnic_setup_intr(adapter, data, txq);
+       /* compute and set default and max tx/sds rings */
+       qlcnic_set_tx_ring_count(adapter, tx_cnt);
+       qlcnic_set_sds_ring_count(adapter, rx_cnt);
+
+       netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings);
+
+       err = qlcnic_setup_intr(adapter);
        if (err) {
                kfree(adapter->msix_entries);
                netdev_err(netdev, "failed to setup interrupt\n");
index 1551360..7763962 100644 (file)
@@ -1187,41 +1187,38 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
                }
 
                if (ops_index == ops_cnt) {
-                       dev_info(&adapter->pdev->dev,
-                                "Invalid entry type %d, exiting dump\n",
+                       dev_info(dev, "Skipping unknown entry opcode %d\n",
                                 entry->hdr.type);
-                       goto error;
+                       entry->hdr.flags |= QLCNIC_DUMP_SKIP;
+                       entry_offset += entry->hdr.offset;
+                       continue;
                }
 
                /* Collect dump for this entry */
                dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer);
-               if (!qlcnic_valid_dump_entry(&adapter->pdev->dev, entry, dump))
+               if (!qlcnic_valid_dump_entry(dev, entry, dump)) {
                        entry->hdr.flags |= QLCNIC_DUMP_SKIP;
+                       entry_offset += entry->hdr.offset;
+                       continue;
+               }
+
                buf_offset += entry->hdr.cap_size;
                entry_offset += entry->hdr.offset;
                buffer = fw_dump->data + buf_offset;
        }
-       if (dump_size != buf_offset) {
-               dev_info(&adapter->pdev->dev,
-                        "Captured(%d) and expected size(%d) do not match\n",
-                        buf_offset, dump_size);
-               goto error;
-       } else {
-               fw_dump->clr = 1;
-               snprintf(mesg, sizeof(mesg), "FW_DUMP=%s",
-                        adapter->netdev->name);
-               dev_info(&adapter->pdev->dev, "%s: Dump data, %d bytes captured\n",
-                        adapter->netdev->name, fw_dump->size);
-               /* Send a udev event to notify availability of FW dump */
-               kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg);
-               return 0;
-       }
-error:
+
+       fw_dump->clr = 1;
+       snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", adapter->netdev->name);
+       dev_info(dev, "%s: Dump data %d bytes captured, template header size %d bytes\n",
+                adapter->netdev->name, fw_dump->size, tmpl_hdr->size);
+       /* Send a udev event to notify availability of FW dump */
+       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg);
+
        if (fw_dump->use_pex_dma)
                dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE,
                                  fw_dump->dma_buffer, fw_dump->phys_addr);
-       vfree(fw_dump->data);
-       return -EINVAL;
+
+       return 0;
 }
 
 void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter)
index 392b9bd..21a4b27 100644 (file)
@@ -500,6 +500,7 @@ static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter)
 static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
                                 int pci_using_dac)
 {
+       struct qlcnic_dcb *dcb;
        int err;
 
        INIT_LIST_HEAD(&adapter->vf_mc_list);
@@ -507,7 +508,11 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
                dev_warn(&adapter->pdev->dev,
                         "Device does not support MSI interrupts\n");
 
-       err = qlcnic_setup_intr(adapter, 1, 0);
+       /* compute and set default and max tx/sds rings */
+       qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING);
+       qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING);
+
+       err = qlcnic_setup_intr(adapter);
        if (err) {
                dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n");
                goto err_out_disable_msi;
@@ -533,8 +538,10 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
        if (err)
                goto err_out_send_channel_term;
 
-       if (adapter->dcb && qlcnic_dcb_attach(adapter))
-               qlcnic_clear_dcb_ops(adapter);
+       dcb = adapter->dcb;
+
+       if (dcb && qlcnic_dcb_attach(dcb))
+               qlcnic_clear_dcb_ops(dcb);
 
        err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac);
        if (err)
@@ -1577,7 +1584,7 @@ static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
        if (err)
                goto err_out_term_channel;
 
-       qlcnic_dcb_get_info(adapter);
+       qlcnic_dcb_get_info(adapter->dcb);
 
        return 0;
 
index 330d9a8..686f460 100644 (file)
@@ -397,6 +397,7 @@ static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
 
+       rtnl_lock();
        if (netif_running(netdev))
                __qlcnic_down(adapter, netdev);
 
@@ -407,12 +408,15 @@ static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
        /* After disabling SRIOV re-init the driver in default mode
           configure opmode based on op_mode of function
         */
-       if (qlcnic_83xx_configure_opmode(adapter))
+       if (qlcnic_83xx_configure_opmode(adapter)) {
+               rtnl_unlock();
                return -EIO;
+       }
 
        if (netif_running(netdev))
                __qlcnic_up(adapter, netdev);
 
+       rtnl_unlock();
        return 0;
 }
 
@@ -533,6 +537,7 @@ static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
                return -EIO;
        }
 
+       rtnl_lock();
        if (netif_running(netdev))
                __qlcnic_down(adapter, netdev);
 
@@ -555,6 +560,7 @@ static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
                __qlcnic_up(adapter, netdev);
 
 error:
+       rtnl_unlock();
        return err;
 }
 
index c6165d0..1a9f8a4 100644 (file)
@@ -156,7 +156,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter,
                                    const char *buf, size_t len)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
-       int err, max_sds_rings = adapter->max_sds_rings;
+       int err, drv_sds_rings = adapter->drv_sds_rings;
        u16 beacon;
        u8 h_beacon_state, b_state, b_rate;
 
@@ -211,7 +211,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter,
        }
 
        if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
-               qlcnic_diag_free_res(adapter->netdev, max_sds_rings);
+               qlcnic_diag_free_res(adapter->netdev, drv_sds_rings);
 
 out:
        if (!ahw->beacon_state)
@@ -1285,8 +1285,12 @@ void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
        if (device_create_bin_file(dev, &bin_attr_mem))
                dev_info(dev, "failed to create mem sysfs entry\n");
 
+       if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state))
+               return;
+
        if (device_create_bin_file(dev, &bin_attr_pci_config))
                dev_info(dev, "failed to create pci config sysfs entry");
+
        if (device_create_file(dev, &dev_attr_beacon))
                dev_info(dev, "failed to create beacon sysfs entry");
 
@@ -1315,6 +1319,10 @@ void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
        device_remove_file(dev, &dev_attr_diag_mode);
        device_remove_bin_file(dev, &bin_attr_crb);
        device_remove_bin_file(dev, &bin_attr_mem);
+
+       if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state))
+               return;
+
        device_remove_bin_file(dev, &bin_attr_pci_config);
        device_remove_file(dev, &dev_attr_beacon);
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
index 8994337..0c9c4e8 100644 (file)
@@ -18,7 +18,7 @@
  */
 #define DRV_NAME       "qlge"
 #define DRV_STRING     "QLogic 10 Gigabit PCI-E Ethernet Driver "
-#define DRV_VERSION    "v1.00.00.32"
+#define DRV_VERSION    "1.00.00.33"
 
 #define WQ_ADDR_ALIGN  0x3     /* 4 byte alignment */
 
@@ -2206,14 +2206,14 @@ extern char qlge_driver_name[];
 extern const char qlge_driver_version[];
 extern const struct ethtool_ops qlge_ethtool_ops;
 
-extern int ql_sem_spinlock(struct ql_adapter *qdev, u32 sem_mask);
-extern void ql_sem_unlock(struct ql_adapter *qdev, u32 sem_mask);
-extern int ql_read_xgmac_reg(struct ql_adapter *qdev, u32 reg, u32 *data);
-extern int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index,
-                              u32 *value);
-extern int ql_get_routing_reg(struct ql_adapter *qdev, u32 index, u32 *value);
-extern int ql_write_cfg(struct ql_adapter *qdev, void *ptr, int size, u32 bit,
-                       u16 q_id);
+int ql_sem_spinlock(struct ql_adapter *qdev, u32 sem_mask);
+void ql_sem_unlock(struct ql_adapter *qdev, u32 sem_mask);
+int ql_read_xgmac_reg(struct ql_adapter *qdev, u32 reg, u32 *data);
+int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index,
+                       u32 *value);
+int ql_get_routing_reg(struct ql_adapter *qdev, u32 index, u32 *value);
+int ql_write_cfg(struct ql_adapter *qdev, void *ptr, int size, u32 bit,
+                u16 q_id);
 void ql_queue_fw_error(struct ql_adapter *qdev);
 void ql_mpi_work(struct work_struct *work);
 void ql_mpi_reset_work(struct work_struct *work);
@@ -2233,10 +2233,9 @@ int ql_unpause_mpi_risc(struct ql_adapter *qdev);
 int ql_pause_mpi_risc(struct ql_adapter *qdev);
 int ql_hard_reset_mpi_risc(struct ql_adapter *qdev);
 int ql_soft_reset_mpi_risc(struct ql_adapter *qdev);
-int ql_dump_risc_ram_area(struct ql_adapter *qdev, void *buf,
-               u32 ram_addr, int word_count);
-int ql_core_dump(struct ql_adapter *qdev,
-               struct ql_mpi_coredump *mpi_coredump);
+int ql_dump_risc_ram_area(struct ql_adapter *qdev, void *buf, u32 ram_addr,
+                         int word_count);
+int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump);
 int ql_mb_about_fw(struct ql_adapter *qdev);
 int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol);
 int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol);
@@ -2249,8 +2248,7 @@ int ql_mb_get_port_cfg(struct ql_adapter *qdev);
 int ql_mb_set_port_cfg(struct ql_adapter *qdev);
 int ql_wait_fifo_empty(struct ql_adapter *qdev);
 void ql_get_dump(struct ql_adapter *qdev, void *buff);
-void ql_gen_reg_dump(struct ql_adapter *qdev,
-                       struct ql_reg_dump *mpi_coredump);
+void ql_gen_reg_dump(struct ql_adapter *qdev, struct ql_reg_dump *mpi_coredump);
 netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev);
 void ql_check_lb_frame(struct ql_adapter *, struct sk_buff *);
 int ql_own_firmware(struct ql_adapter *qdev);
@@ -2264,9 +2262,9 @@ int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget);
 /* #define QL_OB_DUMP */
 
 #ifdef QL_REG_DUMP
-extern void ql_dump_xgmac_control_regs(struct ql_adapter *qdev);
-extern void ql_dump_routing_entries(struct ql_adapter *qdev);
-extern void ql_dump_regs(struct ql_adapter *qdev);
+void ql_dump_xgmac_control_regs(struct ql_adapter *qdev);
+void ql_dump_routing_entries(struct ql_adapter *qdev);
+void ql_dump_regs(struct ql_adapter *qdev);
 #define QL_DUMP_REGS(qdev) ql_dump_regs(qdev)
 #define QL_DUMP_ROUTE(qdev) ql_dump_routing_entries(qdev)
 #define QL_DUMP_XGMAC_CONTROL_REGS(qdev) ql_dump_xgmac_control_regs(qdev)
@@ -2277,26 +2275,26 @@ extern void ql_dump_regs(struct ql_adapter *qdev);
 #endif
 
 #ifdef QL_STAT_DUMP
-extern void ql_dump_stat(struct ql_adapter *qdev);
+void ql_dump_stat(struct ql_adapter *qdev);
 #define QL_DUMP_STAT(qdev) ql_dump_stat(qdev)
 #else
 #define QL_DUMP_STAT(qdev)
 #endif
 
 #ifdef QL_DEV_DUMP
-extern void ql_dump_qdev(struct ql_adapter *qdev);
+void ql_dump_qdev(struct ql_adapter *qdev);
 #define QL_DUMP_QDEV(qdev) ql_dump_qdev(qdev)
 #else
 #define QL_DUMP_QDEV(qdev)
 #endif
 
 #ifdef QL_CB_DUMP
-extern void ql_dump_wqicb(struct wqicb *wqicb);
-extern void ql_dump_tx_ring(struct tx_ring *tx_ring);
-extern void ql_dump_ricb(struct ricb *ricb);
-extern void ql_dump_cqicb(struct cqicb *cqicb);
-extern void ql_dump_rx_ring(struct rx_ring *rx_ring);
-extern void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id);
+void ql_dump_wqicb(struct wqicb *wqicb);
+void ql_dump_tx_ring(struct tx_ring *tx_ring);
+void ql_dump_ricb(struct ricb *ricb);
+void ql_dump_cqicb(struct cqicb *cqicb);
+void ql_dump_rx_ring(struct rx_ring *rx_ring);
+void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id);
 #define QL_DUMP_RICB(ricb) ql_dump_ricb(ricb)
 #define QL_DUMP_WQICB(wqicb) ql_dump_wqicb(wqicb)
 #define QL_DUMP_TX_RING(tx_ring) ql_dump_tx_ring(tx_ring)
@@ -2314,9 +2312,9 @@ extern void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id);
 #endif
 
 #ifdef QL_OB_DUMP
-extern void ql_dump_tx_desc(struct tx_buf_desc *tbd);
-extern void ql_dump_ob_mac_iocb(struct ob_mac_iocb_req *ob_mac_iocb);
-extern void ql_dump_ob_mac_rsp(struct ob_mac_iocb_rsp *ob_mac_rsp);
+void ql_dump_tx_desc(struct tx_buf_desc *tbd);
+void ql_dump_ob_mac_iocb(struct ob_mac_iocb_req *ob_mac_iocb);
+void ql_dump_ob_mac_rsp(struct ob_mac_iocb_rsp *ob_mac_rsp);
 #define QL_DUMP_OB_MAC_IOCB(ob_mac_iocb) ql_dump_ob_mac_iocb(ob_mac_iocb)
 #define QL_DUMP_OB_MAC_RSP(ob_mac_rsp) ql_dump_ob_mac_rsp(ob_mac_rsp)
 #else
@@ -2325,14 +2323,14 @@ extern void ql_dump_ob_mac_rsp(struct ob_mac_iocb_rsp *ob_mac_rsp);
 #endif
 
 #ifdef QL_IB_DUMP
-extern void ql_dump_ib_mac_rsp(struct ib_mac_iocb_rsp *ib_mac_rsp);
+void ql_dump_ib_mac_rsp(struct ib_mac_iocb_rsp *ib_mac_rsp);
 #define QL_DUMP_IB_MAC_RSP(ib_mac_rsp) ql_dump_ib_mac_rsp(ib_mac_rsp)
 #else
 #define QL_DUMP_IB_MAC_RSP(ib_mac_rsp)
 #endif
 
 #ifdef QL_ALL_DUMP
-extern void ql_dump_all(struct ql_adapter *qdev);
+void ql_dump_all(struct ql_adapter *qdev);
 #define QL_DUMP_ALL(qdev) ql_dump_all(qdev)
 #else
 #define QL_DUMP_ALL(qdev)
index 10093f0..6bc5db7 100644 (file)
@@ -740,8 +740,8 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump)
        int i;
 
        if (!mpi_coredump) {
-               netif_err(qdev, drv, qdev->ndev, "No memory available\n");
-               return -ENOMEM;
+               netif_err(qdev, drv, qdev->ndev, "No memory allocated\n");
+               return -EINVAL;
        }
 
        /* Try to get the spinlock, but dont worry if
index 2553cf4..a245dc1 100644 (file)
@@ -96,8 +96,10 @@ static DEFINE_PCI_DEVICE_TABLE(qlge_pci_tbl) = {
 
 MODULE_DEVICE_TABLE(pci, qlge_pci_tbl);
 
-static int ql_wol(struct ql_adapter *qdev);
-static void qlge_set_multicast_list(struct net_device *ndev);
+static int ql_wol(struct ql_adapter *);
+static void qlge_set_multicast_list(struct net_device *);
+static int ql_adapter_down(struct ql_adapter *);
+static int ql_adapter_up(struct ql_adapter *);
 
 /* This hardware semaphore causes exclusive access to
  * resources shared between the NIC driver, MPI firmware,
@@ -1464,6 +1466,29 @@ static void ql_categorize_rx_err(struct ql_adapter *qdev, u8 rx_err,
        }
 }
 
+/**
+ * ql_update_mac_hdr_len - helper routine to update the mac header length
+ * based on vlan tags if present
+ */
+static void ql_update_mac_hdr_len(struct ql_adapter *qdev,
+                                 struct ib_mac_iocb_rsp *ib_mac_rsp,
+                                 void *page, size_t *len)
+{
+       u16 *tags;
+
+       if (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
+               return;
+       if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) {
+               tags = (u16 *)page;
+               /* Look for stacked vlan tags in ethertype field */
+               if (tags[6] == ETH_P_8021Q &&
+                   tags[8] == ETH_P_8021Q)
+                       *len += 2 * VLAN_HLEN;
+               else
+                       *len += VLAN_HLEN;
+       }
+}
+
 /* Process an inbound completion from an rx ring. */
 static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev,
                                        struct rx_ring *rx_ring,
@@ -1523,6 +1548,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,
        void *addr;
        struct bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
        struct napi_struct *napi = &rx_ring->napi;
+       size_t hlen = ETH_HLEN;
 
        skb = netdev_alloc_skb(ndev, length);
        if (!skb) {
@@ -1540,25 +1566,28 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,
                goto err_out;
        }
 
+       /* Update the MAC header length*/
+       ql_update_mac_hdr_len(qdev, ib_mac_rsp, addr, &hlen);
+
        /* The max framesize filter on this chip is set higher than
         * MTU since FCoE uses 2k frames.
         */
-       if (skb->len > ndev->mtu + ETH_HLEN) {
+       if (skb->len > ndev->mtu + hlen) {
                netif_err(qdev, drv, qdev->ndev,
                          "Segment too small, dropping.\n");
                rx_ring->rx_dropped++;
                goto err_out;
        }
-       memcpy(skb_put(skb, ETH_HLEN), addr, ETH_HLEN);
+       memcpy(skb_put(skb, hlen), addr, hlen);
        netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev,
                     "%d bytes of headers and data in large. Chain page to new skb and pull tail.\n",
                     length);
        skb_fill_page_desc(skb, 0, lbq_desc->p.pg_chunk.page,
-                               lbq_desc->p.pg_chunk.offset+ETH_HLEN,
-                               length-ETH_HLEN);
-       skb->len += length-ETH_HLEN;
-       skb->data_len += length-ETH_HLEN;
-       skb->truesize += length-ETH_HLEN;
+                               lbq_desc->p.pg_chunk.offset + hlen,
+                               length - hlen);
+       skb->len += length - hlen;
+       skb->data_len += length - hlen;
+       skb->truesize += length - hlen;
 
        rx_ring->rx_packets++;
        rx_ring->rx_bytes += skb->len;
@@ -1576,7 +1605,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,
                                (ib_mac_rsp->flags3 & IB_MAC_IOCB_RSP_V4)) {
                        /* Unfragmented ipv4 UDP frame. */
                        struct iphdr *iph =
-                               (struct iphdr *) ((u8 *)addr + ETH_HLEN);
+                               (struct iphdr *)((u8 *)addr + hlen);
                        if (!(iph->frag_off &
                                htons(IP_MF|IP_OFFSET))) {
                                skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -1726,7 +1755,8 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
        struct bq_desc *sbq_desc;
        struct sk_buff *skb = NULL;
        u32 length = le32_to_cpu(ib_mac_rsp->data_len);
-       u32 hdr_len = le32_to_cpu(ib_mac_rsp->hdr_len);
+       u32 hdr_len = le32_to_cpu(ib_mac_rsp->hdr_len);
+       size_t hlen = ETH_HLEN;
 
        /*
         * Handle the header buffer if present.
@@ -1853,9 +1883,10 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
                        skb->data_len += length;
                        skb->truesize += length;
                        length -= length;
-                       __pskb_pull_tail(skb,
-                               (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
-                               VLAN_ETH_HLEN : ETH_HLEN);
+                       ql_update_mac_hdr_len(qdev, ib_mac_rsp,
+                                             lbq_desc->p.pg_chunk.va,
+                                             &hlen);
+                       __pskb_pull_tail(skb, hlen);
                }
        } else {
                /*
@@ -1910,8 +1941,9 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
                        length -= size;
                        i++;
                }
-               __pskb_pull_tail(skb, (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
-                               VLAN_ETH_HLEN : ETH_HLEN);
+               ql_update_mac_hdr_len(qdev, ib_mac_rsp, lbq_desc->p.pg_chunk.va,
+                                     &hlen);
+               __pskb_pull_tail(skb, hlen);
        }
        return skb;
 }
@@ -2003,7 +2035,7 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev,
        rx_ring->rx_packets++;
        rx_ring->rx_bytes += skb->len;
        skb_record_rx_queue(skb, rx_ring->cq_id);
-       if ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) && (vlan_id != 0))
+       if (vlan_id != 0xffff)
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);
        if (skb->ip_summed == CHECKSUM_UNNECESSARY)
                napi_gro_receive(&rx_ring->napi, skb);
@@ -2017,7 +2049,8 @@ static unsigned long ql_process_mac_rx_intr(struct ql_adapter *qdev,
                                        struct ib_mac_iocb_rsp *ib_mac_rsp)
 {
        u32 length = le32_to_cpu(ib_mac_rsp->data_len);
-       u16 vlan_id = (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
+       u16 vlan_id = ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) &&
+                       (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)) ?
                        ((le16_to_cpu(ib_mac_rsp->vlan_id) &
                        IB_MAC_IOCB_RSP_VLAN_MASK)) : 0xffff;
 
@@ -2310,9 +2343,39 @@ static void qlge_vlan_mode(struct net_device *ndev, netdev_features_t features)
        }
 }
 
+/**
+ * qlge_update_hw_vlan_features - helper routine to reinitialize the adapter
+ * based on the features to enable/disable hardware vlan accel
+ */
+static int qlge_update_hw_vlan_features(struct net_device *ndev,
+                                       netdev_features_t features)
+{
+       struct ql_adapter *qdev = netdev_priv(ndev);
+       int status = 0;
+
+       status = ql_adapter_down(qdev);
+       if (status) {
+               netif_err(qdev, link, qdev->ndev,
+                         "Failed to bring down the adapter\n");
+               return status;
+       }
+
+       /* update the features with resent change */
+       ndev->features = features;
+
+       status = ql_adapter_up(qdev);
+       if (status) {
+               netif_err(qdev, link, qdev->ndev,
+                         "Failed to bring up the adapter\n");
+               return status;
+       }
+       return status;
+}
+
 static netdev_features_t qlge_fix_features(struct net_device *ndev,
        netdev_features_t features)
 {
+       int err;
        /*
         * Since there is no support for separate rx/tx vlan accel
         * enable/disable make sure tx flag is always in same state as rx.
@@ -2322,6 +2385,11 @@ static netdev_features_t qlge_fix_features(struct net_device *ndev,
        else
                features &= ~NETIF_F_HW_VLAN_CTAG_TX;
 
+       /* Update the behavior of vlan accel in the adapter */
+       err = qlge_update_hw_vlan_features(ndev, features);
+       if (err)
+               return err;
+
        return features;
 }
 
@@ -3704,8 +3772,12 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
        ql_write32(qdev, SYS, mask | value);
 
        /* Set the default queue, and VLAN behavior. */
-       value = NIC_RCV_CFG_DFQ | NIC_RCV_CFG_RV;
-       mask = NIC_RCV_CFG_DFQ_MASK | (NIC_RCV_CFG_RV << 16);
+       value = NIC_RCV_CFG_DFQ;
+       mask = NIC_RCV_CFG_DFQ_MASK;
+       if (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) {
+               value |= NIC_RCV_CFG_RV;
+               mask |= (NIC_RCV_CFG_RV << 16);
+       }
        ql_write32(qdev, NIC_RCV_CFG, (mask | value));
 
        /* Set the MPI interrupt to enabled. */
@@ -4505,7 +4577,6 @@ static void ql_release_all(struct pci_dev *pdev)
                iounmap(qdev->doorbell_area);
        vfree(qdev->mpi_coredump);
        pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static int ql_init_device(struct pci_dev *pdev, struct net_device *ndev,
@@ -4692,11 +4763,15 @@ static int qlge_probe(struct pci_dev *pdev,
 
        qdev = netdev_priv(ndev);
        SET_NETDEV_DEV(ndev, &pdev->dev);
-       ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
-               NETIF_F_TSO | NETIF_F_TSO_ECN |
-               NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_RXCSUM;
-       ndev->features = ndev->hw_features |
-               NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
+       ndev->hw_features = NETIF_F_SG |
+                           NETIF_F_IP_CSUM |
+                           NETIF_F_TSO |
+                           NETIF_F_TSO_ECN |
+                           NETIF_F_HW_VLAN_CTAG_TX |
+                           NETIF_F_HW_VLAN_CTAG_RX |
+                           NETIF_F_HW_VLAN_CTAG_FILTER |
+                           NETIF_F_RXCSUM;
+       ndev->features = ndev->hw_features;
        ndev->vlan_features = ndev->hw_features;
 
        if (test_bit(QL_DMA64, &qdev->flags))
index ff2bf8a..7ad1460 100644 (file)
@@ -1274,7 +1274,7 @@ void ql_mpi_reset_work(struct work_struct *work)
                return;
        }
 
-       if (!ql_core_dump(qdev, qdev->mpi_coredump)) {
+       if (qdev->mpi_coredump && !ql_core_dump(qdev, qdev->mpi_coredump)) {
                netif_err(qdev, drv, qdev->ndev, "Core is dumped!\n");
                qdev->core_is_dumped = 1;
                queue_delayed_work(qdev->workqueue,
index e9dc849..1e49ec5 100644 (file)
@@ -1231,7 +1231,6 @@ err_out_mdio:
        mdiobus_free(lp->mii_bus);
 err_out_unmap:
        netif_napi_del(&lp->napi);
-       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
@@ -1257,7 +1256,6 @@ static void r6040_remove_one(struct pci_dev *pdev)
        pci_release_regions(pdev);
        free_netdev(dev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 
index d2e5919..f2a2128 100644 (file)
@@ -2052,7 +2052,6 @@ static void cp_remove_one (struct pci_dev *pdev)
        pci_release_regions(pdev);
        pci_clear_mwi(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        free_netdev(dev);
 }
 
index 3ccedeb..50a9210 100644 (file)
@@ -727,7 +727,6 @@ static void __rtl8139_cleanup_dev (struct net_device *dev)
        pci_release_regions (pdev);
 
        free_netdev(dev);
-       pci_set_drvdata (pdev, NULL);
 }
 
 
index 3397cee..7993875 100644 (file)
@@ -6811,7 +6811,6 @@ static void rtl_remove_one(struct pci_dev *pdev)
 
        rtl_disable_msi(pdev, tp);
        rtl8169_release_board(pdev, dev, tp->mmio_addr);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static const struct net_device_ops rtl_netdev_ops = {
index 5cd831e..d256ce1 100644 (file)
@@ -483,7 +483,7 @@ static struct sh_eth_cpu_data sh7757_data = {
        .register_type  = SH_ETH_REG_FAST_SH4,
 
        .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
-       .rmcr_value     = 0x00000001,
+       .rmcr_value     = RMCR_RNC,
 
        .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
@@ -561,7 +561,7 @@ static struct sh_eth_cpu_data sh7757_data_giga = {
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
        .fdr_value      = 0x0000072f,
-       .rmcr_value     = 0x00000001,
+       .rmcr_value     = RMCR_RNC,
 
        .irq_flags      = IRQF_SHARED,
        .apr            = 1,
@@ -688,12 +688,16 @@ static struct sh_eth_cpu_data r8a7740_data = {
        .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
+       .fdr_value      = 0x0000070f,
+       .rmcr_value     = RMCR_RNC,
 
        .apr            = 1,
        .mpr            = 1,
        .tpauser        = 1,
        .bculr          = 1,
        .hw_swap        = 1,
+       .rpadir         = 1,
+       .rpadir_value   = 2 << 16,
        .no_trimd       = 1,
        .no_ade         = 1,
        .tsu            = 1,
@@ -868,7 +872,7 @@ static void update_mac_address(struct net_device *ndev)
 static void read_mac_address(struct net_device *ndev, unsigned char *mac)
 {
        if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) {
-               memcpy(ndev->dev_addr, mac, 6);
+               memcpy(ndev->dev_addr, mac, ETH_ALEN);
        } else {
                ndev->dev_addr[0] = (sh_eth_read(ndev, MAHR) >> 24);
                ndev->dev_addr[1] = (sh_eth_read(ndev, MAHR) >> 16) & 0xFF;
@@ -2659,6 +2663,12 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        pm_runtime_resume(&pdev->dev);
 
+       if (!pd) {
+               dev_err(&pdev->dev, "no platform data\n");
+               ret = -EINVAL;
+               goto out_release;
+       }
+
        /* get PHY ID */
        mdp->phy_id = pd->phy;
        mdp->phy_interface = pd->phy_interface;
index a0db02c..f32c169 100644 (file)
@@ -321,6 +321,9 @@ enum TD_STS_BIT {
 #define TD_TFP (TD_TFP1|TD_TFP0)
 
 /* RMCR */
+enum RMCR_BIT {
+       RMCR_RNC = 0x00000001,
+};
 #define DEFAULT_RMCR_VALUE     0x00000000
 
 /* ECMR */
index 9f18ae9..676c3c0 100644 (file)
@@ -285,6 +285,181 @@ static int efx_ef10_free_vis(struct efx_nic *efx)
        return rc;
 }
 
+#ifdef EFX_USE_PIO
+
+static void efx_ef10_free_piobufs(struct efx_nic *efx)
+{
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
+       MCDI_DECLARE_BUF(inbuf, MC_CMD_FREE_PIOBUF_IN_LEN);
+       unsigned int i;
+       int rc;
+
+       BUILD_BUG_ON(MC_CMD_FREE_PIOBUF_OUT_LEN != 0);
+
+       for (i = 0; i < nic_data->n_piobufs; i++) {
+               MCDI_SET_DWORD(inbuf, FREE_PIOBUF_IN_PIOBUF_HANDLE,
+                              nic_data->piobuf_handle[i]);
+               rc = efx_mcdi_rpc(efx, MC_CMD_FREE_PIOBUF, inbuf, sizeof(inbuf),
+                                 NULL, 0, NULL);
+               WARN_ON(rc);
+       }
+
+       nic_data->n_piobufs = 0;
+}
+
+static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
+{
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
+       MCDI_DECLARE_BUF(outbuf, MC_CMD_ALLOC_PIOBUF_OUT_LEN);
+       unsigned int i;
+       size_t outlen;
+       int rc = 0;
+
+       BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0);
+
+       for (i = 0; i < n; i++) {
+               rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
+                                 outbuf, sizeof(outbuf), &outlen);
+               if (rc)
+                       break;
+               if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) {
+                       rc = -EIO;
+                       break;
+               }
+               nic_data->piobuf_handle[i] =
+                       MCDI_DWORD(outbuf, ALLOC_PIOBUF_OUT_PIOBUF_HANDLE);
+               netif_dbg(efx, probe, efx->net_dev,
+                         "allocated PIO buffer %u handle %x\n", i,
+                         nic_data->piobuf_handle[i]);
+       }
+
+       nic_data->n_piobufs = i;
+       if (rc)
+               efx_ef10_free_piobufs(efx);
+       return rc;
+}
+
+static int efx_ef10_link_piobufs(struct efx_nic *efx)
+{
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
+       MCDI_DECLARE_BUF(inbuf,
+                        max(MC_CMD_LINK_PIOBUF_IN_LEN,
+                            MC_CMD_UNLINK_PIOBUF_IN_LEN));
+       struct efx_channel *channel;
+       struct efx_tx_queue *tx_queue;
+       unsigned int offset, index;
+       int rc;
+
+       BUILD_BUG_ON(MC_CMD_LINK_PIOBUF_OUT_LEN != 0);
+       BUILD_BUG_ON(MC_CMD_UNLINK_PIOBUF_OUT_LEN != 0);
+
+       /* Link a buffer to each VI in the write-combining mapping */
+       for (index = 0; index < nic_data->n_piobufs; ++index) {
+               MCDI_SET_DWORD(inbuf, LINK_PIOBUF_IN_PIOBUF_HANDLE,
+                              nic_data->piobuf_handle[index]);
+               MCDI_SET_DWORD(inbuf, LINK_PIOBUF_IN_TXQ_INSTANCE,
+                              nic_data->pio_write_vi_base + index);
+               rc = efx_mcdi_rpc(efx, MC_CMD_LINK_PIOBUF,
+                                 inbuf, MC_CMD_LINK_PIOBUF_IN_LEN,
+                                 NULL, 0, NULL);
+               if (rc) {
+                       netif_err(efx, drv, efx->net_dev,
+                                 "failed to link VI %u to PIO buffer %u (%d)\n",
+                                 nic_data->pio_write_vi_base + index, index,
+                                 rc);
+                       goto fail;
+               }
+               netif_dbg(efx, probe, efx->net_dev,
+                         "linked VI %u to PIO buffer %u\n",
+                         nic_data->pio_write_vi_base + index, index);
+       }
+
+       /* Link a buffer to each TX queue */
+       efx_for_each_channel(channel, efx) {
+               efx_for_each_channel_tx_queue(tx_queue, channel) {
+                       /* We assign the PIO buffers to queues in
+                        * reverse order to allow for the following
+                        * special case.
+                        */
+                       offset = ((efx->tx_channel_offset + efx->n_tx_channels -
+                                  tx_queue->channel->channel - 1) *
+                                 efx_piobuf_size);
+                       index = offset / ER_DZ_TX_PIOBUF_SIZE;
+                       offset = offset % ER_DZ_TX_PIOBUF_SIZE;
+
+                       /* When the host page size is 4K, the first
+                        * host page in the WC mapping may be within
+                        * the same VI page as the last TX queue.  We
+                        * can only link one buffer to each VI.
+                        */
+                       if (tx_queue->queue == nic_data->pio_write_vi_base) {
+                               BUG_ON(index != 0);
+                               rc = 0;
+                       } else {
+                               MCDI_SET_DWORD(inbuf,
+                                              LINK_PIOBUF_IN_PIOBUF_HANDLE,
+                                              nic_data->piobuf_handle[index]);
+                               MCDI_SET_DWORD(inbuf,
+                                              LINK_PIOBUF_IN_TXQ_INSTANCE,
+                                              tx_queue->queue);
+                               rc = efx_mcdi_rpc(efx, MC_CMD_LINK_PIOBUF,
+                                                 inbuf, MC_CMD_LINK_PIOBUF_IN_LEN,
+                                                 NULL, 0, NULL);
+                       }
+
+                       if (rc) {
+                               /* This is non-fatal; the TX path just
+                                * won't use PIO for this queue
+                                */
+                               netif_err(efx, drv, efx->net_dev,
+                                         "failed to link VI %u to PIO buffer %u (%d)\n",
+                                         tx_queue->queue, index, rc);
+                               tx_queue->piobuf = NULL;
+                       } else {
+                               tx_queue->piobuf =
+                                       nic_data->pio_write_base +
+                                       index * EFX_VI_PAGE_SIZE + offset;
+                               tx_queue->piobuf_offset = offset;
+                               netif_dbg(efx, probe, efx->net_dev,
+                                         "linked VI %u to PIO buffer %u offset %x addr %p\n",
+                                         tx_queue->queue, index,
+                                         tx_queue->piobuf_offset,
+                                         tx_queue->piobuf);
+                       }
+               }
+       }
+
+       return 0;
+
+fail:
+       while (index--) {
+               MCDI_SET_DWORD(inbuf, UNLINK_PIOBUF_IN_TXQ_INSTANCE,
+                              nic_data->pio_write_vi_base + index);
+               efx_mcdi_rpc(efx, MC_CMD_UNLINK_PIOBUF,
+                            inbuf, MC_CMD_UNLINK_PIOBUF_IN_LEN,
+                            NULL, 0, NULL);
+       }
+       return rc;
+}
+
+#else /* !EFX_USE_PIO */
+
+static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
+{
+       return n == 0 ? 0 : -ENOBUFS;
+}
+
+static int efx_ef10_link_piobufs(struct efx_nic *efx)
+{
+       return 0;
+}
+
+static void efx_ef10_free_piobufs(struct efx_nic *efx)
+{
+}
+
+#endif /* EFX_USE_PIO */
+
 static void efx_ef10_remove(struct efx_nic *efx)
 {
        struct efx_ef10_nic_data *nic_data = efx->nic_data;
@@ -295,9 +470,15 @@ static void efx_ef10_remove(struct efx_nic *efx)
        /* This needs to be after efx_ptp_remove_channel() with no filters */
        efx_ef10_rx_free_indir_table(efx);
 
+       if (nic_data->wc_membase)
+               iounmap(nic_data->wc_membase);
+
        rc = efx_ef10_free_vis(efx);
        WARN_ON(rc != 0);
 
+       if (!nic_data->must_restore_piobufs)
+               efx_ef10_free_piobufs(efx);
+
        efx_mcdi_fini(efx);
        efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
        kfree(nic_data);
@@ -330,12 +511,126 @@ static int efx_ef10_alloc_vis(struct efx_nic *efx,
        return 0;
 }
 
+/* Note that the failure path of this function does not free
+ * resources, as this will be done by efx_ef10_remove().
+ */
 static int efx_ef10_dimension_resources(struct efx_nic *efx)
 {
-       unsigned int n_vis =
-               max(efx->n_channels, efx->n_tx_channels * EFX_TXQ_TYPES);
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
+       unsigned int uc_mem_map_size, wc_mem_map_size;
+       unsigned int min_vis, pio_write_vi_base, max_vis;
+       void __iomem *membase;
+       int rc;
+
+       min_vis = max(efx->n_channels, efx->n_tx_channels * EFX_TXQ_TYPES);
+
+#ifdef EFX_USE_PIO
+       /* Try to allocate PIO buffers if wanted and if the full
+        * number of PIO buffers would be sufficient to allocate one
+        * copy-buffer per TX channel.  Failure is non-fatal, as there
+        * are only a small number of PIO buffers shared between all
+        * functions of the controller.
+        */
+       if (efx_piobuf_size != 0 &&
+           ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >=
+           efx->n_tx_channels) {
+               unsigned int n_piobufs =
+                       DIV_ROUND_UP(efx->n_tx_channels,
+                                    ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size);
+
+               rc = efx_ef10_alloc_piobufs(efx, n_piobufs);
+               if (rc)
+                       netif_err(efx, probe, efx->net_dev,
+                                 "failed to allocate PIO buffers (%d)\n", rc);
+               else
+                       netif_dbg(efx, probe, efx->net_dev,
+                                 "allocated %u PIO buffers\n", n_piobufs);
+       }
+#else
+       nic_data->n_piobufs = 0;
+#endif
+
+       /* PIO buffers should be mapped with write-combining enabled,
+        * and we want to make single UC and WC mappings rather than
+        * several of each (in fact that's the only option if host
+        * page size is >4K).  So we may allocate some extra VIs just
+        * for writing PIO buffers through.
+        */
+       uc_mem_map_size = PAGE_ALIGN((min_vis - 1) * EFX_VI_PAGE_SIZE +
+                                    ER_DZ_TX_PIOBUF);
+       if (nic_data->n_piobufs) {
+               pio_write_vi_base = uc_mem_map_size / EFX_VI_PAGE_SIZE;
+               wc_mem_map_size = (PAGE_ALIGN((pio_write_vi_base +
+                                              nic_data->n_piobufs) *
+                                             EFX_VI_PAGE_SIZE) -
+                                  uc_mem_map_size);
+               max_vis = pio_write_vi_base + nic_data->n_piobufs;
+       } else {
+               pio_write_vi_base = 0;
+               wc_mem_map_size = 0;
+               max_vis = min_vis;
+       }
+
+       /* In case the last attached driver failed to free VIs, do it now */
+       rc = efx_ef10_free_vis(efx);
+       if (rc != 0)
+               return rc;
+
+       rc = efx_ef10_alloc_vis(efx, min_vis, max_vis);
+       if (rc != 0)
+               return rc;
+
+       /* If we didn't get enough VIs to map all the PIO buffers, free the
+        * PIO buffers
+        */
+       if (nic_data->n_piobufs &&
+           nic_data->n_allocated_vis <
+           pio_write_vi_base + nic_data->n_piobufs) {
+               netif_dbg(efx, probe, efx->net_dev,
+                         "%u VIs are not sufficient to map %u PIO buffers\n",
+                         nic_data->n_allocated_vis, nic_data->n_piobufs);
+               efx_ef10_free_piobufs(efx);
+       }
+
+       /* Shrink the original UC mapping of the memory BAR */
+       membase = ioremap_nocache(efx->membase_phys, uc_mem_map_size);
+       if (!membase) {
+               netif_err(efx, probe, efx->net_dev,
+                         "could not shrink memory BAR to %x\n",
+                         uc_mem_map_size);
+               return -ENOMEM;
+       }
+       iounmap(efx->membase);
+       efx->membase = membase;
+
+       /* Set up the WC mapping if needed */
+       if (wc_mem_map_size) {
+               nic_data->wc_membase = ioremap_wc(efx->membase_phys +
+                                                 uc_mem_map_size,
+                                                 wc_mem_map_size);
+               if (!nic_data->wc_membase) {
+                       netif_err(efx, probe, efx->net_dev,
+                                 "could not allocate WC mapping of size %x\n",
+                                 wc_mem_map_size);
+                       return -ENOMEM;
+               }
+               nic_data->pio_write_vi_base = pio_write_vi_base;
+               nic_data->pio_write_base =
+                       nic_data->wc_membase +
+                       (pio_write_vi_base * EFX_VI_PAGE_SIZE + ER_DZ_TX_PIOBUF -
+                        uc_mem_map_size);
 
-       return efx_ef10_alloc_vis(efx, n_vis, n_vis);
+               rc = efx_ef10_link_piobufs(efx);
+               if (rc)
+                       efx_ef10_free_piobufs(efx);
+       }
+
+       netif_dbg(efx, probe, efx->net_dev,
+                 "memory BAR at %pa (virtual %p+%x UC, %p+%x WC)\n",
+                 &efx->membase_phys, efx->membase, uc_mem_map_size,
+                 nic_data->wc_membase, wc_mem_map_size);
+
+       return 0;
 }
 
 static int efx_ef10_init_nic(struct efx_nic *efx)
@@ -359,6 +654,21 @@ static int efx_ef10_init_nic(struct efx_nic *efx)
                nic_data->must_realloc_vis = false;
        }
 
+       if (nic_data->must_restore_piobufs && nic_data->n_piobufs) {
+               rc = efx_ef10_alloc_piobufs(efx, nic_data->n_piobufs);
+               if (rc == 0) {
+                       rc = efx_ef10_link_piobufs(efx);
+                       if (rc)
+                               efx_ef10_free_piobufs(efx);
+               }
+
+               /* Log an error on failure, but this is non-fatal */
+               if (rc)
+                       netif_err(efx, drv, efx->net_dev,
+                                 "failed to restore PIO buffers (%d)\n", rc);
+               nic_data->must_restore_piobufs = false;
+       }
+
        efx_ef10_rx_push_indir_table(efx);
        return 0;
 }
@@ -444,6 +754,18 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = {
        EF10_DMA_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS),
        EF10_DMA_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS),
        EF10_DMA_STAT(rx_nodesc_drops, RX_NODESC_DROPS),
+       EF10_DMA_STAT(rx_pm_trunc_bb_overflow, PM_TRUNC_BB_OVERFLOW),
+       EF10_DMA_STAT(rx_pm_discard_bb_overflow, PM_DISCARD_BB_OVERFLOW),
+       EF10_DMA_STAT(rx_pm_trunc_vfifo_full, PM_TRUNC_VFIFO_FULL),
+       EF10_DMA_STAT(rx_pm_discard_vfifo_full, PM_DISCARD_VFIFO_FULL),
+       EF10_DMA_STAT(rx_pm_trunc_qbb, PM_TRUNC_QBB),
+       EF10_DMA_STAT(rx_pm_discard_qbb, PM_DISCARD_QBB),
+       EF10_DMA_STAT(rx_pm_discard_mapping, PM_DISCARD_MAPPING),
+       EF10_DMA_STAT(rx_dp_q_disabled_packets, RXDP_Q_DISABLED_PKTS),
+       EF10_DMA_STAT(rx_dp_di_dropped_packets, RXDP_DI_DROPPED_PKTS),
+       EF10_DMA_STAT(rx_dp_streaming_packets, RXDP_STREAMING_PKTS),
+       EF10_DMA_STAT(rx_dp_emerg_fetch, RXDP_EMERGENCY_FETCH_CONDITIONS),
+       EF10_DMA_STAT(rx_dp_emerg_wait, RXDP_EMERGENCY_WAIT_CONDITIONS),
 };
 
 #define HUNT_COMMON_STAT_MASK ((1ULL << EF10_STAT_tx_bytes) |          \
@@ -498,44 +820,72 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = {
 #define HUNT_40G_EXTRA_STAT_MASK ((1ULL << EF10_STAT_rx_align_error) | \
                                  (1ULL << EF10_STAT_rx_length_error))
 
-#if BITS_PER_LONG == 64
-#define STAT_MASK_BITMAP(bits) (bits)
-#else
-#define STAT_MASK_BITMAP(bits) (bits) & 0xffffffff, (bits) >> 32
-#endif
-
-static const unsigned long *efx_ef10_stat_mask(struct efx_nic *efx)
-{
-       static const unsigned long hunt_40g_stat_mask[] = {
-               STAT_MASK_BITMAP(HUNT_COMMON_STAT_MASK |
-                                HUNT_40G_EXTRA_STAT_MASK)
-       };
-       static const unsigned long hunt_10g_only_stat_mask[] = {
-               STAT_MASK_BITMAP(HUNT_COMMON_STAT_MASK |
-                                HUNT_10G_ONLY_STAT_MASK)
-       };
+/* These statistics are only provided if the firmware supports the
+ * capability PM_AND_RXDP_COUNTERS.
+ */
+#define HUNT_PM_AND_RXDP_STAT_MASK (                                   \
+       (1ULL << EF10_STAT_rx_pm_trunc_bb_overflow) |                   \
+       (1ULL << EF10_STAT_rx_pm_discard_bb_overflow) |                 \
+       (1ULL << EF10_STAT_rx_pm_trunc_vfifo_full) |                    \
+       (1ULL << EF10_STAT_rx_pm_discard_vfifo_full) |                  \
+       (1ULL << EF10_STAT_rx_pm_trunc_qbb) |                           \
+       (1ULL << EF10_STAT_rx_pm_discard_qbb) |                         \
+       (1ULL << EF10_STAT_rx_pm_discard_mapping) |                     \
+       (1ULL << EF10_STAT_rx_dp_q_disabled_packets) |                  \
+       (1ULL << EF10_STAT_rx_dp_di_dropped_packets) |                  \
+       (1ULL << EF10_STAT_rx_dp_streaming_packets) |                   \
+       (1ULL << EF10_STAT_rx_dp_emerg_fetch) |                         \
+       (1ULL << EF10_STAT_rx_dp_emerg_wait))
+
+static u64 efx_ef10_raw_stat_mask(struct efx_nic *efx)
+{
+       u64 raw_mask = HUNT_COMMON_STAT_MASK;
        u32 port_caps = efx_mcdi_phy_get_caps(efx);
+       struct efx_ef10_nic_data *nic_data = efx->nic_data;
 
        if (port_caps & (1 << MC_CMD_PHY_CAP_40000FDX_LBN))
-               return hunt_40g_stat_mask;
+               raw_mask |= HUNT_40G_EXTRA_STAT_MASK;
        else
-               return hunt_10g_only_stat_mask;
+               raw_mask |= HUNT_10G_ONLY_STAT_MASK;
+
+       if (nic_data->datapath_caps &
+           (1 << MC_CMD_GET_CAPABILITIES_OUT_PM_AND_RXDP_COUNTERS_LBN))
+               raw_mask |= HUNT_PM_AND_RXDP_STAT_MASK;
+
+       return raw_mask;
+}
+
+static void efx_ef10_get_stat_mask(struct efx_nic *efx, unsigned long *mask)
+{
+       u64 raw_mask = efx_ef10_raw_stat_mask(efx);
+
+#if BITS_PER_LONG == 64
+       mask[0] = raw_mask;
+#else
+       mask[0] = raw_mask & 0xffffffff;
+       mask[1] = raw_mask >> 32;
+#endif
 }
 
 static size_t efx_ef10_describe_stats(struct efx_nic *efx, u8 *names)
 {
+       DECLARE_BITMAP(mask, EF10_STAT_COUNT);
+
+       efx_ef10_get_stat_mask(efx, mask);
        return efx_nic_describe_stats(efx_ef10_stat_desc, EF10_STAT_COUNT,
-                                     efx_ef10_stat_mask(efx), names);
+                                     mask, names);
 }
 
 static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
 {
        struct efx_ef10_nic_data *nic_data = efx->nic_data;
-       const unsigned long *stats_mask = efx_ef10_stat_mask(efx);
+       DECLARE_BITMAP(mask, EF10_STAT_COUNT);
        __le64 generation_start, generation_end;
        u64 *stats = nic_data->stats;
        __le64 *dma_stats;
 
+       efx_ef10_get_stat_mask(efx, mask);
+
        dma_stats = efx->stats_buffer.addr;
        nic_data = efx->nic_data;
 
@@ -543,8 +893,9 @@ static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
        if (generation_end == EFX_MC_STATS_GENERATION_INVALID)
                return 0;
        rmb();
-       efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, stats_mask,
+       efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, mask,
                             stats, efx->stats_buffer.addr, false);
+       rmb();
        generation_start = dma_stats[MC_CMD_MAC_GENERATION_START];
        if (generation_end != generation_start)
                return -EAGAIN;
@@ -563,12 +914,14 @@ static int efx_ef10_try_update_nic_stats(struct efx_nic *efx)
 static size_t efx_ef10_update_stats(struct efx_nic *efx, u64 *full_stats,
                                    struct rtnl_link_stats64 *core_stats)
 {
-       const unsigned long *mask = efx_ef10_stat_mask(efx);
+       DECLARE_BITMAP(mask, EF10_STAT_COUNT);
        struct efx_ef10_nic_data *nic_data = efx->nic_data;
        u64 *stats = nic_data->stats;
        size_t stats_count = 0, index;
        int retry;
 
+       efx_ef10_get_stat_mask(efx, mask);
+
        /* If we're unlucky enough to read statistics during the DMA, wait
         * up to 10ms for it to finish (typically takes <500us)
         */
@@ -716,6 +1069,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx)
        /* All our allocations have been reset */
        nic_data->must_realloc_vis = true;
        nic_data->must_restore_filters = true;
+       nic_data->must_restore_piobufs = true;
        nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
 
        /* The datapath firmware might have been changed */
@@ -2137,7 +2491,7 @@ out_unlock:
        return rc;
 }
 
-void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx)
+static void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx)
 {
        /* no need to do anything here on EF10 */
 }
index b3f4e37..207ac9a 100644 (file)
 #define        ESF_DZ_TX_PIO_TYPE_WIDTH 1
 #define        ESF_DZ_TX_PIO_OPT_LBN 60
 #define        ESF_DZ_TX_PIO_OPT_WIDTH 3
+#define        ESE_DZ_TX_OPTION_DESC_PIO 1
 #define        ESF_DZ_TX_PIO_CONT_LBN 59
 #define        ESF_DZ_TX_PIO_CONT_WIDTH 1
 #define        ESF_DZ_TX_PIO_BYTE_CNT_LBN 32
index 34d00f5..b8235ee 100644 (file)
 #define EFX_MEM_BAR 2
 
 /* TX */
-extern int efx_probe_tx_queue(struct efx_tx_queue *tx_queue);
-extern void efx_remove_tx_queue(struct efx_tx_queue *tx_queue);
-extern void efx_init_tx_queue(struct efx_tx_queue *tx_queue);
-extern void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue);
-extern void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
-extern netdev_tx_t
-efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev);
-extern netdev_tx_t
-efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
-extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
-extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
-extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
+int efx_probe_tx_queue(struct efx_tx_queue *tx_queue);
+void efx_remove_tx_queue(struct efx_tx_queue *tx_queue);
+void efx_init_tx_queue(struct efx_tx_queue *tx_queue);
+void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue);
+void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
+netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb,
+                               struct net_device *net_dev);
+netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
+void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
+int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
+unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
+extern unsigned int efx_piobuf_size;
 
 /* RX */
-extern void efx_rx_config_page_split(struct efx_nic *efx);
-extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
-extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
-extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
-extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
-extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
-extern void efx_rx_slow_fill(unsigned long context);
-extern void __efx_rx_packet(struct efx_channel *channel);
-extern void efx_rx_packet(struct efx_rx_queue *rx_queue,
-                         unsigned int index, unsigned int n_frags,
-                         unsigned int len, u16 flags);
+void efx_rx_config_page_split(struct efx_nic *efx);
+int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
+void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
+void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
+void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
+void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
+void efx_rx_slow_fill(unsigned long context);
+void __efx_rx_packet(struct efx_channel *channel);
+void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
+                  unsigned int n_frags, unsigned int len, u16 flags);
 static inline void efx_rx_flush_packet(struct efx_channel *channel)
 {
        if (channel->rx_pkt_n_frags)
                __efx_rx_packet(channel);
 }
-extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
+void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
 
 #define EFX_MAX_DMAQ_SIZE 4096UL
 #define EFX_DEFAULT_DMAQ_SIZE 1024UL
@@ -162,9 +161,9 @@ static inline s32 efx_filter_get_rx_ids(struct efx_nic *efx,
        return efx->type->filter_get_rx_ids(efx, priority, buf, size);
 }
 #ifdef CONFIG_RFS_ACCEL
-extern int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
-                         u16 rxq_index, u32 flow_id);
-extern bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota);
+int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
+                  u16 rxq_index, u32 flow_id);
+bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota);
 static inline void efx_filter_rfs_expire(struct efx_channel *channel)
 {
        if (channel->rfs_filters_added >= 60 &&
@@ -176,50 +175,48 @@ static inline void efx_filter_rfs_expire(struct efx_channel *channel)
 static inline void efx_filter_rfs_expire(struct efx_channel *channel) {}
 #define efx_filter_rfs_enabled() 0
 #endif
-extern bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);
+bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);
 
 /* Channels */
-extern int efx_channel_dummy_op_int(struct efx_channel *channel);
-extern void efx_channel_dummy_op_void(struct efx_channel *channel);
-extern int
-efx_realloc_channels(struct efx_nic *efx, u32 rxq_entries, u32 txq_entries);
+int efx_channel_dummy_op_int(struct efx_channel *channel);
+void efx_channel_dummy_op_void(struct efx_channel *channel);
+int efx_realloc_channels(struct efx_nic *efx, u32 rxq_entries, u32 txq_entries);
 
 /* Ports */
-extern int efx_reconfigure_port(struct efx_nic *efx);
-extern int __efx_reconfigure_port(struct efx_nic *efx);
+int efx_reconfigure_port(struct efx_nic *efx);
+int __efx_reconfigure_port(struct efx_nic *efx);
 
 /* Ethtool support */
 extern const struct ethtool_ops efx_ethtool_ops;
 
 /* Reset handling */
-extern int efx_reset(struct efx_nic *efx, enum reset_type method);
-extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
-extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
-extern int efx_try_recovery(struct efx_nic *efx);
+int efx_reset(struct efx_nic *efx, enum reset_type method);
+void efx_reset_down(struct efx_nic *efx, enum reset_type method);
+int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
+int efx_try_recovery(struct efx_nic *efx);
 
 /* Global */
-extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
-extern int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
-                                  unsigned int rx_usecs, bool rx_adaptive,
-                                  bool rx_may_override_tx);
-extern void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
-                                  unsigned int *rx_usecs, bool *rx_adaptive);
+void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
+int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
+                           unsigned int rx_usecs, bool rx_adaptive,
+                           bool rx_may_override_tx);
+void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
+                           unsigned int *rx_usecs, bool *rx_adaptive);
 
 /* Dummy PHY ops for PHY drivers */
-extern int efx_port_dummy_op_int(struct efx_nic *efx);
-extern void efx_port_dummy_op_void(struct efx_nic *efx);
-
+int efx_port_dummy_op_int(struct efx_nic *efx);
+void efx_port_dummy_op_void(struct efx_nic *efx);
 
 /* MTD */
 #ifdef CONFIG_SFC_MTD
-extern int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
-                      size_t n_parts, size_t sizeof_part);
+int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
+               size_t n_parts, size_t sizeof_part);
 static inline int efx_mtd_probe(struct efx_nic *efx)
 {
        return efx->type->mtd_probe(efx);
 }
-extern void efx_mtd_rename(struct efx_nic *efx);
-extern void efx_mtd_remove(struct efx_nic *efx);
+void efx_mtd_rename(struct efx_nic *efx);
+void efx_mtd_remove(struct efx_nic *efx);
 #else
 static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; }
 static inline void efx_mtd_rename(struct efx_nic *efx) {}
@@ -241,9 +238,9 @@ static inline void efx_schedule_channel_irq(struct efx_channel *channel)
        efx_schedule_channel(channel);
 }
 
-extern void efx_link_status_changed(struct efx_nic *efx);
-extern void efx_link_set_advertising(struct efx_nic *efx, u32);
-extern void efx_link_set_wanted_fc(struct efx_nic *efx, u8);
+void efx_link_status_changed(struct efx_nic *efx);
+void efx_link_set_advertising(struct efx_nic *efx, u32);
+void efx_link_set_wanted_fc(struct efx_nic *efx, u8);
 
 static inline void efx_device_detach_sync(struct efx_nic *efx)
 {
index 5b471cf..1f529fa 100644 (file)
@@ -70,6 +70,7 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = {
        EFX_ETHTOOL_UINT_TXQ_STAT(tso_long_headers),
        EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets),
        EFX_ETHTOOL_UINT_TXQ_STAT(pushes),
+       EFX_ETHTOOL_UINT_TXQ_STAT(pio_packets),
        EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset),
        EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc),
        EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err),
@@ -1035,8 +1036,8 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
        return 0;
 }
 
-int efx_ethtool_get_ts_info(struct net_device *net_dev,
-                           struct ethtool_ts_info *ts_info)
+static int efx_ethtool_get_ts_info(struct net_device *net_dev,
+                                  struct ethtool_ts_info *ts_info)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
index 96ce507..4d3f119 100644 (file)
 #define EFX_USE_QWORD_IO 1
 #endif
 
+/* PIO is a win only if write-combining is possible */
+#ifdef ARCH_HAS_IOREMAP_WC
+#define EFX_USE_PIO 1
+#endif
+
 #ifdef EFX_USE_QWORD_IO
 static inline void _efx_writeq(struct efx_nic *efx, __le64 value,
                                  unsigned int reg)
index 128d7cd..366c8e3 100644 (file)
 
 /* A reboot/assertion causes the MCDI status word to be set after the
  * command word is set or a REBOOT event is sent. If we notice a reboot
- * via these mechanisms then wait 20ms for the status word to be set.
+ * via these mechanisms then wait 250ms for the status word to be set.
  */
 #define MCDI_STATUS_DELAY_US           100
-#define MCDI_STATUS_DELAY_COUNT                200
+#define MCDI_STATUS_DELAY_COUNT                2500
 #define MCDI_STATUS_SLEEP_MS                                           \
        (MCDI_STATUS_DELAY_US * MCDI_STATUS_DELAY_COUNT / 1000)
 
@@ -800,9 +800,6 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
        } else {
                int count;
 
-               /* Nobody was waiting for an MCDI request, so trigger a reset */
-               efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
-
                /* Consume the status word since efx_mcdi_rpc_finish() won't */
                for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) {
                        if (efx_mcdi_poll_reboot(efx))
@@ -810,6 +807,9 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
                        udelay(MCDI_STATUS_DELAY_US);
                }
                mcdi->new_epoch = true;
+
+               /* Nobody was waiting for an MCDI request, so trigger a reset */
+               efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
        }
 
        spin_unlock(&mcdi->iface_lock);
@@ -963,7 +963,7 @@ static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
                               bool *was_attached)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_DRV_ATTACH_IN_LEN);
-       MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_OUT_LEN);
+       MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
        size_t outlen;
        int rc;
 
@@ -981,6 +981,22 @@ static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
                goto fail;
        }
 
+       /* We currently assume we have control of the external link
+        * and are completely trusted by firmware.  Abort probing
+        * if that's not true for this function.
+        */
+       if (driver_operating &&
+           outlen >= MC_CMD_DRV_ATTACH_EXT_OUT_LEN &&
+           (MCDI_DWORD(outbuf, DRV_ATTACH_EXT_OUT_FUNC_FLAGS) &
+            (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
+             1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)) !=
+           (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
+            1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)) {
+               netif_err(efx, probe, efx->net_dev,
+                         "This driver version only supports one function per port\n");
+               return -ENODEV;
+       }
+
        if (was_attached != NULL)
                *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE);
        return 0;
index c34d0d4..656a327 100644 (file)
@@ -108,38 +108,35 @@ static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx)
 }
 #endif
 
-extern int efx_mcdi_init(struct efx_nic *efx);
-extern void efx_mcdi_fini(struct efx_nic *efx);
+int efx_mcdi_init(struct efx_nic *efx);
+void efx_mcdi_fini(struct efx_nic *efx);
 
-extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
-                       const efx_dword_t *inbuf, size_t inlen,
+int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const efx_dword_t *inbuf,
+                size_t inlen, efx_dword_t *outbuf, size_t outlen,
+                size_t *outlen_actual);
+
+int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
+                      const efx_dword_t *inbuf, size_t inlen);
+int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
                        efx_dword_t *outbuf, size_t outlen,
                        size_t *outlen_actual);
 
-extern int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
-                             const efx_dword_t *inbuf, size_t inlen);
-extern int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
-                              efx_dword_t *outbuf, size_t outlen,
-                              size_t *outlen_actual);
-
 typedef void efx_mcdi_async_completer(struct efx_nic *efx,
                                      unsigned long cookie, int rc,
                                      efx_dword_t *outbuf,
                                      size_t outlen_actual);
-extern int efx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd,
-                             const efx_dword_t *inbuf, size_t inlen,
-                             size_t outlen,
-                             efx_mcdi_async_completer *complete,
-                             unsigned long cookie);
+int efx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd,
+                      const efx_dword_t *inbuf, size_t inlen, size_t outlen,
+                      efx_mcdi_async_completer *complete,
+                      unsigned long cookie);
 
-extern int efx_mcdi_poll_reboot(struct efx_nic *efx);
-extern void efx_mcdi_mode_poll(struct efx_nic *efx);
-extern void efx_mcdi_mode_event(struct efx_nic *efx);
-extern void efx_mcdi_flush_async(struct efx_nic *efx);
+int efx_mcdi_poll_reboot(struct efx_nic *efx);
+void efx_mcdi_mode_poll(struct efx_nic *efx);
+void efx_mcdi_mode_event(struct efx_nic *efx);
+void efx_mcdi_flush_async(struct efx_nic *efx);
 
-extern void efx_mcdi_process_event(struct efx_channel *channel,
-                                  efx_qword_t *event);
-extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
+void efx_mcdi_process_event(struct efx_channel *channel, efx_qword_t *event);
+void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
 
 /* We expect that 16- and 32-bit fields in MCDI requests and responses
  * are appropriately aligned, but 64-bit fields are only
@@ -275,55 +272,54 @@ extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
 #define MCDI_EVENT_FIELD(_ev, _field)                  \
        EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field)
 
-extern void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len);
-extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
-                                 u16 *fw_subtype_list, u32 *capabilities);
-extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart,
-                            u32 dest_evq);
-extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
-extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
-                              size_t *size_out, size_t *erase_size_out,
-                              bool *protected_out);
-extern int efx_mcdi_nvram_test_all(struct efx_nic *efx);
-extern int efx_mcdi_handle_assertion(struct efx_nic *efx);
-extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
-extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx,
-                                        const u8 *mac, int *id_out);
-extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out);
-extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id);
-extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx);
-extern int efx_mcdi_flush_rxqs(struct efx_nic *efx);
-extern int efx_mcdi_port_probe(struct efx_nic *efx);
-extern void efx_mcdi_port_remove(struct efx_nic *efx);
-extern int efx_mcdi_port_reconfigure(struct efx_nic *efx);
-extern int efx_mcdi_port_get_number(struct efx_nic *efx);
-extern u32 efx_mcdi_phy_get_caps(struct efx_nic *efx);
-extern void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev);
-extern int efx_mcdi_set_mac(struct efx_nic *efx);
+void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len);
+int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
+                          u16 *fw_subtype_list, u32 *capabilities);
+int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq);
+int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
+int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
+                       size_t *size_out, size_t *erase_size_out,
+                       bool *protected_out);
+int efx_mcdi_nvram_test_all(struct efx_nic *efx);
+int efx_mcdi_handle_assertion(struct efx_nic *efx);
+void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
+int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac,
+                                 int *id_out);
+int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out);
+int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id);
+int efx_mcdi_wol_filter_reset(struct efx_nic *efx);
+int efx_mcdi_flush_rxqs(struct efx_nic *efx);
+int efx_mcdi_port_probe(struct efx_nic *efx);
+void efx_mcdi_port_remove(struct efx_nic *efx);
+int efx_mcdi_port_reconfigure(struct efx_nic *efx);
+int efx_mcdi_port_get_number(struct efx_nic *efx);
+u32 efx_mcdi_phy_get_caps(struct efx_nic *efx);
+void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev);
+int efx_mcdi_set_mac(struct efx_nic *efx);
 #define EFX_MC_STATS_GENERATION_INVALID ((__force __le64)(-1))
-extern void efx_mcdi_mac_start_stats(struct efx_nic *efx);
-extern void efx_mcdi_mac_stop_stats(struct efx_nic *efx);
-extern bool efx_mcdi_mac_check_fault(struct efx_nic *efx);
-extern enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason);
-extern int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method);
-extern int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled);
+void efx_mcdi_mac_start_stats(struct efx_nic *efx);
+void efx_mcdi_mac_stop_stats(struct efx_nic *efx);
+bool efx_mcdi_mac_check_fault(struct efx_nic *efx);
+enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason);
+int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method);
+int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled);
 
 #ifdef CONFIG_SFC_MCDI_MON
-extern int efx_mcdi_mon_probe(struct efx_nic *efx);
-extern void efx_mcdi_mon_remove(struct efx_nic *efx);
+int efx_mcdi_mon_probe(struct efx_nic *efx);
+void efx_mcdi_mon_remove(struct efx_nic *efx);
 #else
 static inline int efx_mcdi_mon_probe(struct efx_nic *efx) { return 0; }
 static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {}
 #endif
 
 #ifdef CONFIG_SFC_MTD
-extern int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
-                            size_t len, size_t *retlen, u8 *buffer);
-extern int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len);
-extern int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
-                             size_t len, size_t *retlen, const u8 *buffer);
-extern int efx_mcdi_mtd_sync(struct mtd_info *mtd);
-extern void efx_mcdi_mtd_rename(struct efx_mtd_partition *part);
+int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
+                     size_t *retlen, u8 *buffer);
+int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len);
+int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start, size_t len,
+                      size_t *retlen, const u8 *buffer);
+int efx_mcdi_mtd_sync(struct mtd_info *mtd);
+void efx_mcdi_mtd_rename(struct efx_mtd_partition *part);
 #endif
 
 #endif /* EFX_MCDI_H */
index b5cf624..e0a63dd 100644 (file)
 #define          MC_CMD_MAC_RX_LANES01_DISP_ERR  0x39 /* enum */
 #define          MC_CMD_MAC_RX_LANES23_DISP_ERR  0x3a /* enum */
 #define          MC_CMD_MAC_RX_MATCH_FAULT  0x3b /* enum */
-#define          MC_CMD_GMAC_DMABUF_START  0x40 /* enum */
-#define          MC_CMD_GMAC_DMABUF_END    0x5f /* enum */
+/* enum: PM trunc_bb_overflow counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_TRUNC_BB_OVERFLOW  0x3c
+/* enum: PM discard_bb_overflow counter. Valid for EF10 with
+ * PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_BB_OVERFLOW  0x3d
+/* enum: PM trunc_vfifo_full counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_TRUNC_VFIFO_FULL  0x3e
+/* enum: PM discard_vfifo_full counter. Valid for EF10 with
+ * PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_VFIFO_FULL  0x3f
+/* enum: PM trunc_qbb counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_TRUNC_QBB  0x40
+/* enum: PM discard_qbb counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_QBB  0x41
+/* enum: PM discard_mapping counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
+ * capability only.
+ */
+#define          MC_CMD_MAC_PM_DISCARD_MAPPING  0x42
+/* enum: RXDP counter: Number of packets dropped due to the queue being
+ * disabled. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_Q_DISABLED_PKTS  0x43
+/* enum: RXDP counter: Number of packets dropped by the DICPU. Valid for EF10
+ * with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_DI_DROPPED_PKTS  0x45
+/* enum: RXDP counter: Number of non-host packets. Valid for EF10 with
+ * PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_STREAMING_PKTS  0x46
+/* enum: RXDP counter: Number of times an emergency descriptor fetch was
+ * performed. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_EMERGENCY_FETCH_CONDITIONS  0x47
+/* enum: RXDP counter: Number of times the DPCPU waited for an existing
+ * descriptor fetch. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
+ */
+#define          MC_CMD_MAC_RXDP_EMERGENCY_WAIT_CONDITIONS  0x48
+/* enum: Start of GMAC stats buffer space, for Siena only. */
+#define          MC_CMD_GMAC_DMABUF_START  0x40
+/* enum: End of GMAC stats buffer space, for Siena only. */
+#define          MC_CMD_GMAC_DMABUF_END    0x5f
 #define          MC_CMD_MAC_GENERATION_END 0x60 /* enum */
 #define          MC_CMD_MAC_NSTATS  0x61 /* enum */
 
 #define        MC_CMD_GET_CAPABILITIES_OUT_RX_BATCHING_WIDTH 1
 #define        MC_CMD_GET_CAPABILITIES_OUT_MCAST_FILTER_CHAINING_LBN 26
 #define        MC_CMD_GET_CAPABILITIES_OUT_MCAST_FILTER_CHAINING_WIDTH 1
+#define        MC_CMD_GET_CAPABILITIES_OUT_PM_AND_RXDP_COUNTERS_LBN 27
+#define        MC_CMD_GET_CAPABILITIES_OUT_PM_AND_RXDP_COUNTERS_WIDTH 1
 /* RxDPCPU firmware id. */
 #define       MC_CMD_GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID_OFST 4
 #define       MC_CMD_GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID_LEN 2
index 16824fe..4a2dc4c 100644 (file)
@@ -20,7 +20,7 @@
 
 static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; }
 static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; }
-extern unsigned efx_mdio_id_oui(u32 id);
+unsigned efx_mdio_id_oui(u32 id);
 
 static inline int efx_mdio_read(struct efx_nic *efx, int devad, int addr)
 {
@@ -56,7 +56,7 @@ static inline bool efx_mdio_phyxgxs_lane_sync(struct efx_nic *efx)
        return sync;
 }
 
-extern const char *efx_mdio_mmd_name(int mmd);
+const char *efx_mdio_mmd_name(int mmd);
 
 /*
  * Reset a specific MMD and wait for reset to clear.
@@ -64,30 +64,29 @@ extern const char *efx_mdio_mmd_name(int mmd);
  *
  * This function will sleep
  */
-extern int efx_mdio_reset_mmd(struct efx_nic *efx, int mmd,
-                             int spins, int spintime);
+int efx_mdio_reset_mmd(struct efx_nic *efx, int mmd, int spins, int spintime);
 
 /* As efx_mdio_check_mmd but for multiple MMDs */
 int efx_mdio_check_mmds(struct efx_nic *efx, unsigned int mmd_mask);
 
 /* Check the link status of specified mmds in bit mask */
-extern bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask);
+bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask);
 
 /* Generic transmit disable support though PMAPMD */
-extern void efx_mdio_transmit_disable(struct efx_nic *efx);
+void efx_mdio_transmit_disable(struct efx_nic *efx);
 
 /* Generic part of reconfigure: set/clear loopback bits */
-extern void efx_mdio_phy_reconfigure(struct efx_nic *efx);
+void efx_mdio_phy_reconfigure(struct efx_nic *efx);
 
 /* Set the power state of the specified MMDs */
-extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
-                                    int low_power, unsigned int mmd_mask);
+void efx_mdio_set_mmds_lpower(struct efx_nic *efx, int low_power,
+                             unsigned int mmd_mask);
 
 /* Set (some of) the PHY settings over MDIO */
-extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd);
+int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd);
 
 /* Push advertising flags and restart autonegotiation */
-extern void efx_mdio_an_reconfigure(struct efx_nic *efx);
+void efx_mdio_an_reconfigure(struct efx_nic *efx);
 
 /* Get pause parameters from AN if available (otherwise return
  * requested pause parameters)
@@ -95,8 +94,7 @@ extern void efx_mdio_an_reconfigure(struct efx_nic *efx);
 u8 efx_mdio_get_pause(struct efx_nic *efx);
 
 /* Wait for specified MMDs to exit reset within a timeout */
-extern int efx_mdio_wait_reset_mmds(struct efx_nic *efx,
-                                   unsigned int mmd_mask);
+int efx_mdio_wait_reset_mmds(struct efx_nic *efx, unsigned int mmd_mask);
 
 /* Set or clear flag, debouncing */
 static inline void
@@ -107,6 +105,6 @@ efx_mdio_set_flag(struct efx_nic *efx, int devad, int addr,
 }
 
 /* Liveness self-test for MDIO PHYs */
-extern int efx_mdio_test_alive(struct efx_nic *efx);
+int efx_mdio_test_alive(struct efx_nic *efx);
 
 #endif /* EFX_MDIO_10G_H */
index b172ed1..b14a717 100644 (file)
@@ -141,6 +141,8 @@ struct efx_special_buffer {
  * @len: Length of this fragment.
  *     This field is zero when the queue slot is empty.
  * @unmap_len: Length of this fragment to unmap
+ * @dma_offset: Offset of @dma_addr from the address of the backing DMA mapping.
+ * Only valid if @unmap_len != 0.
  */
 struct efx_tx_buffer {
        union {
@@ -154,6 +156,7 @@ struct efx_tx_buffer {
        unsigned short flags;
        unsigned short len;
        unsigned short unmap_len;
+       unsigned short dma_offset;
 };
 #define EFX_TX_BUF_CONT                1       /* not last descriptor of packet */
 #define EFX_TX_BUF_SKB         2       /* buffer is last part of skb */
@@ -182,6 +185,9 @@ struct efx_tx_buffer {
  * @tsoh_page: Array of pages of TSO header buffers
  * @txd: The hardware descriptor ring
  * @ptr_mask: The size of the ring minus 1.
+ * @piobuf: PIO buffer region for this TX queue (shared with its partner).
+ *     Size of the region is efx_piobuf_size.
+ * @piobuf_offset: Buffer offset to be specified in PIO descriptors
  * @initialised: Has hardware queue been initialised?
  * @read_count: Current read pointer.
  *     This is the number of buffers that have been removed from both rings.
@@ -209,6 +215,7 @@ struct efx_tx_buffer {
  *     blocks
  * @tso_packets: Number of packets via the TSO xmit path
  * @pushes: Number of times the TX push feature has been used
+ * @pio_packets: Number of times the TX PIO feature has been used
  * @empty_read_count: If the completion path has seen the queue as empty
  *     and the transmission path has not yet checked this, the value of
  *     @read_count bitwise-added to %EFX_EMPTY_COUNT_VALID; otherwise 0.
@@ -223,6 +230,8 @@ struct efx_tx_queue {
        struct efx_buffer *tsoh_page;
        struct efx_special_buffer txd;
        unsigned int ptr_mask;
+       void __iomem *piobuf;
+       unsigned int piobuf_offset;
        bool initialised;
 
        /* Members used mainly on the completion path */
@@ -238,6 +247,7 @@ struct efx_tx_queue {
        unsigned int tso_long_headers;
        unsigned int tso_packets;
        unsigned int pushes;
+       unsigned int pio_packets;
 
        /* Members shared between paths and sometimes updated */
        unsigned int empty_read_count ____cacheline_aligned_in_smp;
index e7dbd2d..9c90bf5 100644 (file)
@@ -19,6 +19,7 @@
 #include "bitfield.h"
 #include "efx.h"
 #include "nic.h"
+#include "ef10_regs.h"
 #include "farch_regs.h"
 #include "io.h"
 #include "workarounds.h"
@@ -166,26 +167,30 @@ void efx_nic_fini_interrupt(struct efx_nic *efx)
 
 /* Register dump */
 
-#define REGISTER_REVISION_A    1
-#define REGISTER_REVISION_B    2
-#define REGISTER_REVISION_C    3
-#define REGISTER_REVISION_Z    3       /* latest revision */
+#define REGISTER_REVISION_FA   1
+#define REGISTER_REVISION_FB   2
+#define REGISTER_REVISION_FC   3
+#define REGISTER_REVISION_FZ   3       /* last Falcon arch revision */
+#define REGISTER_REVISION_ED   4
+#define REGISTER_REVISION_EZ   4       /* latest EF10 revision */
 
 struct efx_nic_reg {
        u32 offset:24;
-       u32 min_revision:2, max_revision:2;
+       u32 min_revision:3, max_revision:3;
 };
 
-#define REGISTER(name, min_rev, max_rev) {                             \
-       FR_ ## min_rev ## max_rev ## _ ## name,                         \
-       REGISTER_REVISION_ ## min_rev, REGISTER_REVISION_ ## max_rev    \
+#define REGISTER(name, arch, min_rev, max_rev) {                       \
+       arch ## R_ ## min_rev ## max_rev ## _ ## name,                  \
+       REGISTER_REVISION_ ## arch ## min_rev,                          \
+       REGISTER_REVISION_ ## arch ## max_rev                           \
 }
-#define REGISTER_AA(name) REGISTER(name, A, A)
-#define REGISTER_AB(name) REGISTER(name, A, B)
-#define REGISTER_AZ(name) REGISTER(name, A, Z)
-#define REGISTER_BB(name) REGISTER(name, B, B)
-#define REGISTER_BZ(name) REGISTER(name, B, Z)
-#define REGISTER_CZ(name) REGISTER(name, C, Z)
+#define REGISTER_AA(name) REGISTER(name, F, A, A)
+#define REGISTER_AB(name) REGISTER(name, F, A, B)
+#define REGISTER_AZ(name) REGISTER(name, F, A, Z)
+#define REGISTER_BB(name) REGISTER(name, F, B, B)
+#define REGISTER_BZ(name) REGISTER(name, F, B, Z)
+#define REGISTER_CZ(name) REGISTER(name, F, C, Z)
+#define REGISTER_DZ(name) REGISTER(name, E, D, Z)
 
 static const struct efx_nic_reg efx_nic_regs[] = {
        REGISTER_AZ(ADR_REGION),
@@ -292,37 +297,42 @@ static const struct efx_nic_reg efx_nic_regs[] = {
        REGISTER_AB(XX_TXDRV_CTL),
        /* XX_PRBS_CTL, XX_PRBS_CHK and XX_PRBS_ERR are not used */
        /* XX_CORE_STAT is partly RC */
+       REGISTER_DZ(BIU_HW_REV_ID),
+       REGISTER_DZ(MC_DB_LWRD),
+       REGISTER_DZ(MC_DB_HWRD),
 };
 
 struct efx_nic_reg_table {
        u32 offset:24;
-       u32 min_revision:2, max_revision:2;
+       u32 min_revision:3, max_revision:3;
        u32 step:6, rows:21;
 };
 
-#define REGISTER_TABLE_DIMENSIONS(_, offset, min_rev, max_rev, step, rows) { \
+#define REGISTER_TABLE_DIMENSIONS(_, offset, arch, min_rev, max_rev, step, rows) { \
        offset,                                                         \
-       REGISTER_REVISION_ ## min_rev, REGISTER_REVISION_ ## max_rev,   \
+       REGISTER_REVISION_ ## arch ## min_rev,                          \
+       REGISTER_REVISION_ ## arch ## max_rev,                          \
        step, rows                                                      \
 }
-#define REGISTER_TABLE(name, min_rev, max_rev)                         \
+#define REGISTER_TABLE(name, arch, min_rev, max_rev)                   \
        REGISTER_TABLE_DIMENSIONS(                                      \
-               name, FR_ ## min_rev ## max_rev ## _ ## name,           \
-               min_rev, max_rev,                                       \
-               FR_ ## min_rev ## max_rev ## _ ## name ## _STEP,        \
-               FR_ ## min_rev ## max_rev ## _ ## name ## _ROWS)
-#define REGISTER_TABLE_AA(name) REGISTER_TABLE(name, A, A)
-#define REGISTER_TABLE_AZ(name) REGISTER_TABLE(name, A, Z)
-#define REGISTER_TABLE_BB(name) REGISTER_TABLE(name, B, B)
-#define REGISTER_TABLE_BZ(name) REGISTER_TABLE(name, B, Z)
+               name, arch ## R_ ## min_rev ## max_rev ## _ ## name,    \
+               arch, min_rev, max_rev,                                 \
+               arch ## R_ ## min_rev ## max_rev ## _ ## name ## _STEP, \
+               arch ## R_ ## min_rev ## max_rev ## _ ## name ## _ROWS)
+#define REGISTER_TABLE_AA(name) REGISTER_TABLE(name, F, A, A)
+#define REGISTER_TABLE_AZ(name) REGISTER_TABLE(name, F, A, Z)
+#define REGISTER_TABLE_BB(name) REGISTER_TABLE(name, F, B, B)
+#define REGISTER_TABLE_BZ(name) REGISTER_TABLE(name, F, B, Z)
 #define REGISTER_TABLE_BB_CZ(name)                                     \
-       REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, B, B,           \
+       REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, B, B,        \
                                  FR_BZ_ ## name ## _STEP,              \
                                  FR_BB_ ## name ## _ROWS),             \
-       REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, C, Z,           \
+       REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, C, Z,        \
                                  FR_BZ_ ## name ## _STEP,              \
                                  FR_CZ_ ## name ## _ROWS)
-#define REGISTER_TABLE_CZ(name) REGISTER_TABLE(name, C, Z)
+#define REGISTER_TABLE_CZ(name) REGISTER_TABLE(name, F, C, Z)
+#define REGISTER_TABLE_DZ(name) REGISTER_TABLE(name, E, D, Z)
 
 static const struct efx_nic_reg_table efx_nic_reg_tables[] = {
        /* DRIVER is not used */
@@ -340,9 +350,9 @@ static const struct efx_nic_reg_table efx_nic_reg_tables[] = {
         * 1K entries allows for some expansion of queue count and
         * size before we need to change the version. */
        REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL_KER, FR_AA_BUF_FULL_TBL_KER,
-                                 A, A, 8, 1024),
+                                 F, A, A, 8, 1024),
        REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL, FR_BZ_BUF_FULL_TBL,
-                                 B, Z, 8, 1024),
+                                 F, B, Z, 8, 1024),
        REGISTER_TABLE_CZ(RX_MAC_FILTER_TBL0),
        REGISTER_TABLE_BB_CZ(TIMER_TBL),
        REGISTER_TABLE_BB_CZ(TX_PACE_TBL),
@@ -353,6 +363,7 @@ static const struct efx_nic_reg_table efx_nic_reg_tables[] = {
        /* MSIX_PBA_TABLE is not mapped */
        /* SRM_DBG is not mapped (and is redundant with BUF_FLL_TBL) */
        REGISTER_TABLE_BZ(RX_FILTER_TBL0),
+       REGISTER_TABLE_DZ(BIU_MC_SFT_STATUS),
 };
 
 size_t efx_nic_get_regs_len(struct efx_nic *efx)
@@ -469,8 +480,7 @@ size_t efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count,
  * @count: Length of the @desc array
  * @mask: Bitmask of which elements of @desc are enabled
  * @stats: Buffer to update with the converted statistics.  The length
- *     of this array must be at least the number of set bits in the
- *     first @count bits of @mask.
+ *     of this array must be at least @count.
  * @dma_buf: DMA buffer containing hardware statistics
  * @accumulate: If set, the converted values will be added rather than
  *     directly stored to the corresponding elements of @stats
@@ -503,11 +513,9 @@ void efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count,
                        }
 
                        if (accumulate)
-                               *stats += val;
+                               stats[index] += val;
                        else
-                               *stats = val;
+                               stats[index] = val;
                }
-
-               ++stats;
        }
 }
index fda29d3..11b6112 100644 (file)
@@ -30,7 +30,7 @@ static inline int efx_nic_rev(struct efx_nic *efx)
        return efx->type->revision;
 }
 
-extern u32 efx_farch_fpga_ver(struct efx_nic *efx);
+u32 efx_farch_fpga_ver(struct efx_nic *efx);
 
 /* NIC has two interlinked PCI functions for the same port. */
 static inline bool efx_nic_is_dual_func(struct efx_nic *efx)
@@ -71,6 +71,26 @@ efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index)
        return ((efx_qword_t *) (tx_queue->txd.buf.addr)) + index;
 }
 
+/* Report whether the NIC considers this TX queue empty, given the
+ * write_count used for the last doorbell push.  May return false
+ * negative.
+ */
+static inline bool __efx_nic_tx_is_empty(struct efx_tx_queue *tx_queue,
+                                        unsigned int write_count)
+{
+       unsigned int empty_read_count = ACCESS_ONCE(tx_queue->empty_read_count);
+
+       if (empty_read_count == 0)
+               return false;
+
+       return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0;
+}
+
+static inline bool efx_nic_tx_is_empty(struct efx_tx_queue *tx_queue)
+{
+       return __efx_nic_tx_is_empty(tx_queue, tx_queue->write_count);
+}
+
 /* Decide whether to push a TX descriptor to the NIC vs merely writing
  * the doorbell.  This can reduce latency when we are adding a single
  * descriptor to an empty queue, but is otherwise pointless.  Further,
@@ -80,14 +100,10 @@ efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index)
 static inline bool efx_nic_may_push_tx_desc(struct efx_tx_queue *tx_queue,
                                            unsigned int write_count)
 {
-       unsigned empty_read_count = ACCESS_ONCE(tx_queue->empty_read_count);
-
-       if (empty_read_count == 0)
-               return false;
+       bool was_empty = __efx_nic_tx_is_empty(tx_queue, write_count);
 
        tx_queue->empty_read_count = 0;
-       return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0
-               && tx_queue->write_count - write_count == 1;
+       return was_empty && tx_queue->write_count - write_count == 1;
 }
 
 /* Returns a pointer to the specified descriptor in the RX descriptor queue */
@@ -386,9 +402,27 @@ enum {
        EF10_STAT_rx_align_error,
        EF10_STAT_rx_length_error,
        EF10_STAT_rx_nodesc_drops,
+       EF10_STAT_rx_pm_trunc_bb_overflow,
+       EF10_STAT_rx_pm_discard_bb_overflow,
+       EF10_STAT_rx_pm_trunc_vfifo_full,
+       EF10_STAT_rx_pm_discard_vfifo_full,
+       EF10_STAT_rx_pm_trunc_qbb,
+       EF10_STAT_rx_pm_discard_qbb,
+       EF10_STAT_rx_pm_discard_mapping,
+       EF10_STAT_rx_dp_q_disabled_packets,
+       EF10_STAT_rx_dp_di_dropped_packets,
+       EF10_STAT_rx_dp_streaming_packets,
+       EF10_STAT_rx_dp_emerg_fetch,
+       EF10_STAT_rx_dp_emerg_wait,
        EF10_STAT_COUNT
 };
 
+/* Maximum number of TX PIO buffers we may allocate to a function.
+ * This matches the total number of buffers on each SFC9100-family
+ * controller.
+ */
+#define EF10_TX_PIOBUF_COUNT 16
+
 /**
  * struct efx_ef10_nic_data - EF10 architecture NIC state
  * @mcdi_buf: DMA buffer for MCDI
@@ -397,6 +431,13 @@ enum {
  * @n_allocated_vis: Number of VIs allocated to this function
  * @must_realloc_vis: Flag: VIs have yet to be reallocated after MC reboot
  * @must_restore_filters: Flag: filters have yet to be restored after MC reboot
+ * @n_piobufs: Number of PIO buffers allocated to this function
+ * @wc_membase: Base address of write-combining mapping of the memory BAR
+ * @pio_write_base: Base address for writing PIO buffers
+ * @pio_write_vi_base: Relative VI number for @pio_write_base
+ * @piobuf_handle: Handle of each PIO buffer allocated
+ * @must_restore_piobufs: Flag: PIO buffers have yet to be restored after MC
+ *     reboot
  * @rx_rss_context: Firmware handle for our RSS context
  * @stats: Hardware statistics
  * @workaround_35388: Flag: firmware supports workaround for bug 35388
@@ -412,6 +453,11 @@ struct efx_ef10_nic_data {
        unsigned int n_allocated_vis;
        bool must_realloc_vis;
        bool must_restore_filters;
+       unsigned int n_piobufs;
+       void __iomem *wc_membase, *pio_write_base;
+       unsigned int pio_write_vi_base;
+       unsigned int piobuf_handle[EF10_TX_PIOBUF_COUNT];
+       bool must_restore_piobufs;
        u32 rx_rss_context;
        u64 stats[EF10_STAT_COUNT];
        bool workaround_35388;
@@ -463,18 +509,18 @@ static inline unsigned int efx_vf_size(struct efx_nic *efx)
        return 1 << efx->vi_scale;
 }
 
-extern int efx_init_sriov(void);
-extern void efx_sriov_probe(struct efx_nic *efx);
-extern int efx_sriov_init(struct efx_nic *efx);
-extern void efx_sriov_mac_address_changed(struct efx_nic *efx);
-extern void efx_sriov_tx_flush_done(struct efx_nic *efx, efx_qword_t *event);
-extern void efx_sriov_rx_flush_done(struct efx_nic *efx, efx_qword_t *event);
-extern void efx_sriov_event(struct efx_channel *channel, efx_qword_t *event);
-extern void efx_sriov_desc_fetch_err(struct efx_nic *efx, unsigned dmaq);
-extern void efx_sriov_flr(struct efx_nic *efx, unsigned flr);
-extern void efx_sriov_reset(struct efx_nic *efx);
-extern void efx_sriov_fini(struct efx_nic *efx);
-extern void efx_fini_sriov(void);
+int efx_init_sriov(void);
+void efx_sriov_probe(struct efx_nic *efx);
+int efx_sriov_init(struct efx_nic *efx);
+void efx_sriov_mac_address_changed(struct efx_nic *efx);
+void efx_sriov_tx_flush_done(struct efx_nic *efx, efx_qword_t *event);
+void efx_sriov_rx_flush_done(struct efx_nic *efx, efx_qword_t *event);
+void efx_sriov_event(struct efx_channel *channel, efx_qword_t *event);
+void efx_sriov_desc_fetch_err(struct efx_nic *efx, unsigned dmaq);
+void efx_sriov_flr(struct efx_nic *efx, unsigned flr);
+void efx_sriov_reset(struct efx_nic *efx);
+void efx_sriov_fini(struct efx_nic *efx);
+void efx_fini_sriov(void);
 
 #else
 
@@ -500,22 +546,20 @@ static inline void efx_fini_sriov(void) {}
 
 #endif
 
-extern int efx_sriov_set_vf_mac(struct net_device *dev, int vf, u8 *mac);
-extern int efx_sriov_set_vf_vlan(struct net_device *dev, int vf,
-                                u16 vlan, u8 qos);
-extern int efx_sriov_get_vf_config(struct net_device *dev, int vf,
-                                  struct ifla_vf_info *ivf);
-extern int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf,
-                                    bool spoofchk);
+int efx_sriov_set_vf_mac(struct net_device *dev, int vf, u8 *mac);
+int efx_sriov_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos);
+int efx_sriov_get_vf_config(struct net_device *dev, int vf,
+                           struct ifla_vf_info *ivf);
+int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf,
+                             bool spoofchk);
 
 struct ethtool_ts_info;
-extern void efx_ptp_probe(struct efx_nic *efx);
-extern int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd);
-extern void efx_ptp_get_ts_info(struct efx_nic *efx,
-                               struct ethtool_ts_info *ts_info);
-extern bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
-extern int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
-extern void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
+void efx_ptp_probe(struct efx_nic *efx);
+int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd);
+void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info);
+bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
+int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
+void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
 
 extern const struct efx_nic_type falcon_a1_nic_type;
 extern const struct efx_nic_type falcon_b0_nic_type;
@@ -529,7 +573,7 @@ extern const struct efx_nic_type efx_hunt_a0_nic_type;
  **************************************************************************
  */
 
-extern int falcon_probe_board(struct efx_nic *efx, u16 revision_info);
+int falcon_probe_board(struct efx_nic *efx, u16 revision_info);
 
 /* TX data path */
 static inline int efx_nic_probe_tx(struct efx_tx_queue *tx_queue)
@@ -597,58 +641,58 @@ static inline void efx_nic_eventq_read_ack(struct efx_channel *channel)
 {
        channel->efx->type->ev_read_ack(channel);
 }
-extern void efx_nic_event_test_start(struct efx_channel *channel);
+void efx_nic_event_test_start(struct efx_channel *channel);
 
 /* Falcon/Siena queue operations */
-extern int efx_farch_tx_probe(struct efx_tx_queue *tx_queue);
-extern void efx_farch_tx_init(struct efx_tx_queue *tx_queue);
-extern void efx_farch_tx_fini(struct efx_tx_queue *tx_queue);
-extern void efx_farch_tx_remove(struct efx_tx_queue *tx_queue);
-extern void efx_farch_tx_write(struct efx_tx_queue *tx_queue);
-extern int efx_farch_rx_probe(struct efx_rx_queue *rx_queue);
-extern void efx_farch_rx_init(struct efx_rx_queue *rx_queue);
-extern void efx_farch_rx_fini(struct efx_rx_queue *rx_queue);
-extern void efx_farch_rx_remove(struct efx_rx_queue *rx_queue);
-extern void efx_farch_rx_write(struct efx_rx_queue *rx_queue);
-extern void efx_farch_rx_defer_refill(struct efx_rx_queue *rx_queue);
-extern int efx_farch_ev_probe(struct efx_channel *channel);
-extern int efx_farch_ev_init(struct efx_channel *channel);
-extern void efx_farch_ev_fini(struct efx_channel *channel);
-extern void efx_farch_ev_remove(struct efx_channel *channel);
-extern int efx_farch_ev_process(struct efx_channel *channel, int quota);
-extern void efx_farch_ev_read_ack(struct efx_channel *channel);
-extern void efx_farch_ev_test_generate(struct efx_channel *channel);
+int efx_farch_tx_probe(struct efx_tx_queue *tx_queue);
+void efx_farch_tx_init(struct efx_tx_queue *tx_queue);
+void efx_farch_tx_fini(struct efx_tx_queue *tx_queue);
+void efx_farch_tx_remove(struct efx_tx_queue *tx_queue);
+void efx_farch_tx_write(struct efx_tx_queue *tx_queue);
+int efx_farch_rx_probe(struct efx_rx_queue *rx_queue);
+void efx_farch_rx_init(struct efx_rx_queue *rx_queue);
+void efx_farch_rx_fini(struct efx_rx_queue *rx_queue);
+void efx_farch_rx_remove(struct efx_rx_queue *rx_queue);
+void efx_farch_rx_write(struct efx_rx_queue *rx_queue);
+void efx_farch_rx_defer_refill(struct efx_rx_queue *rx_queue);
+int efx_farch_ev_probe(struct efx_channel *channel);
+int efx_farch_ev_init(struct efx_channel *channel);
+void efx_farch_ev_fini(struct efx_channel *channel);
+void efx_farch_ev_remove(struct efx_channel *channel);
+int efx_farch_ev_process(struct efx_channel *channel, int quota);
+void efx_farch_ev_read_ack(struct efx_channel *channel);
+void efx_farch_ev_test_generate(struct efx_channel *channel);
 
 /* Falcon/Siena filter operations */
-extern int efx_farch_filter_table_probe(struct efx_nic *efx);
-extern void efx_farch_filter_table_restore(struct efx_nic *efx);
-extern void efx_farch_filter_table_remove(struct efx_nic *efx);
-extern void efx_farch_filter_update_rx_scatter(struct efx_nic *efx);
-extern s32 efx_farch_filter_insert(struct efx_nic *efx,
-                                  struct efx_filter_spec *spec, bool replace);
-extern int efx_farch_filter_remove_safe(struct efx_nic *efx,
-                                       enum efx_filter_priority priority,
-                                       u32 filter_id);
-extern int efx_farch_filter_get_safe(struct efx_nic *efx,
-                                    enum efx_filter_priority priority,
-                                    u32 filter_id, struct efx_filter_spec *);
-extern void efx_farch_filter_clear_rx(struct efx_nic *efx,
-                                     enum efx_filter_priority priority);
-extern u32 efx_farch_filter_count_rx_used(struct efx_nic *efx,
-                                         enum efx_filter_priority priority);
-extern u32 efx_farch_filter_get_rx_id_limit(struct efx_nic *efx);
-extern s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx,
-                                      enum efx_filter_priority priority,
-                                      u32 *buf, u32 size);
+int efx_farch_filter_table_probe(struct efx_nic *efx);
+void efx_farch_filter_table_restore(struct efx_nic *efx);
+void efx_farch_filter_table_remove(struct efx_nic *efx);
+void efx_farch_filter_update_rx_scatter(struct efx_nic *efx);
+s32 efx_farch_filter_insert(struct efx_nic *efx, struct efx_filter_spec *spec,
+                           bool replace);
+int efx_farch_filter_remove_safe(struct efx_nic *efx,
+                                enum efx_filter_priority priority,
+                                u32 filter_id);
+int efx_farch_filter_get_safe(struct efx_nic *efx,
+                             enum efx_filter_priority priority, u32 filter_id,
+                             struct efx_filter_spec *);
+void efx_farch_filter_clear_rx(struct efx_nic *efx,
+                              enum efx_filter_priority priority);
+u32 efx_farch_filter_count_rx_used(struct efx_nic *efx,
+                                  enum efx_filter_priority priority);
+u32 efx_farch_filter_get_rx_id_limit(struct efx_nic *efx);
+s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx,
+                               enum efx_filter_priority priority, u32 *buf,
+                               u32 size);
 #ifdef CONFIG_RFS_ACCEL
-extern s32 efx_farch_filter_rfs_insert(struct efx_nic *efx,
-                                      struct efx_filter_spec *spec);
-extern bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
-                                           unsigned int index);
+s32 efx_farch_filter_rfs_insert(struct efx_nic *efx,
+                               struct efx_filter_spec *spec);
+bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
+                                    unsigned int index);
 #endif
-extern void efx_farch_filter_sync_rx_mode(struct efx_nic *efx);
+void efx_farch_filter_sync_rx_mode(struct efx_nic *efx);
 
-extern bool efx_nic_event_present(struct efx_channel *channel);
+bool efx_nic_event_present(struct efx_channel *channel);
 
 /* Some statistics are computed as A - B where A and B each increase
  * linearly with some hardware counter(s) and the counters are read
@@ -669,17 +713,17 @@ static inline void efx_update_diff_stat(u64 *stat, u64 diff)
 }
 
 /* Interrupts */
-extern int efx_nic_init_interrupt(struct efx_nic *efx);
-extern void efx_nic_irq_test_start(struct efx_nic *efx);
-extern void efx_nic_fini_interrupt(struct efx_nic *efx);
+int efx_nic_init_interrupt(struct efx_nic *efx);
+void efx_nic_irq_test_start(struct efx_nic *efx);
+void efx_nic_fini_interrupt(struct efx_nic *efx);
 
 /* Falcon/Siena interrupts */
-extern void efx_farch_irq_enable_master(struct efx_nic *efx);
-extern void efx_farch_irq_test_generate(struct efx_nic *efx);
-extern void efx_farch_irq_disable_master(struct efx_nic *efx);
-extern irqreturn_t efx_farch_msi_interrupt(int irq, void *dev_id);
-extern irqreturn_t efx_farch_legacy_interrupt(int irq, void *dev_id);
-extern irqreturn_t efx_farch_fatal_interrupt(struct efx_nic *efx);
+void efx_farch_irq_enable_master(struct efx_nic *efx);
+void efx_farch_irq_test_generate(struct efx_nic *efx);
+void efx_farch_irq_disable_master(struct efx_nic *efx);
+irqreturn_t efx_farch_msi_interrupt(int irq, void *dev_id);
+irqreturn_t efx_farch_legacy_interrupt(int irq, void *dev_id);
+irqreturn_t efx_farch_fatal_interrupt(struct efx_nic *efx);
 
 static inline int efx_nic_event_test_irq_cpu(struct efx_channel *channel)
 {
@@ -691,21 +735,21 @@ static inline int efx_nic_irq_test_irq_cpu(struct efx_nic *efx)
 }
 
 /* Global Resources */
-extern int efx_nic_flush_queues(struct efx_nic *efx);
-extern void siena_prepare_flush(struct efx_nic *efx);
-extern int efx_farch_fini_dmaq(struct efx_nic *efx);
-extern void siena_finish_flush(struct efx_nic *efx);
-extern void falcon_start_nic_stats(struct efx_nic *efx);
-extern void falcon_stop_nic_stats(struct efx_nic *efx);
-extern int falcon_reset_xaui(struct efx_nic *efx);
-extern void efx_farch_dimension_resources(struct efx_nic *efx, unsigned sram_lim_qw);
-extern void efx_farch_init_common(struct efx_nic *efx);
-extern void efx_ef10_handle_drain_event(struct efx_nic *efx);
+int efx_nic_flush_queues(struct efx_nic *efx);
+void siena_prepare_flush(struct efx_nic *efx);
+int efx_farch_fini_dmaq(struct efx_nic *efx);
+void siena_finish_flush(struct efx_nic *efx);
+void falcon_start_nic_stats(struct efx_nic *efx);
+void falcon_stop_nic_stats(struct efx_nic *efx);
+int falcon_reset_xaui(struct efx_nic *efx);
+void efx_farch_dimension_resources(struct efx_nic *efx, unsigned sram_lim_qw);
+void efx_farch_init_common(struct efx_nic *efx);
+void efx_ef10_handle_drain_event(struct efx_nic *efx);
 static inline void efx_nic_push_rx_indir_table(struct efx_nic *efx)
 {
        efx->type->rx_push_indir_table(efx);
 }
-extern void efx_farch_rx_push_indir_table(struct efx_nic *efx);
+void efx_farch_rx_push_indir_table(struct efx_nic *efx);
 
 int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer,
                         unsigned int len, gfp_t gfp_flags);
@@ -716,24 +760,22 @@ struct efx_farch_register_test {
        unsigned address;
        efx_oword_t mask;
 };
-extern int efx_farch_test_registers(struct efx_nic *efx,
-                                   const struct efx_farch_register_test *regs,
-                                   size_t n_regs);
+int efx_farch_test_registers(struct efx_nic *efx,
+                            const struct efx_farch_register_test *regs,
+                            size_t n_regs);
 
-extern size_t efx_nic_get_regs_len(struct efx_nic *efx);
-extern void efx_nic_get_regs(struct efx_nic *efx, void *buf);
+size_t efx_nic_get_regs_len(struct efx_nic *efx);
+void efx_nic_get_regs(struct efx_nic *efx, void *buf);
 
-extern size_t
-efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count,
-                      const unsigned long *mask, u8 *names);
-extern void
-efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count,
-                    const unsigned long *mask,
-                    u64 *stats, const void *dma_buf, bool accumulate);
+size_t efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count,
+                             const unsigned long *mask, u8 *names);
+void efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count,
+                         const unsigned long *mask, u64 *stats,
+                         const void *dma_buf, bool accumulate);
 
 #define EFX_MAX_FLUSH_TIME 5000
 
-extern void efx_farch_generate_event(struct efx_nic *efx, unsigned int evq,
-                                    efx_qword_t *event);
+void efx_farch_generate_event(struct efx_nic *efx, unsigned int evq,
+                             efx_qword_t *event);
 
 #endif /* EFX_NIC_H */
index 45eeb70..803bf44 100644 (file)
@@ -15,7 +15,7 @@
  */
 extern const struct efx_phy_operations falcon_sfx7101_phy_ops;
 
-extern void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
+void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
 
 /****************************************************************************
  * AMCC/Quake QT202x PHYs
@@ -34,7 +34,7 @@ extern const struct efx_phy_operations falcon_qt202x_phy_ops;
 #define QUAKE_LED_TXLINK       (0)
 #define QUAKE_LED_RXLINK       (8)
 
-extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state);
+void falcon_qt202x_set_led(struct efx_nic *p, int led, int state);
 
 /****************************************************************************
 * Transwitch CX4 retimer
@@ -44,7 +44,7 @@ extern const struct efx_phy_operations falcon_txc_phy_ops;
 #define TXC_GPIO_DIR_INPUT     0
 #define TXC_GPIO_DIR_OUTPUT    1
 
-extern void falcon_txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir);
-extern void falcon_txc_set_gpio_val(struct efx_nic *efx, int pin, int val);
+void falcon_txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir);
+void falcon_txc_set_gpio_val(struct efx_nic *efx, int pin, int val);
 
 #endif
index 4a59672..8f09e68 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/in.h>
 #include <linux/slab.h>
 #include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/prefetch.h>
@@ -818,44 +819,70 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
        struct efx_nic *efx = netdev_priv(net_dev);
        struct efx_channel *channel;
        struct efx_filter_spec spec;
-       const struct iphdr *ip;
        const __be16 *ports;
+       __be16 ether_type;
        int nhoff;
        int rc;
 
-       nhoff = skb_network_offset(skb);
+       /* The core RPS/RFS code has already parsed and validated
+        * VLAN, IP and transport headers.  We assume they are in the
+        * header area.
+        */
 
        if (skb->protocol == htons(ETH_P_8021Q)) {
-               EFX_BUG_ON_PARANOID(skb_headlen(skb) <
-                                   nhoff + sizeof(struct vlan_hdr));
-               if (((const struct vlan_hdr *)skb->data + nhoff)->
-                   h_vlan_encapsulated_proto != htons(ETH_P_IP))
-                       return -EPROTONOSUPPORT;
+               const struct vlan_hdr *vh =
+                       (const struct vlan_hdr *)skb->data;
 
-               /* This is IP over 802.1q VLAN.  We can't filter on the
-                * IP 5-tuple and the vlan together, so just strip the
-                * vlan header and filter on the IP part.
+               /* We can't filter on the IP 5-tuple and the vlan
+                * together, so just strip the vlan header and filter
+                * on the IP part.
                 */
-               nhoff += sizeof(struct vlan_hdr);
-       } else if (skb->protocol != htons(ETH_P_IP)) {
-               return -EPROTONOSUPPORT;
+               EFX_BUG_ON_PARANOID(skb_headlen(skb) < sizeof(*vh));
+               ether_type = vh->h_vlan_encapsulated_proto;
+               nhoff = sizeof(struct vlan_hdr);
+       } else {
+               ether_type = skb->protocol;
+               nhoff = 0;
        }
 
-       /* RFS must validate the IP header length before calling us */
-       EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
-       ip = (const struct iphdr *)(skb->data + nhoff);
-       if (ip_is_fragment(ip))
+       if (ether_type != htons(ETH_P_IP) && ether_type != htons(ETH_P_IPV6))
                return -EPROTONOSUPPORT;
-       EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
-       ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
 
        efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT,
                           efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
                           rxq_index);
-       rc = efx_filter_set_ipv4_full(&spec, ip->protocol,
-                                     ip->daddr, ports[1], ip->saddr, ports[0]);
-       if (rc)
-               return rc;
+       spec.match_flags =
+               EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+               EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+               EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
+       spec.ether_type = ether_type;
+
+       if (ether_type == htons(ETH_P_IP)) {
+               const struct iphdr *ip =
+                       (const struct iphdr *)(skb->data + nhoff);
+
+               EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
+               if (ip_is_fragment(ip))
+                       return -EPROTONOSUPPORT;
+               spec.ip_proto = ip->protocol;
+               spec.rem_host[0] = ip->saddr;
+               spec.loc_host[0] = ip->daddr;
+               EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
+               ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
+       } else {
+               const struct ipv6hdr *ip6 =
+                       (const struct ipv6hdr *)(skb->data + nhoff);
+
+               EFX_BUG_ON_PARANOID(skb_headlen(skb) <
+                                   nhoff + sizeof(*ip6) + 4);
+               spec.ip_proto = ip6->nexthdr;
+               memcpy(spec.rem_host, &ip6->saddr, sizeof(ip6->saddr));
+               memcpy(spec.loc_host, &ip6->daddr, sizeof(ip6->daddr));
+               ports = (const __be16 *)(ip6 + 1);
+       }
+
+       spec.rem_port = ports[0];
+       spec.loc_port = ports[1];
 
        rc = efx->type->filter_rfs_insert(efx, &spec);
        if (rc < 0)
@@ -866,11 +893,18 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
        channel = efx_get_channel(efx, skb_get_rx_queue(skb));
        ++channel->rfs_filters_added;
 
-       netif_info(efx, rx_status, efx->net_dev,
-                  "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n",
-                  (ip->protocol == IPPROTO_TCP) ? "TCP" : "UDP",
-                  &ip->saddr, ntohs(ports[0]), &ip->daddr, ntohs(ports[1]),
-                  rxq_index, flow_id, rc);
+       if (ether_type == htons(ETH_P_IP))
+               netif_info(efx, rx_status, efx->net_dev,
+                          "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n",
+                          (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
+                          spec.rem_host, ntohs(ports[0]), spec.loc_host,
+                          ntohs(ports[1]), rxq_index, flow_id, rc);
+       else
+               netif_info(efx, rx_status, efx->net_dev,
+                          "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n",
+                          (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
+                          spec.rem_host, ntohs(ports[0]), spec.loc_host,
+                          ntohs(ports[1]), rxq_index, flow_id, rc);
 
        return rc;
 }
index 87698ae..a2f4a06 100644 (file)
@@ -43,13 +43,12 @@ struct efx_self_tests {
        struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1];
 };
 
-extern void efx_loopback_rx_packet(struct efx_nic *efx,
-                                  const char *buf_ptr, int pkt_len);
-extern int efx_selftest(struct efx_nic *efx,
-                       struct efx_self_tests *tests,
-                       unsigned flags);
-extern void efx_selftest_async_start(struct efx_nic *efx);
-extern void efx_selftest_async_cancel(struct efx_nic *efx);
-extern void efx_selftest_async_work(struct work_struct *data);
+void efx_loopback_rx_packet(struct efx_nic *efx, const char *buf_ptr,
+                           int pkt_len);
+int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
+                unsigned flags);
+void efx_selftest_async_start(struct efx_nic *efx);
+void efx_selftest_async_cancel(struct efx_nic *efx);
+void efx_selftest_async_work(struct work_struct *data);
 
 #endif /* EFX_SELFTEST_H */
index 2ac91c5..c49d1fb 100644 (file)
 #include <net/ipv6.h>
 #include <linux/if_ether.h>
 #include <linux/highmem.h>
+#include <linux/cache.h>
 #include "net_driver.h"
 #include "efx.h"
+#include "io.h"
 #include "nic.h"
 #include "workarounds.h"
+#include "ef10_regs.h"
+
+#ifdef EFX_USE_PIO
+
+#define EFX_PIOBUF_SIZE_MAX ER_DZ_TX_PIOBUF_SIZE
+#define EFX_PIOBUF_SIZE_DEF ALIGN(256, L1_CACHE_BYTES)
+unsigned int efx_piobuf_size __read_mostly = EFX_PIOBUF_SIZE_DEF;
+
+#endif /* EFX_USE_PIO */
+
+static inline unsigned int
+efx_tx_queue_get_insert_index(const struct efx_tx_queue *tx_queue)
+{
+       return tx_queue->insert_count & tx_queue->ptr_mask;
+}
+
+static inline struct efx_tx_buffer *
+__efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue)
+{
+       return &tx_queue->buffer[efx_tx_queue_get_insert_index(tx_queue)];
+}
+
+static inline struct efx_tx_buffer *
+efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue)
+{
+       struct efx_tx_buffer *buffer =
+               __efx_tx_queue_get_insert_buffer(tx_queue);
+
+       EFX_BUG_ON_PARANOID(buffer->len);
+       EFX_BUG_ON_PARANOID(buffer->flags);
+       EFX_BUG_ON_PARANOID(buffer->unmap_len);
+
+       return buffer;
+}
 
 static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
                               struct efx_tx_buffer *buffer,
@@ -29,8 +65,7 @@ static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue,
 {
        if (buffer->unmap_len) {
                struct device *dma_dev = &tx_queue->efx->pci_dev->dev;
-               dma_addr_t unmap_addr = (buffer->dma_addr + buffer->len -
-                                        buffer->unmap_len);
+               dma_addr_t unmap_addr = buffer->dma_addr - buffer->dma_offset;
                if (buffer->flags & EFX_TX_BUF_MAP_SINGLE)
                        dma_unmap_single(dma_dev, unmap_addr, buffer->unmap_len,
                                         DMA_TO_DEVICE);
@@ -83,8 +118,10 @@ unsigned int efx_tx_max_skb_descs(struct efx_nic *efx)
         */
        unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS;
 
-       /* Possibly one more per segment for the alignment workaround */
-       if (EFX_WORKAROUND_5391(efx))
+       /* Possibly one more per segment for the alignment workaround,
+        * or for option descriptors
+        */
+       if (EFX_WORKAROUND_5391(efx) || efx_nic_rev(efx) >= EFX_REV_HUNT_A0)
                max_descs += EFX_TSO_MAX_SEGS;
 
        /* Possibly more for PCIe page boundaries within input fragments */
@@ -145,6 +182,145 @@ static void efx_tx_maybe_stop_queue(struct efx_tx_queue *txq1)
        }
 }
 
+#ifdef EFX_USE_PIO
+
+struct efx_short_copy_buffer {
+       int used;
+       u8 buf[L1_CACHE_BYTES];
+};
+
+/* Copy to PIO, respecting that writes to PIO buffers must be dword aligned.
+ * Advances piobuf pointer. Leaves additional data in the copy buffer.
+ */
+static void efx_memcpy_toio_aligned(struct efx_nic *efx, u8 __iomem **piobuf,
+                                   u8 *data, int len,
+                                   struct efx_short_copy_buffer *copy_buf)
+{
+       int block_len = len & ~(sizeof(copy_buf->buf) - 1);
+
+       memcpy_toio(*piobuf, data, block_len);
+       *piobuf += block_len;
+       len -= block_len;
+
+       if (len) {
+               data += block_len;
+               BUG_ON(copy_buf->used);
+               BUG_ON(len > sizeof(copy_buf->buf));
+               memcpy(copy_buf->buf, data, len);
+               copy_buf->used = len;
+       }
+}
+
+/* Copy to PIO, respecting dword alignment, popping data from copy buffer first.
+ * Advances piobuf pointer. Leaves additional data in the copy buffer.
+ */
+static void efx_memcpy_toio_aligned_cb(struct efx_nic *efx, u8 __iomem **piobuf,
+                                      u8 *data, int len,
+                                      struct efx_short_copy_buffer *copy_buf)
+{
+       if (copy_buf->used) {
+               /* if the copy buffer is partially full, fill it up and write */
+               int copy_to_buf =
+                       min_t(int, sizeof(copy_buf->buf) - copy_buf->used, len);
+
+               memcpy(copy_buf->buf + copy_buf->used, data, copy_to_buf);
+               copy_buf->used += copy_to_buf;
+
+               /* if we didn't fill it up then we're done for now */
+               if (copy_buf->used < sizeof(copy_buf->buf))
+                       return;
+
+               memcpy_toio(*piobuf, copy_buf->buf, sizeof(copy_buf->buf));
+               *piobuf += sizeof(copy_buf->buf);
+               data += copy_to_buf;
+               len -= copy_to_buf;
+               copy_buf->used = 0;
+       }
+
+       efx_memcpy_toio_aligned(efx, piobuf, data, len, copy_buf);
+}
+
+static void efx_flush_copy_buffer(struct efx_nic *efx, u8 __iomem *piobuf,
+                                 struct efx_short_copy_buffer *copy_buf)
+{
+       /* if there's anything in it, write the whole buffer, including junk */
+       if (copy_buf->used)
+               memcpy_toio(piobuf, copy_buf->buf, sizeof(copy_buf->buf));
+}
+
+/* Traverse skb structure and copy fragments in to PIO buffer.
+ * Advances piobuf pointer.
+ */
+static void efx_skb_copy_bits_to_pio(struct efx_nic *efx, struct sk_buff *skb,
+                                    u8 __iomem **piobuf,
+                                    struct efx_short_copy_buffer *copy_buf)
+{
+       int i;
+
+       efx_memcpy_toio_aligned(efx, piobuf, skb->data, skb_headlen(skb),
+                               copy_buf);
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) {
+               skb_frag_t *f = &skb_shinfo(skb)->frags[i];
+               u8 *vaddr;
+
+               vaddr = kmap_atomic(skb_frag_page(f));
+
+               efx_memcpy_toio_aligned_cb(efx, piobuf, vaddr + f->page_offset,
+                                          skb_frag_size(f), copy_buf);
+               kunmap_atomic(vaddr);
+       }
+
+       EFX_BUG_ON_PARANOID(skb_shinfo(skb)->frag_list);
+}
+
+static struct efx_tx_buffer *
+efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
+{
+       struct efx_tx_buffer *buffer =
+               efx_tx_queue_get_insert_buffer(tx_queue);
+       u8 __iomem *piobuf = tx_queue->piobuf;
+
+       /* Copy to PIO buffer. Ensure the writes are padded to the end
+        * of a cache line, as this is required for write-combining to be
+        * effective on at least x86.
+        */
+
+       if (skb_shinfo(skb)->nr_frags) {
+               /* The size of the copy buffer will ensure all writes
+                * are the size of a cache line.
+                */
+               struct efx_short_copy_buffer copy_buf;
+
+               copy_buf.used = 0;
+
+               efx_skb_copy_bits_to_pio(tx_queue->efx, skb,
+                                        &piobuf, &copy_buf);
+               efx_flush_copy_buffer(tx_queue->efx, piobuf, &copy_buf);
+       } else {
+               /* Pad the write to the size of a cache line.
+                * We can do this because we know the skb_shared_info sruct is
+                * after the source, and the destination buffer is big enough.
+                */
+               BUILD_BUG_ON(L1_CACHE_BYTES >
+                            SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
+               memcpy_toio(tx_queue->piobuf, skb->data,
+                           ALIGN(skb->len, L1_CACHE_BYTES));
+       }
+
+       EFX_POPULATE_QWORD_5(buffer->option,
+                            ESF_DZ_TX_DESC_IS_OPT, 1,
+                            ESF_DZ_TX_OPTION_TYPE, ESE_DZ_TX_OPTION_DESC_PIO,
+                            ESF_DZ_TX_PIO_CONT, 0,
+                            ESF_DZ_TX_PIO_BYTE_CNT, skb->len,
+                            ESF_DZ_TX_PIO_BUF_ADDR,
+                            tx_queue->piobuf_offset);
+       ++tx_queue->pio_packets;
+       ++tx_queue->insert_count;
+       return buffer;
+}
+#endif /* EFX_USE_PIO */
+
 /*
  * Add a socket buffer to a TX queue
  *
@@ -167,7 +343,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
        struct device *dma_dev = &efx->pci_dev->dev;
        struct efx_tx_buffer *buffer;
        skb_frag_t *fragment;
-       unsigned int len, unmap_len = 0, insert_ptr;
+       unsigned int len, unmap_len = 0;
        dma_addr_t dma_addr, unmap_addr = 0;
        unsigned int dma_len;
        unsigned short dma_flags;
@@ -189,6 +365,17 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
                        return NETDEV_TX_OK;
        }
 
+       /* Consider using PIO for short packets */
+#ifdef EFX_USE_PIO
+       if (skb->len <= efx_piobuf_size && tx_queue->piobuf &&
+           efx_nic_tx_is_empty(tx_queue) &&
+           efx_nic_tx_is_empty(efx_tx_queue_partner(tx_queue))) {
+               buffer = efx_enqueue_skb_pio(tx_queue, skb);
+               dma_flags = EFX_TX_BUF_OPTION;
+               goto finish_packet;
+       }
+#endif
+
        /* Map for DMA.  Use dma_map_single rather than dma_map_page
         * since this is more efficient on machines with sparse
         * memory.
@@ -208,11 +395,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
 
                /* Add to TX queue, splitting across DMA boundaries */
                do {
-                       insert_ptr = tx_queue->insert_count & tx_queue->ptr_mask;
-                       buffer = &tx_queue->buffer[insert_ptr];
-                       EFX_BUG_ON_PARANOID(buffer->flags);
-                       EFX_BUG_ON_PARANOID(buffer->len);
-                       EFX_BUG_ON_PARANOID(buffer->unmap_len);
+                       buffer = efx_tx_queue_get_insert_buffer(tx_queue);
 
                        dma_len = efx_max_tx_len(efx, dma_addr);
                        if (likely(dma_len >= len))
@@ -230,6 +413,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
                /* Transfer ownership of the unmapping to the final buffer */
                buffer->flags = EFX_TX_BUF_CONT | dma_flags;
                buffer->unmap_len = unmap_len;
+               buffer->dma_offset = buffer->dma_addr - unmap_addr;
                unmap_len = 0;
 
                /* Get address and size of next fragment */
@@ -245,6 +429,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
        }
 
        /* Transfer ownership of the skb to the final buffer */
+finish_packet:
        buffer->skb = skb;
        buffer->flags = EFX_TX_BUF_SKB | dma_flags;
 
@@ -270,8 +455,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
        while (tx_queue->insert_count != tx_queue->write_count) {
                unsigned int pkts_compl = 0, bytes_compl = 0;
                --tx_queue->insert_count;
-               insert_ptr = tx_queue->insert_count & tx_queue->ptr_mask;
-               buffer = &tx_queue->buffer[insert_ptr];
+               buffer = __efx_tx_queue_get_insert_buffer(tx_queue);
                efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl);
        }
 
@@ -628,6 +812,9 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
  * @tcp_off: Offset of TCP header
  * @header_len: Number of bytes of header
  * @ip_base_len: IPv4 tot_len or IPv6 payload_len, before TCP payload
+ * @header_dma_addr: Header DMA address, when using option descriptors
+ * @header_unmap_len: Header DMA mapped length, or 0 if not using option
+ *     descriptors
  *
  * The state used during segmentation.  It is put into this data structure
  * just to make it easy to pass into inline functions.
@@ -636,7 +823,7 @@ struct tso_state {
        /* Output position */
        unsigned out_len;
        unsigned seqnum;
-       unsigned ipv4_id;
+       u16 ipv4_id;
        unsigned packet_space;
 
        /* Input position */
@@ -651,6 +838,8 @@ struct tso_state {
        unsigned int tcp_off;
        unsigned header_len;
        unsigned int ip_base_len;
+       dma_addr_t header_dma_addr;
+       unsigned int header_unmap_len;
 };
 
 
@@ -737,23 +926,18 @@ static void efx_tx_queue_insert(struct efx_tx_queue *tx_queue,
 {
        struct efx_tx_buffer *buffer;
        struct efx_nic *efx = tx_queue->efx;
-       unsigned dma_len, insert_ptr;
+       unsigned dma_len;
 
        EFX_BUG_ON_PARANOID(len <= 0);
 
        while (1) {
-               insert_ptr = tx_queue->insert_count & tx_queue->ptr_mask;
-               buffer = &tx_queue->buffer[insert_ptr];
+               buffer = efx_tx_queue_get_insert_buffer(tx_queue);
                ++tx_queue->insert_count;
 
                EFX_BUG_ON_PARANOID(tx_queue->insert_count -
                                    tx_queue->read_count >=
                                    efx->txq_entries);
 
-               EFX_BUG_ON_PARANOID(buffer->len);
-               EFX_BUG_ON_PARANOID(buffer->unmap_len);
-               EFX_BUG_ON_PARANOID(buffer->flags);
-
                buffer->dma_addr = dma_addr;
 
                dma_len = efx_max_tx_len(efx, dma_addr);
@@ -796,6 +980,7 @@ static int efx_tso_put_header(struct efx_tx_queue *tx_queue,
                        return -ENOMEM;
                }
                buffer->unmap_len = buffer->len;
+               buffer->dma_offset = 0;
                buffer->flags |= EFX_TX_BUF_MAP_SINGLE;
        }
 
@@ -814,19 +999,27 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue)
        /* Work backwards until we hit the original insert pointer value */
        while (tx_queue->insert_count != tx_queue->write_count) {
                --tx_queue->insert_count;
-               buffer = &tx_queue->buffer[tx_queue->insert_count &
-                                          tx_queue->ptr_mask];
+               buffer = __efx_tx_queue_get_insert_buffer(tx_queue);
                efx_dequeue_buffer(tx_queue, buffer, NULL, NULL);
        }
 }
 
 
 /* Parse the SKB header and initialise state. */
-static void tso_start(struct tso_state *st, const struct sk_buff *skb)
+static int tso_start(struct tso_state *st, struct efx_nic *efx,
+                    const struct sk_buff *skb)
 {
+       bool use_options = efx_nic_rev(efx) >= EFX_REV_HUNT_A0;
+       struct device *dma_dev = &efx->pci_dev->dev;
+       unsigned int header_len, in_len;
+       dma_addr_t dma_addr;
+
        st->ip_off = skb_network_header(skb) - skb->data;
        st->tcp_off = skb_transport_header(skb) - skb->data;
-       st->header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u);
+       header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u);
+       in_len = skb_headlen(skb) - header_len;
+       st->header_len = header_len;
+       st->in_len = in_len;
        if (st->protocol == htons(ETH_P_IP)) {
                st->ip_base_len = st->header_len - st->ip_off;
                st->ipv4_id = ntohs(ip_hdr(skb)->id);
@@ -840,9 +1033,34 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb)
        EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn);
        EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst);
 
-       st->out_len = skb->len - st->header_len;
-       st->unmap_len = 0;
-       st->dma_flags = 0;
+       st->out_len = skb->len - header_len;
+
+       if (!use_options) {
+               st->header_unmap_len = 0;
+
+               if (likely(in_len == 0)) {
+                       st->dma_flags = 0;
+                       st->unmap_len = 0;
+                       return 0;
+               }
+
+               dma_addr = dma_map_single(dma_dev, skb->data + header_len,
+                                         in_len, DMA_TO_DEVICE);
+               st->dma_flags = EFX_TX_BUF_MAP_SINGLE;
+               st->dma_addr = dma_addr;
+               st->unmap_addr = dma_addr;
+               st->unmap_len = in_len;
+       } else {
+               dma_addr = dma_map_single(dma_dev, skb->data,
+                                         skb_headlen(skb), DMA_TO_DEVICE);
+               st->header_dma_addr = dma_addr;
+               st->header_unmap_len = skb_headlen(skb);
+               st->dma_flags = 0;
+               st->dma_addr = dma_addr + header_len;
+               st->unmap_len = 0;
+       }
+
+       return unlikely(dma_mapping_error(dma_dev, dma_addr)) ? -ENOMEM : 0;
 }
 
 static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx,
@@ -860,24 +1078,6 @@ static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx,
        return -ENOMEM;
 }
 
-static int tso_get_head_fragment(struct tso_state *st, struct efx_nic *efx,
-                                const struct sk_buff *skb)
-{
-       int hl = st->header_len;
-       int len = skb_headlen(skb) - hl;
-
-       st->unmap_addr = dma_map_single(&efx->pci_dev->dev, skb->data + hl,
-                                       len, DMA_TO_DEVICE);
-       if (likely(!dma_mapping_error(&efx->pci_dev->dev, st->unmap_addr))) {
-               st->dma_flags = EFX_TX_BUF_MAP_SINGLE;
-               st->unmap_len = len;
-               st->in_len = len;
-               st->dma_addr = st->unmap_addr;
-               return 0;
-       }
-       return -ENOMEM;
-}
-
 
 /**
  * tso_fill_packet_with_fragment - form descriptors for the current fragment
@@ -922,6 +1122,7 @@ static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue,
        if (st->in_len == 0) {
                /* Transfer ownership of the DMA mapping */
                buffer->unmap_len = st->unmap_len;
+               buffer->dma_offset = buffer->unmap_len - buffer->len;
                buffer->flags |= st->dma_flags;
                st->unmap_len = 0;
        }
@@ -944,55 +1145,98 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue,
                                struct tso_state *st)
 {
        struct efx_tx_buffer *buffer =
-               &tx_queue->buffer[tx_queue->insert_count & tx_queue->ptr_mask];
-       struct tcphdr *tsoh_th;
-       unsigned ip_length;
-       u8 *header;
-       int rc;
+               efx_tx_queue_get_insert_buffer(tx_queue);
+       bool is_last = st->out_len <= skb_shinfo(skb)->gso_size;
+       u8 tcp_flags_clear;
 
-       /* Allocate and insert a DMA-mapped header buffer. */
-       header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len);
-       if (!header)
-               return -ENOMEM;
-
-       tsoh_th = (struct tcphdr *)(header + st->tcp_off);
-
-       /* Copy and update the headers. */
-       memcpy(header, skb->data, st->header_len);
-
-       tsoh_th->seq = htonl(st->seqnum);
-       st->seqnum += skb_shinfo(skb)->gso_size;
-       if (st->out_len > skb_shinfo(skb)->gso_size) {
-               /* This packet will not finish the TSO burst. */
+       if (!is_last) {
                st->packet_space = skb_shinfo(skb)->gso_size;
-               tsoh_th->fin = 0;
-               tsoh_th->psh = 0;
+               tcp_flags_clear = 0x09; /* mask out FIN and PSH */
        } else {
-               /* This packet will be the last in the TSO burst. */
                st->packet_space = st->out_len;
-               tsoh_th->fin = tcp_hdr(skb)->fin;
-               tsoh_th->psh = tcp_hdr(skb)->psh;
+               tcp_flags_clear = 0x00;
        }
-       ip_length = st->ip_base_len + st->packet_space;
 
-       if (st->protocol == htons(ETH_P_IP)) {
-               struct iphdr *tsoh_iph = (struct iphdr *)(header + st->ip_off);
+       if (!st->header_unmap_len) {
+               /* Allocate and insert a DMA-mapped header buffer. */
+               struct tcphdr *tsoh_th;
+               unsigned ip_length;
+               u8 *header;
+               int rc;
+
+               header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len);
+               if (!header)
+                       return -ENOMEM;
 
-               tsoh_iph->tot_len = htons(ip_length);
+               tsoh_th = (struct tcphdr *)(header + st->tcp_off);
+
+               /* Copy and update the headers. */
+               memcpy(header, skb->data, st->header_len);
+
+               tsoh_th->seq = htonl(st->seqnum);
+               ((u8 *)tsoh_th)[13] &= ~tcp_flags_clear;
+
+               ip_length = st->ip_base_len + st->packet_space;
+
+               if (st->protocol == htons(ETH_P_IP)) {
+                       struct iphdr *tsoh_iph =
+                               (struct iphdr *)(header + st->ip_off);
+
+                       tsoh_iph->tot_len = htons(ip_length);
+                       tsoh_iph->id = htons(st->ipv4_id);
+               } else {
+                       struct ipv6hdr *tsoh_iph =
+                               (struct ipv6hdr *)(header + st->ip_off);
+
+                       tsoh_iph->payload_len = htons(ip_length);
+               }
 
-               /* Linux leaves suitable gaps in the IP ID space for us to fill. */
-               tsoh_iph->id = htons(st->ipv4_id);
-               st->ipv4_id++;
+               rc = efx_tso_put_header(tx_queue, buffer, header);
+               if (unlikely(rc))
+                       return rc;
        } else {
-               struct ipv6hdr *tsoh_iph =
-                       (struct ipv6hdr *)(header + st->ip_off);
+               /* Send the original headers with a TSO option descriptor
+                * in front
+                */
+               u8 tcp_flags = ((u8 *)tcp_hdr(skb))[13] & ~tcp_flags_clear;
 
-               tsoh_iph->payload_len = htons(ip_length);
+               buffer->flags = EFX_TX_BUF_OPTION;
+               buffer->len = 0;
+               buffer->unmap_len = 0;
+               EFX_POPULATE_QWORD_5(buffer->option,
+                                    ESF_DZ_TX_DESC_IS_OPT, 1,
+                                    ESF_DZ_TX_OPTION_TYPE,
+                                    ESE_DZ_TX_OPTION_DESC_TSO,
+                                    ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags,
+                                    ESF_DZ_TX_TSO_IP_ID, st->ipv4_id,
+                                    ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum);
+               ++tx_queue->insert_count;
+
+               /* We mapped the headers in tso_start().  Unmap them
+                * when the last segment is completed.
+                */
+               buffer = efx_tx_queue_get_insert_buffer(tx_queue);
+               buffer->dma_addr = st->header_dma_addr;
+               buffer->len = st->header_len;
+               if (is_last) {
+                       buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE;
+                       buffer->unmap_len = st->header_unmap_len;
+                       buffer->dma_offset = 0;
+                       /* Ensure we only unmap them once in case of a
+                        * later DMA mapping error and rollback
+                        */
+                       st->header_unmap_len = 0;
+               } else {
+                       buffer->flags = EFX_TX_BUF_CONT;
+                       buffer->unmap_len = 0;
+               }
+               ++tx_queue->insert_count;
        }
 
-       rc = efx_tso_put_header(tx_queue, buffer, header);
-       if (unlikely(rc))
-               return rc;
+       st->seqnum += skb_shinfo(skb)->gso_size;
+
+       /* Linux leaves suitable gaps in the IP ID space for us to fill. */
+       ++st->ipv4_id;
 
        ++tx_queue->tso_packets;
 
@@ -1023,12 +1267,11 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
 
        EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
 
-       tso_start(&state, skb);
+       rc = tso_start(&state, efx, skb);
+       if (rc)
+               goto mem_err;
 
-       /* Assume that skb header area contains exactly the headers, and
-        * all payload is in the frag list.
-        */
-       if (skb_headlen(skb) == state.header_len) {
+       if (likely(state.in_len == 0)) {
                /* Grab the first payload fragment. */
                EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1);
                frag_i = 0;
@@ -1037,9 +1280,7 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
                if (rc)
                        goto mem_err;
        } else {
-               rc = tso_get_head_fragment(&state, efx, skb);
-               if (rc)
-                       goto mem_err;
+               /* Payload starts in the header area. */
                frag_i = -1;
        }
 
@@ -1091,6 +1332,11 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
                                       state.unmap_len, DMA_TO_DEVICE);
        }
 
+       /* Free the header DMA mapping, if using option descriptors */
+       if (state.header_unmap_len)
+               dma_unmap_single(&efx->pci_dev->dev, state.header_dma_addr,
+                                state.header_unmap_len, DMA_TO_DEVICE);
+
        efx_enqueue_unwind(tx_queue);
        return NETDEV_TX_OK;
 }
index 770036b..513ed8b 100644 (file)
@@ -839,7 +839,7 @@ static int meth_probe(struct platform_device *pdev)
        dev->watchdog_timeo     = timeout;
        dev->irq                = MACE_ETHERNET_IRQ;
        dev->base_addr          = (unsigned long)&mace->eth;
-       memcpy(dev->dev_addr, o2meth_eaddr, 6);
+       memcpy(dev->dev_addr, o2meth_eaddr, ETH_ALEN);
 
        priv = netdev_priv(dev);
        spin_lock_init(&priv->meth_lock);
index ee18e6f..acbbe48 100644 (file)
@@ -1921,7 +1921,6 @@ static void sis190_remove_one(struct pci_dev *pdev)
        cancel_work_sync(&tp->phy_task);
        unregister_netdev(dev);
        sis190_release_board(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static struct pci_driver sis190_pci_driver = {
index 068fc44..753630f 100644 (file)
@@ -6,7 +6,7 @@ config NET_VENDOR_SMSC
        bool "SMC (SMSC)/Western Digital devices"
        default y
        depends on ARM || ISA || MAC || ARM64 || MIPS || M32R || SUPERH || \
-               BLACKFIN || MN10300 || COLDFIRE || PCI || PCMCIA
+               BLACKFIN || MN10300 || COLDFIRE || XTENSA || PCI || PCMCIA
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
@@ -39,7 +39,7 @@ config SMC91X
        select CRC32
        select MII
        depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \
-                   MN10300 || COLDFIRE || ARM64)
+                   MN10300 || COLDFIRE || ARM64 || XTENSA)
        ---help---
          This is a driver for SMC's 91x series of Ethernet chipsets,
          including the SMC91C94 and the SMC91C111. Say Y if you want it
index 03b256a..8ae1f8a 100644 (file)
@@ -91,9 +91,9 @@ static int rx_copybreak;
 
 /* These identify the driver base version and may not be removed. */
 static char version[] =
-DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
+DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>";
 static char version2[] =
-"  (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n";
+"  (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")";
 
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
@@ -332,9 +332,7 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 /* when built into the kernel, we only print version if device is found */
 #ifndef MODULE
-       static int printed_version;
-       if (!printed_version++)
-               printk(KERN_INFO "%s%s", version, version2);
+       pr_info_once("%s%s\n", version, version2);
 #endif
 
        card_idx++;
@@ -423,9 +421,9 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(er16(LAN0 + i*4));
 
        if (debug > 2) {
-               dev_printk(KERN_DEBUG, &pdev->dev, "EEPROM contents:\n");
+               dev_dbg(&pdev->dev, "EEPROM contents:\n");
                for (i = 0; i < 64; i++)
-                       printk(" %4.4x%s", read_eeprom(ep, i),
+                       pr_cont(" %4.4x%s", read_eeprom(ep, i),
                                   i % 16 == 15 ? "\n" : "");
        }
 
@@ -490,10 +488,10 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret < 0)
                goto err_out_unmap_rx;
 
-       printk(KERN_INFO "%s: %s at %lx, IRQ %d, %pM\n",
-              dev->name, pci_id_tbl[chip_idx].name,
-              (long)pci_resource_start(pdev, EPIC_BAR), pdev->irq,
-              dev->dev_addr);
+       netdev_info(dev, "%s at %lx, IRQ %d, %pM\n",
+                   pci_id_tbl[chip_idx].name,
+                   (long)pci_resource_start(pdev, EPIC_BAR), pdev->irq,
+                   dev->dev_addr);
 
 out:
        return ret;
@@ -703,9 +701,8 @@ static int epic_open(struct net_device *dev)
                        mdio_write(dev, ep->phys[0], MII_BMCR, media2miictl[dev->if_port&15]);
                if (dev->if_port == 1) {
                        if (debug > 1)
-                               printk(KERN_INFO "%s: Using the 10base2 transceiver, MII "
-                                          "status %4.4x.\n",
-                                          dev->name, mdio_read(dev, ep->phys[0], MII_BMSR));
+                               netdev_info(dev, "Using the 10base2 transceiver, MII status %4.4x.\n",
+                                           mdio_read(dev, ep->phys[0], MII_BMSR));
                }
        } else {
                int mii_lpa = mdio_read(dev, ep->phys[0], MII_LPA);
@@ -715,10 +712,10 @@ static int epic_open(struct net_device *dev)
                        else if (! (mii_lpa & LPA_LPACK))
                                mdio_write(dev, ep->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
                        if (debug > 1)
-                               printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d"
-                                          " register read of %4.4x.\n", dev->name,
-                                          ep->mii.full_duplex ? "full" : "half",
-                                          ep->phys[0], mii_lpa);
+                               netdev_info(dev, "Setting %s-duplex based on MII xcvr %d register read of %4.4x.\n",
+                                           ep->mii.full_duplex ? "full"
+                                                               : "half",
+                                           ep->phys[0], mii_lpa);
                }
        }
 
@@ -738,10 +735,9 @@ static int epic_open(struct net_device *dev)
             TxUnderrun);
 
        if (debug > 1) {
-               printk(KERN_DEBUG "%s: epic_open() ioaddr %p IRQ %d "
-                      "status %4.4x %s-duplex.\n",
-                      dev->name, ioaddr, irq, er32(GENCTL),
-                      ep->mii.full_duplex ? "full" : "half");
+               netdev_dbg(dev, "epic_open() ioaddr %p IRQ %d status %4.4x %s-duplex.\n",
+                          ioaddr, irq, er32(GENCTL),
+                          ep->mii.full_duplex ? "full" : "half");
        }
 
        /* Set the timer to switch to check for link beat and perhaps switch
@@ -790,8 +786,8 @@ static void epic_restart(struct net_device *dev)
        /* Soft reset the chip. */
        ew32(GENCTL, 0x4001);
 
-       printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n",
-                  dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx);
+       netdev_dbg(dev, "Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n",
+                  ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx);
        udelay(1);
 
        /* This magic is documented in SMSC app note 7.15 */
@@ -827,9 +823,8 @@ static void epic_restart(struct net_device *dev)
             ((ep->chip_flags & TYPE2_INTR) ? PCIBusErr175 : PCIBusErr170) |
             TxUnderrun);
 
-       printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x"
-                  " interrupt %4.4x.\n",
-                  dev->name, er32(COMMAND), er32(GENCTL), er32(INTSTAT));
+       netdev_dbg(dev, "epic_restart() done, cmd status %4.4x, ctl %4.4x interrupt %4.4x.\n",
+                  er32(COMMAND), er32(GENCTL), er32(INTSTAT));
 }
 
 static void check_media(struct net_device *dev)
@@ -846,9 +841,9 @@ static void check_media(struct net_device *dev)
                return;
        if (ep->mii.full_duplex != duplex) {
                ep->mii.full_duplex = duplex;
-               printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
-                          " partner capability of %4.4x.\n", dev->name,
-                          ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa);
+               netdev_info(dev, "Setting %s-duplex based on MII #%d link partner capability of %4.4x.\n",
+                           ep->mii.full_duplex ? "full" : "half",
+                           ep->phys[0], mii_lpa);
                ew32(TxCtrl, ep->mii.full_duplex ? 0x7F : 0x79);
        }
 }
@@ -861,11 +856,10 @@ static void epic_timer(unsigned long data)
        int next_tick = 5*HZ;
 
        if (debug > 3) {
-               printk(KERN_DEBUG "%s: Media monitor tick, Tx status %8.8x.\n",
-                      dev->name, er32(TxSTAT));
-               printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x "
-                      "IntStatus %4.4x RxStatus %4.4x.\n", dev->name,
-                      er32(INTMASK), er32(INTSTAT), er32(RxSTAT));
+               netdev_dbg(dev, "Media monitor tick, Tx status %8.8x.\n",
+                          er32(TxSTAT));
+               netdev_dbg(dev, "Other registers are IntMask %4.4x IntStatus %4.4x RxStatus %4.4x.\n",
+                          er32(INTMASK), er32(INTSTAT), er32(RxSTAT));
        }
 
        check_media(dev);
@@ -880,11 +874,11 @@ static void epic_tx_timeout(struct net_device *dev)
        void __iomem *ioaddr = ep->ioaddr;
 
        if (debug > 0) {
-               printk(KERN_WARNING "%s: Transmit timeout using MII device, "
-                      "Tx status %4.4x.\n", dev->name, er16(TxSTAT));
+               netdev_warn(dev, "Transmit timeout using MII device, Tx status %4.4x.\n",
+                           er16(TxSTAT));
                if (debug > 1) {
-                       printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n",
-                                  dev->name, ep->dirty_tx, ep->cur_tx);
+                       netdev_dbg(dev, "Tx indices: dirty_tx %d, cur_tx %d.\n",
+                                  ep->dirty_tx, ep->cur_tx);
                }
        }
        if (er16(TxSTAT) & 0x10) {              /* Tx FIFO underflow. */
@@ -994,9 +988,8 @@ static netdev_tx_t epic_start_xmit(struct sk_buff *skb, struct net_device *dev)
        ew32(COMMAND, TxQueued);
 
        if (debug > 4)
-               printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, "
-                      "flag %2.2x Tx status %8.8x.\n", dev->name, skb->len,
-                      entry, ctrl_word, er32(TxSTAT));
+               netdev_dbg(dev, "Queued Tx packet size %d to slot %d, flag %2.2x Tx status %8.8x.\n",
+                          skb->len, entry, ctrl_word, er32(TxSTAT));
 
        return NETDEV_TX_OK;
 }
@@ -1009,8 +1002,8 @@ static void epic_tx_error(struct net_device *dev, struct epic_private *ep,
 #ifndef final_version
        /* There was an major error, log it. */
        if (debug > 1)
-               printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
-                      dev->name, status);
+               netdev_dbg(dev, "Transmit error, Tx status %8.8x.\n",
+                          status);
 #endif
        stats->tx_errors++;
        if (status & 0x1050)
@@ -1057,9 +1050,8 @@ static void epic_tx(struct net_device *dev, struct epic_private *ep)
 
 #ifndef final_version
        if (cur_tx - dirty_tx > TX_RING_SIZE) {
-               printk(KERN_WARNING
-                      "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-                      dev->name, dirty_tx, cur_tx, ep->tx_full);
+               netdev_warn(dev, "Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+                           dirty_tx, cur_tx, ep->tx_full);
                dirty_tx += TX_RING_SIZE;
        }
 #endif
@@ -1086,8 +1078,8 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance)
        ew32(INTSTAT, status & EpicNormalEvent);
 
        if (debug > 4) {
-               printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new "
-                      "intstat=%#8.8x.\n", dev->name, status, er32(INTSTAT));
+               netdev_dbg(dev, "Interrupt, status=%#8.8x new intstat=%#8.8x.\n",
+                          status, er32(INTSTAT));
        }
 
        if ((status & IntrSummary) == 0)
@@ -1125,8 +1117,8 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance)
                        ew32(COMMAND, RestartTx);
                }
                if (status & PCIBusErr170) {
-                       printk(KERN_ERR "%s: PCI Bus Error! status %4.4x.\n",
-                                        dev->name, status);
+                       netdev_err(dev, "PCI Bus Error! status %4.4x.\n",
+                                  status);
                        epic_pause(dev);
                        epic_restart(dev);
                }
@@ -1136,8 +1128,8 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance)
 
 out:
        if (debug > 3) {
-               printk(KERN_DEBUG "%s: exit interrupt, intr_status=%#4.4x.\n",
-                                  dev->name, status);
+               netdev_dbg(dev, "exit interrupt, intr_status=%#4.4x.\n",
+                          status);
        }
 
        return IRQ_RETVAL(handled);
@@ -1151,7 +1143,7 @@ static int epic_rx(struct net_device *dev, int budget)
        int work_done = 0;
 
        if (debug > 4)
-               printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry,
+               netdev_dbg(dev, " In epic_rx(), entry %d %8.8x.\n", entry,
                           ep->rx_ring[entry].rxstatus);
 
        if (rx_work_limit > budget)
@@ -1162,16 +1154,17 @@ static int epic_rx(struct net_device *dev, int budget)
                int status = ep->rx_ring[entry].rxstatus;
 
                if (debug > 4)
-                       printk(KERN_DEBUG "  epic_rx() status was %8.8x.\n", status);
+                       netdev_dbg(dev, "  epic_rx() status was %8.8x.\n",
+                                  status);
                if (--rx_work_limit < 0)
                        break;
                if (status & 0x2006) {
                        if (debug > 2)
-                               printk(KERN_DEBUG "%s: epic_rx() error status was %8.8x.\n",
-                                          dev->name, status);
+                               netdev_dbg(dev, "epic_rx() error status was %8.8x.\n",
+                                          status);
                        if (status & 0x2000) {
-                               printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
-                                          "multiple buffers, status %4.4x!\n", dev->name, status);
+                               netdev_warn(dev, "Oversized Ethernet frame spanned multiple buffers, status %4.4x!\n",
+                                           status);
                                dev->stats.rx_length_errors++;
                        } else if (status & 0x0006)
                                /* Rx Frame errors are counted in hardware. */
@@ -1183,9 +1176,8 @@ static int epic_rx(struct net_device *dev, int budget)
                        struct sk_buff *skb;
 
                        if (pkt_len > PKT_BUF_SZ - 4) {
-                               printk(KERN_ERR "%s: Oversized Ethernet frame, status %x "
-                                          "%d bytes.\n",
-                                          dev->name, status, pkt_len);
+                               netdev_err(dev, "Oversized Ethernet frame, status %x %d bytes.\n",
+                                          status, pkt_len);
                                pkt_len = 1514;
                        }
                        /* Check if the packet is long enough to accept without copying
@@ -1305,8 +1297,8 @@ static int epic_close(struct net_device *dev)
        napi_disable(&ep->napi);
 
        if (debug > 1)
-               printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
-                      dev->name, er32(INTSTAT));
+               netdev_dbg(dev, "Shutting down ethercard, status was %2.2x.\n",
+                          er32(INTSTAT));
 
        del_timer_sync(&ep->timer);
 
@@ -1324,7 +1316,7 @@ static int epic_close(struct net_device *dev)
                ep->rx_ring[i].buflength = 0;
                if (skb) {
                        pci_unmap_single(pdev, ep->rx_ring[i].bufaddr,
-                                        ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+                                        ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
                        dev_kfree_skb(skb);
                }
                ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */
@@ -1535,7 +1527,6 @@ static void epic_remove_one(struct pci_dev *pdev)
        pci_release_regions(pdev);
        free_netdev(dev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        /* pci_power_off(pdev, -1); */
 }
 
@@ -1588,8 +1579,7 @@ static int __init epic_init (void)
 {
 /* when a module, this is printed whether or not devices are found in probe */
 #ifdef MODULE
-       printk (KERN_INFO "%s%s",
-               version, version2);
+       pr_info("%s%s\n", version, version2);
 #endif
 
        return pci_register_driver(&epic_driver);
index afe01c4..0f096a8 100644 (file)
@@ -106,16 +106,16 @@ MODULE_ALIAS("platform:smc911x");
 #define POWER_DOWN              1
 
 #if SMC_DEBUG > 0
-#define DBG(n, args...)                                 \
+#define DBG(n, dev, args...)                    \
        do {                                     \
                if (SMC_DEBUG & (n))             \
-                       printk(args);            \
+                       netdev_dbg(dev, args);   \
        } while (0)
 
-#define PRINTK(args...)   printk(args)
+#define PRINTK(dev, args...)   netdev_info(dev, args)
 #else
-#define DBG(n, args...)   do { } while (0)
-#define PRINTK(args...)   printk(KERN_DEBUG args)
+#define DBG(n, dev, args...)   do { } while (0)
+#define PRINTK(dev, args...)   netdev_dbg(dev, args)
 #endif
 
 #if SMC_DEBUG_PKTS > 0
@@ -130,21 +130,23 @@ static void PRINT_PKT(u_char *buf, int length)
 
        for (i = 0; i < lines ; i ++) {
                int cur;
+               printk(KERN_DEBUG);
                for (cur = 0; cur < 8; cur++) {
                        u_char a, b;
                        a = *buf++;
                        b = *buf++;
-                       printk("%02x%02x ", a, b);
+                       pr_cont("%02x%02x ", a, b);
                }
-               printk("\n");
+               pr_cont("\n");
        }
+       printk(KERN_DEBUG);
        for (i = 0; i < remainder/2 ; i++) {
                u_char a, b;
                a = *buf++;
                b = *buf++;
-               printk("%02x%02x ", a, b);
+               pr_cont("%02x%02x ", a, b);
        }
-       printk("\n");
+       pr_cont("\n");
 }
 #else
 #define PRINT_PKT(x...)  do { } while (0)
@@ -176,7 +178,7 @@ static void smc911x_reset(struct net_device *dev)
        unsigned int reg, timeout=0, resets=1, irq_cfg;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        /*       Take out of PM setting first */
        if ((SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_) == 0) {
@@ -188,7 +190,7 @@ static void smc911x_reset(struct net_device *dev)
                        reg = SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_;
                } while (--timeout && !reg);
                if (timeout == 0) {
-                       PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name);
+                       PRINTK(dev, "smc911x_reset timeout waiting for PM restore\n");
                        return;
                }
        }
@@ -206,14 +208,14 @@ static void smc911x_reset(struct net_device *dev)
                        reg = SMC_GET_HW_CFG(lp);
                        /* If chip indicates reset timeout then try again */
                        if (reg & HW_CFG_SRST_TO_) {
-                               PRINTK("%s: chip reset timeout, retrying...\n", dev->name);
+                               PRINTK(dev, "chip reset timeout, retrying...\n");
                                resets++;
                                break;
                        }
                } while (--timeout && (reg & HW_CFG_SRST_));
        }
        if (timeout == 0) {
-               PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name);
+               PRINTK(dev, "smc911x_reset timeout waiting for reset\n");
                return;
        }
 
@@ -223,7 +225,7 @@ static void smc911x_reset(struct net_device *dev)
                udelay(10);
 
        if (timeout == 0){
-               PRINTK("%s: smc911x_reset timeout waiting for EEPROM busy\n", dev->name);
+               PRINTK(dev, "smc911x_reset timeout waiting for EEPROM busy\n");
                return;
        }
 
@@ -270,7 +272,7 @@ static void smc911x_enable(struct net_device *dev)
        unsigned mask, cfg, cr;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        spin_lock_irqsave(&lp->lock, flags);
 
@@ -296,7 +298,7 @@ static void smc911x_enable(struct net_device *dev)
 
        /* Turn on receiver and enable RX */
        if (cr & MAC_CR_RXEN_)
-               DBG(SMC_DEBUG_RX, "%s: Receiver already enabled\n", dev->name);
+               DBG(SMC_DEBUG_RX, dev, "Receiver already enabled\n");
 
        SMC_SET_MAC_CR(lp, cr | MAC_CR_RXEN_);
 
@@ -327,7 +329,7 @@ static void smc911x_shutdown(struct net_device *dev)
        unsigned cr;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", CARDNAME, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "%s: --> %s\n", CARDNAME, __func__);
 
        /* Disable IRQ's */
        SMC_SET_INT_EN(lp, 0);
@@ -346,7 +348,8 @@ static inline void smc911x_drop_pkt(struct net_device *dev)
        struct smc911x_local *lp = netdev_priv(dev);
        unsigned int fifo_count, timeout, reg;
 
-       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", CARDNAME, __func__);
+       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, dev, "%s: --> %s\n",
+           CARDNAME, __func__);
        fifo_count = SMC_GET_RX_FIFO_INF(lp) & 0xFFFF;
        if (fifo_count <= 4) {
                /* Manually dump the packet data */
@@ -361,7 +364,7 @@ static inline void smc911x_drop_pkt(struct net_device *dev)
                        reg = SMC_GET_RX_DP_CTRL(lp) & RX_DP_CTRL_FFWD_BUSY_;
                } while (--timeout && reg);
                if (timeout == 0) {
-                       PRINTK("%s: timeout waiting for RX fast forward\n", dev->name);
+                       PRINTK(dev, "timeout waiting for RX fast forward\n");
                }
        }
 }
@@ -379,11 +382,11 @@ static inline void         smc911x_rcv(struct net_device *dev)
        struct sk_buff *skb;
        unsigned char *data;
 
-       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n",
-               dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, dev, "--> %s\n",
+           __func__);
        status = SMC_GET_RX_STS_FIFO(lp);
-       DBG(SMC_DEBUG_RX, "%s: Rx pkt len %d status 0x%08x\n",
-               dev->name, (status & 0x3fff0000) >> 16, status & 0xc000ffff);
+       DBG(SMC_DEBUG_RX, dev, "Rx pkt len %d status 0x%08x\n",
+           (status & 0x3fff0000) >> 16, status & 0xc000ffff);
        pkt_len = (status & RX_STS_PKT_LEN_) >> 16;
        if (status & RX_STS_ES_) {
                /* Deal with a bad packet */
@@ -403,8 +406,7 @@ static inline void   smc911x_rcv(struct net_device *dev)
                /* Alloc a buffer with extra room for DMA alignment */
                skb = netdev_alloc_skb(dev, pkt_len+32);
                if (unlikely(skb == NULL)) {
-                       PRINTK( "%s: Low memory, rcvd packet dropped.\n",
-                               dev->name);
+                       PRINTK(dev, "Low memory, rcvd packet dropped.\n");
                        dev->stats.rx_dropped++;
                        smc911x_drop_pkt(dev);
                        return;
@@ -422,8 +424,8 @@ static inline void   smc911x_rcv(struct net_device *dev)
                /* Lower the FIFO threshold if possible */
                fifo = SMC_GET_FIFO_INT(lp);
                if (fifo & 0xFF) fifo--;
-               DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n",
-                       dev->name, fifo & 0xff);
+               DBG(SMC_DEBUG_RX, dev, "Setting RX stat FIFO threshold to %d\n",
+                   fifo & 0xff);
                SMC_SET_FIFO_INT(lp, fifo);
                /* Setup RX DMA */
                SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_));
@@ -436,7 +438,7 @@ static inline void   smc911x_rcv(struct net_device *dev)
                SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_));
                SMC_PULL_DATA(lp, data, pkt_len+2+3);
 
-               DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name);
+               DBG(SMC_DEBUG_PKTS, dev, "Received packet\n");
                PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
                skb->protocol = eth_type_trans(skb, dev);
                netif_rx(skb);
@@ -456,7 +458,7 @@ static void smc911x_hardware_send_pkt(struct net_device *dev)
        unsigned int cmdA, cmdB, len;
        unsigned char *buf;
 
-       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", __func__);
        BUG_ON(lp->pending_tx_skb == NULL);
 
        skb = lp->pending_tx_skb;
@@ -481,12 +483,12 @@ static void smc911x_hardware_send_pkt(struct net_device *dev)
        /* tag is packet length so we can use this in stats update later */
        cmdB = (skb->len  << 16) | (skb->len & 0x7FF);
 
-       DBG(SMC_DEBUG_TX, "%s: TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n",
-                dev->name, len, len, buf, cmdA, cmdB);
+       DBG(SMC_DEBUG_TX, dev, "TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n",
+           len, len, buf, cmdA, cmdB);
        SMC_SET_TX_FIFO(lp, cmdA);
        SMC_SET_TX_FIFO(lp, cmdB);
 
-       DBG(SMC_DEBUG_PKTS, "%s: Transmitted packet\n", dev->name);
+       DBG(SMC_DEBUG_PKTS, dev, "Transmitted packet\n");
        PRINT_PKT(buf, len <= 64 ? len : 64);
 
        /* Send pkt via PIO or DMA */
@@ -517,20 +519,20 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int free;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
-               dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n",
+           __func__);
 
        spin_lock_irqsave(&lp->lock, flags);
 
        BUG_ON(lp->pending_tx_skb != NULL);
 
        free = SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TDFREE_;
-       DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free);
+       DBG(SMC_DEBUG_TX, dev, "TX free space %d\n", free);
 
        /* Turn off the flow when running out of space in FIFO */
        if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) {
-               DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n",
-                       dev->name, free);
+               DBG(SMC_DEBUG_TX, dev, "Disabling data flow due to low FIFO space (%d)\n",
+                   free);
                /* Reenable when at least 1 packet of size MTU present */
                SMC_SET_FIFO_TDA(lp, (SMC911X_TX_FIFO_LOW_THRESHOLD)/64);
                lp->tx_throttle = 1;
@@ -545,8 +547,8 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
         *        End padding                            15 bytes
         */
        if (unlikely(free < (skb->len + 8 + 15 + 15))) {
-               printk("%s: No Tx free space %d < %d\n",
-                       dev->name, free, skb->len);
+               netdev_warn(dev, "No Tx free space %d < %d\n",
+                           free, skb->len);
                lp->pending_tx_skb = NULL;
                dev->stats.tx_errors++;
                dev->stats.tx_dropped++;
@@ -561,13 +563,13 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                 * the DMA IRQ starts it
                 */
                if (lp->txdma_active) {
-                       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name);
+                       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "Tx DMA running, deferring packet\n");
                        lp->pending_tx_skb = skb;
                        netif_stop_queue(dev);
                        spin_unlock_irqrestore(&lp->lock, flags);
                        return NETDEV_TX_OK;
                } else {
-                       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name);
+                       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "Activating Tx DMA\n");
                        lp->txdma_active = 1;
                }
        }
@@ -589,20 +591,19 @@ static void smc911x_tx(struct net_device *dev)
        struct smc911x_local *lp = netdev_priv(dev);
        unsigned int tx_status;
 
-       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
-               dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n",
+           __func__);
 
        /* Collect the TX status */
        while (((SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16) != 0) {
-               DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n",
-                       dev->name,
-                       (SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16);
+               DBG(SMC_DEBUG_TX, dev, "Tx stat FIFO used 0x%04x\n",
+                   (SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16);
                tx_status = SMC_GET_TX_STS_FIFO(lp);
                dev->stats.tx_packets++;
                dev->stats.tx_bytes+=tx_status>>16;
-               DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n",
-                       dev->name, (tx_status & 0xffff0000) >> 16,
-                       tx_status & 0x0000ffff);
+               DBG(SMC_DEBUG_TX, dev, "Tx FIFO tag 0x%04x status 0x%04x\n",
+                   (tx_status & 0xffff0000) >> 16,
+                   tx_status & 0x0000ffff);
                /* count Tx errors, but ignore lost carrier errors when in
                 * full-duplex mode */
                if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx &&
@@ -640,8 +641,8 @@ static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg)
 
        SMC_GET_MII(lp, phyreg, phyaddr, phydata);
 
-       DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n",
-               __func__, phyaddr, phyreg, phydata);
+       DBG(SMC_DEBUG_MISC, dev, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n",
+           __func__, phyaddr, phyreg, phydata);
        return phydata;
 }
 
@@ -654,8 +655,8 @@ static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg,
 {
        struct smc911x_local *lp = netdev_priv(dev);
 
-       DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
-               __func__, phyaddr, phyreg, phydata);
+       DBG(SMC_DEBUG_MISC, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+           __func__, phyaddr, phyreg, phydata);
 
        SMC_SET_MII(lp, phyreg, phyaddr, phydata);
 }
@@ -670,7 +671,7 @@ static void smc911x_phy_detect(struct net_device *dev)
        int phyaddr;
        unsigned int cfg, id1, id2;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        lp->phy_type = 0;
 
@@ -731,8 +732,8 @@ static void smc911x_phy_detect(struct net_device *dev)
                        lp->phy_type = id1 << 16 | id2;
        }
 
-       DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n",
-               dev->name, id1, id2, lp->mii.phy_id);
+       DBG(SMC_DEBUG_MISC, dev, "phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n",
+           id1, id2, lp->mii.phy_id);
 }
 
 /*
@@ -745,7 +746,7 @@ static int smc911x_phy_fixed(struct net_device *dev)
        int phyaddr = lp->mii.phy_id;
        int bmcr;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        /* Enter Link Disable state */
        SMC_GET_PHY_BMCR(lp, phyaddr, bmcr);
@@ -792,7 +793,7 @@ static int smc911x_phy_reset(struct net_device *dev, int phy)
        unsigned long flags;
        unsigned int reg;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s()\n", __func__);
 
        spin_lock_irqsave(&lp->lock, flags);
        reg = SMC_GET_PMT_CTRL(lp);
@@ -851,18 +852,18 @@ static void smc911x_phy_check_media(struct net_device *dev, int init)
        int phyaddr = lp->mii.phy_id;
        unsigned int bmcr, cr;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
                /* duplex state has changed */
                SMC_GET_PHY_BMCR(lp, phyaddr, bmcr);
                SMC_GET_MAC_CR(lp, cr);
                if (lp->mii.full_duplex) {
-                       DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name);
+                       DBG(SMC_DEBUG_MISC, dev, "Configuring for full-duplex mode\n");
                        bmcr |= BMCR_FULLDPLX;
                        cr |= MAC_CR_RCVOWN_;
                } else {
-                       DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name);
+                       DBG(SMC_DEBUG_MISC, dev, "Configuring for half-duplex mode\n");
                        bmcr &= ~BMCR_FULLDPLX;
                        cr &= ~MAC_CR_RCVOWN_;
                }
@@ -891,7 +892,7 @@ static void smc911x_phy_configure(struct work_struct *work)
        int status;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s()\n", __func__);
 
        /*
         * We should not be called if phy_type is zero.
@@ -900,7 +901,7 @@ static void smc911x_phy_configure(struct work_struct *work)
                return;
 
        if (smc911x_phy_reset(dev, phyaddr)) {
-               printk("%s: PHY reset timed out\n", dev->name);
+               netdev_info(dev, "PHY reset timed out\n");
                return;
        }
        spin_lock_irqsave(&lp->lock, flags);
@@ -922,7 +923,7 @@ static void smc911x_phy_configure(struct work_struct *work)
        /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
        SMC_GET_PHY_BMSR(lp, phyaddr, my_phy_caps);
        if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
-               printk(KERN_INFO "Auto negotiation NOT supported\n");
+               netdev_info(dev, "Auto negotiation NOT supported\n");
                smc911x_phy_fixed(dev);
                goto smc911x_phy_configure_exit;
        }
@@ -960,8 +961,8 @@ static void smc911x_phy_configure(struct work_struct *work)
        udelay(10);
        SMC_GET_PHY_MII_ADV(lp, phyaddr, status);
 
-       DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps);
-       DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps);
+       DBG(SMC_DEBUG_MISC, dev, "phy caps=0x%04x\n", my_phy_caps);
+       DBG(SMC_DEBUG_MISC, dev, "phy advertised caps=0x%04x\n", my_ad_caps);
 
        /* Restart auto-negotiation process in order to advertise my caps */
        SMC_SET_PHY_BMCR(lp, phyaddr, BMCR_ANENABLE | BMCR_ANRESTART);
@@ -984,7 +985,7 @@ static void smc911x_phy_interrupt(struct net_device *dev)
        int phyaddr = lp->mii.phy_id;
        int status;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        if (lp->phy_type == 0)
                return;
@@ -992,10 +993,10 @@ static void smc911x_phy_interrupt(struct net_device *dev)
        smc911x_phy_check_media(dev, 0);
        /* read to clear status bits */
        SMC_GET_PHY_INT_SRC(lp, phyaddr,status);
-       DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n",
-               dev->name, status & 0xffff);
-       DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n",
-               dev->name, SMC_GET_AFC_CFG(lp));
+       DBG(SMC_DEBUG_MISC, dev, "PHY interrupt status 0x%04x\n",
+           status & 0xffff);
+       DBG(SMC_DEBUG_MISC, dev, "AFC_CFG 0x%08x\n",
+           SMC_GET_AFC_CFG(lp));
 }
 
 /*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
@@ -1012,7 +1013,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
        unsigned int rx_overrun=0, cr, pkts;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        spin_lock_irqsave(&lp->lock, flags);
 
@@ -1033,8 +1034,8 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
        do {
                status = SMC_GET_INT(lp);
 
-               DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n",
-                       dev->name, status, mask, status & ~mask);
+               DBG(SMC_DEBUG_MISC, dev, "INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n",
+                   status, mask, status & ~mask);
 
                status &= mask;
                if (!status)
@@ -1066,7 +1067,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
                                SMC_GET_MAC_CR(lp, cr);
                                cr &= ~MAC_CR_RXEN_;
                                SMC_SET_MAC_CR(lp, cr);
-                               DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+                               DBG(SMC_DEBUG_RX, dev, "RX overrun\n");
                                dev->stats.rx_errors++;
                                dev->stats.rx_fifo_errors++;
                        }
@@ -1078,7 +1079,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
                                cr &= ~MAC_CR_RXEN_;
                                SMC_SET_MAC_CR(lp, cr);
                                rx_overrun=1;
-                               DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+                               DBG(SMC_DEBUG_RX, dev, "RX overrun\n");
                                dev->stats.rx_errors++;
                                dev->stats.rx_fifo_errors++;
                        }
@@ -1087,23 +1088,23 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
                /* Handle receive condition */
                if ((status & INT_STS_RSFL_) || rx_overrun) {
                        unsigned int fifo;
-                       DBG(SMC_DEBUG_RX, "%s: RX irq\n", dev->name);
+                       DBG(SMC_DEBUG_RX, dev, "RX irq\n");
                        fifo = SMC_GET_RX_FIFO_INF(lp);
                        pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16;
-                       DBG(SMC_DEBUG_RX, "%s: Rx FIFO pkts %d, bytes %d\n",
-                               dev->name, pkts, fifo & 0xFFFF );
+                       DBG(SMC_DEBUG_RX, dev, "Rx FIFO pkts %d, bytes %d\n",
+                           pkts, fifo & 0xFFFF);
                        if (pkts != 0) {
 #ifdef SMC_USE_DMA
                                unsigned int fifo;
                                if (lp->rxdma_active){
-                                       DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
-                                               "%s: RX DMA active\n", dev->name);
+                                       DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev,
+                                           "RX DMA active\n");
                                        /* The DMA is already running so up the IRQ threshold */
                                        fifo = SMC_GET_FIFO_INT(lp) & ~0xFF;
                                        fifo |= pkts & 0xFF;
-                                       DBG(SMC_DEBUG_RX,
-                                               "%s: Setting RX stat FIFO threshold to %d\n",
-                                               dev->name, fifo & 0xff);
+                                       DBG(SMC_DEBUG_RX, dev,
+                                           "Setting RX stat FIFO threshold to %d\n",
+                                           fifo & 0xff);
                                        SMC_SET_FIFO_INT(lp, fifo);
                                } else
 #endif
@@ -1113,7 +1114,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
                }
                /* Handle transmit FIFO available */
                if (status & INT_STS_TDFA_) {
-                       DBG(SMC_DEBUG_TX, "%s: TX data FIFO space available irq\n", dev->name);
+                       DBG(SMC_DEBUG_TX, dev, "TX data FIFO space available irq\n");
                        SMC_SET_FIFO_TDA(lp, 0xFF);
                        lp->tx_throttle = 0;
 #ifdef SMC_USE_DMA
@@ -1125,9 +1126,9 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
                /* Handle transmit done condition */
 #if 1
                if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) {
-                       DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC,
-                               "%s: Tx stat FIFO limit (%d) /GPT irq\n",
-                               dev->name, (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16);
+                       DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC, dev,
+                           "Tx stat FIFO limit (%d) /GPT irq\n",
+                           (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16);
                        smc911x_tx(dev);
                        SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
                        SMC_ACK_INT(lp, INT_STS_TSFL_);
@@ -1135,23 +1136,20 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
                }
 #else
                if (status & INT_STS_TSFL_) {
-                       DBG(SMC_DEBUG_TX, "%s: TX status FIFO limit (%d) irq\n", dev->name, );
+                       DBG(SMC_DEBUG_TX, dev, "TX status FIFO limit (%d) irq\n", ?);
                        smc911x_tx(dev);
                        SMC_ACK_INT(lp, INT_STS_TSFL_);
                }
 
                if (status & INT_STS_GPT_INT_) {
-                       DBG(SMC_DEBUG_RX, "%s: IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n",
-                               dev->name,
-                               SMC_GET_IRQ_CFG(lp),
-                               SMC_GET_FIFO_INT(lp),
-                               SMC_GET_RX_CFG(lp));
-                       DBG(SMC_DEBUG_RX, "%s: Rx Stat FIFO Used 0x%02x "
-                               "Data FIFO Used 0x%04x Stat FIFO 0x%08x\n",
-                               dev->name,
-                               (SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16,
-                               SMC_GET_RX_FIFO_INF(lp) & 0xffff,
-                               SMC_GET_RX_STS_FIFO_PEEK(lp));
+                       DBG(SMC_DEBUG_RX, dev, "IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n",
+                           SMC_GET_IRQ_CFG(lp),
+                           SMC_GET_FIFO_INT(lp),
+                           SMC_GET_RX_CFG(lp));
+                       DBG(SMC_DEBUG_RX, dev, "Rx Stat FIFO Used 0x%02x Data FIFO Used 0x%04x Stat FIFO 0x%08x\n",
+                           (SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16,
+                           SMC_GET_RX_FIFO_INF(lp) & 0xffff,
+                           SMC_GET_RX_STS_FIFO_PEEK(lp));
                        SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
                        SMC_ACK_INT(lp, INT_STS_GPT_INT_);
                }
@@ -1159,7 +1157,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
 
                /* Handle PHY interrupt condition */
                if (status & INT_STS_PHY_INT_) {
-                       DBG(SMC_DEBUG_MISC, "%s: PHY irq\n", dev->name);
+                       DBG(SMC_DEBUG_MISC, dev, "PHY irq\n");
                        smc911x_phy_interrupt(dev);
                        SMC_ACK_INT(lp, INT_STS_PHY_INT_);
                }
@@ -1168,8 +1166,8 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
        /* restore mask state */
        SMC_SET_INT_EN(lp, mask);
 
-       DBG(SMC_DEBUG_MISC, "%s: Interrupt done (%d loops)\n",
-               dev->name, 8-timeout);
+       DBG(SMC_DEBUG_MISC, dev, "Interrupt done (%d loops)\n",
+           8-timeout);
 
        spin_unlock_irqrestore(&lp->lock, flags);
 
@@ -1185,9 +1183,9 @@ smc911x_tx_dma_irq(int dma, void *data)
        struct sk_buff *skb = lp->current_tx_skb;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
-       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq handler\n", dev->name);
+       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "TX DMA irq handler\n");
        /* Clear the DMA interrupt sources */
        SMC_DMA_ACK_IRQ(dev, dma);
        BUG_ON(skb == NULL);
@@ -1198,8 +1196,8 @@ smc911x_tx_dma_irq(int dma, void *data)
        if (lp->pending_tx_skb != NULL)
                smc911x_hardware_send_pkt(dev);
        else {
-               DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
-                       "%s: No pending Tx packets. DMA disabled\n", dev->name);
+               DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev,
+                   "No pending Tx packets. DMA disabled\n");
                spin_lock_irqsave(&lp->lock, flags);
                lp->txdma_active = 0;
                if (!lp->tx_throttle) {
@@ -1208,8 +1206,8 @@ smc911x_tx_dma_irq(int dma, void *data)
                spin_unlock_irqrestore(&lp->lock, flags);
        }
 
-       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
-               "%s: TX DMA irq completed\n", dev->name);
+       DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev,
+           "TX DMA irq completed\n");
 }
 static void
 smc911x_rx_dma_irq(int dma, void *data)
@@ -1221,8 +1219,8 @@ smc911x_rx_dma_irq(int dma, void *data)
        unsigned long flags;
        unsigned int pkts;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
-       DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq handler\n", dev->name);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
+       DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, "RX DMA irq handler\n");
        /* Clear the DMA interrupt sources */
        SMC_DMA_ACK_IRQ(dev, dma);
        dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE);
@@ -1242,9 +1240,9 @@ smc911x_rx_dma_irq(int dma, void *data)
                lp->rxdma_active = 0;
        }
        spin_unlock_irqrestore(&lp->lock, flags);
-       DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
-               "%s: RX DMA irq completed. DMA RX FIFO PKTS %d\n",
-               dev->name, pkts);
+       DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev,
+           "RX DMA irq completed. DMA RX FIFO PKTS %d\n",
+           pkts);
 }
 #endif  /* SMC_USE_DMA */
 
@@ -1268,14 +1266,14 @@ static void smc911x_timeout(struct net_device *dev)
        int status, mask;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        spin_lock_irqsave(&lp->lock, flags);
        status = SMC_GET_INT(lp);
        mask = SMC_GET_INT_EN(lp);
        spin_unlock_irqrestore(&lp->lock, flags);
-       DBG(SMC_DEBUG_MISC, "%s: INT 0x%02x MASK 0x%02x\n",
-               dev->name, status, mask);
+       DBG(SMC_DEBUG_MISC, dev, "INT 0x%02x MASK 0x%02x\n",
+           status, mask);
 
        /* Dump the current TX FIFO contents and restart */
        mask = SMC_GET_TX_CFG(lp);
@@ -1306,7 +1304,7 @@ static void smc911x_set_multicast_list(struct net_device *dev)
        unsigned int mcr, update_multicast = 0;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        spin_lock_irqsave(&lp->lock, flags);
        SMC_GET_MAC_CR(lp, mcr);
@@ -1314,7 +1312,7 @@ static void smc911x_set_multicast_list(struct net_device *dev)
 
        if (dev->flags & IFF_PROMISC) {
 
-               DBG(SMC_DEBUG_MISC, "%s: RCR_PRMS\n", dev->name);
+               DBG(SMC_DEBUG_MISC, dev, "RCR_PRMS\n");
                mcr |= MAC_CR_PRMS_;
        }
        /*
@@ -1323,7 +1321,7 @@ static void smc911x_set_multicast_list(struct net_device *dev)
         * checked before the table is
         */
        else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) {
-               DBG(SMC_DEBUG_MISC, "%s: RCR_ALMUL\n", dev->name);
+               DBG(SMC_DEBUG_MISC, dev, "RCR_ALMUL\n");
                mcr |= MAC_CR_MCPAS_;
        }
 
@@ -1363,8 +1361,7 @@ static void smc911x_set_multicast_list(struct net_device *dev)
                /* now, the table can be loaded into the chipset */
                update_multicast = 1;
        } else   {
-               DBG(SMC_DEBUG_MISC, "%s: ~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n",
-                       dev->name);
+               DBG(SMC_DEBUG_MISC, dev, "~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n");
                mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
 
                /*
@@ -1378,9 +1375,9 @@ static void smc911x_set_multicast_list(struct net_device *dev)
        spin_lock_irqsave(&lp->lock, flags);
        SMC_SET_MAC_CR(lp, mcr);
        if (update_multicast) {
-               DBG(SMC_DEBUG_MISC,
-                       "%s: update mcast hash table 0x%08x 0x%08x\n",
-                       dev->name, multicast_table[0], multicast_table[1]);
+               DBG(SMC_DEBUG_MISC, dev,
+                   "update mcast hash table 0x%08x 0x%08x\n",
+                   multicast_table[0], multicast_table[1]);
                SMC_SET_HASHL(lp, multicast_table[0]);
                SMC_SET_HASHH(lp, multicast_table[1]);
        }
@@ -1398,7 +1395,7 @@ smc911x_open(struct net_device *dev)
 {
        struct smc911x_local *lp = netdev_priv(dev);
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        /* reset the hardware */
        smc911x_reset(dev);
@@ -1425,7 +1422,7 @@ static int smc911x_close(struct net_device *dev)
 {
        struct smc911x_local *lp = netdev_priv(dev);
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        netif_stop_queue(dev);
        netif_carrier_off(dev);
@@ -1459,7 +1456,7 @@ smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
        int ret, status;
        unsigned long flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
        cmd->maxtxpkt = 1;
        cmd->maxrxpkt = 1;
 
@@ -1597,16 +1594,16 @@ static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev)
        e2p_cmd = SMC_GET_E2P_CMD(lp);
        for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) {
                if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) {
-                       PRINTK("%s: %s timeout waiting for EEPROM to respond\n",
-                               dev->name, __func__);
+                       PRINTK(dev, "%s timeout waiting for EEPROM to respond\n",
+                              __func__);
                        return -EFAULT;
                }
                mdelay(1);
                e2p_cmd = SMC_GET_E2P_CMD(lp);
        }
        if (timeout == 0) {
-               PRINTK("%s: %s timeout waiting for EEPROM CMD not busy\n",
-                       dev->name, __func__);
+               PRINTK(dev, "%s timeout waiting for EEPROM CMD not busy\n",
+                      __func__);
                return -ETIMEDOUT;
        }
        return 0;
@@ -1719,7 +1716,7 @@ static int smc911x_findirq(struct net_device *dev)
        int timeout = 20;
        unsigned long cookie;
 
-       DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        cookie = probe_irq_on();
 
@@ -1799,13 +1796,14 @@ static int smc911x_probe(struct net_device *dev)
        const char *version_string;
        unsigned long irq_flags;
 
-       DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+       DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
 
        /* First, see if the endian word is recognized */
        val = SMC_GET_BYTE_TEST(lp);
-       DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val);
+       DBG(SMC_DEBUG_MISC, dev, "%s: endian probe returned 0x%04x\n",
+           CARDNAME, val);
        if (val != 0x87654321) {
-               printk(KERN_ERR "Invalid chip endian 0x%08x\n",val);
+               netdev_err(dev, "Invalid chip endian 0x%08x\n", val);
                retval = -ENODEV;
                goto err_out;
        }
@@ -1816,26 +1814,29 @@ static int smc911x_probe(struct net_device *dev)
         * as future revisions could be added.
         */
        chip_id = SMC_GET_PN(lp);
-       DBG(SMC_DEBUG_MISC, "%s: id probe returned 0x%04x\n", CARDNAME, chip_id);
+       DBG(SMC_DEBUG_MISC, dev, "%s: id probe returned 0x%04x\n",
+           CARDNAME, chip_id);
        for(i=0;chip_ids[i].id != 0; i++) {
                if (chip_ids[i].id == chip_id) break;
        }
        if (!chip_ids[i].id) {
-               printk(KERN_ERR "Unknown chip ID %04x\n", chip_id);
+               netdev_err(dev, "Unknown chip ID %04x\n", chip_id);
                retval = -ENODEV;
                goto err_out;
        }
        version_string = chip_ids[i].name;
 
        revision = SMC_GET_REV(lp);
-       DBG(SMC_DEBUG_MISC, "%s: revision = 0x%04x\n", CARDNAME, revision);
+       DBG(SMC_DEBUG_MISC, dev, "%s: revision = 0x%04x\n", CARDNAME, revision);
 
        /* At this point I'll assume that the chip is an SMC911x. */
-       DBG(SMC_DEBUG_MISC, "%s: Found a %s\n", CARDNAME, chip_ids[i].name);
+       DBG(SMC_DEBUG_MISC, dev, "%s: Found a %s\n",
+           CARDNAME, chip_ids[i].name);
 
        /* Validate the TX FIFO size requested */
        if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) {
-               printk(KERN_ERR "Invalid TX FIFO size requested %d\n", tx_fifo_kb);
+               netdev_err(dev, "Invalid TX FIFO size requested %d\n",
+                          tx_fifo_kb);
                retval = -EINVAL;
                goto err_out;
        }
@@ -1887,14 +1888,13 @@ static int smc911x_probe(struct net_device *dev)
                case 14:/* 1920 Rx Data Fifo Size */
                        lp->afc_cfg=0x0006032F;break;
                 default:
-                        PRINTK("%s: ERROR -- no AFC_CFG setting found",
-                               dev->name);
+                        PRINTK(dev, "ERROR -- no AFC_CFG setting found");
                         break;
        }
 
-       DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX,
-               "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME,
-               lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg);
+       DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX, dev,
+           "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME,
+           lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg);
 
        spin_lock_init(&lp->lock);
 
@@ -1924,8 +1924,7 @@ static int smc911x_probe(struct net_device *dev)
                }
        }
        if (dev->irq == 0) {
-               printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
-                       dev->name);
+               netdev_warn(dev, "Couldn't autodetect your IRQ. Use irq=xx.\n");
                retval = -ENODEV;
                goto err_out;
        }
@@ -1980,33 +1979,32 @@ static int smc911x_probe(struct net_device *dev)
        retval = register_netdev(dev);
        if (retval == 0) {
                /* now, print out the card info, in a short format.. */
-               printk("%s: %s (rev %d) at %#lx IRQ %d",
-                       dev->name, version_string, lp->revision,
-                       dev->base_addr, dev->irq);
+               netdev_info(dev, "%s (rev %d) at %#lx IRQ %d",
+                           version_string, lp->revision,
+                           dev->base_addr, dev->irq);
 
 #ifdef SMC_USE_DMA
                if (lp->rxdma != -1)
-                       printk(" RXDMA %d ", lp->rxdma);
+                       pr_cont(" RXDMA %d", lp->rxdma);
 
                if (lp->txdma != -1)
-                       printk("TXDMA %d", lp->txdma);
+                       pr_cont(" TXDMA %d", lp->txdma);
 #endif
-               printk("\n");
+               pr_cont("\n");
                if (!is_valid_ether_addr(dev->dev_addr)) {
-                       printk("%s: Invalid ethernet MAC address. Please "
-                                       "set using ifconfig\n", dev->name);
+                       netdev_warn(dev, "Invalid ethernet MAC address. Please set using ifconfig\n");
                } else {
                        /* Print the Ethernet address */
-                       printk("%s: Ethernet addr: %pM\n",
-                               dev->name, dev->dev_addr);
+                       netdev_info(dev, "Ethernet addr: %pM\n",
+                                   dev->dev_addr);
                }
 
                if (lp->phy_type == 0) {
-                       PRINTK("%s: No PHY found\n", dev->name);
+                       PRINTK(dev, "No PHY found\n");
                } else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) {
-                       PRINTK("%s: LAN911x Internal PHY\n", dev->name);
+                       PRINTK(dev, "LAN911x Internal PHY\n");
                } else {
-                       PRINTK("%s: External PHY 0x%08x\n", dev->name, lp->phy_type);
+                       PRINTK(dev, "External PHY 0x%08x\n", lp->phy_type);
                }
        }
 
@@ -2025,7 +2023,7 @@ err_out:
 }
 
 /*
- * smc911x_init(void)
+ * smc911x_drv_probe(void)
  *
  *       Output:
  *      0 --> there is a device
@@ -2039,6 +2037,7 @@ static int smc911x_drv_probe(struct platform_device *pdev)
        void __iomem *addr;
        int ret;
 
+       /* ndev is not valid yet, so avoid passing it in. */
        DBG(SMC_DEBUG_FUNC, "--> %s\n",  __func__);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -2093,7 +2092,7 @@ release_both:
 release_1:
                release_mem_region(res->start, SMC911X_IO_EXTENT);
 out:
-               printk("%s: not found (%d).\n", CARDNAME, ret);
+               pr_info("%s: not found (%d).\n", CARDNAME, ret);
        }
 #ifdef SMC_USE_DMA
        else {
@@ -2111,7 +2110,7 @@ static int smc911x_drv_remove(struct platform_device *pdev)
        struct smc911x_local *lp = netdev_priv(ndev);
        struct resource *res;
 
-       DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+       DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__);
 
        unregister_netdev(ndev);
 
@@ -2140,7 +2139,7 @@ static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state)
        struct net_device *ndev = platform_get_drvdata(dev);
        struct smc911x_local *lp = netdev_priv(ndev);
 
-       DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+       DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__);
        if (ndev) {
                if (netif_running(ndev)) {
                        netif_device_detach(ndev);
@@ -2158,7 +2157,7 @@ static int smc911x_drv_resume(struct platform_device *dev)
 {
        struct net_device *ndev = platform_get_drvdata(dev);
 
-       DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+       DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__);
        if (ndev) {
                struct smc911x_local *lp = netdev_priv(ndev);
 
index d51261b..9965da3 100644 (file)
@@ -227,7 +227,7 @@ static inline void SMC_outsl(struct smc911x_local *lp, int reg,
 #define SMC_DMA_ACK_IRQ(dev, dma)                                      \
 {                                                                      \
        if (DCSR(dma) & DCSR_BUSERR) {                                  \
-               printk("%s: DMA %d bus error!\n", dev->name, dma);      \
+               netdev_err(dev, "DMA %d bus error!\n", dma);            \
        }                                                               \
        DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;            \
 }
index e85c2e7..4092fed 100644 (file)
@@ -55,7 +55,7 @@
  ----------------------------------------------------------------------------*/
 
 static const char version[] =
-       "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n";
+       "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -612,7 +612,7 @@ static void smc_hardware_send_packet( struct net_device * dev )
        packet_no = inb( ioaddr + PNR_ARR + 1 );
        if ( packet_no & 0x80 ) {
                /* or isn't there?  BAD CHIP! */
-               printk(KERN_DEBUG CARDNAME": Memory allocation failed.\n");
+               netdev_dbg(dev, CARDNAME": Memory allocation failed.\n");
                dev_kfree_skb_any(skb);
                lp->saved_skb = NULL;
                netif_wake_queue(dev);
@@ -625,7 +625,7 @@ static void smc_hardware_send_packet( struct net_device * dev )
        /* point to the beginning of the packet */
        outw( PTR_AUTOINC , ioaddr + POINTER );
 
-       PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length ));
+       PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length));
 #if SMC_DEBUG > 2
        print_packet( buf, length );
 #endif
@@ -865,7 +865,6 @@ static const struct net_device_ops smc_netdev_ops = {
 static int __init smc_probe(struct net_device *dev, int ioaddr)
 {
        int i, memory, retval;
-       static unsigned version_printed;
        unsigned int bank;
 
        const char *version_string;
@@ -937,8 +936,7 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
           It might be prudent to check a listing of MAC addresses
           against the hardware address, or do some other tests. */
 
-       if (version_printed++ == 0)
-               printk("%s", version);
+       pr_info_once("%s\n", version);
 
        /* fill in some of the fields */
        dev->base_addr = ioaddr;
@@ -1027,21 +1025,21 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
 
        /* now, print out the card info, in a short format.. */
 
-       printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name,
-               version_string, revision_register & 0xF, ioaddr, dev->irq,
-               if_string, memory );
+       netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ",
+                   version_string, revision_register & 0xF, ioaddr, dev->irq,
+                   if_string, memory);
        /*
         . Print the Ethernet address
        */
-       printk("ADDR: %pM\n", dev->dev_addr);
+       netdev_info(dev, "ADDR: %pM\n", dev->dev_addr);
 
        /* Grab the IRQ */
-       retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
-       if (retval) {
-               printk("%s: unable to get IRQ %d (irqval=%d).\n", DRV_NAME,
-                       dev->irq, retval);
-               goto err_out;
-       }
+       retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
+       if (retval) {
+               netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n",
+                           DRV_NAME, dev->irq, retval);
+               goto err_out;
+       }
 
        dev->netdev_ops                 = &smc_netdev_ops;
        dev->watchdog_timeo             = HZ/20;
@@ -1061,30 +1059,32 @@ static void print_packet( byte * buf, int length )
        int remainder;
        int lines;
 
-       printk("Packet of length %d\n", length);
+       pr_dbg("Packet of length %d\n", length);
        lines = length / 16;
        remainder = length % 16;
 
        for ( i = 0; i < lines ; i ++ ) {
                int cur;
 
+               printk(KERN_DEBUG);
                for ( cur = 0; cur < 8; cur ++ ) {
                        byte a, b;
 
                        a = *(buf ++ );
                        b = *(buf ++ );
-                       printk("%02x%02x ", a, b );
+                       pr_cont("%02x%02x ", a, b);
                }
-               printk("\n");
+               pr_cont("\n");
        }
+       printk(KERN_DEBUG);
        for ( i = 0; i < remainder/2 ; i++ ) {
                byte a, b;
 
                a = *(buf ++ );
                b = *(buf ++ );
-               printk("%02x%02x ", a, b );
+               pr_cont("%02x%02x ", a, b);
        }
-       printk("\n");
+       pr_cont("\n");
 #endif
 }
 #endif
@@ -1151,9 +1151,8 @@ static void smc_timeout(struct net_device *dev)
 {
        /* If we get here, some higher level has decided we are broken.
           There should really be a "kick me" function call instead. */
-       printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n",
-               tx_done(dev) ? "IRQ conflict" :
-               "network cable problem");
+       netdev_warn(dev, CARDNAME": transmit timed out, %s?\n",
+                   tx_done(dev) ? "IRQ conflict" : "network cable problem");
        /* "kick" the adaptor */
        smc_reset( dev->base_addr );
        smc_enable( dev->base_addr );
@@ -1323,8 +1322,7 @@ static void smc_tx( struct net_device * dev )
        dev->stats.tx_errors++;
        if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++;
        if ( tx_status & TS_LATCOL  ) {
-               printk(KERN_DEBUG CARDNAME
-                       ": Late collision occurred on last xmit.\n");
+               netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n");
                dev->stats.tx_window_errors++;
        }
 #if 0
@@ -1332,7 +1330,7 @@ static void smc_tx( struct net_device * dev )
 #endif
 
        if ( tx_status & TS_SUCCESS ) {
-               printk(CARDNAME": Successful packet caused interrupt\n");
+               netdev_info(dev, CARDNAME": Successful packet caused interrupt\n");
        }
        /* re-enable transmit */
        SMC_SELECT_BANK( 0 );
@@ -1571,9 +1569,7 @@ int __init init_module(void)
 
        /* copy the parameters from insmod into the device structure */
        devSMC9194 = smc_init(-1);
-       if (IS_ERR(devSMC9194))
-               return PTR_ERR(devSMC9194);
-       return 0;
+       return PTR_ERR_OR_ZERO(devSMC9194);
 }
 
 void __exit cleanup_module(void)
index 656d2e2..8ef70d9 100644 (file)
@@ -740,7 +740,7 @@ static int smc91c92_resume(struct pcmcia_device *link)
             (smc->cardid == PRODID_PSION_NET100))) {
                i = osi_load_firmware(link);
                if (i) {
-                       pr_err("smc91c92_cs: Failed to load firmware\n");
+                       netdev_err(dev, "Failed to load firmware\n");
                        return i;
                }
        }
@@ -793,7 +793,7 @@ static int check_sig(struct pcmcia_device *link)
     }
 
     if (width) {
-           pr_info("using 8-bit IO window\n");
+           netdev_info(dev, "using 8-bit IO window\n");
 
            smc91c92_suspend(link);
            pcmcia_fixup_iowidth(link);
@@ -1036,7 +1036,7 @@ static void smc_dump(struct net_device *dev)
     save = inw(ioaddr + BANK_SELECT);
     for (w = 0; w < 4; w++) {
        SMC_SELECT_BANK(w);
-       netdev_printk(KERN_DEBUG, dev, "bank %d: ", w);
+       netdev_dbg(dev, "bank %d: ", w);
        for (i = 0; i < 14; i += 2)
            pr_cont(" %04x", inw(ioaddr + i));
        pr_cont("\n");
@@ -1213,8 +1213,7 @@ static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
     if (smc->saved_skb) {
        /* THIS SHOULD NEVER HAPPEN. */
        dev->stats.tx_aborted_errors++;
-       netdev_printk(KERN_DEBUG, dev,
-                     "Internal error -- sent packet while busy\n");
+       netdev_dbg(dev, "Internal error -- sent packet while busy\n");
        return NETDEV_TX_BUSY;
     }
     smc->saved_skb = skb;
@@ -1254,7 +1253,7 @@ static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
     }
 
     /* Otherwise defer until the Tx-space-allocated interrupt. */
-    pr_debug("%s: memory allocation deferred.\n", dev->name);
+    netdev_dbg(dev, "memory allocation deferred.\n");
     outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
     spin_unlock_irqrestore(&smc->lock, flags);
 
@@ -1317,8 +1316,8 @@ static void smc_eph_irq(struct net_device *dev)
 
     SMC_SELECT_BANK(0);
     ephs = inw(ioaddr + EPH);
-    pr_debug("%s: Ethernet protocol handler interrupt, status"
-         " %4.4x.\n", dev->name, ephs);
+    netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n",
+              ephs);
     /* Could be a counter roll-over warning: update stats. */
     card_stats = inw(ioaddr + COUNTER);
     /* single collisions */
@@ -1357,8 +1356,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
 
     ioaddr = dev->base_addr;
 
-    pr_debug("%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
-         irq, ioaddr);
+    netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n",
+              irq, ioaddr);
 
     spin_lock(&smc->lock);
     smc->watchdog = 0;
@@ -1366,8 +1365,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
     if ((saved_bank & 0xff00) != 0x3300) {
        /* The device does not exist -- the card could be off-line, or
           maybe it has been ejected. */
-       pr_debug("%s: SMC91c92 interrupt %d for non-existent"
-             "/ejected device.\n", dev->name, irq);
+       netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n",
+                  irq);
        handled = 0;
        goto irq_done;
     }
@@ -1380,8 +1379,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
 
     do { /* read the status flag, and mask it */
        status = inw(ioaddr + INTERRUPT) & 0xff;
-       pr_debug("%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
-             status, mask);
+       netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n",
+                  status, mask);
        if ((status & mask) == 0) {
            if (bogus_cnt == INTR_WORK)
                handled = 0;
@@ -1425,15 +1424,15 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
            smc_eph_irq(dev);
     } while (--bogus_cnt);
 
-    pr_debug("  Restoring saved registers mask %2.2x bank %4.4x"
-         " pointer %4.4x.\n", mask, saved_bank, saved_pointer);
+    netdev_dbg(dev, "  Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n",
+              mask, saved_bank, saved_pointer);
 
     /* restore state register */
     outw((mask<<8), ioaddr + INTERRUPT);
     outw(saved_pointer, ioaddr + POINTER);
     SMC_SELECT_BANK(saved_bank);
 
-    pr_debug("%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+    netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq);
 
 irq_done:
 
@@ -1491,10 +1490,10 @@ static void smc_rx(struct net_device *dev)
     rx_status = inw(ioaddr + DATA_1);
     packet_length = inw(ioaddr + DATA_1) & 0x07ff;
 
-    pr_debug("%s: Receive status %4.4x length %d.\n",
-         dev->name, rx_status, packet_length);
+    netdev_dbg(dev, "Receive status %4.4x length %d.\n",
+              rx_status, packet_length);
 
-    if (!(rx_status & RS_ERRORS)) {            
+    if (!(rx_status & RS_ERRORS)) {
        /* do stuff to make a new packet */
        struct sk_buff *skb;
        
@@ -1502,7 +1501,7 @@ static void smc_rx(struct net_device *dev)
        skb = netdev_alloc_skb(dev, packet_length+2);
        
        if (skb == NULL) {
-           pr_debug("%s: Low memory, packet dropped.\n", dev->name);
+           netdev_dbg(dev, "Low memory, packet dropped.\n");
            dev->stats.rx_dropped++;
            outw(MC_RELEASE, ioaddr + MMU_CMD);
            return;
@@ -1643,7 +1642,7 @@ static void smc_reset(struct net_device *dev)
     struct smc_private *smc = netdev_priv(dev);
     int i;
 
-    pr_debug("%s: smc91c92 reset called.\n", dev->name);
+    netdev_dbg(dev, "smc91c92 reset called.\n");
 
     /* The first interaction must be a write to bring the chip out
        of sleep mode. */
index 73be7f3..0c9b5d9 100644 (file)
@@ -58,7 +58,7 @@
  *   22/09/04  Nicolas Pitre      big update (see commit log for details)
  */
 static const char version[] =
-       "smc91x.c: v1.1, sep 22 2004 by Nicolas Pitre <nico@fluxnic.net>\n";
+       "smc91x.c: v1.1, sep 22 2004 by Nicolas Pitre <nico@fluxnic.net>";
 
 /* Debugging level */
 #ifndef SMC_DEBUG
@@ -149,16 +149,16 @@ MODULE_ALIAS("platform:smc91x");
 #define MII_DELAY              1
 
 #if SMC_DEBUG > 0
-#define DBG(n, args...)                                        \
+#define DBG(n, dev, args...)                           \
        do {                                            \
                if (SMC_DEBUG >= (n))                   \
-                       printk(args);   \
+                       netdev_dbg(dev, args);          \
        } while (0)
 
-#define PRINTK(args...)   printk(args)
+#define PRINTK(dev, args...)   netdev_info(dev, args)
 #else
-#define DBG(n, args...)   do { } while(0)
-#define PRINTK(args...)   printk(KERN_DEBUG args)
+#define DBG(n, dev, args...)   do { } while (0)
+#define PRINTK(dev, args...)   netdev_dbg(dev, args)
 #endif
 
 #if SMC_DEBUG > 3
@@ -173,24 +173,26 @@ static void PRINT_PKT(u_char *buf, int length)
 
        for (i = 0; i < lines ; i ++) {
                int cur;
+               printk(KERN_DEBUG);
                for (cur = 0; cur < 8; cur++) {
                        u_char a, b;
                        a = *buf++;
                        b = *buf++;
-                       printk("%02x%02x ", a, b);
+                       pr_cont("%02x%02x ", a, b);
                }
-               printk("\n");
+               pr_cont("\n");
        }
+       printk(KERN_DEBUG);
        for (i = 0; i < remainder/2 ; i++) {
                u_char a, b;
                a = *buf++;
                b = *buf++;
-               printk("%02x%02x ", a, b);
+               pr_cont("%02x%02x ", a, b);
        }
-       printk("\n");
+       pr_cont("\n");
 }
 #else
-#define PRINT_PKT(x...)  do { } while(0)
+#define PRINT_PKT(x...)  do { } while (0)
 #endif
 
 
@@ -226,8 +228,8 @@ static void PRINT_PKT(u_char *buf, int length)
                unsigned long timeout = jiffies + 2;                    \
                while (SMC_GET_MMU_CMD(lp) & MC_BUSY) {         \
                        if (time_after(jiffies, timeout)) {             \
-                               printk("%s: timeout %s line %d\n",      \
-                                       dev->name, __FILE__, __LINE__); \
+                               netdev_dbg(dev, "timeout %s line %d\n", \
+                                          __FILE__, __LINE__);         \
                                break;                                  \
                        }                                               \
                        cpu_relax();                                    \
@@ -246,7 +248,7 @@ static void smc_reset(struct net_device *dev)
        unsigned int ctl, cfg;
        struct sk_buff *pending_skb;
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        /* Disable all interrupts, block TX tasklet */
        spin_lock_irq(&lp->lock);
@@ -339,7 +341,7 @@ static void smc_enable(struct net_device *dev)
        void __iomem *ioaddr = lp->base;
        int mask;
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        /* see the header file for options in TCR/RCR DEFAULT */
        SMC_SELECT_BANK(lp, 0);
@@ -373,7 +375,7 @@ static void smc_shutdown(struct net_device *dev)
        void __iomem *ioaddr = lp->base;
        struct sk_buff *pending_skb;
 
-       DBG(2, "%s: %s\n", CARDNAME, __func__);
+       DBG(2, dev, "%s: %s\n", CARDNAME, __func__);
 
        /* no more interrupts for me */
        spin_lock_irq(&lp->lock);
@@ -406,11 +408,11 @@ static inline void  smc_rcv(struct net_device *dev)
        void __iomem *ioaddr = lp->base;
        unsigned int packet_number, status, packet_len;
 
-       DBG(3, "%s: %s\n", dev->name, __func__);
+       DBG(3, dev, "%s\n", __func__);
 
        packet_number = SMC_GET_RXFIFO(lp);
        if (unlikely(packet_number & RXFIFO_REMPTY)) {
-               PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name);
+               PRINTK(dev, "smc_rcv with nothing on FIFO.\n");
                return;
        }
 
@@ -420,9 +422,8 @@ static inline void  smc_rcv(struct net_device *dev)
        /* First two words are status and packet length */
        SMC_GET_PKT_HDR(lp, status, packet_len);
        packet_len &= 0x07ff;  /* mask off top bits */
-       DBG(2, "%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",
-               dev->name, packet_number, status,
-               packet_len, packet_len);
+       DBG(2, dev, "RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",
+           packet_number, status, packet_len, packet_len);
 
        back:
        if (unlikely(packet_len < 6 || status & RS_ERRORS)) {
@@ -433,8 +434,8 @@ static inline void  smc_rcv(struct net_device *dev)
                }
                if (packet_len < 6) {
                        /* bloody hardware */
-                       printk(KERN_ERR "%s: fubar (rxlen %u status %x\n",
-                                       dev->name, packet_len, status);
+                       netdev_err(dev, "fubar (rxlen %u status %x\n",
+                                  packet_len, status);
                        status |= RS_TOOSHORT;
                }
                SMC_WAIT_MMU_BUSY(lp);
@@ -551,7 +552,7 @@ static void smc_hardware_send_pkt(unsigned long data)
        unsigned char *buf;
        unsigned long flags;
 
-       DBG(3, "%s: %s\n", dev->name, __func__);
+       DBG(3, dev, "%s\n", __func__);
 
        if (!smc_special_trylock(&lp->lock, flags)) {
                netif_stop_queue(dev);
@@ -568,7 +569,7 @@ static void smc_hardware_send_pkt(unsigned long data)
 
        packet_no = SMC_GET_AR(lp);
        if (unlikely(packet_no & AR_FAILED)) {
-               printk("%s: Memory allocation failed.\n", dev->name);
+               netdev_err(dev, "Memory allocation failed.\n");
                dev->stats.tx_errors++;
                dev->stats.tx_fifo_errors++;
                smc_special_unlock(&lp->lock, flags);
@@ -581,8 +582,8 @@ static void smc_hardware_send_pkt(unsigned long data)
 
        buf = skb->data;
        len = skb->len;
-       DBG(2, "%s: TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n",
-               dev->name, packet_no, len, len, buf);
+       DBG(2, dev, "TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n",
+           packet_no, len, len, buf);
        PRINT_PKT(buf, len);
 
        /*
@@ -637,7 +638,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int numPages, poll_count, status;
        unsigned long flags;
 
-       DBG(3, "%s: %s\n", dev->name, __func__);
+       DBG(3, dev, "%s\n", __func__);
 
        BUG_ON(lp->pending_tx_skb != NULL);
 
@@ -654,7 +655,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
         */
        numPages = ((skb->len & ~1) + (6 - 1)) >> 8;
        if (unlikely(numPages > 7)) {
-               printk("%s: Far too big packet error.\n", dev->name);
+               netdev_warn(dev, "Far too big packet error.\n");
                dev->stats.tx_errors++;
                dev->stats.tx_dropped++;
                dev_kfree_skb(skb);
@@ -685,7 +686,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!poll_count) {
                /* oh well, wait until the chip finds memory later */
                netif_stop_queue(dev);
-               DBG(2, "%s: TX memory allocation deferred.\n", dev->name);
+               DBG(2, dev, "TX memory allocation deferred.\n");
                SMC_ENABLE_INT(lp, IM_ALLOC_INT);
        } else {
                /*
@@ -709,12 +710,12 @@ static void smc_tx(struct net_device *dev)
        void __iomem *ioaddr = lp->base;
        unsigned int saved_packet, packet_no, tx_status, pkt_len;
 
-       DBG(3, "%s: %s\n", dev->name, __func__);
+       DBG(3, dev, "%s\n", __func__);
 
        /* If the TX FIFO is empty then nothing to do */
        packet_no = SMC_GET_TXFIFO(lp);
        if (unlikely(packet_no & TXFIFO_TEMPTY)) {
-               PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name);
+               PRINTK(dev, "smc_tx with nothing on FIFO.\n");
                return;
        }
 
@@ -725,8 +726,8 @@ static void smc_tx(struct net_device *dev)
        /* read the first word (status word) from this packet */
        SMC_SET_PTR(lp, PTR_AUTOINC | PTR_READ);
        SMC_GET_PKT_HDR(lp, tx_status, pkt_len);
-       DBG(2, "%s: TX STATUS 0x%04x PNR 0x%02x\n",
-               dev->name, tx_status, packet_no);
+       DBG(2, dev, "TX STATUS 0x%04x PNR 0x%02x\n",
+           tx_status, packet_no);
 
        if (!(tx_status & ES_TX_SUC))
                dev->stats.tx_errors++;
@@ -735,14 +736,12 @@ static void smc_tx(struct net_device *dev)
                dev->stats.tx_carrier_errors++;
 
        if (tx_status & (ES_LATCOL | ES_16COL)) {
-               PRINTK("%s: %s occurred on last xmit\n", dev->name,
+               PRINTK(dev, "%s occurred on last xmit\n",
                       (tx_status & ES_LATCOL) ?
                        "late collision" : "too many collisions");
                dev->stats.tx_window_errors++;
                if (!(dev->stats.tx_window_errors & 63) && net_ratelimit()) {
-                       printk(KERN_INFO "%s: unexpectedly large number of "
-                              "bad collisions. Please check duplex "
-                              "setting.\n", dev->name);
+                       netdev_info(dev, "unexpectedly large number of bad collisions. Please check duplex setting.\n");
                }
        }
 
@@ -830,8 +829,8 @@ static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg)
        /* Return to idle state */
        SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO));
 
-       DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
-               __func__, phyaddr, phyreg, phydata);
+       DBG(3, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+           __func__, phyaddr, phyreg, phydata);
 
        SMC_SELECT_BANK(lp, 2);
        return phydata;
@@ -857,8 +856,8 @@ static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg,
        /* Return to idle state */
        SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO));
 
-       DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
-               __func__, phyaddr, phyreg, phydata);
+       DBG(3, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+           __func__, phyaddr, phyreg, phydata);
 
        SMC_SELECT_BANK(lp, 2);
 }
@@ -871,7 +870,7 @@ static void smc_phy_detect(struct net_device *dev)
        struct smc_local *lp = netdev_priv(dev);
        int phyaddr;
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        lp->phy_type = 0;
 
@@ -886,8 +885,8 @@ static void smc_phy_detect(struct net_device *dev)
                id1 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID1);
                id2 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID2);
 
-               DBG(3, "%s: phy_id1=0x%x, phy_id2=0x%x\n",
-                       dev->name, id1, id2);
+               DBG(3, dev, "phy_id1=0x%x, phy_id2=0x%x\n",
+                   id1, id2);
 
                /* Make sure it is a valid identifier */
                if (id1 != 0x0000 && id1 != 0xffff && id1 != 0x8000 &&
@@ -910,7 +909,7 @@ static int smc_phy_fixed(struct net_device *dev)
        int phyaddr = lp->mii.phy_id;
        int bmcr, cfg1;
 
-       DBG(3, "%s: %s\n", dev->name, __func__);
+       DBG(3, dev, "%s\n", __func__);
 
        /* Enter Link Disable state */
        cfg1 = smc_phy_read(dev, phyaddr, PHY_CFG1_REG);
@@ -1044,7 +1043,7 @@ static void smc_phy_configure(struct work_struct *work)
        int my_ad_caps; /* My Advertised capabilities */
        int status;
 
-       DBG(3, "%s:smc_program_phy()\n", dev->name);
+       DBG(3, dev, "smc_program_phy()\n");
 
        spin_lock_irq(&lp->lock);
 
@@ -1055,7 +1054,7 @@ static void smc_phy_configure(struct work_struct *work)
                goto smc_phy_configure_exit;
 
        if (smc_phy_reset(dev, phyaddr)) {
-               printk("%s: PHY reset timed out\n", dev->name);
+               netdev_info(dev, "PHY reset timed out\n");
                goto smc_phy_configure_exit;
        }
 
@@ -1082,7 +1081,7 @@ static void smc_phy_configure(struct work_struct *work)
        my_phy_caps = smc_phy_read(dev, phyaddr, MII_BMSR);
 
        if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
-               printk(KERN_INFO "Auto negotiation NOT supported\n");
+               netdev_info(dev, "Auto negotiation NOT supported\n");
                smc_phy_fixed(dev);
                goto smc_phy_configure_exit;
        }
@@ -1118,8 +1117,8 @@ static void smc_phy_configure(struct work_struct *work)
         */
        status = smc_phy_read(dev, phyaddr, MII_ADVERTISE);
 
-       DBG(2, "%s: phy caps=%x\n", dev->name, my_phy_caps);
-       DBG(2, "%s: phy advertised caps=%x\n", dev->name, my_ad_caps);
+       DBG(2, dev, "phy caps=%x\n", my_phy_caps);
+       DBG(2, dev, "phy advertised caps=%x\n", my_ad_caps);
 
        /* Restart auto-negotiation process in order to advertise my caps */
        smc_phy_write(dev, phyaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
@@ -1143,7 +1142,7 @@ static void smc_phy_interrupt(struct net_device *dev)
        int phyaddr = lp->mii.phy_id;
        int phy18;
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        if (lp->phy_type == 0)
                return;
@@ -1179,8 +1178,8 @@ static void smc_10bt_check_media(struct net_device *dev, int init)
                        netif_carrier_on(dev);
                }
                if (netif_msg_link(lp))
-                       printk(KERN_INFO "%s: link %s\n", dev->name,
-                              new_carrier ? "up" : "down");
+                       netdev_info(dev, "link %s\n",
+                                   new_carrier ? "up" : "down");
        }
 }
 
@@ -1211,7 +1210,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
        int status, mask, timeout, card_stats;
        int saved_pointer;
 
-       DBG(3, "%s: %s\n", dev->name, __func__);
+       DBG(3, dev, "%s\n", __func__);
 
        spin_lock(&lp->lock);
 
@@ -1230,12 +1229,12 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
        do {
                status = SMC_GET_INT(lp);
 
-               DBG(2, "%s: INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
-                       dev->name, status, mask,
-                       ({ int meminfo; SMC_SELECT_BANK(lp, 0);
-                          meminfo = SMC_GET_MIR(lp);
-                          SMC_SELECT_BANK(lp, 2); meminfo; }),
-                       SMC_GET_FIFO(lp));
+               DBG(2, dev, "INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
+                   status, mask,
+                   ({ int meminfo; SMC_SELECT_BANK(lp, 0);
+                      meminfo = SMC_GET_MIR(lp);
+                      SMC_SELECT_BANK(lp, 2); meminfo; }),
+                   SMC_GET_FIFO(lp));
 
                status &= mask;
                if (!status)
@@ -1243,20 +1242,20 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
 
                if (status & IM_TX_INT) {
                        /* do this before RX as it will free memory quickly */
-                       DBG(3, "%s: TX int\n", dev->name);
+                       DBG(3, dev, "TX int\n");
                        smc_tx(dev);
                        SMC_ACK_INT(lp, IM_TX_INT);
                        if (THROTTLE_TX_PKTS)
                                netif_wake_queue(dev);
                } else if (status & IM_RCV_INT) {
-                       DBG(3, "%s: RX irq\n", dev->name);
+                       DBG(3, dev, "RX irq\n");
                        smc_rcv(dev);
                } else if (status & IM_ALLOC_INT) {
-                       DBG(3, "%s: Allocation irq\n", dev->name);
+                       DBG(3, dev, "Allocation irq\n");
                        tasklet_hi_schedule(&lp->tx_task);
                        mask &= ~IM_ALLOC_INT;
                } else if (status & IM_TX_EMPTY_INT) {
-                       DBG(3, "%s: TX empty\n", dev->name);
+                       DBG(3, dev, "TX empty\n");
                        mask &= ~IM_TX_EMPTY_INT;
 
                        /* update stats */
@@ -1271,10 +1270,10 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
                        /* multiple collisions */
                        dev->stats.collisions += card_stats & 0xF;
                } else if (status & IM_RX_OVRN_INT) {
-                       DBG(1, "%s: RX overrun (EPH_ST 0x%04x)\n", dev->name,
-                              ({ int eph_st; SMC_SELECT_BANK(lp, 0);
-                                 eph_st = SMC_GET_EPH_STATUS(lp);
-                                 SMC_SELECT_BANK(lp, 2); eph_st; }));
+                       DBG(1, dev, "RX overrun (EPH_ST 0x%04x)\n",
+                           ({ int eph_st; SMC_SELECT_BANK(lp, 0);
+                              eph_st = SMC_GET_EPH_STATUS(lp);
+                              SMC_SELECT_BANK(lp, 2); eph_st; }));
                        SMC_ACK_INT(lp, IM_RX_OVRN_INT);
                        dev->stats.rx_errors++;
                        dev->stats.rx_fifo_errors++;
@@ -1285,7 +1284,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
                        smc_phy_interrupt(dev);
                } else if (status & IM_ERCV_INT) {
                        SMC_ACK_INT(lp, IM_ERCV_INT);
-                       PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT\n", dev->name);
+                       PRINTK(dev, "UNSUPPORTED: ERCV INTERRUPT\n");
                }
        } while (--timeout);
 
@@ -1296,11 +1295,11 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
 
 #ifndef CONFIG_NET_POLL_CONTROLLER
        if (timeout == MAX_IRQ_LOOPS)
-               PRINTK("%s: spurious interrupt (mask = 0x%02x)\n",
-                      dev->name, mask);
+               PRINTK(dev, "spurious interrupt (mask = 0x%02x)\n",
+                      mask);
 #endif
-       DBG(3, "%s: Interrupt done (%d loops)\n",
-              dev->name, MAX_IRQ_LOOPS - timeout);
+       DBG(3, dev, "Interrupt done (%d loops)\n",
+           MAX_IRQ_LOOPS - timeout);
 
        /*
         * We return IRQ_HANDLED unconditionally here even if there was
@@ -1333,7 +1332,7 @@ static void smc_timeout(struct net_device *dev)
        void __iomem *ioaddr = lp->base;
        int status, mask, eph_st, meminfo, fifo;
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        spin_lock_irq(&lp->lock);
        status = SMC_GET_INT(lp);
@@ -1344,9 +1343,8 @@ static void smc_timeout(struct net_device *dev)
        meminfo = SMC_GET_MIR(lp);
        SMC_SELECT_BANK(lp, 2);
        spin_unlock_irq(&lp->lock);
-       PRINTK( "%s: TX timeout (INT 0x%02x INTMASK 0x%02x "
-               "MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n",
-               dev->name, status, mask, meminfo, fifo, eph_st );
+       PRINTK(dev, "TX timeout (INT 0x%02x INTMASK 0x%02x MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n",
+              status, mask, meminfo, fifo, eph_st);
 
        smc_reset(dev);
        smc_enable(dev);
@@ -1377,10 +1375,10 @@ static void smc_set_multicast_list(struct net_device *dev)
        unsigned char multicast_table[8];
        int update_multicast = 0;
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        if (dev->flags & IFF_PROMISC) {
-               DBG(2, "%s: RCR_PRMS\n", dev->name);
+               DBG(2, dev, "RCR_PRMS\n");
                lp->rcr_cur_mode |= RCR_PRMS;
        }
 
@@ -1395,7 +1393,7 @@ static void smc_set_multicast_list(struct net_device *dev)
         * checked before the table is
         */
        else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) {
-               DBG(2, "%s: RCR_ALMUL\n", dev->name);
+               DBG(2, dev, "RCR_ALMUL\n");
                lp->rcr_cur_mode |= RCR_ALMUL;
        }
 
@@ -1437,7 +1435,7 @@ static void smc_set_multicast_list(struct net_device *dev)
                /* now, the table can be loaded into the chipset */
                update_multicast = 1;
        } else  {
-               DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);
+               DBG(2, dev, "~(RCR_PRMS|RCR_ALMUL)\n");
                lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
 
                /*
@@ -1470,7 +1468,7 @@ smc_open(struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        /* Setup the default Register Modes */
        lp->tcr_cur_mode = TCR_DEFAULT;
@@ -1514,7 +1512,7 @@ static int smc_close(struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
 
-       DBG(2, "%s: %s\n", dev->name, __func__);
+       DBG(2, dev, "%s\n", __func__);
 
        netif_stop_queue(dev);
        netif_carrier_off(dev);
@@ -1694,7 +1692,7 @@ static int smc_ethtool_geteeprom(struct net_device *dev,
        int i;
        int imax;
 
-       DBG(1, "Reading %d bytes at %d(0x%x)\n",
+       DBG(1, dev, "Reading %d bytes at %d(0x%x)\n",
                eeprom->len, eeprom->offset, eeprom->offset);
        imax = smc_ethtool_geteeprom_len(dev);
        for (i = 0; i < eeprom->len; i += 2) {
@@ -1706,7 +1704,7 @@ static int smc_ethtool_geteeprom(struct net_device *dev,
                ret = smc_read_eeprom_word(dev, offset >> 1, &wbuf);
                if (ret != 0)
                        return ret;
-               DBG(2, "Read 0x%x from 0x%x\n", wbuf, offset >> 1);
+               DBG(2, dev, "Read 0x%x from 0x%x\n", wbuf, offset >> 1);
                data[i] = (wbuf >> 8) & 0xff;
                data[i+1] = wbuf & 0xff;
        }
@@ -1719,8 +1717,8 @@ static int smc_ethtool_seteeprom(struct net_device *dev,
        int i;
        int imax;
 
-       DBG(1, "Writing %d bytes to %d(0x%x)\n",
-                       eeprom->len, eeprom->offset, eeprom->offset);
+       DBG(1, dev, "Writing %d bytes to %d(0x%x)\n",
+           eeprom->len, eeprom->offset, eeprom->offset);
        imax = smc_ethtool_geteeprom_len(dev);
        for (i = 0; i < eeprom->len; i += 2) {
                int ret;
@@ -1729,7 +1727,7 @@ static int smc_ethtool_seteeprom(struct net_device *dev,
                if (offset > imax)
                        break;
                wbuf = (data[i] << 8) | data[i + 1];
-               DBG(2, "Writing 0x%x to 0x%x\n", wbuf, offset >> 1);
+               DBG(2, dev, "Writing 0x%x to 0x%x\n", wbuf, offset >> 1);
                ret = smc_write_eeprom_word(dev, offset >> 1, wbuf);
                if (ret != 0)
                        return ret;
@@ -1784,7 +1782,7 @@ static int smc_findirq(struct smc_local *lp)
        int timeout = 20;
        unsigned long cookie;
 
-       DBG(2, "%s: %s\n", CARDNAME, __func__);
+       DBG(2, dev, "%s: %s\n", CARDNAME, __func__);
 
        cookie = probe_irq_on();
 
@@ -1856,21 +1854,21 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
                     unsigned long irq_flags)
 {
        struct smc_local *lp = netdev_priv(dev);
-       static int version_printed = 0;
        int retval;
        unsigned int val, revision_register;
        const char *version_string;
 
-       DBG(2, "%s: %s\n", CARDNAME, __func__);
+       DBG(2, dev, "%s: %s\n", CARDNAME, __func__);
 
        /* First, see if the high byte is 0x33 */
        val = SMC_CURRENT_BANK(lp);
-       DBG(2, "%s: bank signature probe returned 0x%04x\n", CARDNAME, val);
+       DBG(2, dev, "%s: bank signature probe returned 0x%04x\n",
+           CARDNAME, val);
        if ((val & 0xFF00) != 0x3300) {
                if ((val & 0xFF) == 0x33) {
-                       printk(KERN_WARNING
-                               "%s: Detected possible byte-swapped interface"
-                               " at IOADDR %p\n", CARDNAME, ioaddr);
+                       netdev_warn(dev,
+                                   "%s: Detected possible byte-swapped interface at IOADDR %p\n",
+                                   CARDNAME, ioaddr);
                }
                retval = -ENODEV;
                goto err_out;
@@ -1897,8 +1895,8 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
        val = SMC_GET_BASE(lp);
        val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
        if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) {
-               printk("%s: IOADDR %p doesn't match configuration (%x).\n",
-                       CARDNAME, ioaddr, val);
+               netdev_warn(dev, "%s: IOADDR %p doesn't match configuration (%x).\n",
+                           CARDNAME, ioaddr, val);
        }
 
        /*
@@ -1908,21 +1906,19 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
         */
        SMC_SELECT_BANK(lp, 3);
        revision_register = SMC_GET_REV(lp);
-       DBG(2, "%s: revision = 0x%04x\n", CARDNAME, revision_register);
+       DBG(2, dev, "%s: revision = 0x%04x\n", CARDNAME, revision_register);
        version_string = chip_ids[ (revision_register >> 4) & 0xF];
        if (!version_string || (revision_register & 0xff00) != 0x3300) {
                /* I don't recognize this chip, so... */
-               printk("%s: IO %p: Unrecognized revision register 0x%04x"
-                       ", Contact author.\n", CARDNAME,
-                       ioaddr, revision_register);
+               netdev_warn(dev, "%s: IO %p: Unrecognized revision register 0x%04x, Contact author.\n",
+                           CARDNAME, ioaddr, revision_register);
 
                retval = -ENODEV;
                goto err_out;
        }
 
        /* At this point I'll assume that the chip is an SMC91x. */
-       if (version_printed++ == 0)
-               printk("%s", version);
+       pr_info_once("%s\n", version);
 
        /* fill in some of the fields */
        dev->base_addr = (unsigned long)ioaddr;
@@ -1940,7 +1936,7 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
        /*
         * If dev->irq is 0, then the device has to be banged on to see
         * what the IRQ is.
-        *
+        *
         * This banging doesn't always detect the IRQ, for unknown reasons.
         * a workaround is to reset the chip and try again.
         *
@@ -1965,8 +1961,7 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
                }
        }
        if (dev->irq == 0) {
-               printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
-                       dev->name);
+               netdev_warn(dev, "Couldn't autodetect your IRQ. Use irq=xx.\n");
                retval = -ENODEV;
                goto err_out;
        }
@@ -2030,32 +2025,31 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr,
        retval = register_netdev(dev);
        if (retval == 0) {
                /* now, print out the card info, in a short format.. */
-               printk("%s: %s (rev %d) at %p IRQ %d",
-                       dev->name, version_string, revision_register & 0x0f,
-                       lp->base, dev->irq);
+               netdev_info(dev, "%s (rev %d) at %p IRQ %d",
+                           version_string, revision_register & 0x0f,
+                           lp->base, dev->irq);
 
                if (dev->dma != (unsigned char)-1)
-                       printk(" DMA %d", dev->dma);
+                       pr_cont(" DMA %d", dev->dma);
 
-               printk("%s%s\n",
+               pr_cont("%s%s\n",
                        lp->cfg.flags & SMC91X_NOWAIT ? " [nowait]" : "",
                        THROTTLE_TX_PKTS ? " [throttle_tx]" : "");
 
                if (!is_valid_ether_addr(dev->dev_addr)) {
-                       printk("%s: Invalid ethernet MAC address.  Please "
-                              "set using ifconfig\n", dev->name);
+                       netdev_warn(dev, "Invalid ethernet MAC address. Please set using ifconfig\n");
                } else {
                        /* Print the Ethernet address */
-                       printk("%s: Ethernet addr: %pM\n",
-                              dev->name, dev->dev_addr);
+                       netdev_info(dev, "Ethernet addr: %pM\n",
+                                   dev->dev_addr);
                }
 
                if (lp->phy_type == 0) {
-                       PRINTK("%s: No PHY found\n", dev->name);
+                       PRINTK(dev, "No PHY found\n");
                } else if ((lp->phy_type & 0xfffffff0) == 0x0016f840) {
-                       PRINTK("%s: PHY LAN83C183 (LAN91C111 Internal)\n", dev->name);
+                       PRINTK(dev, "PHY LAN83C183 (LAN91C111 Internal)\n");
                } else if ((lp->phy_type & 0xfffffff0) == 0x02821c50) {
-                       PRINTK("%s: PHY LAN83C180\n", dev->name);
+                       PRINTK(dev, "PHY LAN83C180\n");
                }
        }
 
@@ -2165,7 +2159,8 @@ static inline void smc_request_datacs(struct platform_device *pdev, struct net_d
                        return;
 
                if(!request_mem_region(res->start, SMC_DATA_EXTENT, CARDNAME)) {
-                       printk(KERN_INFO "%s: failed to request datacs memory region.\n", CARDNAME);
+                       netdev_info(ndev, "%s: failed to request datacs memory region.\n",
+                                   CARDNAME);
                        return;
                }
 
@@ -2307,7 +2302,7 @@ static int smc_drv_probe(struct platform_device *pdev)
  out_free_netdev:
        free_netdev(ndev);
  out:
-       printk("%s: not found (%d).\n", CARDNAME, ret);
+       pr_info("%s: not found (%d).\n", CARDNAME, ret);
 
        return ret;
 }
index 5730fe2..c9d4c87 100644 (file)
@@ -907,8 +907,8 @@ static const char * chip_ids[ 16 ] =  {
        ({                                                              \
                int __b = SMC_CURRENT_BANK(lp);                 \
                if (unlikely((__b & ~0xf0) != (0x3300 | bank))) {       \
-                       printk( "%s: bank reg screwed (0x%04x)\n",      \
-                               CARDNAME, __b );                        \
+                       pr_err("%s: bank reg screwed (0x%04x)\n",       \
+                              CARDNAME, __b);                          \
                        BUG();                                          \
                }                                                       \
                reg<<SMC_IO_SHIFT;                                      \
@@ -1124,8 +1124,7 @@ static const char * chip_ids[ 16 ] =  {
                        void __iomem *__ioaddr = ioaddr;                \
                        if (__len >= 2 && (unsigned long)__ptr & 2) {   \
                                __len -= 2;                             \
-                               SMC_outw(*(u16 *)__ptr, ioaddr,         \
-                                       DATA_REG(lp));          \
+                               SMC_outsw(ioaddr, DATA_REG(lp), __ptr, 1); \
                                __ptr += 2;                             \
                        }                                               \
                        if (SMC_CAN_USE_DATACS && lp->datacs)           \
@@ -1133,8 +1132,7 @@ static const char * chip_ids[ 16 ] =  {
                        SMC_outsl(__ioaddr, DATA_REG(lp), __ptr, __len>>2); \
                        if (__len & 2) {                                \
                                __ptr += (__len & ~3);                  \
-                               SMC_outw(*((u16 *)__ptr), ioaddr,       \
-                                        DATA_REG(lp));         \
+                               SMC_outsw(ioaddr, DATA_REG(lp), __ptr, 1); \
                        }                                               \
                } else if (SMC_16BIT(lp))                               \
                        SMC_outsw(ioaddr, DATA_REG(lp), p, (l) >> 1);   \
index 5fdbc26..8564f23 100644 (file)
@@ -2167,7 +2167,7 @@ static int smsc911x_init(struct net_device *dev)
                udelay(1000);
 
        if (to == 0) {
-               pr_err("Device not READY in 100ms aborting\n");
+               netdev_err(dev, "Device not READY in 100ms aborting\n");
                return -ENODEV;
        }
 
@@ -2502,7 +2502,7 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
                SMSC_TRACE(pdata, probe,
                           "MAC Address is specified by configuration");
        } else if (is_valid_ether_addr(pdata->config.mac)) {
-               memcpy(dev->dev_addr, pdata->config.mac, 6);
+               memcpy(dev->dev_addr, pdata->config.mac, ETH_ALEN);
                SMSC_TRACE(pdata, probe,
                           "MAC Address specified by platform data");
        } else {
index 5f9e79f..f433d97 100644 (file)
@@ -19,6 +19,8 @@
  ***************************************************************************
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
@@ -33,7 +35,6 @@
 #include "smsc9420.h"
 
 #define DRV_NAME               "smsc9420"
-#define PFX                    DRV_NAME ": "
 #define DRV_MDIONAME           "smsc9420-mdio"
 #define DRV_DESCRIPTION                "SMSC LAN9420 driver"
 #define DRV_VERSION            "1.01"
@@ -97,21 +98,6 @@ static uint debug = -1;
 module_param(debug, uint, 0);
 MODULE_PARM_DESC(debug, "debug level");
 
-#define smsc_dbg(TYPE, f, a...) \
-do {   if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
-               printk(KERN_DEBUG PFX f "\n", ## a); \
-} while (0)
-
-#define smsc_info(TYPE, f, a...) \
-do {   if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
-               printk(KERN_INFO PFX f "\n", ## a); \
-} while (0)
-
-#define smsc_warn(TYPE, f, a...) \
-do {   if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
-               printk(KERN_WARNING PFX f "\n", ## a); \
-} while (0)
-
 static inline u32 smsc9420_reg_read(struct smsc9420_pdata *pd, u32 offset)
 {
        return ioread32(pd->ioaddr + offset);
@@ -140,7 +126,7 @@ static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
 
        /*  confirm MII not busy */
        if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
-               smsc_warn(DRV, "MII is busy???");
+               netif_warn(pd, drv, pd->dev, "MII is busy???\n");
                goto out;
        }
 
@@ -159,7 +145,7 @@ static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
                udelay(10);
        }
 
-       smsc_warn(DRV, "MII busy timeout!");
+       netif_warn(pd, drv, pd->dev, "MII busy timeout!\n");
 
 out:
        spin_unlock_irqrestore(&pd->phy_lock, flags);
@@ -178,7 +164,7 @@ static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
 
        /* confirm MII not busy */
        if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
-               smsc_warn(DRV, "MII is busy???");
+               netif_warn(pd, drv, pd->dev, "MII is busy???\n");
                goto out;
        }
 
@@ -200,7 +186,7 @@ static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
                udelay(10);
        }
 
-       smsc_warn(DRV, "MII busy timeout!");
+       netif_warn(pd, drv, pd->dev, "MII busy timeout!\n");
 
 out:
        spin_unlock_irqrestore(&pd->phy_lock, flags);
@@ -222,7 +208,7 @@ static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd)
        BUG_ON(!pd);
 
        if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
-               smsc_dbg(DRV, "smsc9420_eeprom_reload: Eeprom busy");
+               netif_dbg(pd, drv, pd->dev, "%s: Eeprom busy\n", __func__);
                return -EIO;
        }
 
@@ -235,7 +221,7 @@ static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd)
                        return 0;
        } while (timeout--);
 
-       smsc_warn(DRV, "smsc9420_eeprom_reload: Eeprom timed out");
+       netif_warn(pd, drv, pd->dev, "%s: Eeprom timed out\n", __func__);
        return -EIO;
 }
 
@@ -347,9 +333,9 @@ static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op)
        int timeout = 100;
        u32 e2cmd;
 
-       smsc_dbg(HW, "op 0x%08x", op);
+       netif_dbg(pd, hw, pd->dev, "op 0x%08x\n", op);
        if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
-               smsc_warn(HW, "Busy at start");
+               netif_warn(pd, hw, pd->dev, "Busy at start\n");
                return -EBUSY;
        }
 
@@ -362,12 +348,13 @@ static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op)
        } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout));
 
        if (!timeout) {
-               smsc_info(HW, "TIMED OUT");
+               netif_info(pd, hw, pd->dev, "TIMED OUT\n");
                return -EAGAIN;
        }
 
        if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
-               smsc_info(HW, "Error occurred during eeprom operation");
+               netif_info(pd, hw, pd->dev,
+                          "Error occurred during eeprom operation\n");
                return -EINVAL;
        }
 
@@ -380,7 +367,7 @@ static int smsc9420_eeprom_read_location(struct smsc9420_pdata *pd,
        u32 op = E2P_CMD_EPC_CMD_READ_ | address;
        int ret;
 
-       smsc_dbg(HW, "address 0x%x", address);
+       netif_dbg(pd, hw, pd->dev, "address 0x%x\n", address);
        ret = smsc9420_eeprom_send_cmd(pd, op);
 
        if (!ret)
@@ -395,7 +382,7 @@ static int smsc9420_eeprom_write_location(struct smsc9420_pdata *pd,
        u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
        int ret;
 
-       smsc_dbg(HW, "address 0x%x, data 0x%x", address, data);
+       netif_dbg(pd, hw, pd->dev, "address 0x%x, data 0x%x\n", address, data);
        ret = smsc9420_eeprom_send_cmd(pd, op);
 
        if (!ret) {
@@ -492,7 +479,8 @@ static void smsc9420_check_mac_address(struct net_device *dev)
        /* Check if mac address has been specified when bringing interface up */
        if (is_valid_ether_addr(dev->dev_addr)) {
                smsc9420_set_mac_address(dev);
-               smsc_dbg(PROBE, "MAC Address is specified by configuration");
+               netif_dbg(pd, probe, pd->dev,
+                         "MAC Address is specified by configuration\n");
        } else {
                /* Try reading mac address from device. if EEPROM is present
                 * it will already have been set */
@@ -507,12 +495,14 @@ static void smsc9420_check_mac_address(struct net_device *dev)
 
                if (is_valid_ether_addr(dev->dev_addr)) {
                        /* eeprom values are valid  so use them */
-                       smsc_dbg(PROBE, "Mac Address is read from EEPROM");
+                       netif_dbg(pd, probe, pd->dev,
+                                 "Mac Address is read from EEPROM\n");
                } else {
                        /* eeprom values are invalid, generate random MAC */
                        eth_hw_addr_random(dev);
                        smsc9420_set_mac_address(dev);
-                       smsc_dbg(PROBE, "MAC Address is set to random");
+                       netif_dbg(pd, probe, pd->dev,
+                                 "MAC Address is set to random\n");
                }
        }
 }
@@ -535,7 +525,7 @@ static void smsc9420_stop_tx(struct smsc9420_pdata *pd)
        }
 
        if (!timeout)
-               smsc_warn(IFDOWN, "TX DMAC failed to stop");
+               netif_warn(pd, ifdown, pd->dev, "TX DMAC failed to stop\n");
 
        /* ACK Tx DMAC stop bit */
        smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_TXPS_);
@@ -646,7 +636,8 @@ static void smsc9420_stop_rx(struct smsc9420_pdata *pd)
        }
 
        if (!timeout)
-               smsc_warn(IFDOWN, "RX DMAC did not stop! timeout.");
+               netif_warn(pd, ifdown, pd->dev,
+                          "RX DMAC did not stop! timeout\n");
 
        /* ACK the Rx DMAC stop bit */
        smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_RXPS_);
@@ -736,7 +727,7 @@ static void smsc9420_dmac_soft_reset(struct smsc9420_pdata *pd)
        smsc9420_reg_read(pd, BUS_MODE);
        udelay(2);
        if (smsc9420_reg_read(pd, BUS_MODE) & BUS_MODE_SWR_)
-               smsc_warn(DRV, "Software reset not cleared");
+               netif_warn(pd, drv, pd->dev, "Software reset not cleared\n");
 }
 
 static int smsc9420_stop(struct net_device *dev)
@@ -855,7 +846,7 @@ static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index)
                                 PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
        if (pci_dma_mapping_error(pd->pdev, mapping)) {
                dev_kfree_skb_any(skb);
-               smsc_warn(RX_ERR, "pci_map_single failed!");
+               netif_warn(pd, rx_err, pd->dev, "pci_map_single failed!\n");
                return -ENOMEM;
        }
 
@@ -1004,7 +995,8 @@ static netdev_tx_t smsc9420_hard_start_xmit(struct sk_buff *skb,
        mapping = pci_map_single(pd->pdev, skb->data,
                                 skb->len, PCI_DMA_TODEVICE);
        if (pci_dma_mapping_error(pd->pdev, mapping)) {
-               smsc_warn(TX_ERR, "pci_map_single failed, dropping packet");
+               netif_warn(pd, tx_err, pd->dev,
+                          "pci_map_single failed, dropping packet\n");
                return NETDEV_TX_BUSY;
        }
 
@@ -1056,12 +1048,12 @@ static void smsc9420_set_multicast_list(struct net_device *dev)
        u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
 
        if (dev->flags & IFF_PROMISC) {
-               smsc_dbg(HW, "Promiscuous Mode Enabled");
+               netif_dbg(pd, hw, pd->dev, "Promiscuous Mode Enabled\n");
                mac_cr |= MAC_CR_PRMS_;
                mac_cr &= (~MAC_CR_MCPAS_);
                mac_cr &= (~MAC_CR_HPFILT_);
        } else if (dev->flags & IFF_ALLMULTI) {
-               smsc_dbg(HW, "Receive all Multicast Enabled");
+               netif_dbg(pd, hw, pd->dev, "Receive all Multicast Enabled\n");
                mac_cr &= (~MAC_CR_PRMS_);
                mac_cr |= MAC_CR_MCPAS_;
                mac_cr &= (~MAC_CR_HPFILT_);
@@ -1069,7 +1061,7 @@ static void smsc9420_set_multicast_list(struct net_device *dev)
                struct netdev_hw_addr *ha;
                u32 hash_lo = 0, hash_hi = 0;
 
-               smsc_dbg(HW, "Multicast filter enabled");
+               netif_dbg(pd, hw, pd->dev, "Multicast filter enabled\n");
                netdev_for_each_mc_addr(ha, dev) {
                        u32 bit_num = smsc9420_hash(ha->addr);
                        u32 mask = 1 << (bit_num & 0x1F);
@@ -1087,7 +1079,7 @@ static void smsc9420_set_multicast_list(struct net_device *dev)
                mac_cr &= (~MAC_CR_MCPAS_);
                mac_cr |= MAC_CR_HPFILT_;
        } else {
-               smsc_dbg(HW, "Receive own packets only.");
+               netif_dbg(pd, hw, pd->dev, "Receive own packets only\n");
                smsc9420_reg_write(pd, HASHH, 0);
                smsc9420_reg_write(pd, HASHL, 0);
 
@@ -1115,11 +1107,11 @@ static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd)
                else
                        flow = 0;
 
-               smsc_info(LINK, "rx pause %s, tx pause %s",
-                       (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
-                       (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+               netif_info(pd, link, pd->dev, "rx pause %s, tx pause %s\n",
+                          cap & FLOW_CTRL_RX ? "enabled" : "disabled",
+                          cap & FLOW_CTRL_TX ? "enabled" : "disabled");
        } else {
-               smsc_info(LINK, "half duplex");
+               netif_info(pd, link, pd->dev, "half duplex\n");
                flow = 0;
        }
 
@@ -1137,10 +1129,10 @@ static void smsc9420_phy_adjust_link(struct net_device *dev)
        if (phy_dev->duplex != pd->last_duplex) {
                u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
                if (phy_dev->duplex) {
-                       smsc_dbg(LINK, "full duplex mode");
+                       netif_dbg(pd, link, pd->dev, "full duplex mode\n");
                        mac_cr |= MAC_CR_FDPX_;
                } else {
-                       smsc_dbg(LINK, "half duplex mode");
+                       netif_dbg(pd, link, pd->dev, "half duplex mode\n");
                        mac_cr &= ~MAC_CR_FDPX_;
                }
                smsc9420_reg_write(pd, MAC_CR, mac_cr);
@@ -1152,9 +1144,9 @@ static void smsc9420_phy_adjust_link(struct net_device *dev)
        carrier = netif_carrier_ok(dev);
        if (carrier != pd->last_carrier) {
                if (carrier)
-                       smsc_dbg(LINK, "carrier OK");
+                       netif_dbg(pd, link, pd->dev, "carrier OK\n");
                else
-                       smsc_dbg(LINK, "no carrier");
+                       netif_dbg(pd, link, pd->dev, "no carrier\n");
                pd->last_carrier = carrier;
        }
 }
@@ -1168,24 +1160,24 @@ static int smsc9420_mii_probe(struct net_device *dev)
 
        /* Device only supports internal PHY at address 1 */
        if (!pd->mii_bus->phy_map[1]) {
-               pr_err("%s: no PHY found at address 1\n", dev->name);
+               netdev_err(dev, "no PHY found at address 1\n");
                return -ENODEV;
        }
 
        phydev = pd->mii_bus->phy_map[1];
-       smsc_info(PROBE, "PHY addr %d, phy_id 0x%08X", phydev->addr,
-               phydev->phy_id);
+       netif_info(pd, probe, pd->dev, "PHY addr %d, phy_id 0x%08X\n",
+                  phydev->addr, phydev->phy_id);
 
        phydev = phy_connect(dev, dev_name(&phydev->dev),
                             smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII);
 
        if (IS_ERR(phydev)) {
-               pr_err("%s: Could not attach to PHY\n", dev->name);
+               netdev_err(dev, "Could not attach to PHY\n");
                return PTR_ERR(phydev);
        }
 
-       pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
-               dev->name, phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+       netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+                   phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
 
        /* mask with MAC supported features */
        phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
@@ -1223,12 +1215,12 @@ static int smsc9420_mii_init(struct net_device *dev)
        pd->mii_bus->phy_mask = ~(1 << 1);
 
        if (mdiobus_register(pd->mii_bus)) {
-               smsc_warn(PROBE, "Error registering mii bus");
+               netif_warn(pd, probe, pd->dev, "Error registering mii bus\n");
                goto err_out_free_bus_2;
        }
 
        if (smsc9420_mii_probe(dev) < 0) {
-               smsc_warn(PROBE, "Error probing mii bus");
+               netif_warn(pd, probe, pd->dev, "Error probing mii bus\n");
                goto err_out_unregister_bus_3;
        }
 
@@ -1281,12 +1273,11 @@ static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd)
 
        BUG_ON(!pd->rx_ring);
 
-       pd->rx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
-               RX_RING_SIZE), GFP_KERNEL);
-       if (pd->rx_buffers == NULL) {
-               smsc_warn(IFUP, "Failed to allocated rx_buffers");
+       pd->rx_buffers = kmalloc_array(RX_RING_SIZE,
+                                      sizeof(struct smsc9420_ring_info),
+                                      GFP_KERNEL);
+       if (pd->rx_buffers == NULL)
                goto out;
-       }
 
        /* initialize the rx ring */
        for (i = 0; i < RX_RING_SIZE; i++) {
@@ -1301,7 +1292,8 @@ static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd)
        /* now allocate the entire ring of skbs */
        for (i = 0; i < RX_RING_SIZE; i++) {
                if (smsc9420_alloc_rx_buffer(pd, i)) {
-                       smsc_warn(IFUP, "failed to allocate rx skb %d", i);
+                       netif_warn(pd, ifup, pd->dev,
+                                  "failed to allocate rx skb %d\n", i);
                        goto out_free_rx_skbs;
                }
        }
@@ -1310,13 +1302,14 @@ static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd)
        pd->rx_ring_tail = 0;
 
        smsc9420_reg_write(pd, VLAN1, ETH_P_8021Q);
-       smsc_dbg(IFUP, "VLAN1 = 0x%08x", smsc9420_reg_read(pd, VLAN1));
+       netif_dbg(pd, ifup, pd->dev, "VLAN1 = 0x%08x\n",
+                 smsc9420_reg_read(pd, VLAN1));
 
        if (pd->rx_csum) {
                /* Enable RX COE */
                u32 coe = smsc9420_reg_read(pd, COE_CR) | RX_COE_EN;
                smsc9420_reg_write(pd, COE_CR, coe);
-               smsc_dbg(IFUP, "COE_CR = 0x%08x", coe);
+               netif_dbg(pd, ifup, pd->dev, "COE_CR = 0x%08x\n", coe);
        }
 
        smsc9420_reg_write(pd, RX_BASE_ADDR, pd->rx_dma_addr);
@@ -1339,7 +1332,8 @@ static int smsc9420_open(struct net_device *dev)
        int result = 0, timeout;
 
        if (!is_valid_ether_addr(dev->dev_addr)) {
-               smsc_warn(IFUP, "dev_addr is not a valid MAC address");
+               netif_warn(pd, ifup, pd->dev,
+                          "dev_addr is not a valid MAC address\n");
                result = -EADDRNOTAVAIL;
                goto out_0;
        }
@@ -1358,7 +1352,7 @@ static int smsc9420_open(struct net_device *dev)
 
        result = request_irq(irq, smsc9420_isr, IRQF_SHARED, DRV_NAME, pd);
        if (result) {
-               smsc_warn(IFUP, "Unable to use IRQ = %d", irq);
+               netif_warn(pd, ifup, pd->dev, "Unable to use IRQ = %d\n", irq);
                result = -ENODEV;
                goto out_0;
        }
@@ -1393,7 +1387,7 @@ static int smsc9420_open(struct net_device *dev)
        smsc9420_pci_flush_write(pd);
 
        /* test the IRQ connection to the ISR */
-       smsc_dbg(IFUP, "Testing ISR using IRQ %d", irq);
+       netif_dbg(pd, ifup, pd->dev, "Testing ISR using IRQ %d\n", irq);
        pd->software_irq_signal = false;
 
        spin_lock_irqsave(&pd->int_lock, flags);
@@ -1423,30 +1417,32 @@ static int smsc9420_open(struct net_device *dev)
        spin_unlock_irqrestore(&pd->int_lock, flags);
 
        if (!pd->software_irq_signal) {
-               smsc_warn(IFUP, "ISR failed signaling test");
+               netif_warn(pd, ifup, pd->dev, "ISR failed signaling test\n");
                result = -ENODEV;
                goto out_free_irq_1;
        }
 
-       smsc_dbg(IFUP, "ISR passed test using IRQ %d", irq);
+       netif_dbg(pd, ifup, pd->dev, "ISR passed test using IRQ %d\n", irq);
 
        result = smsc9420_alloc_tx_ring(pd);
        if (result) {
-               smsc_warn(IFUP, "Failed to Initialize tx dma ring");
+               netif_warn(pd, ifup, pd->dev,
+                          "Failed to Initialize tx dma ring\n");
                result = -ENOMEM;
                goto out_free_irq_1;
        }
 
        result = smsc9420_alloc_rx_ring(pd);
        if (result) {
-               smsc_warn(IFUP, "Failed to Initialize rx dma ring");
+               netif_warn(pd, ifup, pd->dev,
+                          "Failed to Initialize rx dma ring\n");
                result = -ENOMEM;
                goto out_free_tx_ring_2;
        }
 
        result = smsc9420_mii_init(dev);
        if (result) {
-               smsc_warn(IFUP, "Failed to initialize Phy");
+               netif_warn(pd, ifup, pd->dev, "Failed to initialize Phy\n");
                result = -ENODEV;
                goto out_free_rx_ring_3;
        }
@@ -1547,7 +1543,8 @@ static int smsc9420_resume(struct pci_dev *pdev)
 
        err = pci_enable_wake(pdev, 0, 0);
        if (err)
-               smsc_warn(IFUP, "pci_enable_wake failed: %d", err);
+               netif_warn(pd, ifup, pd->dev, "pci_enable_wake failed: %d\n",
+                          err);
 
        if (netif_running(dev)) {
                /* FIXME: gross. It looks like ancient PM relic.*/
@@ -1582,12 +1579,12 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        int result = 0;
        u32 id_rev;
 
-       printk(KERN_INFO DRV_DESCRIPTION " version " DRV_VERSION "\n");
+       pr_info("%s version %s\n", DRV_DESCRIPTION, DRV_VERSION);
 
        /* First do the PCI initialisation */
        result = pci_enable_device(pdev);
        if (unlikely(result)) {
-               printk(KERN_ERR "Cannot enable smsc9420\n");
+               pr_err("Cannot enable smsc9420\n");
                goto out_0;
        }
 
@@ -1600,24 +1597,24 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        if (!(pci_resource_flags(pdev, SMSC_BAR) & IORESOURCE_MEM)) {
-               printk(KERN_ERR "Cannot find PCI device base address\n");
+               netdev_err(dev, "Cannot find PCI device base address\n");
                goto out_free_netdev_2;
        }
 
        if ((pci_request_regions(pdev, DRV_NAME))) {
-               printk(KERN_ERR "Cannot obtain PCI resources, aborting.\n");
+               netdev_err(dev, "Cannot obtain PCI resources, aborting\n");
                goto out_free_netdev_2;
        }
 
        if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
-               printk(KERN_ERR "No usable DMA configuration, aborting.\n");
+               netdev_err(dev, "No usable DMA configuration, aborting\n");
                goto out_free_regions_3;
        }
 
        virt_addr = ioremap(pci_resource_start(pdev, SMSC_BAR),
                pci_resource_len(pdev, SMSC_BAR));
        if (!virt_addr) {
-               printk(KERN_ERR "Cannot map device registers, aborting.\n");
+               netdev_err(dev, "Cannot map device registers, aborting\n");
                goto out_free_regions_3;
        }
 
@@ -1646,16 +1643,17 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        pd->msg_enable = smsc_debug;
        pd->rx_csum = true;
 
-       smsc_dbg(PROBE, "lan_base=0x%08lx", (ulong)virt_addr);
+       netif_dbg(pd, probe, pd->dev, "lan_base=0x%08lx\n", (ulong)virt_addr);
 
        id_rev = smsc9420_reg_read(pd, ID_REV);
        switch (id_rev & 0xFFFF0000) {
        case 0x94200000:
-               smsc_info(PROBE, "LAN9420 identified, ID_REV=0x%08X", id_rev);
+               netif_info(pd, probe, pd->dev,
+                          "LAN9420 identified, ID_REV=0x%08X\n", id_rev);
                break;
        default:
-               smsc_warn(PROBE, "LAN9420 NOT identified");
-               smsc_warn(PROBE, "ID_REV=0x%08X", id_rev);
+               netif_warn(pd, probe, pd->dev, "LAN9420 NOT identified\n");
+               netif_warn(pd, probe, pd->dev, "ID_REV=0x%08X\n", id_rev);
                goto out_free_dmadesc_5;
        }
 
@@ -1670,7 +1668,8 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        result = register_netdev(dev);
        if (result) {
-               smsc_warn(PROBE, "error %i registering device", result);
+               netif_warn(pd, probe, pd->dev, "error %i registering device\n",
+                          result);
                goto out_free_dmadesc_5;
        }
 
@@ -1707,8 +1706,6 @@ static void smsc9420_remove(struct pci_dev *pdev)
        if (!dev)
                return;
 
-       pci_set_drvdata(pdev, NULL);
-
        pd = netdev_priv(dev);
        unregister_netdev(dev);
 
index 7eb8bab..fc94f20 100644 (file)
@@ -451,14 +451,14 @@ struct mac_device_info {
 struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr);
 struct mac_device_info *dwmac100_setup(void __iomem *ioaddr);
 
-extern void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
-                               unsigned int high, unsigned int low);
-extern void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
-                               unsigned int high, unsigned int low);
+void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
+                        unsigned int high, unsigned int low);
+void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
+                        unsigned int high, unsigned int low);
 
-extern void stmmac_set_mac(void __iomem *ioaddr, bool enable);
+void stmmac_set_mac(void __iomem *ioaddr, bool enable);
 
-extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
+void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
 extern const struct stmmac_ring_mode_ops ring_mode_ops;
 extern const struct stmmac_chain_mode_ops chain_mode_ops;
 
index 8e5662c..def266d 100644 (file)
 #define DMA_STATUS_TI  0x00000001      /* Transmit Interrupt */
 #define DMA_CONTROL_FTF                0x00100000      /* Flush transmit FIFO */
 
-extern void dwmac_enable_dma_transmission(void __iomem *ioaddr);
-extern void dwmac_enable_dma_irq(void __iomem *ioaddr);
-extern void dwmac_disable_dma_irq(void __iomem *ioaddr);
-extern void dwmac_dma_start_tx(void __iomem *ioaddr);
-extern void dwmac_dma_stop_tx(void __iomem *ioaddr);
-extern void dwmac_dma_start_rx(void __iomem *ioaddr);
-extern void dwmac_dma_stop_rx(void __iomem *ioaddr);
-extern int dwmac_dma_interrupt(void __iomem *ioaddr,
-                              struct stmmac_extra_stats *x);
+void dwmac_enable_dma_transmission(void __iomem *ioaddr);
+void dwmac_enable_dma_irq(void __iomem *ioaddr);
+void dwmac_disable_dma_irq(void __iomem *ioaddr);
+void dwmac_dma_start_tx(void __iomem *ioaddr);
+void dwmac_dma_stop_tx(void __iomem *ioaddr);
+void dwmac_dma_start_rx(void __iomem *ioaddr);
+void dwmac_dma_stop_rx(void __iomem *ioaddr);
+int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x);
 
 #endif /* __DWMAC_DMA_H__ */
index 48ec001..8607488 100644 (file)
@@ -128,8 +128,8 @@ struct stmmac_counters {
        unsigned int mmc_rx_icmp_err_octets;
 };
 
-extern void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode);
-extern void dwmac_mmc_intr_all_mask(void __iomem *ioaddr);
-extern void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc);
+void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode);
+void dwmac_mmc_intr_all_mask(void __iomem *ioaddr);
+void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc);
 
 #endif /* __MMC_H__ */
index f16a9bd..22f89ff 100644 (file)
@@ -110,14 +110,14 @@ struct stmmac_priv {
 
 extern int phyaddr;
 
-extern int stmmac_mdio_unregister(struct net_device *ndev);
-extern int stmmac_mdio_register(struct net_device *ndev);
-extern void stmmac_set_ethtool_ops(struct net_device *netdev);
+int stmmac_mdio_unregister(struct net_device *ndev);
+int stmmac_mdio_register(struct net_device *ndev);
+void stmmac_set_ethtool_ops(struct net_device *netdev);
 extern const struct stmmac_desc_ops enh_desc_ops;
 extern const struct stmmac_desc_ops ndesc_ops;
 extern const struct stmmac_hwtimestamp stmmac_ptp;
-extern int stmmac_ptp_register(struct stmmac_priv *priv);
-extern void stmmac_ptp_unregister(struct stmmac_priv *priv);
+int stmmac_ptp_register(struct stmmac_priv *priv);
+void stmmac_ptp_unregister(struct stmmac_priv *priv);
 int stmmac_freeze(struct net_device *ndev);
 int stmmac_restore(struct net_device *ndev);
 int stmmac_resume(struct net_device *ndev);
index 023b7c2..644d80e 100644 (file)
@@ -138,7 +138,6 @@ static void stmmac_pci_remove(struct pci_dev *pdev)
 
        stmmac_dvr_remove(ndev);
 
-       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, priv->ioaddr);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
index 759441b..b4d50d7 100644 (file)
@@ -3354,7 +3354,7 @@ use_random_mac_addr:
 #if defined(CONFIG_SPARC)
        addr = of_get_property(cp->of_node, "local-mac-address", NULL);
        if (addr != NULL) {
-               memcpy(dev_addr, addr, 6);
+               memcpy(dev_addr, addr, ETH_ALEN);
                goto done;
        }
 #endif
@@ -5168,7 +5168,6 @@ err_out_free_netdev:
 
 err_out_disable_pdev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        return -ENODEV;
 }
 
@@ -5206,7 +5205,6 @@ static void cas_remove_one(struct pci_dev *pdev)
        free_netdev(dev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
index f28460c..388540f 100644 (file)
@@ -9875,7 +9875,6 @@ err_out_free_res:
 
 err_out_disable_pdev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        return err;
 }
@@ -9900,7 +9899,6 @@ static void niu_pci_remove_one(struct pci_dev *pdev)
                free_netdev(dev);
                pci_release_regions(pdev);
                pci_disable_device(pdev);
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index e62df2b..b5655b7 100644 (file)
@@ -2779,7 +2779,7 @@ static int gem_get_device_address(struct gem *gp)
                return -1;
 #endif
        }
-       memcpy(dev->dev_addr, addr, 6);
+       memcpy(dev->dev_addr, addr, ETH_ALEN);
 #else
        get_gem_mac_nonobp(gp->pdev, gp->dev->dev_addr);
 #endif
@@ -2806,8 +2806,6 @@ static void gem_remove_one(struct pci_dev *pdev)
                iounmap(gp->regs);
                pci_release_regions(pdev);
                free_netdev(dev);
-
-               pci_set_drvdata(pdev, NULL);
        }
 }
 
index e37b587..0dbf46f 100644 (file)
@@ -2675,10 +2675,10 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe)
 
                addr = of_get_property(dp, "local-mac-address", &len);
 
-               if (qfe_slot != -1 && addr && len == 6)
-                       memcpy(dev->dev_addr, addr, 6);
+               if (qfe_slot != -1 && addr && len == ETH_ALEN)
+                       memcpy(dev->dev_addr, addr, ETH_ALEN);
                else
-                       memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+                       memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
        }
 
        hp = netdev_priv(dev);
@@ -3024,9 +3024,9 @@ static int happy_meal_pci_probe(struct pci_dev *pdev,
                    (addr = of_get_property(dp, "local-mac-address", &len))
                        != NULL &&
                    len == 6) {
-                       memcpy(dev->dev_addr, addr, 6);
+                       memcpy(dev->dev_addr, addr, ETH_ALEN);
                } else {
-                       memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+                       memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
                }
 #else
                get_hme_mac_nonsparc(pdev, &dev->dev_addr[0]);
@@ -3170,8 +3170,6 @@ static void happy_meal_pci_remove(struct pci_dev *pdev)
        pci_release_regions(hp->happy_dev);
 
        free_netdev(net_dev);
-
-       pci_set_drvdata(pdev, NULL);
 }
 
 static DEFINE_PCI_DEVICE_TABLE(happymeal_pci_ids) = {
index b072f4d..5695ae2 100644 (file)
@@ -843,7 +843,7 @@ static int qec_ether_init(struct platform_device *op)
        if (!dev)
                return -ENOMEM;
 
-       memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+       memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
 
        qe = netdev_priv(dev);
 
index 571452e..dd0dd62 100644 (file)
@@ -2447,7 +2447,6 @@ static void bdx_remove(struct pci_dev *pdev)
        iounmap(nic->regs);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        vfree(nic);
 
        RET();
index de71b1e..53150c2 100644 (file)
@@ -49,11 +49,19 @@ config TI_DAVINCI_CPDMA
          To compile this driver as a module, choose M here: the module
          will be called davinci_cpdma.  This is recommended.
 
+config TI_CPSW_PHY_SEL
+       boolean "TI CPSW Switch Phy sel Support"
+       depends on TI_CPSW
+       ---help---
+         This driver supports configuring of the phy mode connected to
+         the CPSW.
+
 config TI_CPSW
        tristate "TI CPSW Switch Support"
        depends on ARM && (ARCH_DAVINCI || SOC_AM33XX)
        select TI_DAVINCI_CPDMA
        select TI_DAVINCI_MDIO
+       select TI_CPSW_PHY_SEL
        ---help---
          This driver supports TI's CPSW Ethernet Switch.
 
index c65148e..9cfaab8 100644 (file)
@@ -7,5 +7,6 @@ obj-$(CONFIG_CPMAC) += cpmac.o
 obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
 obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
 obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
+obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o
 obj-$(CONFIG_TI_CPSW) += ti_cpsw.o
 ti_cpsw-y := cpsw_ale.o cpsw.o cpts.o
diff --git a/drivers/net/ethernet/ti/cpsw-phy-sel.c b/drivers/net/ethernet/ti/cpsw-phy-sel.c
new file mode 100644 (file)
index 0000000..148da9a
--- /dev/null
@@ -0,0 +1,161 @@
+/* Texas Instruments Ethernet Switch Driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "cpsw.h"
+
+/* AM33xx SoC specific definitions for the CONTROL port */
+#define AM33XX_GMII_SEL_MODE_MII       0
+#define AM33XX_GMII_SEL_MODE_RMII      1
+#define AM33XX_GMII_SEL_MODE_RGMII     2
+
+#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN        BIT(7)
+#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN        BIT(6)
+
+struct cpsw_phy_sel_priv {
+       struct device   *dev;
+       u32 __iomem     *gmii_sel;
+       bool            rmii_clock_external;
+       void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,
+                            phy_interface_t phy_mode, int slave);
+};
+
+
+static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
+                                phy_interface_t phy_mode, int slave)
+{
+       u32 reg;
+       u32 mask;
+       u32 mode = 0;
+
+       reg = readl(priv->gmii_sel);
+
+       switch (phy_mode) {
+       case PHY_INTERFACE_MODE_RMII:
+               mode = AM33XX_GMII_SEL_MODE_RMII;
+               break;
+
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               mode = AM33XX_GMII_SEL_MODE_RGMII;
+               break;
+
+       case PHY_INTERFACE_MODE_MII:
+       default:
+               mode = AM33XX_GMII_SEL_MODE_MII;
+               break;
+       };
+
+       mask = 0x3 << (slave * 2) | BIT(slave + 6);
+       mode <<= slave * 2;
+
+       if (priv->rmii_clock_external) {
+               if (slave == 0)
+                       mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
+               else
+                       mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
+       }
+
+       reg &= ~mask;
+       reg |= mode;
+
+       writel(reg, priv->gmii_sel);
+}
+
+static struct platform_driver cpsw_phy_sel_driver;
+static int match(struct device *dev, void *data)
+{
+       struct device_node *node = (struct device_node *)data;
+       return dev->of_node == node &&
+               dev->driver == &cpsw_phy_sel_driver.driver;
+}
+
+void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
+{
+       struct device_node *node;
+       struct cpsw_phy_sel_priv *priv;
+
+       node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");
+       if (!node) {
+               dev_err(dev, "Phy mode driver DT not found\n");
+               return;
+       }
+
+       dev = bus_find_device(&platform_bus_type, NULL, node, match);
+       priv = dev_get_drvdata(dev);
+
+       priv->cpsw_phy_sel(priv, phy_mode, slave);
+}
+EXPORT_SYMBOL_GPL(cpsw_phy_sel);
+
+static const struct of_device_id cpsw_phy_sel_id_table[] = {
+       {
+               .compatible     = "ti,am3352-cpsw-phy-sel",
+               .data           = &cpsw_gmii_sel_am3352,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table);
+
+static int cpsw_phy_sel_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       const struct of_device_id *of_id;
+       struct cpsw_phy_sel_priv *priv;
+
+       of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);
+       if (!of_id)
+               return -EINVAL;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");
+               return -ENOMEM;
+       }
+
+       priv->cpsw_phy_sel = of_id->data;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel");
+       priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->gmii_sel))
+               return PTR_ERR(priv->gmii_sel);
+
+       if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL))
+               priv->rmii_clock_external = true;
+
+       dev_set_drvdata(&pdev->dev, priv);
+
+       return 0;
+}
+
+static struct platform_driver cpsw_phy_sel_driver = {
+       .probe          = cpsw_phy_sel_probe,
+       .driver         = {
+               .name   = "cpsw-phy-sel",
+               .owner  = THIS_MODULE,
+               .of_match_table = cpsw_phy_sel_id_table,
+       },
+};
+
+module_platform_driver(cpsw_phy_sel_driver);
+MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>");
+MODULE_LICENSE("GPL v2");
index 79974e3..90d41d2 100644 (file)
@@ -367,8 +367,6 @@ struct cpsw_priv {
        spinlock_t                      lock;
        struct platform_device          *pdev;
        struct net_device               *ndev;
-       struct resource                 *cpsw_res;
-       struct resource                 *cpsw_wr_res;
        struct napi_struct              napi;
        struct device                   *dev;
        struct cpsw_platform_data       data;
@@ -639,13 +637,6 @@ void cpsw_rx_handler(void *token, int len, int status)
 static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
 {
        struct cpsw_priv *priv = dev_id;
-       u32 rx, tx, rx_thresh;
-
-       rx_thresh = __raw_readl(&priv->wr_regs->rx_thresh_stat);
-       rx = __raw_readl(&priv->wr_regs->rx_stat);
-       tx = __raw_readl(&priv->wr_regs->tx_stat);
-       if (!rx_thresh && !rx && !tx)
-               return IRQ_NONE;
 
        cpsw_intr_disable(priv);
        if (priv->irq_enabled == true) {
@@ -1023,6 +1014,10 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
                dev_info(priv->dev, "phy found : id is : 0x%x\n",
                         slave->phy->phy_id);
                phy_start(slave->phy);
+
+               /* Configure GMII_SEL register */
+               cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface,
+                            slave->slave_num);
        }
 }
 
@@ -1169,9 +1164,9 @@ static int cpsw_ndo_open(struct net_device *ndev)
                }
        }
 
+       napi_enable(&priv->napi);
        cpdma_ctlr_start(priv->dma);
        cpsw_intr_enable(priv);
-       napi_enable(&priv->napi);
        cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
        cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX);
 
@@ -1712,67 +1707,60 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
 
        if (of_property_read_u32(node, "active_slave", &prop)) {
                pr_err("Missing active_slave property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->active_slave = prop;
 
        if (of_property_read_u32(node, "cpts_clock_mult", &prop)) {
                pr_err("Missing cpts_clock_mult property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->cpts_clock_mult = prop;
 
        if (of_property_read_u32(node, "cpts_clock_shift", &prop)) {
                pr_err("Missing cpts_clock_shift property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->cpts_clock_shift = prop;
 
-       data->slave_data = kcalloc(data->slaves, sizeof(struct cpsw_slave_data),
-                                  GFP_KERNEL);
+       data->slave_data = devm_kzalloc(&pdev->dev, data->slaves
+                                       * sizeof(struct cpsw_slave_data),
+                                       GFP_KERNEL);
        if (!data->slave_data)
-               return -EINVAL;
+               return -ENOMEM;
 
        if (of_property_read_u32(node, "cpdma_channels", &prop)) {
                pr_err("Missing cpdma_channels property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->channels = prop;
 
        if (of_property_read_u32(node, "ale_entries", &prop)) {
                pr_err("Missing ale_entries property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->ale_entries = prop;
 
        if (of_property_read_u32(node, "bd_ram_size", &prop)) {
                pr_err("Missing bd_ram_size property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->bd_ram_size = prop;
 
        if (of_property_read_u32(node, "rx_descs", &prop)) {
                pr_err("Missing rx_descs property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->rx_descs = prop;
 
        if (of_property_read_u32(node, "mac_control", &prop)) {
                pr_err("Missing mac_control property in the DT.\n");
-               ret = -EINVAL;
-               goto error_ret;
+               return -EINVAL;
        }
        data->mac_control = prop;
 
-       if (!of_property_read_u32(node, "dual_emac", &prop))
-               data->dual_emac = prop;
+       if (of_property_read_bool(node, "dual_emac"))
+               data->dual_emac = 1;
 
        /*
         * Populate all the child nodes here...
@@ -1782,7 +1770,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        if (ret)
                pr_warn("Doesn't have any child node\n");
 
-       for_each_node_by_name(slave_node, "slave") {
+       for_each_child_of_node(node, slave_node) {
                struct cpsw_slave_data *slave_data = data->slave_data + i;
                const void *mac_addr = NULL;
                u32 phyid;
@@ -1791,11 +1779,14 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                struct device_node *mdio_node;
                struct platform_device *mdio;
 
+               /* This is no slave child node, continue */
+               if (strcmp(slave_node->name, "slave"))
+                       continue;
+
                parp = of_get_property(slave_node, "phy_id", &lenp);
                if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
                        pr_err("Missing slave[%d] phy_id property\n", i);
-                       ret = -EINVAL;
-                       goto error_ret;
+                       return -EINVAL;
                }
                mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
                phyid = be32_to_cpup(parp+1);
@@ -1825,10 +1816,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        }
 
        return 0;
-
-error_ret:
-       kfree(data->slave_data);
-       return ret;
 }
 
 static int cpsw_probe_dual_emac(struct platform_device *pdev,
@@ -1870,7 +1857,6 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
        priv_sl2->coal_intvl = 0;
        priv_sl2->bus_freq_mhz = priv->bus_freq_mhz;
 
-       priv_sl2->cpsw_res = priv->cpsw_res;
        priv_sl2->regs = priv->regs;
        priv_sl2->host_port = priv->host_port;
        priv_sl2->host_port_regs = priv->host_port_regs;
@@ -1914,8 +1900,8 @@ static int cpsw_probe(struct platform_device *pdev)
        struct cpsw_priv                *priv;
        struct cpdma_params             dma_params;
        struct cpsw_ale_params          ale_params;
-       void __iomem                    *ss_regs, *wr_regs;
-       struct resource                 *res;
+       void __iomem                    *ss_regs;
+       struct resource                 *res, *ss_res;
        u32 slave_offset, sliver_offset, slave_size;
        int ret = 0, i, k = 0;
 
@@ -1951,7 +1937,7 @@ static int cpsw_probe(struct platform_device *pdev)
        if (cpsw_probe_dt(&priv->data, pdev)) {
                pr_err("cpsw: platform data missing\n");
                ret = -ENODEV;
-               goto clean_ndev_ret;
+               goto clean_runtime_disable_ret;
        }
        data = &priv->data;
 
@@ -1965,11 +1951,12 @@ static int cpsw_probe(struct platform_device *pdev)
 
        memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
 
-       priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves,
-                              GFP_KERNEL);
+       priv->slaves = devm_kzalloc(&pdev->dev,
+                                   sizeof(struct cpsw_slave) * data->slaves,
+                                   GFP_KERNEL);
        if (!priv->slaves) {
-               ret = -EBUSY;
-               goto clean_ndev_ret;
+               ret = -ENOMEM;
+               goto clean_runtime_disable_ret;
        }
        for (i = 0; i < data->slaves; i++)
                priv->slaves[i].slave_num = i;
@@ -1977,55 +1964,31 @@ static int cpsw_probe(struct platform_device *pdev)
        priv->slaves[0].ndev = ndev;
        priv->emac_port = 0;
 
-       priv->clk = clk_get(&pdev->dev, "fck");
+       priv->clk = devm_clk_get(&pdev->dev, "fck");
        if (IS_ERR(priv->clk)) {
-               dev_err(&pdev->dev, "fck is not found\n");
+               dev_err(priv->dev, "fck is not found\n");
                ret = -ENODEV;
-               goto clean_slave_ret;
+               goto clean_runtime_disable_ret;
        }
        priv->coal_intvl = 0;
        priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
 
-       priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!priv->cpsw_res) {
-               dev_err(priv->dev, "error getting i/o resource\n");
-               ret = -ENOENT;
-               goto clean_clk_ret;
-       }
-       if (!request_mem_region(priv->cpsw_res->start,
-                               resource_size(priv->cpsw_res), ndev->name)) {
-               dev_err(priv->dev, "failed request i/o region\n");
-               ret = -ENXIO;
-               goto clean_clk_ret;
-       }
-       ss_regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res));
-       if (!ss_regs) {
-               dev_err(priv->dev, "unable to map i/o region\n");
-               goto clean_cpsw_iores_ret;
+       ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ss_regs = devm_ioremap_resource(&pdev->dev, ss_res);
+       if (IS_ERR(ss_regs)) {
+               ret = PTR_ERR(ss_regs);
+               goto clean_runtime_disable_ret;
        }
        priv->regs = ss_regs;
        priv->version = __raw_readl(&priv->regs->id_ver);
        priv->host_port = HOST_PORT_NUM;
 
-       priv->cpsw_wr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!priv->cpsw_wr_res) {
-               dev_err(priv->dev, "error getting i/o resource\n");
-               ret = -ENOENT;
-               goto clean_iomap_ret;
-       }
-       if (!request_mem_region(priv->cpsw_wr_res->start,
-                       resource_size(priv->cpsw_wr_res), ndev->name)) {
-               dev_err(priv->dev, "failed request i/o region\n");
-               ret = -ENXIO;
-               goto clean_iomap_ret;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       priv->wr_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->wr_regs)) {
+               ret = PTR_ERR(priv->wr_regs);
+               goto clean_runtime_disable_ret;
        }
-       wr_regs = ioremap(priv->cpsw_wr_res->start,
-                               resource_size(priv->cpsw_wr_res));
-       if (!wr_regs) {
-               dev_err(priv->dev, "unable to map i/o region\n");
-               goto clean_cpsw_wr_iores_ret;
-       }
-       priv->wr_regs = wr_regs;
 
        memset(&dma_params, 0, sizeof(dma_params));
        memset(&ale_params, 0, sizeof(ale_params));
@@ -2056,12 +2019,12 @@ static int cpsw_probe(struct platform_device *pdev)
                slave_size           = CPSW2_SLAVE_SIZE;
                sliver_offset        = CPSW2_SLIVER_OFFSET;
                dma_params.desc_mem_phys =
-                       (u32 __force) priv->cpsw_res->start + CPSW2_BD_OFFSET;
+                       (u32 __force) ss_res->start + CPSW2_BD_OFFSET;
                break;
        default:
                dev_err(priv->dev, "unknown version 0x%08x\n", priv->version);
                ret = -ENODEV;
-               goto clean_cpsw_wr_iores_ret;
+               goto clean_runtime_disable_ret;
        }
        for (i = 0; i < priv->data.slaves; i++) {
                struct cpsw_slave *slave = &priv->slaves[i];
@@ -2089,7 +2052,7 @@ static int cpsw_probe(struct platform_device *pdev)
        if (!priv->dma) {
                dev_err(priv->dev, "error initializing dma\n");
                ret = -ENOMEM;
-               goto clean_wr_iomap_ret;
+               goto clean_runtime_disable_ret;
        }
 
        priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0),
@@ -2124,8 +2087,8 @@ static int cpsw_probe(struct platform_device *pdev)
 
        while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
                for (i = res->start; i <= res->end; i++) {
-                       if (request_irq(i, cpsw_interrupt, 0,
-                                       dev_name(&pdev->dev), priv)) {
+                       if (devm_request_irq(&pdev->dev, i, cpsw_interrupt, 0,
+                                            dev_name(priv->dev), priv)) {
                                dev_err(priv->dev, "error attaching irq\n");
                                goto clean_ale_ret;
                        }
@@ -2147,7 +2110,7 @@ static int cpsw_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(priv->dev, "error registering net device\n");
                ret = -ENODEV;
-               goto clean_irq_ret;
+               goto clean_ale_ret;
        }
 
        if (cpts_register(&pdev->dev, priv->cpts,
@@ -2155,44 +2118,27 @@ static int cpsw_probe(struct platform_device *pdev)
                dev_err(priv->dev, "error registering cpts device\n");
 
        cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n",
-                 priv->cpsw_res->start, ndev->irq);
+                   ss_res->start, ndev->irq);
 
        if (priv->data.dual_emac) {
                ret = cpsw_probe_dual_emac(pdev, priv);
                if (ret) {
                        cpsw_err(priv, probe, "error probe slave 2 emac interface\n");
-                       goto clean_irq_ret;
+                       goto clean_ale_ret;
                }
        }
 
        return 0;
 
-clean_irq_ret:
-       for (i = 0; i < priv->num_irqs; i++)
-               free_irq(priv->irqs_table[i], priv);
 clean_ale_ret:
        cpsw_ale_destroy(priv->ale);
 clean_dma_ret:
        cpdma_chan_destroy(priv->txch);
        cpdma_chan_destroy(priv->rxch);
        cpdma_ctlr_destroy(priv->dma);
-clean_wr_iomap_ret:
-       iounmap(priv->wr_regs);
-clean_cpsw_wr_iores_ret:
-       release_mem_region(priv->cpsw_wr_res->start,
-                          resource_size(priv->cpsw_wr_res));
-clean_iomap_ret:
-       iounmap(priv->regs);
-clean_cpsw_iores_ret:
-       release_mem_region(priv->cpsw_res->start,
-                          resource_size(priv->cpsw_res));
-clean_clk_ret:
-       clk_put(priv->clk);
-clean_slave_ret:
+clean_runtime_disable_ret:
        pm_runtime_disable(&pdev->dev);
-       kfree(priv->slaves);
 clean_ndev_ret:
-       kfree(priv->data.slave_data);
        free_netdev(priv->ndev);
        return ret;
 }
@@ -2201,30 +2147,18 @@ static int cpsw_remove(struct platform_device *pdev)
 {
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct cpsw_priv *priv = netdev_priv(ndev);
-       int i;
 
        if (priv->data.dual_emac)
                unregister_netdev(cpsw_get_slave_ndev(priv, 1));
        unregister_netdev(ndev);
 
        cpts_unregister(priv->cpts);
-       for (i = 0; i < priv->num_irqs; i++)
-               free_irq(priv->irqs_table[i], priv);
 
        cpsw_ale_destroy(priv->ale);
        cpdma_chan_destroy(priv->txch);
        cpdma_chan_destroy(priv->rxch);
        cpdma_ctlr_destroy(priv->dma);
-       iounmap(priv->regs);
-       release_mem_region(priv->cpsw_res->start,
-                          resource_size(priv->cpsw_res));
-       iounmap(priv->wr_regs);
-       release_mem_region(priv->cpsw_wr_res->start,
-                          resource_size(priv->cpsw_wr_res));
        pm_runtime_disable(&pdev->dev);
-       clk_put(priv->clk);
-       kfree(priv->slaves);
-       kfree(priv->data.slave_data);
        if (priv->data.dual_emac)
                free_netdev(cpsw_get_slave_ndev(priv, 1));
        free_netdev(ndev);
@@ -2280,7 +2214,7 @@ static struct platform_driver cpsw_driver = {
                .name    = "cpsw",
                .owner   = THIS_MODULE,
                .pm      = &cpsw_pm_ops,
-               .of_match_table = of_match_ptr(cpsw_of_mtable),
+               .of_match_table = cpsw_of_mtable,
        },
        .probe = cpsw_probe,
        .remove = cpsw_remove,
index eb3e101..574f49d 100644 (file)
@@ -39,4 +39,6 @@ struct cpsw_platform_data {
        bool    dual_emac;      /* Enable Dual EMAC mode */
 };
 
+void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave);
+
 #endif /* __CPSW_H__ */
index fe993cd..1a581ef 100644 (file)
@@ -127,8 +127,8 @@ struct cpts {
 };
 
 #ifdef CONFIG_TI_CPTS
-extern void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);
-extern void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb);
+void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);
+void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb);
 #else
 static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
@@ -138,8 +138,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 }
 #endif
 
-extern int cpts_register(struct device *dev, struct cpts *cpts,
-                        u32 mult, u32 shift);
-extern void cpts_unregister(struct cpts *cpts);
+int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift);
+void cpts_unregister(struct cpts *cpts);
 
 #endif
index 67df09e..41ba974 100644 (file)
@@ -876,8 +876,7 @@ static void emac_dev_mcast_set(struct net_device *ndev)
                    netdev_mc_count(ndev) > EMAC_DEF_MAX_MULTICAST_ADDRESSES) {
                        mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
                        emac_add_mcast(priv, EMAC_ALL_MULTI_SET, NULL);
-               }
-               if (!netdev_mc_empty(ndev)) {
+               } else if (!netdev_mc_empty(ndev)) {
                        struct netdev_hw_addr *ha;
 
                        mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
@@ -1853,7 +1852,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        }
 
        /* MAC addr and PHY mask , RMII enable info from platform_data */
-       memcpy(priv->mac_addr, pdata->mac_addr, 6);
+       memcpy(priv->mac_addr, pdata->mac_addr, ETH_ALEN);
        priv->phy_id = pdata->phy_id;
        priv->rmii_en = pdata->rmii_en;
        priv->version = pdata->version;
index 591437e..62b19be 100644 (file)
@@ -319,7 +319,6 @@ static void tlan_remove_one(struct pci_dev *pdev)
 
        free_netdev(dev);
 
-       pci_set_drvdata(pdev, NULL);
        cancel_work_sync(&priv->tlan_tqueue);
 }
 
index 13e6fff..628b736 100644 (file)
@@ -2230,7 +2230,7 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
                nz_addr |= mac[i];
 
        if (nz_addr) {
-               memcpy(dev->dev_addr, mac, 6);
+               memcpy(dev->dev_addr, mac, ETH_ALEN);
                dev->addr_len = 6;
        } else {
                eth_hw_addr_random(dev);
index 309abb4..8505196 100644 (file)
@@ -359,27 +359,26 @@ static inline void *port_priv(struct gelic_port *port)
 }
 
 #ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC
-extern void udbg_shutdown_ps3gelic(void);
+void udbg_shutdown_ps3gelic(void);
 #else
 static inline void udbg_shutdown_ps3gelic(void) {}
 #endif
 
-extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
+int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
 /* shared netdev ops */
-extern void gelic_card_up(struct gelic_card *card);
-extern void gelic_card_down(struct gelic_card *card);
-extern int gelic_net_open(struct net_device *netdev);
-extern int gelic_net_stop(struct net_device *netdev);
-extern int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev);
-extern void gelic_net_set_multi(struct net_device *netdev);
-extern void gelic_net_tx_timeout(struct net_device *netdev);
-extern int gelic_net_change_mtu(struct net_device *netdev, int new_mtu);
-extern int gelic_net_setup_netdev(struct net_device *netdev,
-                                 struct gelic_card *card);
+void gelic_card_up(struct gelic_card *card);
+void gelic_card_down(struct gelic_card *card);
+int gelic_net_open(struct net_device *netdev);
+int gelic_net_stop(struct net_device *netdev);
+int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev);
+void gelic_net_set_multi(struct net_device *netdev);
+void gelic_net_tx_timeout(struct net_device *netdev);
+int gelic_net_change_mtu(struct net_device *netdev, int new_mtu);
+int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card);
 
 /* shared ethtool ops */
-extern void gelic_net_get_drvinfo(struct net_device *netdev,
-                                 struct ethtool_drvinfo *info);
-extern void gelic_net_poll_controller(struct net_device *netdev);
+void gelic_net_get_drvinfo(struct net_device *netdev,
+                          struct ethtool_drvinfo *info);
+void gelic_net_poll_controller(struct net_device *netdev);
 
 #endif /* _GELIC_NET_H */
index f7e51b7..11f443d 100644 (file)
@@ -320,7 +320,7 @@ struct gelic_eurus_cmd {
 #define GELIC_WL_PRIV_SET_PSK          (SIOCIWFIRSTPRIV + 0)
 #define GELIC_WL_PRIV_GET_PSK          (SIOCIWFIRSTPRIV + 1)
 
-extern int gelic_wl_driver_probe(struct gelic_card *card);
-extern int gelic_wl_driver_remove(struct gelic_card *card);
-extern void gelic_wl_interrupt(struct net_device *netdev, u64 status);
+int gelic_wl_driver_probe(struct gelic_card *card);
+int gelic_wl_driver_remove(struct gelic_card *card);
+void gelic_wl_interrupt(struct net_device *netdev, u64 status);
 #endif /* _GELIC_WIRELESS_H */
index 5734480..3f4a32e 100644 (file)
@@ -2478,7 +2478,6 @@ out_release_regions:
        pci_release_regions(pdev);
 out_disable_dev:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        return NULL;
 }
 
index 4ba2135..9b6af08 100644 (file)
@@ -29,8 +29,8 @@
 
 #include <linux/sungem_phy.h>
 
-extern int spider_net_stop(struct net_device *netdev);
-extern int spider_net_open(struct net_device *netdev);
+int spider_net_stop(struct net_device *netdev);
+int spider_net_open(struct net_device *netdev);
 
 extern const struct ethtool_ops spider_net_ethtool_ops;
 
index a971b9c..1322546 100644 (file)
@@ -887,7 +887,6 @@ static void tc35815_remove_one(struct pci_dev *pdev)
        mdiobus_free(lp->mii_bus);
        unregister_netdev(dev);
        free_netdev(dev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static int
index c8f088a..4a7293e 100644 (file)
@@ -32,7 +32,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #define DRV_NAME       "via-rhine"
-#define DRV_VERSION    "1.5.0"
+#define DRV_VERSION    "1.5.1"
 #define DRV_RELDATE    "2010-10-09"
 
 #include <linux/types.h>
@@ -1704,7 +1704,12 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
                cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
 
        if (unlikely(vlan_tx_tag_present(skb))) {
-               rp->tx_ring[entry].tx_status = cpu_to_le32((vlan_tx_tag_get(skb)) << 16);
+               u16 vid_pcp = vlan_tx_tag_get(skb);
+
+               /* drop CFI/DEI bit, register needs VID and PCP */
+               vid_pcp = (vid_pcp & VLAN_VID_MASK) |
+                         ((vid_pcp & VLAN_PRIO_MASK) >> 1);
+               rp->tx_ring[entry].tx_status = cpu_to_le32((vid_pcp) << 16);
                /* request tagging */
                rp->tx_ring[entry].desc_length |= cpu_to_le32(0x020000);
        }
@@ -2287,7 +2292,6 @@ static void rhine_remove_one(struct pci_dev *pdev)
 
        free_netdev(dev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static void rhine_shutdown (struct pci_dev *pdev)
index b88121f..0029148 100644 (file)
@@ -297,6 +297,12 @@ static int temac_dma_bd_init(struct net_device *ndev)
                       lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1)));
        lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p);
 
+       /* Init descriptor indexes */
+       lp->tx_bd_ci = 0;
+       lp->tx_bd_next = 0;
+       lp->tx_bd_tail = 0;
+       lp->rx_bd_ci = 0;
+
        return 0;
 
 out:
index 4c619ea..74234a5 100644 (file)
@@ -31,7 +31,7 @@
 #define DRIVER_NAME "xilinx_emaclite"
 
 /* Register offsets for the EmacLite Core */
-#define XEL_TXBUFF_OFFSET      0x0             /* Transmit Buffer */
+#define XEL_TXBUFF_OFFSET      0x0             /* Transmit Buffer */
 #define XEL_MDIOADDR_OFFSET    0x07E4          /* MDIO Address Register */
 #define XEL_MDIOWR_OFFSET      0x07E8          /* MDIO Write Data Register */
 #define XEL_MDIORD_OFFSET      0x07EC          /* MDIO Read Data Register */
 #define XEL_MDIOCTRL_MDIOEN_MASK  0x00000008   /* MDIO Enable */
 
 /* Global Interrupt Enable Register (GIER) Bit Masks */
-#define XEL_GIER_GIE_MASK      0x80000000      /* Global Enable */
+#define XEL_GIER_GIE_MASK      0x80000000      /* Global Enable */
 
 /* Transmit Status Register (TSR) Bit Masks */
-#define XEL_TSR_XMIT_BUSY_MASK  0x00000001     /* Tx complete */
-#define XEL_TSR_PROGRAM_MASK    0x00000002     /* Program the MAC address */
-#define XEL_TSR_XMIT_IE_MASK    0x00000008     /* Tx interrupt enable bit */
-#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000    /* Buffer is active, SW bit
+#define XEL_TSR_XMIT_BUSY_MASK  0x00000001     /* Tx complete */
+#define XEL_TSR_PROGRAM_MASK    0x00000002     /* Program the MAC address */
+#define XEL_TSR_XMIT_IE_MASK    0x00000008     /* Tx interrupt enable bit */
+#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000    /* Buffer is active, SW bit
                                                 * only. This is not documented
                                                 * in the HW spec */
 
 #define XEL_TSR_PROG_MAC_ADDR  (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK)
 
 /* Receive Status Register (RSR) */
-#define XEL_RSR_RECV_DONE_MASK 0x00000001      /* Rx complete */
-#define XEL_RSR_RECV_IE_MASK   0x00000008      /* Rx interrupt enable bit */
+#define XEL_RSR_RECV_DONE_MASK 0x00000001      /* Rx complete */
+#define XEL_RSR_RECV_IE_MASK   0x00000008      /* Rx interrupt enable bit */
 
 /* Transmit Packet Length Register (TPLR) */
-#define XEL_TPLR_LENGTH_MASK   0x0000FFFF      /* Tx packet length */
+#define XEL_TPLR_LENGTH_MASK   0x0000FFFF      /* Tx packet length */
 
 /* Receive Packet Length Register (RPLR) */
-#define XEL_RPLR_LENGTH_MASK   0x0000FFFF      /* Rx packet length */
+#define XEL_RPLR_LENGTH_MASK   0x0000FFFF      /* Rx packet length */
 
-#define XEL_HEADER_OFFSET      12              /* Offset to length field */
-#define XEL_HEADER_SHIFT       16              /* Shift value for length */
+#define XEL_HEADER_OFFSET      12              /* Offset to length field */
+#define XEL_HEADER_SHIFT       16              /* Shift value for length */
 
 /* General Ethernet Definitions */
-#define XEL_ARP_PACKET_SIZE            28      /* Max ARP packet size */
-#define XEL_HEADER_IP_LENGTH_OFFSET    16      /* IP Length Offset */
+#define XEL_ARP_PACKET_SIZE            28      /* Max ARP packet size */
+#define XEL_HEADER_IP_LENGTH_OFFSET    16      /* IP Length Offset */
 
 
 
@@ -1075,14 +1075,9 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
  * This function un maps the IO region of the Emaclite device and frees the net
  * device.
  */
-static void xemaclite_remove_ndev(struct net_device *ndev,
-                                 struct platform_device *pdev)
+static void xemaclite_remove_ndev(struct net_device *ndev)
 {
        if (ndev) {
-               struct net_local *lp = netdev_priv(ndev);
-
-               if (lp->base_addr)
-                       devm_iounmap(&pdev->dev, lp->base_addr);
                free_netdev(ndev);
        }
 }
@@ -1177,7 +1172,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
 
        if (mac_address)
                /* Set the MAC address. */
-               memcpy(ndev->dev_addr, mac_address, 6);
+               memcpy(ndev->dev_addr, mac_address, ETH_ALEN);
        else
                dev_warn(dev, "No MAC address found\n");
 
@@ -1214,7 +1209,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
        return 0;
 
 error:
-       xemaclite_remove_ndev(ndev, ofdev);
+       xemaclite_remove_ndev(ndev);
        return rc;
 }
 
@@ -1248,7 +1243,7 @@ static int xemaclite_of_remove(struct platform_device *of_dev)
                of_node_put(lp->phy_node);
        lp->phy_node = NULL;
 
-       xemaclite_remove_ndev(ndev, of_dev);
+       xemaclite_remove_ndev(ndev);
 
        return 0;
 }
index a20ed1a..f839935 100644 (file)
@@ -453,7 +453,7 @@ static void directed_beacon(struct s_smc *smc)
         */
        * (char *) a = (char) ((long)DBEACON_INFO<<24L) ;
        a[1] = 0 ;
-       memcpy((char *)a+1,(char *) &smc->mib.m[MAC0].fddiMACUpstreamNbr,6) ;
+       memcpy((char *)a+1, (char *) &smc->mib.m[MAC0].fddiMACUpstreamNbr, ETH_ALEN);
 
        CHECK_NPP() ;
         /* set memory address reg for writes */
index 3ca308b..bd1166b 100644 (file)
@@ -469,20 +469,20 @@ struct s_smc {
 
 extern const struct fddi_addr fddi_broadcast;
 
-extern void all_selection_criteria(struct s_smc *smc);
-extern void card_stop(struct s_smc *smc);
-extern void init_board(struct s_smc *smc, u_char *mac_addr);
-extern int init_fplus(struct s_smc *smc);
-extern void init_plc(struct s_smc *smc);
-extern int init_smt(struct s_smc *smc, u_char * mac_addr);
-extern void mac1_irq(struct s_smc *smc, u_short stu, u_short stl);
-extern void mac2_irq(struct s_smc *smc, u_short code_s2u, u_short code_s2l);
-extern void mac3_irq(struct s_smc *smc, u_short code_s3u, u_short code_s3l);
-extern int pcm_status_twisted(struct s_smc *smc);
-extern void plc1_irq(struct s_smc *smc);
-extern void plc2_irq(struct s_smc *smc);
-extern void read_address(struct s_smc *smc, u_char * mac_addr);
-extern void timer_irq(struct s_smc *smc);
+void all_selection_criteria(struct s_smc *smc);
+void card_stop(struct s_smc *smc);
+void init_board(struct s_smc *smc, u_char *mac_addr);
+int init_fplus(struct s_smc *smc);
+void init_plc(struct s_smc *smc);
+int init_smt(struct s_smc *smc, u_char *mac_addr);
+void mac1_irq(struct s_smc *smc, u_short stu, u_short stl);
+void mac2_irq(struct s_smc *smc, u_short code_s2u, u_short code_s2l);
+void mac3_irq(struct s_smc *smc, u_short code_s3u, u_short code_s3l);
+int pcm_status_twisted(struct s_smc *smc);
+void plc1_irq(struct s_smc *smc);
+void plc2_irq(struct s_smc *smc);
+void read_address(struct s_smc *smc, u_char *mac_addr);
+void timer_irq(struct s_smc *smc);
 
 #endif /* _SCMECM_ */
 
index f5d7305..713d303 100644 (file)
@@ -436,7 +436,7 @@ static  int skfp_driver_init(struct net_device *dev)
        }
        read_address(smc, NULL);
        pr_debug("HW-Addr: %pMF\n", smc->hw.fddi_canon_addr.a);
-       memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6);
+       memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, ETH_ALEN);
 
        smt_reset_defaults(smc, 0);
 
@@ -503,7 +503,7 @@ static int skfp_open(struct net_device *dev)
         *               address.
         */
        read_address(smc, NULL);
-       memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6);
+       memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, ETH_ALEN);
 
        init_smt(smc, NULL);
        smt_online(smc, 1);
@@ -1213,7 +1213,7 @@ static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr)
        if ((unsigned short) frame[1 + 10] != 0)
                return;
        SRBit = frame[1 + 6] & 0x01;
-       memcpy(&frame[1 + 6], hw_addr, 6);
+       memcpy(&frame[1 + 6], hw_addr, ETH_ALEN);
        frame[8] |= SRBit;
 }                              // CheckSourceAddress
 
index a974727..636b65c 100644 (file)
@@ -445,7 +445,7 @@ static int ser12_open(struct net_device *dev)
        outb(0, FCR(dev->base_addr));  /* disable FIFOs */
        outb(0x0d, MCR(dev->base_addr));
        outb(0, IER(dev->base_addr));
-       if (request_irq(dev->irq, ser12_interrupt, IRQF_DISABLED | IRQF_SHARED,
+       if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
                        "baycom_ser_fdx", dev)) {
                release_region(dev->base_addr, SER12_EXTENT);
                return -EBUSY;
index e349d86..f9a8976 100644 (file)
@@ -490,7 +490,7 @@ static int ser12_open(struct net_device *dev)
        outb(0, FCR(dev->base_addr));  /* disable FIFOs */
        outb(0x0d, MCR(dev->base_addr));
        outb(0, IER(dev->base_addr));
-       if (request_irq(dev->irq, ser12_interrupt, IRQF_DISABLED | IRQF_SHARED,
+       if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
                        "baycom_ser12", dev)) {
                release_region(dev->base_addr, SER12_EXTENT);       
                return -EBUSY;
index bc1d521..4bc6ee8 100644 (file)
@@ -1734,7 +1734,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        if (!Ivec[hwcfg.irq].used && hwcfg.irq)
                        {
                                if (request_irq(hwcfg.irq, scc_isr,
-                                               IRQF_DISABLED, "AX.25 SCC",
+                                               0, "AX.25 SCC",
                                                (void *)(long) hwcfg.irq))
                                        printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
                                else
index 0721e72..1971411 100644 (file)
@@ -888,7 +888,7 @@ static int yam_open(struct net_device *dev)
                goto out_release_base;
        }
        outb(0, IER(dev->base_addr));
-       if (request_irq(dev->irq, yam_interrupt, IRQF_DISABLED | IRQF_SHARED, dev->name, dev)) {
+       if (request_irq(dev->irq, yam_interrupt, IRQF_SHARED, dev->name, dev)) {
                printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq);
                ret = -EBUSY;
                goto out_release_base;
@@ -975,7 +975,6 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        return -EINVAL;         /* Cannot change this parameter when up */
                if ((ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_KERNEL)) == NULL)
                        return -ENOBUFS;
-               ym->bitrate = 9600;
                if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs))) {
                        kfree(ym);
                        return -EFAULT;
index 42e6dee..0632d34 100644 (file)
@@ -82,7 +82,6 @@ struct mrf24j40 {
 
        struct mutex buffer_mutex; /* only used to protect buf */
        struct completion tx_complete;
-       struct work_struct irqwork;
        u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */
 };
 
@@ -344,6 +343,8 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (ret)
                goto err;
 
+       INIT_COMPLETION(devrec->tx_complete);
+
        /* Set TXNTRIG bit of TXNCON to send packet */
        ret = read_short_reg(devrec, REG_TXNCON, &val);
        if (ret)
@@ -354,8 +355,6 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
                val |= 0x4;
        write_short_reg(devrec, REG_TXNCON, val);
 
-       INIT_COMPLETION(devrec->tx_complete);
-
        /* Wait for the device to send the TX complete interrupt. */
        ret = wait_for_completion_interruptible_timeout(
                                                &devrec->tx_complete,
@@ -590,17 +589,6 @@ static struct ieee802154_ops mrf24j40_ops = {
 static irqreturn_t mrf24j40_isr(int irq, void *data)
 {
        struct mrf24j40 *devrec = data;
-
-       disable_irq_nosync(irq);
-
-       schedule_work(&devrec->irqwork);
-
-       return IRQ_HANDLED;
-}
-
-static void mrf24j40_isrwork(struct work_struct *work)
-{
-       struct mrf24j40 *devrec = container_of(work, struct mrf24j40, irqwork);
        u8 intstat;
        int ret;
 
@@ -618,7 +606,7 @@ static void mrf24j40_isrwork(struct work_struct *work)
                mrf24j40_handle_rx(devrec);
 
 out:
-       enable_irq(devrec->spi->irq);
+       return IRQ_HANDLED;
 }
 
 static int mrf24j40_probe(struct spi_device *spi)
@@ -642,7 +630,6 @@ static int mrf24j40_probe(struct spi_device *spi)
 
        mutex_init(&devrec->buffer_mutex);
        init_completion(&devrec->tx_complete);
-       INIT_WORK(&devrec->irqwork, mrf24j40_isrwork);
        devrec->spi = spi;
        spi_set_drvdata(spi, devrec);
 
@@ -688,11 +675,12 @@ static int mrf24j40_probe(struct spi_device *spi)
        val &= ~0x3; /* Clear RX mode (normal) */
        write_short_reg(devrec, REG_RXMCR, val);
 
-       ret = request_irq(spi->irq,
-                         mrf24j40_isr,
-                         IRQF_TRIGGER_FALLING,
-                         dev_name(&spi->dev),
-                         devrec);
+       ret = request_threaded_irq(spi->irq,
+                                  NULL,
+                                  mrf24j40_isr,
+                                  IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+                                  dev_name(&spi->dev),
+                                  devrec);
 
        if (ret) {
                dev_err(printdev(devrec), "Unable to get IRQ");
@@ -721,7 +709,6 @@ static int mrf24j40_remove(struct spi_device *spi)
        dev_dbg(printdev(devrec), "remove\n");
 
        free_irq(spi->irq, devrec);
-       flush_work(&devrec->irqwork); /* TODO: Is this the right call? */
        ieee802154_unregister_device(devrec->dev);
        ieee802154_free_device(devrec->dev);
        /* TODO: Will ieee802154_free_device() wait until ->xmit() is
index c74f384..303c4bd 100644 (file)
@@ -411,12 +411,12 @@ static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev)
 
 #else
 
-       if (request_irq(port->irq, bfin_sir_rx_int, IRQF_DISABLED, "BFIN_SIR_RX", dev)) {
+       if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) {
                dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n");
                return -EBUSY;
        }
 
-       if (request_irq(port->irq+1, bfin_sir_tx_int, IRQF_DISABLED, "BFIN_SIR_TX", dev)) {
+       if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) {
                dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n");
                free_irq(port->irq, dev);
                return -EBUSY;
index 31bcb98..768dfe9 100644 (file)
@@ -1352,7 +1352,7 @@ toshoboe_net_open (struct net_device *dev)
     return 0;
 
   rc = request_irq (self->io.irq, toshoboe_interrupt,
-                    IRQF_SHARED | IRQF_DISABLED, dev->name, self);
+                    IRQF_SHARED, dev->name, self);
   if (rc)
        return rc;
 
@@ -1559,7 +1559,7 @@ toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
   self->io.fir_base = self->base;
   self->io.fir_ext = OBOE_IO_EXTENT;
   self->io.irq = pci_dev->irq;
-  self->io.irqflags = IRQF_SHARED | IRQF_DISABLED;
+  self->io.irqflags = IRQF_SHARED;
 
   self->speed = self->io.speed = 9600;
   self->async = 0;
index 4455425..ff45cd0 100644 (file)
@@ -804,7 +804,7 @@ static int sh_irda_probe(struct platform_device *pdev)
                goto err_mem_4;
 
        platform_set_drvdata(pdev, ndev);
-       err = request_irq(irq, sh_irda_irq, IRQF_DISABLED, "sh_irda", self);
+       err = request_irq(irq, sh_irda_irq, 0, "sh_irda", self);
        if (err) {
                dev_warn(&pdev->dev, "Unable to attach sh_irda interrupt\n");
                goto err_mem_4;
index 89682b4..8d9ae5a 100644 (file)
@@ -761,7 +761,7 @@ static int sh_sir_probe(struct platform_device *pdev)
                goto err_mem_4;
 
        platform_set_drvdata(pdev, ndev);
-       err = request_irq(irq, sh_sir_irq, IRQF_DISABLED, "sh_sir", self);
+       err = request_irq(irq, sh_sir_irq, 0, "sh_sir", self);
        if (err) {
                dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n");
                goto err_mem_4;
index 6d5b1e2..f50b9c1 100644 (file)
@@ -102,28 +102,29 @@ struct sir_driver {
 
 /* exported */
 
-extern int irda_register_dongle(struct dongle_driver *new);
-extern int irda_unregister_dongle(struct dongle_driver *drv);
+int irda_register_dongle(struct dongle_driver *new);
+int irda_unregister_dongle(struct dongle_driver *drv);
 
-extern struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name);
-extern int sirdev_put_instance(struct sir_dev *self);
+struct sir_dev *sirdev_get_instance(const struct sir_driver *drv,
+                                   const char *name);
+int sirdev_put_instance(struct sir_dev *self);
 
-extern int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type);
-extern void sirdev_write_complete(struct sir_dev *dev);
-extern int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count);
+int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type);
+void sirdev_write_complete(struct sir_dev *dev);
+int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count);
 
 /* low level helpers for SIR device/dongle setup */
-extern int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len);
-extern int sirdev_raw_read(struct sir_dev *dev, char *buf, int len);
-extern int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts);
+int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len);
+int sirdev_raw_read(struct sir_dev *dev, char *buf, int len);
+int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts);
 
 /* not exported */
 
-extern int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type);
-extern int sirdev_put_dongle(struct sir_dev *self);
+int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type);
+int sirdev_put_dongle(struct sir_dev *self);
 
-extern void sirdev_enable_rx(struct sir_dev *dev);
-extern int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param);
+void sirdev_enable_rx(struct sir_dev *dev);
+int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param);
 
 /* inline helpers */
 
index 9bf46bd..af4aaa5 100644 (file)
@@ -297,7 +297,13 @@ netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
        int ret;
        const struct macvlan_dev *vlan = netdev_priv(dev);
 
-       ret = macvlan_queue_xmit(skb, dev);
+       if (vlan->fwd_priv) {
+               skb->dev = vlan->lowerdev;
+               ret = dev_hard_start_xmit(skb, skb->dev, NULL, vlan->fwd_priv);
+       } else {
+               ret = macvlan_queue_xmit(skb, dev);
+       }
+
        if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
                struct macvlan_pcpu_stats *pcpu_stats;
 
@@ -347,6 +353,21 @@ static int macvlan_open(struct net_device *dev)
                goto hash_add;
        }
 
+       if (lowerdev->features & NETIF_F_HW_L2FW_DOFFLOAD) {
+               vlan->fwd_priv =
+                     lowerdev->netdev_ops->ndo_dfwd_add_station(lowerdev, dev);
+
+               /* If we get a NULL pointer back, or if we get an error
+                * then we should just fall through to the non accelerated path
+                */
+               if (IS_ERR_OR_NULL(vlan->fwd_priv)) {
+                       vlan->fwd_priv = NULL;
+               } else {
+                       dev->features &= ~NETIF_F_LLTX;
+                       return 0;
+               }
+       }
+
        err = -EBUSY;
        if (macvlan_addr_busy(vlan->port, dev->dev_addr))
                goto out;
@@ -367,6 +388,11 @@ hash_add:
 del_unicast:
        dev_uc_del(lowerdev, dev->dev_addr);
 out:
+       if (vlan->fwd_priv) {
+               lowerdev->netdev_ops->ndo_dfwd_del_station(lowerdev,
+                                                          vlan->fwd_priv);
+               vlan->fwd_priv = NULL;
+       }
        return err;
 }
 
@@ -375,6 +401,13 @@ static int macvlan_stop(struct net_device *dev)
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
 
+       if (vlan->fwd_priv) {
+               lowerdev->netdev_ops->ndo_dfwd_del_station(lowerdev,
+                                                          vlan->fwd_priv);
+               vlan->fwd_priv = NULL;
+               return 0;
+       }
+
        dev_uc_unsync(lowerdev, dev);
        dev_mc_unsync(lowerdev, dev);
 
@@ -828,22 +861,22 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
                eth_hw_addr_inherit(dev, lowerdev);
        }
 
+       port->count += 1;
+       err = register_netdevice(dev);
+       if (err < 0)
+               goto destroy_port;
+
+       dev->priv_flags |= IFF_MACVLAN;
        err = netdev_upper_dev_link(lowerdev, dev);
        if (err)
                goto destroy_port;
 
-       port->count += 1;
-       err = register_netdevice(dev);
-       if (err < 0)
-               goto upper_dev_unlink;
 
        list_add_tail_rcu(&vlan->list, &port->vlans);
        netif_stacked_transfer_operstate(lowerdev, dev);
 
        return 0;
 
-upper_dev_unlink:
-       netdev_upper_dev_unlink(lowerdev, dev);
 destroy_port:
        port->count -= 1;
        if (!port->count)
index adeee61..ba2f5e7 100644 (file)
@@ -34,6 +34,8 @@
  *
  ****************************************************************/
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -310,6 +312,7 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                             const char *buf,
                             size_t count)
 {
+       unsigned long flags;
        int enabled;
        int err;
 
@@ -319,14 +322,12 @@ static ssize_t store_enabled(struct netconsole_target *nt,
        if (enabled < 0 || enabled > 1)
                return -EINVAL;
        if (enabled == nt->enabled) {
-               printk(KERN_INFO "netconsole: network logging has already %s\n",
-                               nt->enabled ? "started" : "stopped");
+               pr_info("network logging has already %s\n",
+                       nt->enabled ? "started" : "stopped");
                return -EINVAL;
        }
 
-       mutex_lock(&nt->mutex);
        if (enabled) {  /* 1 */
-
                /*
                 * Skip netpoll_parse_options() -- all the attributes are
                 * already configured via configfs. Just print them out.
@@ -334,19 +335,22 @@ static ssize_t store_enabled(struct netconsole_target *nt,
                netpoll_print_options(&nt->np);
 
                err = netpoll_setup(&nt->np);
-               if (err) {
-                       mutex_unlock(&nt->mutex);
+               if (err)
                        return err;
-               }
-
-               printk(KERN_INFO "netconsole: network logging started\n");
 
+               pr_info("netconsole: network logging started\n");
        } else {        /* 0 */
+               /* We need to disable the netconsole before cleaning it up
+                * otherwise we might end up in write_msg() with
+                * nt->np.dev == NULL and nt->enabled == 1
+                */
+               spin_lock_irqsave(&target_list_lock, flags);
+               nt->enabled = 0;
+               spin_unlock_irqrestore(&target_list_lock, flags);
                netpoll_cleanup(&nt->np);
        }
 
        nt->enabled = enabled;
-       mutex_unlock(&nt->mutex);
 
        return strnlen(buf, count);
 }
@@ -358,9 +362,8 @@ static ssize_t store_dev_name(struct netconsole_target *nt,
        size_t len;
 
        if (nt->enabled) {
-               printk(KERN_ERR "netconsole: target (%s) is enabled, "
-                               "disable to update parameters\n",
-                               config_item_name(&nt->item));
+               pr_err("target (%s) is enabled, disable to update parameters\n",
+                      config_item_name(&nt->item));
                return -EINVAL;
        }
 
@@ -381,9 +384,8 @@ static ssize_t store_local_port(struct netconsole_target *nt,
        int rv;
 
        if (nt->enabled) {
-               printk(KERN_ERR "netconsole: target (%s) is enabled, "
-                               "disable to update parameters\n",
-                               config_item_name(&nt->item));
+               pr_err("target (%s) is enabled, disable to update parameters\n",
+                      config_item_name(&nt->item));
                return -EINVAL;
        }
 
@@ -400,9 +402,8 @@ static ssize_t store_remote_port(struct netconsole_target *nt,
        int rv;
 
        if (nt->enabled) {
-               printk(KERN_ERR "netconsole: target (%s) is enabled, "
-                               "disable to update parameters\n",
-                               config_item_name(&nt->item));
+               pr_err("target (%s) is enabled, disable to update parameters\n",
+                      config_item_name(&nt->item));
                return -EINVAL;
        }
 
@@ -417,9 +418,8 @@ static ssize_t store_local_ip(struct netconsole_target *nt,
                              size_t count)
 {
        if (nt->enabled) {
-               printk(KERN_ERR "netconsole: target (%s) is enabled, "
-                               "disable to update parameters\n",
-                               config_item_name(&nt->item));
+               pr_err("target (%s) is enabled, disable to update parameters\n",
+                      config_item_name(&nt->item));
                return -EINVAL;
        }
 
@@ -427,7 +427,7 @@ static ssize_t store_local_ip(struct netconsole_target *nt,
                const char *end;
                if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) {
                        if (*end && *end != '\n') {
-                               printk(KERN_ERR "netconsole: invalid IPv6 address at: <%c>\n", *end);
+                               pr_err("invalid IPv6 address at: <%c>\n", *end);
                                return -EINVAL;
                        }
                        nt->np.ipv6 = true;
@@ -448,9 +448,8 @@ static ssize_t store_remote_ip(struct netconsole_target *nt,
                               size_t count)
 {
        if (nt->enabled) {
-               printk(KERN_ERR "netconsole: target (%s) is enabled, "
-                               "disable to update parameters\n",
-                               config_item_name(&nt->item));
+               pr_err("target (%s) is enabled, disable to update parameters\n",
+                      config_item_name(&nt->item));
                return -EINVAL;
        }
 
@@ -458,7 +457,7 @@ static ssize_t store_remote_ip(struct netconsole_target *nt,
                const char *end;
                if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) {
                        if (*end && *end != '\n') {
-                               printk(KERN_ERR "netconsole: invalid IPv6 address at: <%c>\n", *end);
+                               pr_err("invalid IPv6 address at: <%c>\n", *end);
                                return -EINVAL;
                        }
                        nt->np.ipv6 = true;
@@ -481,9 +480,8 @@ static ssize_t store_remote_mac(struct netconsole_target *nt,
        u8 remote_mac[ETH_ALEN];
 
        if (nt->enabled) {
-               printk(KERN_ERR "netconsole: target (%s) is enabled, "
-                               "disable to update parameters\n",
-                               config_item_name(&nt->item));
+               pr_err("target (%s) is enabled, disable to update parameters\n",
+                      config_item_name(&nt->item));
                return -EINVAL;
        }
 
@@ -563,8 +561,10 @@ static ssize_t netconsole_target_attr_store(struct config_item *item,
        struct netconsole_target_attr *na =
                container_of(attr, struct netconsole_target_attr, attr);
 
+       mutex_lock(&nt->mutex);
        if (na->store)
                ret = na->store(nt, buf, count);
+       mutex_unlock(&nt->mutex);
 
        return ret;
 }
@@ -704,19 +704,20 @@ restart:
        }
        spin_unlock_irqrestore(&target_list_lock, flags);
        if (stopped) {
-               printk(KERN_INFO "netconsole: network logging stopped on "
-                      "interface %s as it ", dev->name);
+               const char *msg = "had an event";
                switch (event) {
                case NETDEV_UNREGISTER:
-                       printk(KERN_CONT "unregistered\n");
+                       msg = "unregistered";
                        break;
                case NETDEV_RELEASE:
-                       printk(KERN_CONT "released slaves\n");
+                       msg = "released slaves";
                        break;
                case NETDEV_JOIN:
-                       printk(KERN_CONT "is joining a master device\n");
+                       msg = "is joining a master device";
                        break;
                }
+               pr_info("network logging stopped on interface %s as it %s\n",
+                       dev->name, msg);
        }
 
 done:
@@ -802,7 +803,7 @@ static int __init init_netconsole(void)
                goto undonotifier;
 
        register_console(&netconsole);
-       printk(KERN_INFO "netconsole: network logging started\n");
+       pr_info("network logging started\n");
 
        return err;
 
@@ -810,7 +811,7 @@ undonotifier:
        unregister_netdevice_notifier(&netconsole_netdev_notifier);
 
 fail:
-       printk(KERN_ERR "netconsole: cleaning up\n");
+       pr_err("cleaning up\n");
 
        /*
         * Remove all targets and destroy them (only targets created
index 342561a..9b5d46c 100644 (file)
@@ -154,6 +154,13 @@ config MDIO_SUN4I
          interface units of the Allwinner SoC that have an EMAC (A10,
          A12, A10s, etc.)
 
+config MDIO_MOXART
+        tristate "MOXA ART MDIO interface support"
+        depends on ARCH_MOXART
+        help
+          This driver supports the MDIO interface found in the network
+          interface units of the MOXA ART SoC
+
 config MDIO_BUS_MUX
        tristate
        depends on OF_MDIO
index 23a2ab2..9013dfa 100644 (file)
@@ -31,3 +31,4 @@ obj-$(CONFIG_MDIO_BUS_MUX)    += mdio-mux.o
 obj-$(CONFIG_MDIO_BUS_MUX_GPIO)        += mdio-mux-gpio.o
 obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
 obj-$(CONFIG_MDIO_SUN4I)       += mdio-sun4i.o
+obj-$(CONFIG_MDIO_MOXART)      += mdio-moxart.o
index ac22283..bc71947 100644 (file)
@@ -100,6 +100,45 @@ static void at803x_get_wol(struct phy_device *phydev,
                wol->wolopts |= WAKE_MAGIC;
 }
 
+static int at803x_suspend(struct phy_device *phydev)
+{
+       int value;
+       int wol_enabled;
+
+       mutex_lock(&phydev->lock);
+
+       value = phy_read(phydev, AT803X_INTR_ENABLE);
+       wol_enabled = value & AT803X_WOL_ENABLE;
+
+       value = phy_read(phydev, MII_BMCR);
+
+       if (wol_enabled)
+               value |= BMCR_ISOLATE;
+       else
+               value |= BMCR_PDOWN;
+
+       phy_write(phydev, MII_BMCR, value);
+
+       mutex_unlock(&phydev->lock);
+
+       return 0;
+}
+
+static int at803x_resume(struct phy_device *phydev)
+{
+       int value;
+
+       mutex_lock(&phydev->lock);
+
+       value = phy_read(phydev, MII_BMCR);
+       value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
+       phy_write(phydev, MII_BMCR, value);
+
+       mutex_unlock(&phydev->lock);
+
+       return 0;
+}
+
 static int at803x_config_init(struct phy_device *phydev)
 {
        int val;
@@ -161,10 +200,12 @@ static struct phy_driver at803x_driver[] = {
        .config_init    = at803x_config_init,
        .set_wol        = at803x_set_wol,
        .get_wol        = at803x_get_wol,
+       .suspend        = at803x_suspend,
+       .resume         = at803x_resume,
        .features       = PHY_GBIT_FEATURES,
        .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
        .driver         = {
                .owner = THIS_MODULE,
        },
@@ -176,10 +217,12 @@ static struct phy_driver at803x_driver[] = {
        .config_init    = at803x_config_init,
        .set_wol        = at803x_set_wol,
        .get_wol        = at803x_get_wol,
+       .suspend        = at803x_suspend,
+       .resume         = at803x_resume,
        .features       = PHY_GBIT_FEATURES,
        .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
        .driver         = {
                .owner = THIS_MODULE,
        },
@@ -191,10 +234,12 @@ static struct phy_driver at803x_driver[] = {
        .config_init    = at803x_config_init,
        .set_wol        = at803x_set_wol,
        .get_wol        = at803x_get_wol,
+       .suspend        = at803x_suspend,
+       .resume         = at803x_resume,
        .features       = PHY_GBIT_FEATURES,
        .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
        .driver         = {
                .owner = THIS_MODULE,
        },
index 2e91477..2e3c778 100644 (file)
@@ -34,9 +34,9 @@
 #include <linux/marvell_phy.h>
 #include <linux/of.h>
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <asm/irq.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 
 #define MII_MARVELL_PHY_PAGE           22
 
diff --git a/drivers/net/phy/mdio-moxart.c b/drivers/net/phy/mdio-moxart.c
new file mode 100644 (file)
index 0000000..a5741cb
--- /dev/null
@@ -0,0 +1,201 @@
+/* MOXA ART Ethernet (RTL8201CP) MDIO interface driver
+ *
+ * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define REG_PHY_CTRL            0
+#define REG_PHY_WRITE_DATA      4
+
+/* REG_PHY_CTRL */
+#define MIIWR                   BIT(27) /* init write sequence (auto cleared)*/
+#define MIIRD                   BIT(26)
+#define REGAD_MASK              0x3e00000
+#define PHYAD_MASK              0x1f0000
+#define MIIRDATA_MASK           0xffff
+
+/* REG_PHY_WRITE_DATA */
+#define MIIWDATA_MASK           0xffff
+
+struct moxart_mdio_data {
+       void __iomem            *base;
+};
+
+static int moxart_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+       struct moxart_mdio_data *data = bus->priv;
+       u32 ctrl = 0;
+       unsigned int count = 5;
+
+       dev_dbg(&bus->dev, "%s\n", __func__);
+
+       ctrl |= MIIRD | ((mii_id << 16) & PHYAD_MASK) |
+               ((regnum << 21) & REGAD_MASK);
+
+       writel(ctrl, data->base + REG_PHY_CTRL);
+
+       do {
+               ctrl = readl(data->base + REG_PHY_CTRL);
+
+               if (!(ctrl & MIIRD))
+                       return ctrl & MIIRDATA_MASK;
+
+               mdelay(10);
+               count--;
+       } while (count > 0);
+
+       dev_dbg(&bus->dev, "%s timed out\n", __func__);
+
+       return -ETIMEDOUT;
+}
+
+static int moxart_mdio_write(struct mii_bus *bus, int mii_id,
+                            int regnum, u16 value)
+{
+       struct moxart_mdio_data *data = bus->priv;
+       u32 ctrl = 0;
+       unsigned int count = 5;
+
+       dev_dbg(&bus->dev, "%s\n", __func__);
+
+       ctrl |= MIIWR | ((mii_id << 16) & PHYAD_MASK) |
+               ((regnum << 21) & REGAD_MASK);
+
+       value &= MIIWDATA_MASK;
+
+       writel(value, data->base + REG_PHY_WRITE_DATA);
+       writel(ctrl, data->base + REG_PHY_CTRL);
+
+       do {
+               ctrl = readl(data->base + REG_PHY_CTRL);
+
+               if (!(ctrl & MIIWR))
+                       return 0;
+
+               mdelay(10);
+               count--;
+       } while (count > 0);
+
+       dev_dbg(&bus->dev, "%s timed out\n", __func__);
+
+       return -ETIMEDOUT;
+}
+
+static int moxart_mdio_reset(struct mii_bus *bus)
+{
+       int data, i;
+
+       for (i = 0; i < PHY_MAX_ADDR; i++) {
+               data = moxart_mdio_read(bus, i, MII_BMCR);
+               if (data < 0)
+                       continue;
+
+               data |= BMCR_RESET;
+               if (moxart_mdio_write(bus, i, MII_BMCR, data) < 0)
+                       continue;
+       }
+
+       return 0;
+}
+
+static int moxart_mdio_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct mii_bus *bus;
+       struct moxart_mdio_data *data;
+       struct resource *res;
+       int ret, i;
+
+       bus = mdiobus_alloc_size(sizeof(*data));
+       if (!bus)
+               return -ENOMEM;
+
+       bus->name = "MOXA ART Ethernet MII";
+       bus->read = &moxart_mdio_read;
+       bus->write = &moxart_mdio_write;
+       bus->reset = &moxart_mdio_reset;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id);
+       bus->parent = &pdev->dev;
+
+       bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
+                       GFP_KERNEL);
+       if (!bus->irq) {
+               ret = -ENOMEM;
+               goto err_out_free_mdiobus;
+       }
+
+       /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
+        * of_mdiobus_register() sets these PHY_POLL.
+        * Ideally, the interrupt from MAC controller could be used to
+        * detect link state changes, not polling, i.e. if there was
+        * a way phy_driver could set PHY_HAS_INTERRUPT but have that
+        * interrupt handled in ethernet drivercode.
+        */
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               bus->irq[i] = PHY_IGNORE_INTERRUPT;
+
+       data = bus->priv;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       data->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(data->base)) {
+               ret = PTR_ERR(data->base);
+               goto err_out_free_mdiobus;
+       }
+
+       ret = of_mdiobus_register(bus, np);
+       if (ret < 0)
+               goto err_out_free_mdiobus;
+
+       platform_set_drvdata(pdev, bus);
+
+       return 0;
+
+err_out_free_mdiobus:
+       mdiobus_free(bus);
+       return ret;
+}
+
+static int moxart_mdio_remove(struct platform_device *pdev)
+{
+       struct mii_bus *bus = platform_get_drvdata(pdev);
+
+       mdiobus_unregister(bus);
+       mdiobus_free(bus);
+
+       return 0;
+}
+
+static const struct of_device_id moxart_mdio_dt_ids[] = {
+       { .compatible = "moxa,moxart-mdio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, moxart_mdio_dt_ids);
+
+static struct platform_driver moxart_mdio_driver = {
+       .probe = moxart_mdio_probe,
+       .remove = moxart_mdio_remove,
+       .driver = {
+               .name = "moxart-mdio",
+               .of_match_table = moxart_mdio_dt_ids,
+       },
+};
+
+module_platform_driver(moxart_mdio_driver);
+
+MODULE_DESCRIPTION("MOXA ART MDIO interface driver");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
+MODULE_LICENSE("GPL");
index c31aad0..3ae28f4 100644 (file)
@@ -287,6 +287,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = ks8737_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8021,
@@ -300,6 +302,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8031,
@@ -313,6 +317,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8041,
@@ -326,6 +332,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8051,
@@ -339,6 +347,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8001,
@@ -351,6 +361,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8081,
@@ -363,6 +375,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ8061,
@@ -375,6 +389,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE,},
 }, {
        .phy_id         = PHY_ID_KSZ9021,
@@ -387,6 +403,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = ksz9021_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE, },
 }, {
        .phy_id         = PHY_ID_KSZ9031,
@@ -400,6 +418,8 @@ static struct phy_driver ksphy_driver[] = {
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = ksz9021_config_intr,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE, },
 }, {
        .phy_id         = PHY_ID_KSZ8873MLL,
@@ -410,6 +430,8 @@ static struct phy_driver ksphy_driver[] = {
        .config_init    = kszphy_config_init,
        .config_aneg    = ksz8873mll_config_aneg,
        .read_status    = ksz8873mll_read_status,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE, },
 }, {
        .phy_id         = PHY_ID_KSZ886X,
@@ -420,6 +442,8 @@ static struct phy_driver ksphy_driver[] = {
        .config_init    = kszphy_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
+       .suspend        = genphy_suspend,
+       .resume         = genphy_resume,
        .driver         = { .owner = THIS_MODULE, },
 } };
 
index 1f7bef9..7b4ff35 100644 (file)
@@ -1002,7 +1002,7 @@ plip_rewrite_address(const struct net_device *dev, struct ethhdr *eth)
                /* Any address will do - we take the first */
                const struct in_ifaddr *ifa = in_dev->ifa_list;
                if (ifa) {
-                       memcpy(eth->h_source, dev->dev_addr, 6);
+                       memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
                        memset(eth->h_dest, 0xfc, 2);
                        memcpy(eth->h_dest+2, &ifa->ifa_address, 4);
                }
index a34d6bf..cc70ecf 100644 (file)
@@ -429,11 +429,13 @@ static void slip_write_wakeup(struct tty_struct *tty)
        if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev))
                return;
 
+       spin_lock(&sl->lock);
        if (sl->xleft <= 0)  {
                /* Now serial buffer is almost free & we can start
                 * transmission of another packet */
                sl->dev->stats.tx_packets++;
                clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+               spin_unlock(&sl->lock);
                sl_unlock(sl);
                return;
        }
@@ -441,6 +443,7 @@ static void slip_write_wakeup(struct tty_struct *tty)
        actual = tty->ops->write(tty, sl->xhead, sl->xleft);
        sl->xleft -= actual;
        sl->xhead += actual;
+       spin_unlock(&sl->lock);
 }
 
 static void sl_tx_timeout(struct net_device *dev)
index 807815f..7cb105c 100644 (file)
@@ -1293,7 +1293,8 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
        if (unlikely(!noblock))
                add_wait_queue(&tfile->wq.wait, &wait);
        while (len) {
-               current->state = TASK_INTERRUPTIBLE;
+               if (unlikely(!noblock))
+                       current->state = TASK_INTERRUPTIBLE;
 
                /* Read frames from the queue */
                if (!(skb = skb_dequeue(&tfile->socket.sk->sk_receive_queue))) {
@@ -1320,9 +1321,10 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
                break;
        }
 
-       current->state = TASK_RUNNING;
-       if (unlikely(!noblock))
+       if (unlikely(!noblock)) {
+               current->state = TASK_RUNNING;
                remove_wait_queue(&tfile->wq.wait, &wait);
+       }
 
        return ret;
 }
index 40db312..85e4a01 100644 (file)
@@ -242,6 +242,21 @@ config USB_NET_CDC_NCM
            * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design)
            * Ericsson F5521gw Mobile Broadband Module
 
+config USB_NET_HUAWEI_CDC_NCM
+       tristate "Huawei NCM embedded AT channel support"
+       depends on USB_USBNET
+       select USB_WDM
+       select USB_NET_CDC_NCM
+       help
+               This driver supports huawei-style NCM devices, that use NCM as a
+               transport for other protocols, usually an embedded AT channel.
+               Good examples are:
+               * Huawei E3131
+               * Huawei E3251
+
+               To compile this driver as a module, choose M here: the module will be
+               called huawei_cdc_ncm.ko.
+
 config USB_NET_CDC_MBIM
        tristate "CDC MBIM support"
        depends on USB_USBNET
index 8b342cf..b17b5e8 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_USB_IPHETH)      += ipheth.o
 obj-$(CONFIG_USB_SIERRA_NET)   += sierra_net.o
 obj-$(CONFIG_USB_NET_CX82310_ETH)      += cx82310_eth.o
 obj-$(CONFIG_USB_NET_CDC_NCM)  += cdc_ncm.o
+obj-$(CONFIG_USB_NET_HUAWEI_CDC_NCM)   += huawei_cdc_ncm.o
 obj-$(CONFIG_USB_VL600)                += lg-vl600.o
 obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
 obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o
index 3569293..8e8d0fc 100644 (file)
@@ -36,8 +36,8 @@
 #define AX_RXHDR_L4_TYPE_TCP                   16
 #define AX_RXHDR_L3CSUM_ERR                    2
 #define AX_RXHDR_L4CSUM_ERR                    1
-#define AX_RXHDR_CRC_ERR                       ((u32)BIT(31))
-#define AX_RXHDR_DROP_ERR                      ((u32)BIT(30))
+#define AX_RXHDR_CRC_ERR                       ((u32)BIT(29))
+#define AX_RXHDR_DROP_ERR                      ((u32)BIT(31))
 #define AX_ACCESS_MAC                          0x01
 #define AX_ACCESS_PHY                          0x02
 #define AX_ACCESS_EEPROM                       0x04
@@ -78,7 +78,6 @@
 #define AX_MEDIUM_STATUS_MODE                  0x22
        #define AX_MEDIUM_GIGAMODE      0x01
        #define AX_MEDIUM_FULL_DUPLEX   0x02
-       #define AX_MEDIUM_ALWAYS_ONE    0x04
        #define AX_MEDIUM_EN_125MHZ     0x08
        #define AX_MEDIUM_RXFLOW_CTRLEN 0x10
        #define AX_MEDIUM_TXFLOW_CTRLEN 0x20
@@ -1065,8 +1064,8 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 
        /* Configure default medium type => giga */
        *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
-                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
-                AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
+                AX_MEDIUM_GIGAMODE;
        ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
                          2, 2, tmp16);
 
@@ -1225,7 +1224,7 @@ static int ax88179_link_reset(struct usbnet *dev)
        }
 
        mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
-              AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+              AX_MEDIUM_RXFLOW_CTRLEN;
 
        ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
                         1, 1, &link_sts);
@@ -1339,8 +1338,8 @@ static int ax88179_reset(struct usbnet *dev)
 
        /* Configure default medium type => giga */
        *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
-                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
-                AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+                AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
+                AX_MEDIUM_GIGAMODE;
        ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
                          2, 2, tmp16);
 
@@ -1406,6 +1405,19 @@ static const struct driver_info sitecom_info = {
        .tx_fixup = ax88179_tx_fixup,
 };
 
+static const struct driver_info samsung_info = {
+       .description = "Samsung USB Ethernet Adapter",
+       .bind = ax88179_bind,
+       .unbind = ax88179_unbind,
+       .status = ax88179_status,
+       .link_reset = ax88179_link_reset,
+       .reset = ax88179_reset,
+       .stop = ax88179_stop,
+       .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+       .rx_fixup = ax88179_rx_fixup,
+       .tx_fixup = ax88179_tx_fixup,
+};
+
 static const struct usb_device_id products[] = {
 {
        /* ASIX AX88179 10/100/1000 */
@@ -1418,7 +1430,11 @@ static const struct usb_device_id products[] = {
 }, {
        /* Sitecom USB 3.0 to Gigabit Adapter */
        USB_DEVICE(0x0df6, 0x0072),
-       .driver_info = (unsigned long) &sitecom_info,
+       .driver_info = (unsigned long)&sitecom_info,
+}, {
+       /* Samsung USB Ethernet Adapter */
+       USB_DEVICE(0x04e8, 0xa100),
+       .driver_info = (unsigned long)&samsung_info,
 },
        { },
 };
index 8d5cac2..df507e6 100644 (file)
@@ -640,10 +640,10 @@ static void catc_set_multicast_list(struct net_device *netdev)
 {
        struct catc *catc = netdev_priv(netdev);
        struct netdev_hw_addr *ha;
-       u8 broadcast[6];
+       u8 broadcast[ETH_ALEN];
        u8 rx = RxEnable | RxPolarity | RxMultiCast;
 
-       memset(broadcast, 0xff, 6);
+       memset(broadcast, 0xff, ETH_ALEN);
        memset(catc->multicast, 0, 64);
 
        catc_multicast(broadcast, catc->multicast);
@@ -778,7 +778,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id
        struct usb_device *usbdev = interface_to_usbdev(intf);
        struct net_device *netdev;
        struct catc *catc;
-       u8 broadcast[6];
+       u8 broadcast[ETH_ALEN];
        int i, pktsz;
 
        if (usb_set_interface(usbdev,
@@ -882,7 +882,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id
                
                dev_dbg(dev, "Filling the multicast list.\n");
          
-               memset(broadcast, 0xff, 6);
+               memset(broadcast, 0xff, ETH_ALEN);
                catc_multicast(broadcast, catc->multicast);
                catc_multicast(netdev->dev_addr, catc->multicast);
                catc_write_mem(catc, 0xfa80, catc->multicast, 64);
index 7d78669..6358d42 100644 (file)
@@ -328,7 +328,7 @@ MODULE_DEVICE_TABLE(usb, usbpn_ids);
 
 static struct usb_driver usbpn_driver;
 
-int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)
+static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        static const char ifname[] = "usbpn%d";
        const struct usb_cdc_union_desc *union_header = NULL;
index 25ba7ec..c9f3281 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/usb/usbnet.h>
 #include <linux/usb/cdc-wdm.h>
 #include <linux/usb/cdc_ncm.h>
+#include <net/ipv6.h>
+#include <net/addrconf.h>
 
 /* driver specific data - must match cdc_ncm usage */
 struct cdc_mbim_state {
@@ -42,13 +44,11 @@ static int cdc_mbim_manage_power(struct usbnet *dev, int on)
        if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) {
                /* need autopm_get/put here to ensure the usbcore sees the new value */
                rv = usb_autopm_get_interface(dev->intf);
-               if (rv < 0)
-                       goto err;
                dev->intf->needs_remote_wakeup = on;
-               usb_autopm_put_interface(dev->intf);
+               if (!rv)
+                       usb_autopm_put_interface(dev->intf);
        }
-err:
-       return rv;
+       return 0;
 }
 
 static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)
@@ -173,7 +173,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
        }
 
        spin_lock_bh(&ctx->mtx);
-       skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign);
+       skb_out = cdc_ncm_fill_tx_frame(dev, skb, sign);
        spin_unlock_bh(&ctx->mtx);
        return skb_out;
 
@@ -184,6 +184,60 @@ error:
        return NULL;
 }
 
+/* Some devices are known to send Neigbor Solicitation messages and
+ * require Neigbor Advertisement replies.  The IPv6 core will not
+ * respond since IFF_NOARP is set, so we must handle them ourselves.
+ */
+static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci)
+{
+       struct ipv6hdr *iph = (void *)buf;
+       struct nd_msg *msg = (void *)(iph + 1);
+       struct net_device *netdev;
+       struct inet6_dev *in6_dev;
+       bool is_router;
+
+       /* we'll only respond to requests from unicast addresses to
+        * our solicited node addresses.
+        */
+       if (!ipv6_addr_is_solict_mult(&iph->daddr) ||
+           !(ipv6_addr_type(&iph->saddr) & IPV6_ADDR_UNICAST))
+               return;
+
+       /* need to send the NA on the VLAN dev, if any */
+       if (tci)
+               netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q),
+                                             tci);
+       else
+               netdev = dev->net;
+       if (!netdev)
+               return;
+
+       in6_dev = in6_dev_get(netdev);
+       if (!in6_dev)
+               return;
+       is_router = !!in6_dev->cnf.forwarding;
+       in6_dev_put(in6_dev);
+
+       /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */
+       ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target,
+                                is_router /* router */,
+                                true /* solicited */,
+                                false /* override */,
+                                true /* inc_opt */);
+}
+
+static bool is_neigh_solicit(u8 *buf, size_t len)
+{
+       struct ipv6hdr *iph = (void *)buf;
+       struct nd_msg *msg = (void *)(iph + 1);
+
+       return (len >= sizeof(struct ipv6hdr) + sizeof(struct nd_msg) &&
+               iph->nexthdr == IPPROTO_ICMPV6 &&
+               msg->icmph.icmp6_code == 0 &&
+               msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION);
+}
+
+
 static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci)
 {
        __be16 proto = htons(ETH_P_802_3);
@@ -198,6 +252,8 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
                        proto = htons(ETH_P_IP);
                        break;
                case 0x60:
+                       if (is_neigh_solicit(buf, len))
+                               do_neigh_solicit(dev, buf, tci);
                        proto = htons(ETH_P_IPV6);
                        break;
                default:
@@ -313,15 +369,13 @@ error:
 
 static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message)
 {
-       int ret = 0;
+       int ret = -ENODEV;
        struct usbnet *dev = usb_get_intfdata(intf);
        struct cdc_mbim_state *info = (void *)&dev->data;
        struct cdc_ncm_ctx *ctx = info->ctx;
 
-       if (ctx == NULL) {
-               ret = -1;
+       if (!ctx)
                goto error;
-       }
 
        /*
         * Both usbnet_suspend() and subdriver->suspend() MUST return 0
@@ -354,7 +408,7 @@ static int cdc_mbim_resume(struct usb_interface *intf)
        if (ret < 0)
                goto err;
        ret = usbnet_resume(intf);
-       if (ret < 0 && callsub && info->subdriver->suspend)
+       if (ret < 0 && callsub)
                info->subdriver->suspend(intf, PMSG_SUSPEND);
 err:
        return ret;
@@ -371,9 +425,18 @@ static const struct driver_info cdc_mbim_info = {
 };
 
 /* MBIM and NCM devices should not need a ZLP after NTBs with
- * dwNtbOutMaxSize length. This driver_info is for the exceptional
- * devices requiring it anyway, allowing them to be supported without
- * forcing the performance penalty on all the sane devices.
+ * dwNtbOutMaxSize length. Nevertheless, a number of devices from
+ * different vendor IDs will fail unless we send ZLPs, forcing us
+ * to make this the default.
+ *
+ * This default may cause a performance penalty for spec conforming
+ * devices wanting to take advantage of optimizations possible without
+ * ZLPs.  A whitelist is added in an attempt to avoid this for devices
+ * known to conform to the MBIM specification.
+ *
+ * All known devices supporting NCM compatibility mode are also
+ * conforming to the NCM and MBIM specifications. For this reason, the
+ * NCM subclass entry is also in the ZLP whitelist.
  */
 static const struct driver_info cdc_mbim_info_zlp = {
        .description = "CDC MBIM",
@@ -396,16 +459,13 @@ static const struct usb_device_id mbim_devs[] = {
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
          .driver_info = (unsigned long)&cdc_mbim_info,
        },
-       /* Sierra Wireless MC7710 need ZLPs */
-       { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
-         .driver_info = (unsigned long)&cdc_mbim_info_zlp,
-       },
-       /* HP hs2434 Mobile Broadband Module needs ZLPs */
-       { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
-         .driver_info = (unsigned long)&cdc_mbim_info_zlp,
+       /* ZLP conformance whitelist: All Ericsson MBIM devices */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0bdb, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+         .driver_info = (unsigned long)&cdc_mbim_info,
        },
+       /* default entry */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
-         .driver_info = (unsigned long)&cdc_mbim_info,
+         .driver_info = (unsigned long)&cdc_mbim_info_zlp,
        },
        {
        },
index 43afde8..f74786a 100644 (file)
@@ -53,8 +53,6 @@
 #include <linux/usb/cdc.h>
 #include <linux/usb/cdc_ncm.h>
 
-#define        DRIVER_VERSION                          "14-Mar-2012"
-
 #if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
 static bool prefer_mbim = true;
 #else
@@ -68,71 +66,67 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
 static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
 static struct usb_driver cdc_ncm_driver;
 
-static void
-cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
-{
-       struct usbnet *dev = netdev_priv(net);
-
-       strlcpy(info->driver, dev->driver_name, sizeof(info->driver));
-       strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
-       strlcpy(info->fw_version, dev->driver_info->description,
-               sizeof(info->fw_version));
-       usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
-}
-
-static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
+static u8 cdc_ncm_setup(struct usbnet *dev)
 {
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       struct usb_cdc_ncm_ntb_parameters ncm_parm;
        u32 val;
        u8 flags;
        u8 iface_no;
        int err;
        int eth_hlen;
        u16 ntb_fmt_supported;
-       u32 min_dgram_size;
-       u32 min_hdr_size;
-       struct usbnet *dev = netdev_priv(ctx->netdev);
+       __le16 max_datagram_size;
 
        iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
 
        err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
                              USB_TYPE_CLASS | USB_DIR_IN
                              |USB_RECIP_INTERFACE,
-                             0, iface_no, &ctx->ncm_parm,
-                             sizeof(ctx->ncm_parm));
+                             0, iface_no, &ncm_parm,
+                             sizeof(ncm_parm));
        if (err < 0) {
-               pr_debug("failed GET_NTB_PARAMETERS\n");
-               return 1;
+               dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n");
+               return err; /* GET_NTB_PARAMETERS is required */
        }
 
        /* read correct set of parameters according to device mode */
-       ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);
-       ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);
-       ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder);
-       ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor);
-       ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);
+       ctx->rx_max = le32_to_cpu(ncm_parm.dwNtbInMaxSize);
+       ctx->tx_max = le32_to_cpu(ncm_parm.dwNtbOutMaxSize);
+       ctx->tx_remainder = le16_to_cpu(ncm_parm.wNdpOutPayloadRemainder);
+       ctx->tx_modulus = le16_to_cpu(ncm_parm.wNdpOutDivisor);
+       ctx->tx_ndp_modulus = le16_to_cpu(ncm_parm.wNdpOutAlignment);
        /* devices prior to NCM Errata shall set this field to zero */
-       ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams);
-       ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported);
+       ctx->tx_max_datagrams = le16_to_cpu(ncm_parm.wNtbOutMaxDatagrams);
+       ntb_fmt_supported = le16_to_cpu(ncm_parm.bmNtbFormatsSupported);
 
-       eth_hlen = ETH_HLEN;
-       min_dgram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
-       min_hdr_size = CDC_NCM_MIN_HDR_SIZE;
-       if (ctx->mbim_desc != NULL) {
-               flags = ctx->mbim_desc->bmNetworkCapabilities;
+       /* there are some minor differences in NCM and MBIM defaults */
+       if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) {
+               if (!ctx->mbim_desc)
+                       return -EINVAL;
                eth_hlen = 0;
-               min_dgram_size = CDC_MBIM_MIN_DATAGRAM_SIZE;
-               min_hdr_size = 0;
-       } else if (ctx->func_desc != NULL) {
-               flags = ctx->func_desc->bmNetworkCapabilities;
+               flags = ctx->mbim_desc->bmNetworkCapabilities;
+               ctx->max_datagram_size = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
+               if (ctx->max_datagram_size < CDC_MBIM_MIN_DATAGRAM_SIZE)
+                       ctx->max_datagram_size = CDC_MBIM_MIN_DATAGRAM_SIZE;
        } else {
-               flags = 0;
+               if (!ctx->func_desc)
+                       return -EINVAL;
+               eth_hlen = ETH_HLEN;
+               flags = ctx->func_desc->bmNetworkCapabilities;
+               ctx->max_datagram_size = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
+               if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
+                       ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
        }
 
-       pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u "
-                "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u "
-                "wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n",
-                ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
-                ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags);
+       /* common absolute max for NCM and MBIM */
+       if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
+               ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;
+
+       dev_dbg(&dev->intf->dev,
+               "dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n",
+               ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
+               ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags);
 
        /* max count of tx datagrams */
        if ((ctx->tx_max_datagrams == 0) ||
@@ -141,19 +135,19 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
        /* verify maximum size of received NTB in bytes */
        if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) {
-               pr_debug("Using min receive length=%d\n",
-                                               USB_CDC_NCM_NTB_MIN_IN_SIZE);
+               dev_dbg(&dev->intf->dev, "Using min receive length=%d\n",
+                       USB_CDC_NCM_NTB_MIN_IN_SIZE);
                ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE;
        }
 
        if (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX) {
-               pr_debug("Using default maximum receive length=%d\n",
-                                               CDC_NCM_NTB_MAX_SIZE_RX);
+               dev_dbg(&dev->intf->dev, "Using default maximum receive length=%d\n",
+                       CDC_NCM_NTB_MAX_SIZE_RX);
                ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX;
        }
 
        /* inform device about NTB input size changes */
-       if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
+       if (ctx->rx_max != le32_to_cpu(ncm_parm.dwNtbInMaxSize)) {
                __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
 
                err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
@@ -161,16 +155,22 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
                                       | USB_RECIP_INTERFACE,
                                       0, iface_no, &dwNtbInMaxSize, 4);
                if (err < 0)
-                       pr_debug("Setting NTB Input Size failed\n");
+                       dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
        }
 
        /* verify maximum size of transmitted NTB in bytes */
-       if ((ctx->tx_max <
-           (min_hdr_size + min_dgram_size)) ||
-           (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX)) {
-               pr_debug("Using default maximum transmit length=%d\n",
-                                               CDC_NCM_NTB_MAX_SIZE_TX);
+       if (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX) {
+               dev_dbg(&dev->intf->dev, "Using default maximum transmit length=%d\n",
+                       CDC_NCM_NTB_MAX_SIZE_TX);
                ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX;
+
+               /* Adding a pad byte here simplifies the handling in
+                * cdc_ncm_fill_tx_frame, by making tx_max always
+                * represent the real skb max size.
+                */
+               if (ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0)
+                       ctx->tx_max++;
+
        }
 
        /*
@@ -183,7 +183,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
        if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
            (val != ((-val) & val)) || (val >= ctx->tx_max)) {
-               pr_debug("Using default alignment: 4 bytes\n");
+               dev_dbg(&dev->intf->dev, "Using default alignment: 4 bytes\n");
                ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
        }
 
@@ -197,13 +197,13 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
        if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
            (val != ((-val) & val)) || (val >= ctx->tx_max)) {
-               pr_debug("Using default transmit modulus: 4 bytes\n");
+               dev_dbg(&dev->intf->dev, "Using default transmit modulus: 4 bytes\n");
                ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
        }
 
        /* verify the payload remainder */
        if (ctx->tx_remainder >= ctx->tx_modulus) {
-               pr_debug("Using default transmit remainder: 0 bytes\n");
+               dev_dbg(&dev->intf->dev, "Using default transmit remainder: 0 bytes\n");
                ctx->tx_remainder = 0;
        }
 
@@ -221,7 +221,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
                                       USB_CDC_NCM_CRC_NOT_APPENDED,
                                       iface_no, NULL, 0);
                if (err < 0)
-                       pr_debug("Setting CRC mode off failed\n");
+                       dev_dbg(&dev->intf->dev, "Setting CRC mode off failed\n");
        }
 
        /* set NTB format, if both formats are supported */
@@ -232,69 +232,43 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
                                       USB_CDC_NCM_NTB16_FORMAT,
                                       iface_no, NULL, 0);
                if (err < 0)
-                       pr_debug("Setting NTB format to 16-bit failed\n");
+                       dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit failed\n");
        }
 
-       ctx->max_datagram_size = min_dgram_size;
+       /* inform the device about the selected Max Datagram Size */
+       if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
+               goto out;
 
-       /* set Max Datagram Size (MTU) */
-       if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) {
-               __le16 max_datagram_size;
-               u16 eth_max_sz;
-               if (ctx->ether_desc != NULL)
-                       eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
-               else if (ctx->mbim_desc != NULL)
-                       eth_max_sz = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
-               else
-                       goto max_dgram_err;
-
-               err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
-                                     USB_TYPE_CLASS | USB_DIR_IN
-                                     | USB_RECIP_INTERFACE,
-                                     0, iface_no, &max_datagram_size, 2);
-               if (err < 0) {
-                       pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n",
-                                min_dgram_size);
-               } else {
-                       ctx->max_datagram_size =
-                               le16_to_cpu(max_datagram_size);
-                       /* Check Eth descriptor value */
-                       if (ctx->max_datagram_size > eth_max_sz)
-                                       ctx->max_datagram_size = eth_max_sz;
-
-                       if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
-                               ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;
-
-                       if (ctx->max_datagram_size < min_dgram_size)
-                               ctx->max_datagram_size = min_dgram_size;
-
-                       /* if value changed, update device */
-                       if (ctx->max_datagram_size !=
-                                       le16_to_cpu(max_datagram_size)) {
-                               err = usbnet_write_cmd(dev,
-                                               USB_CDC_SET_MAX_DATAGRAM_SIZE,
-                                               USB_TYPE_CLASS | USB_DIR_OUT
-                                                | USB_RECIP_INTERFACE,
-                                               0,
-                                               iface_no, &max_datagram_size,
-                                               2);
-                               if (err < 0)
-                                       pr_debug("SET_MAX_DGRAM_SIZE failed\n");
-                       }
-               }
+       /* read current mtu value from device */
+       err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
+                             USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+                             0, iface_no, &max_datagram_size, 2);
+       if (err < 0) {
+               dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
+               goto out;
        }
 
-max_dgram_err:
-       if (ctx->netdev->mtu != (ctx->max_datagram_size - eth_hlen))
-               ctx->netdev->mtu = ctx->max_datagram_size - eth_hlen;
+       if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
+               goto out;
+
+       max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
+       err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE,
+                              USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+                              0, iface_no, &max_datagram_size, 2);
+       if (err < 0)
+               dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");
 
+out:
+       /* set MTU to max supported by the device if necessary */
+       if (dev->net->mtu > ctx->max_datagram_size - eth_hlen)
+               dev->net->mtu = ctx->max_datagram_size - eth_hlen;
        return 0;
 }
 
 static void
-cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)
+cdc_ncm_find_endpoints(struct usbnet *dev, struct usb_interface *intf)
 {
-       struct usb_host_endpoint *e;
+       struct usb_host_endpoint *e, *in = NULL, *out = NULL;
        u8 ep;
 
        for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) {
@@ -303,18 +277,18 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)
                switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
                case USB_ENDPOINT_XFER_INT:
                        if (usb_endpoint_dir_in(&e->desc)) {
-                               if (ctx->status_ep == NULL)
-                                       ctx->status_ep = e;
+                               if (!dev->status)
+                                       dev->status = e;
                        }
                        break;
 
                case USB_ENDPOINT_XFER_BULK:
                        if (usb_endpoint_dir_in(&e->desc)) {
-                               if (ctx->in_ep == NULL)
-                                       ctx->in_ep = e;
+                               if (!in)
+                                       in = e;
                        } else {
-                               if (ctx->out_ep == NULL)
-                                       ctx->out_ep = e;
+                               if (!out)
+                                       out = e;
                        }
                        break;
 
@@ -322,6 +296,14 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf)
                        break;
                }
        }
+       if (in && !dev->in)
+               dev->in = usb_rcvbulkpipe(dev->udev,
+                                         in->desc.bEndpointAddress &
+                                         USB_ENDPOINT_NUMBER_MASK);
+       if (out && !dev->out)
+               dev->out = usb_sndbulkpipe(dev->udev,
+                                          out->desc.bEndpointAddress &
+                                          USB_ENDPOINT_NUMBER_MASK);
 }
 
 static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
@@ -342,18 +324,9 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
        kfree(ctx);
 }
 
-static const struct ethtool_ops cdc_ncm_ethtool_ops = {
-       .get_drvinfo = cdc_ncm_get_drvinfo,
-       .get_link = usbnet_get_link,
-       .get_msglevel = usbnet_get_msglevel,
-       .set_msglevel = usbnet_set_msglevel,
-       .get_settings = usbnet_get_settings,
-       .set_settings = usbnet_set_settings,
-       .nway_reset = usbnet_nway_reset,
-};
-
 int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting)
 {
+       const struct usb_cdc_union_desc *union_desc = NULL;
        struct cdc_ncm_ctx *ctx;
        struct usb_driver *driver;
        u8 *buf;
@@ -367,23 +340,22 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
 
        hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        ctx->tx_timer.function = &cdc_ncm_tx_timer_cb;
-       ctx->bh.data = (unsigned long)ctx;
+       ctx->bh.data = (unsigned long)dev;
        ctx->bh.func = cdc_ncm_txpath_bh;
        atomic_set(&ctx->stop, 0);
        spin_lock_init(&ctx->mtx);
-       ctx->netdev = dev->net;
 
        /* store ctx pointer in device data field */
        dev->data[0] = (unsigned long)ctx;
 
+       /* only the control interface can be successfully probed */
+       ctx->control = intf;
+
        /* get some pointers */
        driver = driver_of(intf);
        buf = intf->cur_altsetting->extra;
        len = intf->cur_altsetting->extralen;
 
-       ctx->udev = dev->udev;
-       ctx->intf = intf;
-
        /* parse through descriptors associated with control interface */
        while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) {
 
@@ -392,16 +364,18 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
 
                switch (buf[2]) {
                case USB_CDC_UNION_TYPE:
-                       if (buf[0] < sizeof(*(ctx->union_desc)))
+                       if (buf[0] < sizeof(*union_desc))
                                break;
 
-                       ctx->union_desc =
-                                       (const struct usb_cdc_union_desc *)buf;
-
-                       ctx->control = usb_ifnum_to_if(dev->udev,
-                                       ctx->union_desc->bMasterInterface0);
+                       union_desc = (const struct usb_cdc_union_desc *)buf;
+                       /* the master must be the interface we are probing */
+                       if (intf->cur_altsetting->desc.bInterfaceNumber !=
+                           union_desc->bMasterInterface0) {
+                               dev_dbg(&intf->dev, "bogus CDC Union\n");
+                               goto error;
+                       }
                        ctx->data = usb_ifnum_to_if(dev->udev,
-                                       ctx->union_desc->bSlaveInterface0);
+                                                   union_desc->bSlaveInterface0);
                        break;
 
                case USB_CDC_ETHERNET_TYPE:
@@ -410,13 +384,6 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
 
                        ctx->ether_desc =
                                        (const struct usb_cdc_ether_desc *)buf;
-                       dev->hard_mtu =
-                               le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
-
-                       if (dev->hard_mtu < CDC_NCM_MIN_DATAGRAM_SIZE)
-                               dev->hard_mtu = CDC_NCM_MIN_DATAGRAM_SIZE;
-                       else if (dev->hard_mtu > CDC_NCM_MAX_DATAGRAM_SIZE)
-                               dev->hard_mtu = CDC_NCM_MAX_DATAGRAM_SIZE;
                        break;
 
                case USB_CDC_NCM_TYPE:
@@ -444,69 +411,71 @@ advance:
        }
 
        /* some buggy devices have an IAD but no CDC Union */
-       if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
-               ctx->control = intf;
+       if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
                ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);
                dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");
        }
 
        /* check if we got everything */
-       if ((ctx->control == NULL) || (ctx->data == NULL) ||
-           ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf))))
+       if (!ctx->data || (!ctx->mbim_desc && !ctx->ether_desc)) {
+               dev_dbg(&intf->dev, "CDC descriptors missing\n");
                goto error;
+       }
 
        /* claim data interface, if different from control */
        if (ctx->data != ctx->control) {
                temp = usb_driver_claim_interface(driver, ctx->data, dev);
-               if (temp)
+               if (temp) {
+                       dev_dbg(&intf->dev, "failed to claim data intf\n");
                        goto error;
+               }
        }
 
        iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
 
        /* reset data interface */
        temp = usb_set_interface(dev->udev, iface_no, 0);
-       if (temp)
-               goto error2;
-
-       /* initialize data interface */
-       if (cdc_ncm_setup(ctx))
+       if (temp) {
+               dev_dbg(&intf->dev, "set interface failed\n");
                goto error2;
+       }
 
        /* configure data interface */
        temp = usb_set_interface(dev->udev, iface_no, data_altsetting);
-       if (temp)
+       if (temp) {
+               dev_dbg(&intf->dev, "set interface failed\n");
                goto error2;
+       }
 
-       cdc_ncm_find_endpoints(ctx, ctx->data);
-       cdc_ncm_find_endpoints(ctx, ctx->control);
-
-       if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) ||
-           (ctx->status_ep == NULL))
+       cdc_ncm_find_endpoints(dev, ctx->data);
+       cdc_ncm_find_endpoints(dev, ctx->control);
+       if (!dev->in || !dev->out || !dev->status) {
+               dev_dbg(&intf->dev, "failed to collect endpoints\n");
                goto error2;
+       }
 
-       dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
+       /* initialize data interface */
+       if (cdc_ncm_setup(dev)) {
+               dev_dbg(&intf->dev, "cdc_ncm_setup() failed\n");
+               goto error2;
+       }
 
        usb_set_intfdata(ctx->data, dev);
        usb_set_intfdata(ctx->control, dev);
-       usb_set_intfdata(ctx->intf, dev);
 
        if (ctx->ether_desc) {
                temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress);
-               if (temp)
+               if (temp) {
+                       dev_dbg(&intf->dev, "failed to get mac address\n");
                        goto error2;
-               dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
+               }
+               dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
        }
 
-
-       dev->in = usb_rcvbulkpipe(dev->udev,
-               ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
-       dev->out = usb_sndbulkpipe(dev->udev,
-               ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
-       dev->status = ctx->status_ep;
+       /* usbnet use these values for sizing tx/rx queues */
+       dev->hard_mtu = ctx->tx_max;
        dev->rx_urb_size = ctx->rx_max;
 
-       ctx->tx_speed = ctx->rx_speed = 0;
        return 0;
 
 error2:
@@ -517,7 +486,7 @@ error2:
 error:
        cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]);
        dev->data[0] = 0;
-       dev_info(&dev->udev->dev, "bind() failure\n");
+       dev_info(&intf->dev, "bind() failure\n");
        return -ENODEV;
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_bind_common);
@@ -553,7 +522,7 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
                ctx->control = NULL;
        }
 
-       usb_set_intfdata(ctx->intf, NULL);
+       usb_set_intfdata(intf, NULL);
        cdc_ncm_free(ctx);
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
@@ -662,8 +631,9 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
 }
 
 struct sk_buff *
-cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
+cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
 {
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
        struct usb_cdc_ncm_nth16 *nth16;
        struct usb_cdc_ncm_ndp16 *ndp16;
        struct sk_buff *skb_out;
@@ -683,11 +653,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
 
        /* allocate a new OUT skb */
        if (!skb_out) {
-               skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC);
+               skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC);
                if (skb_out == NULL) {
                        if (skb != NULL) {
                                dev_kfree_skb_any(skb);
-                               ctx->netdev->stats.tx_dropped++;
+                               dev->net->stats.tx_dropped++;
                        }
                        goto exit_no_skb;
                }
@@ -725,12 +695,12 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
                                /* won't fit, MTU problem? */
                                dev_kfree_skb_any(skb);
                                skb = NULL;
-                               ctx->netdev->stats.tx_dropped++;
+                               dev->net->stats.tx_dropped++;
                        } else {
                                /* no room for skb - store for later */
                                if (ctx->tx_rem_skb != NULL) {
                                        dev_kfree_skb_any(ctx->tx_rem_skb);
-                                       ctx->netdev->stats.tx_dropped++;
+                                       dev->net->stats.tx_dropped++;
                                }
                                ctx->tx_rem_skb = skb;
                                ctx->tx_rem_sign = sign;
@@ -763,7 +733,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
        if (skb != NULL) {
                dev_kfree_skb_any(skb);
                skb = NULL;
-               ctx->netdev->stats.tx_dropped++;
+               dev->net->stats.tx_dropped++;
        }
 
        ctx->tx_curr_frame_num = n;
@@ -788,19 +758,20 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
                /* variables will be reset at next call */
        }
 
-       /*
-        * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes,
-        * we send buffers as it is. If we get more data, it would be more
-        * efficient for USB HS mobile device with DMA engine to receive a full
-        * size NTB, than canceling DMA transfer and receiving a short packet.
+       /* If collected data size is less or equal CDC_NCM_MIN_TX_PKT
+        * bytes, we send buffers as it is. If we get more data, it
+        * would be more efficient for USB HS mobile device with DMA
+        * engine to receive a full size NTB, than canceling DMA
+        * transfer and receiving a short packet.
+        *
+        * This optimization support is pointless if we end up sending
+        * a ZLP after full sized NTBs.
         */
-       if (skb_out->len > CDC_NCM_MIN_TX_PKT)
-               /* final zero padding */
-               memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len);
-
-       /* do we need to prevent a ZLP? */
-       if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) &&
-           (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out))
+       if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
+           skb_out->len > CDC_NCM_MIN_TX_PKT)
+               memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0,
+                      ctx->tx_max - skb_out->len);
+       else if ((skb_out->len % dev->maxpacket) == 0)
                *skb_put(skb_out, 1) = 0;       /* force short packet */
 
        /* set final frame length */
@@ -809,7 +780,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
 
        /* return skb */
        ctx->tx_curr_skb = NULL;
-       ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num;
+       dev->net->stats.tx_packets += ctx->tx_curr_frame_num;
        return skb_out;
 
 exit_no_skb:
@@ -841,24 +812,25 @@ static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer)
 
 static void cdc_ncm_txpath_bh(unsigned long param)
 {
-       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)param;
+       struct usbnet *dev = (struct usbnet *)param;
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
 
        spin_lock_bh(&ctx->mtx);
        if (ctx->tx_timer_pending != 0) {
                ctx->tx_timer_pending--;
                cdc_ncm_tx_timeout_start(ctx);
                spin_unlock_bh(&ctx->mtx);
-       } else if (ctx->netdev != NULL) {
+       } else if (dev->net != NULL) {
                spin_unlock_bh(&ctx->mtx);
-               netif_tx_lock_bh(ctx->netdev);
-               usbnet_start_xmit(NULL, ctx->netdev);
-               netif_tx_unlock_bh(ctx->netdev);
+               netif_tx_lock_bh(dev->net);
+               usbnet_start_xmit(NULL, dev->net);
+               netif_tx_unlock_bh(dev->net);
        } else {
                spin_unlock_bh(&ctx->mtx);
        }
 }
 
-static struct sk_buff *
+struct sk_buff *
 cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
 {
        struct sk_buff *skb_out;
@@ -875,7 +847,7 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
                goto error;
 
        spin_lock_bh(&ctx->mtx);
-       skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
+       skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN));
        spin_unlock_bh(&ctx->mtx);
        return skb_out;
 
@@ -885,10 +857,12 @@ error:
 
        return NULL;
 }
+EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup);
 
 /* verify NTB header and return offset of first NDP, or negative error */
 int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
 {
+       struct usbnet *dev = netdev_priv(skb_in->dev);
        struct usb_cdc_ncm_nth16 *nth16;
        int len;
        int ret = -EINVAL;
@@ -898,30 +872,33 @@ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
 
        if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +
                                        sizeof(struct usb_cdc_ncm_ndp16))) {
-               pr_debug("frame too short\n");
+               netif_dbg(dev, rx_err, dev->net, "frame too short\n");
                goto error;
        }
 
        nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data;
 
-       if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) {
-               pr_debug("invalid NTH16 signature <%u>\n",
-                                       le32_to_cpu(nth16->dwSignature));
+       if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) {
+               netif_dbg(dev, rx_err, dev->net,
+                         "invalid NTH16 signature <%#010x>\n",
+                         le32_to_cpu(nth16->dwSignature));
                goto error;
        }
 
        len = le16_to_cpu(nth16->wBlockLength);
        if (len > ctx->rx_max) {
-               pr_debug("unsupported NTB block length %u/%u\n", len,
-                                                               ctx->rx_max);
+               netif_dbg(dev, rx_err, dev->net,
+                         "unsupported NTB block length %u/%u\n", len,
+                         ctx->rx_max);
                goto error;
        }
 
        if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) &&
-               (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) &&
-               !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) {
-               pr_debug("sequence number glitch prev=%d curr=%d\n",
-                               ctx->rx_seq, le16_to_cpu(nth16->wSequence));
+           (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) &&
+           !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) {
+               netif_dbg(dev, rx_err, dev->net,
+                         "sequence number glitch prev=%d curr=%d\n",
+                         ctx->rx_seq, le16_to_cpu(nth16->wSequence));
        }
        ctx->rx_seq = le16_to_cpu(nth16->wSequence);
 
@@ -934,18 +911,20 @@ EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16);
 /* verify NDP header and return number of datagrams, or negative error */
 int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
 {
+       struct usbnet *dev = netdev_priv(skb_in->dev);
        struct usb_cdc_ncm_ndp16 *ndp16;
        int ret = -EINVAL;
 
        if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
-               pr_debug("invalid NDP offset  <%u>\n", ndpoffset);
+               netif_dbg(dev, rx_err, dev->net, "invalid NDP offset  <%u>\n",
+                         ndpoffset);
                goto error;
        }
        ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
 
        if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
-               pr_debug("invalid DPT16 length <%u>\n",
-                                       le32_to_cpu(ndp16->dwSignature));
+               netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n",
+                         le16_to_cpu(ndp16->wLength));
                goto error;
        }
 
@@ -954,9 +933,9 @@ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
                                        sizeof(struct usb_cdc_ncm_dpe16));
        ret--; /* we process NDP entries except for the last one */
 
-       if ((sizeof(struct usb_cdc_ncm_ndp16) + ret * (sizeof(struct usb_cdc_ncm_dpe16))) >
-                                                               skb_in->len) {
-               pr_debug("Invalid nframes = %d\n", ret);
+       if ((sizeof(struct usb_cdc_ncm_ndp16) +
+            ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) {
+               netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
                ret = -EINVAL;
        }
 
@@ -965,7 +944,7 @@ error:
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16);
 
-static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
+int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
 {
        struct sk_buff *skb;
        struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
@@ -989,9 +968,10 @@ next_ndp:
 
        ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
 
-       if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) {
-               pr_debug("invalid DPT16 signature <%u>\n",
-                        le32_to_cpu(ndp16->dwSignature));
+       if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) {
+               netif_dbg(dev, rx_err, dev->net,
+                         "invalid DPT16 signature <%#010x>\n",
+                         le32_to_cpu(ndp16->dwSignature));
                goto err_ndp;
        }
        dpe16 = ndp16->dpe16;
@@ -1013,9 +993,9 @@ next_ndp:
                /* sanity checking */
                if (((offset + len) > skb_in->len) ||
                                (len > ctx->rx_max) || (len < ETH_HLEN)) {
-                       pr_debug("invalid frame detected (ignored)"
-                                       "offset[%u]=%u, length=%u, skb=%p\n",
-                                       x, offset, len, skb_in);
+                       netif_dbg(dev, rx_err, dev->net,
+                                 "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n",
+                                 x, offset, len, skb_in);
                        if (!x)
                                goto err_ndp;
                        break;
@@ -1040,9 +1020,10 @@ err_ndp:
 error:
        return 0;
 }
+EXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup);
 
 static void
-cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx,
+cdc_ncm_speed_change(struct usbnet *dev,
                     struct usb_cdc_speed_change *data)
 {
        uint32_t rx_speed = le32_to_cpu(data->DLBitRRate);
@@ -1052,25 +1033,16 @@ cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx,
         * Currently the USB-NET API does not support reporting the actual
         * device speed. Do print it instead.
         */
-       if ((tx_speed != ctx->tx_speed) || (rx_speed != ctx->rx_speed)) {
-               ctx->tx_speed = tx_speed;
-               ctx->rx_speed = rx_speed;
-
-               if ((tx_speed > 1000000) && (rx_speed > 1000000)) {
-                       printk(KERN_INFO KBUILD_MODNAME
-                               ": %s: %u mbit/s downlink "
-                               "%u mbit/s uplink\n",
-                               ctx->netdev->name,
-                               (unsigned int)(rx_speed / 1000000U),
-                               (unsigned int)(tx_speed / 1000000U));
-               } else {
-                       printk(KERN_INFO KBUILD_MODNAME
-                               ": %s: %u kbit/s downlink "
-                               "%u kbit/s uplink\n",
-                               ctx->netdev->name,
-                               (unsigned int)(rx_speed / 1000U),
-                               (unsigned int)(tx_speed / 1000U));
-               }
+       if ((tx_speed > 1000000) && (rx_speed > 1000000)) {
+               netif_info(dev, link, dev->net,
+                      "%u mbit/s downlink %u mbit/s uplink\n",
+                      (unsigned int)(rx_speed / 1000000U),
+                      (unsigned int)(tx_speed / 1000000U));
+       } else {
+               netif_info(dev, link, dev->net,
+                      "%u kbit/s downlink %u kbit/s uplink\n",
+                      (unsigned int)(rx_speed / 1000U),
+                      (unsigned int)(tx_speed / 1000U));
        }
 }
 
@@ -1086,7 +1058,7 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
 
        /* test for split data in 8-byte chunks */
        if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
-               cdc_ncm_speed_change(ctx,
+               cdc_ncm_speed_change(dev,
                      (struct usb_cdc_speed_change *)urb->transfer_buffer);
                return;
        }
@@ -1101,14 +1073,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
                 */
                ctx->connected = le16_to_cpu(event->wValue);
-
-               printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:"
-                       " %sconnected\n",
-                       ctx->netdev->name, ctx->connected ? "" : "dis");
-
+               netif_info(dev, link, dev->net,
+                          "network connection: %sconnected\n",
+                          ctx->connected ? "" : "dis");
                usbnet_link_change(dev, ctx->connected, 0);
-               if (!ctx->connected)
-                       ctx->tx_speed = ctx->rx_speed = 0;
                break;
 
        case USB_CDC_NOTIFY_SPEED_CHANGE:
@@ -1116,8 +1084,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                                        sizeof(struct usb_cdc_speed_change)))
                        set_bit(EVENT_STS_SPLIT, &dev->flags);
                else
-                       cdc_ncm_speed_change(ctx,
-                               (struct usb_cdc_speed_change *) &event[1]);
+                       cdc_ncm_speed_change(dev,
+                                            (struct usb_cdc_speed_change *)&event[1]);
                break;
 
        default:
@@ -1139,22 +1107,6 @@ static int cdc_ncm_check_connect(struct usbnet *dev)
        return !ctx->connected;
 }
 
-static int
-cdc_ncm_probe(struct usb_interface *udev, const struct usb_device_id *prod)
-{
-       return usbnet_probe(udev, prod);
-}
-
-static void cdc_ncm_disconnect(struct usb_interface *intf)
-{
-       struct usbnet *dev = usb_get_intfdata(intf);
-
-       if (dev == NULL)
-               return;         /* already disconnected */
-
-       usbnet_disconnect(intf);
-}
-
 static const struct driver_info cdc_ncm_info = {
        .description = "CDC NCM",
        .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,
@@ -1234,17 +1186,6 @@ static const struct usb_device_id cdc_devs[] = {
          .driver_info = (unsigned long)&wwan_info,
        },
 
-       /* Huawei NCM devices disguised as vendor specific */
-       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16),
-         .driver_info = (unsigned long)&wwan_info,
-       },
-       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46),
-         .driver_info = (unsigned long)&wwan_info,
-       },
-       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76),
-         .driver_info = (unsigned long)&wwan_info,
-       },
-
        /* Infineon(now Intel) HSPA Modem platform */
        { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,
                USB_CLASS_COMM,
@@ -1265,8 +1206,8 @@ MODULE_DEVICE_TABLE(usb, cdc_devs);
 static struct usb_driver cdc_ncm_driver = {
        .name = "cdc_ncm",
        .id_table = cdc_devs,
-       .probe = cdc_ncm_probe,
-       .disconnect = cdc_ncm_disconnect,
+       .probe = usbnet_probe,
+       .disconnect = usbnet_disconnect,
        .suspend = usbnet_suspend,
        .resume = usbnet_resume,
        .reset_resume = usbnet_resume,
index 2dbb946..c6867f9 100644 (file)
@@ -303,7 +303,7 @@ static void dm9601_set_multicast(struct net_device *net)
                rx_ctl |= 0x02;
        } else if (net->flags & IFF_ALLMULTI ||
                   netdev_mc_count(net) > DM_MAX_MCAST) {
-               rx_ctl |= 0x04;
+               rx_ctl |= 0x08;
        } else if (!netdev_mc_empty(net)) {
                struct netdev_hw_addr *ha;
 
diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c
new file mode 100644 (file)
index 0000000..312178d
--- /dev/null
@@ -0,0 +1,230 @@
+/* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as
+ * transport layer.
+ * Copyright (C) 2013   Enrico Mioso <mrkiko.rs@gmail.com>
+ *
+ *
+ * ABSTRACT:
+ * This driver handles devices resembling the CDC NCM standard, but
+ * encapsulating another protocol inside it. An example are some Huawei 3G
+ * devices, exposing an embedded AT channel where you can set up the NCM
+ * connection.
+ * This code has been heavily inspired by the cdc_mbim.c driver, which is
+ * Copyright (c) 2012  Smith Micro Software, Inc.
+ * Copyright (c) 2012  Bjørn Mork <bjorn@mork.no>
+ *
+ * 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/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/usb/cdc-wdm.h>
+#include <linux/usb/cdc_ncm.h>
+
+/* Driver data */
+struct huawei_cdc_ncm_state {
+       struct cdc_ncm_ctx *ctx;
+       atomic_t pmcount;
+       struct usb_driver *subdriver;
+       struct usb_interface *control;
+       struct usb_interface *data;
+};
+
+static int huawei_cdc_ncm_manage_power(struct usbnet *usbnet_dev, int on)
+{
+       struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
+       int rv;
+
+       if ((on && atomic_add_return(1, &drvstate->pmcount) == 1) ||
+                       (!on && atomic_dec_and_test(&drvstate->pmcount))) {
+               rv = usb_autopm_get_interface(usbnet_dev->intf);
+               usbnet_dev->intf->needs_remote_wakeup = on;
+               if (!rv)
+                       usb_autopm_put_interface(usbnet_dev->intf);
+       }
+       return 0;
+}
+
+static int huawei_cdc_ncm_wdm_manage_power(struct usb_interface *intf,
+                                          int status)
+{
+       struct usbnet *usbnet_dev = usb_get_intfdata(intf);
+
+       /* can be called while disconnecting */
+       if (!usbnet_dev)
+               return 0;
+
+       return huawei_cdc_ncm_manage_power(usbnet_dev, status);
+}
+
+
+static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev,
+                              struct usb_interface *intf)
+{
+       struct cdc_ncm_ctx *ctx;
+       struct usb_driver *subdriver = ERR_PTR(-ENODEV);
+       int ret = -ENODEV;
+       struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
+
+       /* altsetting should always be 1 for NCM devices - so we hard-coded
+        * it here
+        */
+       ret = cdc_ncm_bind_common(usbnet_dev, intf, 1);
+       if (ret)
+               goto err;
+
+       ctx = drvstate->ctx;
+
+       if (usbnet_dev->status)
+               /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256
+                * decimal (0x100)"
+                */
+               subdriver = usb_cdc_wdm_register(ctx->control,
+                                                &usbnet_dev->status->desc,
+                                                256, /* wMaxCommand */
+                                                huawei_cdc_ncm_wdm_manage_power);
+       if (IS_ERR(subdriver)) {
+               ret = PTR_ERR(subdriver);
+               cdc_ncm_unbind(usbnet_dev, intf);
+               goto err;
+       }
+
+       /* Prevent usbnet from using the status descriptor */
+       usbnet_dev->status = NULL;
+
+       drvstate->subdriver = subdriver;
+
+err:
+       return ret;
+}
+
+static void huawei_cdc_ncm_unbind(struct usbnet *usbnet_dev,
+                                 struct usb_interface *intf)
+{
+       struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
+       struct cdc_ncm_ctx *ctx = drvstate->ctx;
+
+       if (drvstate->subdriver && drvstate->subdriver->disconnect)
+               drvstate->subdriver->disconnect(ctx->control);
+       drvstate->subdriver = NULL;
+
+       cdc_ncm_unbind(usbnet_dev, intf);
+}
+
+static int huawei_cdc_ncm_suspend(struct usb_interface *intf,
+                                 pm_message_t message)
+{
+       int ret = 0;
+       struct usbnet *usbnet_dev = usb_get_intfdata(intf);
+       struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
+       struct cdc_ncm_ctx *ctx = drvstate->ctx;
+
+       if (ctx == NULL) {
+               ret = -ENODEV;
+               goto error;
+       }
+
+       ret = usbnet_suspend(intf, message);
+       if (ret < 0)
+               goto error;
+
+       if (intf == ctx->control &&
+               drvstate->subdriver &&
+               drvstate->subdriver->suspend)
+               ret = drvstate->subdriver->suspend(intf, message);
+       if (ret < 0)
+               usbnet_resume(intf);
+
+error:
+       return ret;
+}
+
+static int huawei_cdc_ncm_resume(struct usb_interface *intf)
+{
+       int ret = 0;
+       struct usbnet *usbnet_dev = usb_get_intfdata(intf);
+       struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
+       bool callsub;
+       struct cdc_ncm_ctx *ctx = drvstate->ctx;
+
+       /* should we call subdriver's resume function? */
+       callsub =
+               (intf == ctx->control &&
+               drvstate->subdriver &&
+               drvstate->subdriver->resume);
+
+       if (callsub)
+               ret = drvstate->subdriver->resume(intf);
+       if (ret < 0)
+               goto err;
+       ret = usbnet_resume(intf);
+       if (ret < 0 && callsub)
+               drvstate->subdriver->suspend(intf, PMSG_SUSPEND);
+err:
+       return ret;
+}
+
+static int huawei_cdc_ncm_check_connect(struct usbnet *usbnet_dev)
+{
+       struct cdc_ncm_ctx *ctx;
+
+       ctx = (struct cdc_ncm_ctx *)usbnet_dev->data[0];
+
+       if (ctx == NULL)
+               return 1; /* disconnected */
+
+       return !ctx->connected;
+}
+
+static const struct driver_info huawei_cdc_ncm_info = {
+       .description = "Huawei CDC NCM device",
+       .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
+       .bind = huawei_cdc_ncm_bind,
+       .unbind = huawei_cdc_ncm_unbind,
+       .check_connect = huawei_cdc_ncm_check_connect,
+       .manage_power = huawei_cdc_ncm_manage_power,
+       .rx_fixup = cdc_ncm_rx_fixup,
+       .tx_fixup = cdc_ncm_tx_fixup,
+};
+
+static const struct usb_device_id huawei_cdc_ncm_devs[] = {
+       /* Huawei NCM devices disguised as vendor specific */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16),
+         .driver_info = (unsigned long)&huawei_cdc_ncm_info,
+       },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46),
+         .driver_info = (unsigned long)&huawei_cdc_ncm_info,
+       },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76),
+         .driver_info = (unsigned long)&huawei_cdc_ncm_info,
+       },
+
+       /* Terminating entry */
+       {
+       },
+};
+MODULE_DEVICE_TABLE(usb, huawei_cdc_ncm_devs);
+
+static struct usb_driver huawei_cdc_ncm_driver = {
+       .name = "huawei_cdc_ncm",
+       .id_table = huawei_cdc_ncm_devs,
+       .probe = usbnet_probe,
+       .disconnect = usbnet_disconnect,
+       .suspend = huawei_cdc_ncm_suspend,
+       .resume = huawei_cdc_ncm_resume,
+       .reset_resume = huawei_cdc_ncm_resume,
+       .supports_autosuspend = 1,
+       .disable_hub_initiated_lpm = 1,
+};
+module_usb_driver(huawei_cdc_ncm_driver);
+MODULE_AUTHOR("Enrico Mioso <mrkiko.rs@gmail.com>");
+MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support");
+MODULE_LICENSE("GPL");
index 6312332..23bdd5b 100644 (file)
@@ -143,24 +143,28 @@ static const struct net_device_ops qmi_wwan_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
-/* using a counter to merge subdriver requests with our own into a combined state */
+/* using a counter to merge subdriver requests with our own into a
+ * combined state
+ */
 static int qmi_wwan_manage_power(struct usbnet *dev, int on)
 {
        struct qmi_wwan_state *info = (void *)&dev->data;
-       int rv = 0;
+       int rv;
 
-       dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on);
+       dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__,
+               atomic_read(&info->pmcount), on);
 
-       if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) {
-               /* need autopm_get/put here to ensure the usbcore sees the new value */
+       if ((on && atomic_add_return(1, &info->pmcount) == 1) ||
+           (!on && atomic_dec_and_test(&info->pmcount))) {
+               /* need autopm_get/put here to ensure the usbcore sees
+                * the new value
+                */
                rv = usb_autopm_get_interface(dev->intf);
-               if (rv < 0)
-                       goto err;
                dev->intf->needs_remote_wakeup = on;
-               usb_autopm_put_interface(dev->intf);
+               if (!rv)
+                       usb_autopm_put_interface(dev->intf);
        }
-err:
-       return rv;
+       return 0;
 }
 
 static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on)
@@ -199,7 +203,8 @@ static int qmi_wwan_register_subdriver(struct usbnet *dev)
        atomic_set(&info->pmcount, 0);
 
        /* register subdriver */
-       subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, 4096, &qmi_wwan_cdc_wdm_manage_power);
+       subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc,
+                                        4096, &qmi_wwan_cdc_wdm_manage_power);
        if (IS_ERR(subdriver)) {
                dev_err(&info->control->dev, "subdriver registration failed\n");
                rv = PTR_ERR(subdriver);
@@ -228,7 +233,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
        struct usb_driver *driver = driver_of(intf);
        struct qmi_wwan_state *info = (void *)&dev->data;
 
-       BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state)));
+       BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) <
+                     sizeof(struct qmi_wwan_state)));
 
        /* set up initial state */
        info->control = intf;
@@ -250,7 +256,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
                                goto err;
                        }
                        if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
-                               dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength);
+                               dev_dbg(&intf->dev, "CDC header len %u\n",
+                                       h->bLength);
                                goto err;
                        }
                        break;
@@ -260,7 +267,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
                                goto err;
                        }
                        if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
-                               dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength);
+                               dev_dbg(&intf->dev, "CDC union len %u\n",
+                                       h->bLength);
                                goto err;
                        }
                        cdc_union = (struct usb_cdc_union_desc *)buf;
@@ -271,15 +279,15 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
                                goto err;
                        }
                        if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
-                               dev_dbg(&intf->dev, "CDC ether len %u\n",  h->bLength);
+                               dev_dbg(&intf->dev, "CDC ether len %u\n",
+                                       h->bLength);
                                goto err;
                        }
                        cdc_ether = (struct usb_cdc_ether_desc *)buf;
                        break;
                }
 
-               /*
-                * Remember which CDC functional descriptors we've seen.  Works
+               /* Remember which CDC functional descriptors we've seen.  Works
                 * for all types we care about, of which USB_CDC_ETHERNET_TYPE
                 * (0x0f) is the highest numbered
                 */
@@ -293,10 +301,14 @@ next_desc:
 
        /* Use separate control and data interfaces if we found a CDC Union */
        if (cdc_union) {
-               info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0);
-               if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) {
-                       dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n",
-                               cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0);
+               info->data = usb_ifnum_to_if(dev->udev,
+                                            cdc_union->bSlaveInterface0);
+               if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 ||
+                   !info->data) {
+                       dev_err(&intf->dev,
+                               "bogus CDC Union: master=%u, slave=%u\n",
+                               cdc_union->bMasterInterface0,
+                               cdc_union->bSlaveInterface0);
                        goto err;
                }
        }
@@ -374,8 +386,7 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
        struct qmi_wwan_state *info = (void *)&dev->data;
        int ret;
 
-       /*
-        * Both usbnet_suspend() and subdriver->suspend() MUST return 0
+       /* Both usbnet_suspend() and subdriver->suspend() MUST return 0
         * in system sleep context, otherwise, the resume callback has
         * to recover device from previous suspend failure.
         */
@@ -383,7 +394,8 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
        if (ret < 0)
                goto err;
 
-       if (intf == info->control && info->subdriver && info->subdriver->suspend)
+       if (intf == info->control && info->subdriver &&
+           info->subdriver->suspend)
                ret = info->subdriver->suspend(intf, message);
        if (ret < 0)
                usbnet_resume(intf);
@@ -396,14 +408,15 @@ static int qmi_wwan_resume(struct usb_interface *intf)
        struct usbnet *dev = usb_get_intfdata(intf);
        struct qmi_wwan_state *info = (void *)&dev->data;
        int ret = 0;
-       bool callsub = (intf == info->control && info->subdriver && info->subdriver->resume);
+       bool callsub = (intf == info->control && info->subdriver &&
+                       info->subdriver->resume);
 
        if (callsub)
                ret = info->subdriver->resume(intf);
        if (ret < 0)
                goto err;
        ret = usbnet_resume(intf);
-       if (ret < 0 && callsub && info->subdriver->suspend)
+       if (ret < 0 && callsub)
                info->subdriver->suspend(intf, PMSG_SUSPEND);
 err:
        return ret;
@@ -714,7 +727,9 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
        {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
-       {QMI_FIXED_INTF(0x1e2d, 0x12d1, 4)},    /* Cinterion PLxx */
+       {QMI_FIXED_INTF(0x1bc7, 0x1201, 2)},    /* Telit LE920 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},    /* Olivetti Olicard 200 */
+       {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},    /* Cinterion PLxx */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
@@ -776,7 +791,8 @@ static const struct usb_device_id products[] = {
 };
 MODULE_DEVICE_TABLE(usb, products);
 
-static int qmi_wwan_probe(struct usb_interface *intf, const struct usb_device_id *prod)
+static int qmi_wwan_probe(struct usb_interface *intf,
+                         const struct usb_device_id *prod)
 {
        struct usb_device_id *id = (struct usb_device_id *)prod;
 
index 7b331e6..90a429b 100644 (file)
@@ -1241,7 +1241,9 @@ static int build_dma_sg(const struct sk_buff *skb, struct urb *urb)
        if (num_sgs == 1)
                return 0;
 
-       urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), GFP_ATOMIC);
+       /* reserve one for zero packet */
+       urb->sg = kmalloc((num_sgs + 1) * sizeof(struct scatterlist),
+                         GFP_ATOMIC);
        if (!urb->sg)
                return -ENOMEM;
 
@@ -1305,7 +1307,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
                if (build_dma_sg(skb, urb) < 0)
                        goto drop;
        }
-       entry->length = length = urb->transfer_buffer_length;
+       length = urb->transfer_buffer_length;
 
        /* don't assume the hardware handles USB_ZERO_PACKET
         * NOTE:  strictly conforming cdc-ether devices should expect
@@ -1317,15 +1319,18 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
        if (length % dev->maxpacket == 0) {
                if (!(info->flags & FLAG_SEND_ZLP)) {
                        if (!(info->flags & FLAG_MULTI_PACKET)) {
-                               urb->transfer_buffer_length++;
-                               if (skb_tailroom(skb)) {
+                               length++;
+                               if (skb_tailroom(skb) && !urb->num_sgs) {
                                        skb->data[skb->len] = 0;
                                        __skb_put(skb, 1);
-                               }
+                               } else if (urb->num_sgs)
+                                       sg_set_buf(&urb->sg[urb->num_sgs++],
+                                                       dev->padding_pkt, 1);
                        }
                } else
                        urb->transfer_flags |= URB_ZERO_PACKET;
        }
+       entry->length = urb->transfer_buffer_length = length;
 
        spin_lock_irqsave(&dev->txq.lock, flags);
        retval = usb_autopm_get_interface_async(dev->intf);
@@ -1509,6 +1514,7 @@ void usbnet_disconnect (struct usb_interface *intf)
 
        usb_kill_urb(dev->interrupt);
        usb_free_urb(dev->interrupt);
+       kfree(dev->padding_pkt);
 
        free_netdev(net);
 }
@@ -1679,9 +1685,18 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
        /* initialize max rx_qlen and tx_qlen */
        usbnet_update_max_qlen(dev);
 
+       if (dev->can_dma_sg && !(info->flags & FLAG_SEND_ZLP) &&
+               !(info->flags & FLAG_MULTI_PACKET)) {
+               dev->padding_pkt = kzalloc(1, GFP_KERNEL);
+               if (!dev->padding_pkt) {
+                       status = -ENOMEM;
+                       goto out4;
+               }
+       }
+
        status = register_netdev (net);
        if (status)
-               goto out4;
+               goto out5;
        netif_info(dev, probe, dev->net,
                   "register '%s' at usb-%s-%s, %s, %pM\n",
                   udev->dev.driver->name,
@@ -1699,6 +1714,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 
        return 0;
 
+out5:
+       kfree(dev->padding_pkt);
 out4:
        usb_free_urb(dev->interrupt);
 out3:
index eee1f19..b24db7a 100644 (file)
@@ -188,6 +188,11 @@ static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev,
        return tot;
 }
 
+/* fake multicast ability */
+static void veth_set_multicast_list(struct net_device *dev)
+{
+}
+
 static int veth_open(struct net_device *dev)
 {
        struct veth_priv *priv = netdev_priv(dev);
@@ -250,11 +255,14 @@ static const struct net_device_ops veth_netdev_ops = {
        .ndo_start_xmit      = veth_xmit,
        .ndo_change_mtu      = veth_change_mtu,
        .ndo_get_stats64     = veth_get_stats64,
+       .ndo_set_rx_mode     = veth_set_multicast_list,
        .ndo_set_mac_address = eth_mac_addr,
 };
 
 #define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO |    \
                       NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_HIGHDMA | \
+                      NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL |           \
+                      NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT | NETIF_F_UFO |   \
                       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | \
                       NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_STAG_RX )
 
@@ -273,6 +281,7 @@ static void veth_setup(struct net_device *dev)
        dev->destructor = veth_dev_free;
 
        dev->hw_features = VETH_FEATURES;
+       dev->hw_enc_features = VETH_FEATURES;
 }
 
 /*
index defec2b..01f4eb5 100644 (file)
@@ -124,12 +124,14 @@ struct virtnet_info {
        /* Lock for config space updates */
        struct mutex config_lock;
 
+       /* Page_frag for GFP_KERNEL packet buffer allocation when we run
+        * low on memory.
+        */
+       struct page_frag alloc_frag;
+
        /* Does the affinity hint is set for virtqueues? */
        bool affinity_hint_set;
 
-       /* Per-cpu variable to show the mapping from CPU to virtqueue */
-       int __percpu *vq_index;
-
        /* CPU hot plug notifier */
        struct notifier_block nb;
 };
@@ -217,33 +219,18 @@ static void skb_xmit_done(struct virtqueue *vq)
        netif_wake_subqueue(vi->dev, vq2txq(vq));
 }
 
-static void set_skb_frag(struct sk_buff *skb, struct page *page,
-                        unsigned int offset, unsigned int *len)
-{
-       int size = min((unsigned)PAGE_SIZE - offset, *len);
-       int i = skb_shinfo(skb)->nr_frags;
-
-       __skb_fill_page_desc(skb, i, page, offset, size);
-
-       skb->data_len += size;
-       skb->len += size;
-       skb->truesize += PAGE_SIZE;
-       skb_shinfo(skb)->nr_frags++;
-       skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
-       *len -= size;
-}
-
 /* Called from bottom half context */
 static struct sk_buff *page_to_skb(struct receive_queue *rq,
-                                  struct page *page, unsigned int len)
+                                  struct page *page, unsigned int offset,
+                                  unsigned int len, unsigned int truesize)
 {
        struct virtnet_info *vi = rq->vq->vdev->priv;
        struct sk_buff *skb;
        struct skb_vnet_hdr *hdr;
-       unsigned int copy, hdr_len, offset;
+       unsigned int copy, hdr_len, hdr_padded_len;
        char *p;
 
-       p = page_address(page);
+       p = page_address(page) + offset;
 
        /* copy small packet so we can reuse these pages for small data */
        skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN);
@@ -254,16 +241,17 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq,
 
        if (vi->mergeable_rx_bufs) {
                hdr_len = sizeof hdr->mhdr;
-               offset = hdr_len;
+               hdr_padded_len = sizeof hdr->mhdr;
        } else {
                hdr_len = sizeof hdr->hdr;
-               offset = sizeof(struct padded_vnet_hdr);
+               hdr_padded_len = sizeof(struct padded_vnet_hdr);
        }
 
        memcpy(hdr, p, hdr_len);
 
        len -= hdr_len;
-       p += offset;
+       offset += hdr_padded_len;
+       p += hdr_padded_len;
 
        copy = len;
        if (copy > skb_tailroom(skb))
@@ -273,6 +261,14 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq,
        len -= copy;
        offset += copy;
 
+       if (vi->mergeable_rx_bufs) {
+               if (len)
+                       skb_add_rx_frag(skb, 0, page, offset, len, truesize);
+               else
+                       put_page(page);
+               return skb;
+       }
+
        /*
         * Verify that we can indeed put this data into a skb.
         * This is here to handle cases when the device erroneously
@@ -284,9 +280,12 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq,
                dev_kfree_skb(skb);
                return NULL;
        }
-
+       BUG_ON(offset >= PAGE_SIZE);
        while (len) {
-               set_skb_frag(skb, page, offset, &len);
+               unsigned int frag_size = min((unsigned)PAGE_SIZE - offset, len);
+               skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset,
+                               frag_size, truesize);
+               len -= frag_size;
                page = (struct page *)page->private;
                offset = 0;
        }
@@ -297,33 +296,59 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq,
        return skb;
 }
 
-static int receive_mergeable(struct receive_queue *rq, struct sk_buff *skb)
+static int receive_mergeable(struct receive_queue *rq, struct sk_buff *head_skb)
 {
-       struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb);
+       struct skb_vnet_hdr *hdr = skb_vnet_hdr(head_skb);
+       struct sk_buff *curr_skb = head_skb;
+       char *buf;
        struct page *page;
-       int num_buf, i, len;
+       int num_buf, len, offset;
 
        num_buf = hdr->mhdr.num_buffers;
        while (--num_buf) {
-               i = skb_shinfo(skb)->nr_frags;
-               if (i >= MAX_SKB_FRAGS) {
-                       pr_debug("%s: packet too long\n", skb->dev->name);
-                       skb->dev->stats.rx_length_errors++;
-                       return -EINVAL;
-               }
-               page = virtqueue_get_buf(rq->vq, &len);
-               if (!page) {
+               int num_skb_frags = skb_shinfo(curr_skb)->nr_frags;
+               buf = virtqueue_get_buf(rq->vq, &len);
+               if (unlikely(!buf)) {
                        pr_debug("%s: rx error: %d buffers missing\n",
-                                skb->dev->name, hdr->mhdr.num_buffers);
-                       skb->dev->stats.rx_length_errors++;
+                                head_skb->dev->name, hdr->mhdr.num_buffers);
+                       head_skb->dev->stats.rx_length_errors++;
                        return -EINVAL;
                }
-
-               if (len > PAGE_SIZE)
-                       len = PAGE_SIZE;
-
-               set_skb_frag(skb, page, 0, &len);
-
+               if (unlikely(len > MAX_PACKET_LEN)) {
+                       pr_debug("%s: rx error: merge buffer too long\n",
+                                head_skb->dev->name);
+                       len = MAX_PACKET_LEN;
+               }
+               if (unlikely(num_skb_frags == MAX_SKB_FRAGS)) {
+                       struct sk_buff *nskb = alloc_skb(0, GFP_ATOMIC);
+                       if (unlikely(!nskb)) {
+                               head_skb->dev->stats.rx_dropped++;
+                               return -ENOMEM;
+                       }
+                       if (curr_skb == head_skb)
+                               skb_shinfo(curr_skb)->frag_list = nskb;
+                       else
+                               curr_skb->next = nskb;
+                       curr_skb = nskb;
+                       head_skb->truesize += nskb->truesize;
+                       num_skb_frags = 0;
+               }
+               if (curr_skb != head_skb) {
+                       head_skb->data_len += len;
+                       head_skb->len += len;
+                       head_skb->truesize += MAX_PACKET_LEN;
+               }
+               page = virt_to_head_page(buf);
+               offset = buf - (char *)page_address(page);
+               if (skb_can_coalesce(curr_skb, num_skb_frags, page, offset)) {
+                       put_page(page);
+                       skb_coalesce_rx_frag(curr_skb, num_skb_frags - 1,
+                                            len, MAX_PACKET_LEN);
+               } else {
+                       skb_add_rx_frag(curr_skb, num_skb_frags, page,
+                                       offset, len,
+                                       MAX_PACKET_LEN);
+               }
                --rq->num;
        }
        return 0;
@@ -341,8 +366,10 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
        if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) {
                pr_debug("%s: short packet %i\n", dev->name, len);
                dev->stats.rx_length_errors++;
-               if (vi->mergeable_rx_bufs || vi->big_packets)
+               if (vi->big_packets)
                        give_pages(rq, buf);
+               else if (vi->mergeable_rx_bufs)
+                       put_page(virt_to_head_page(buf));
                else
                        dev_kfree_skb(buf);
                return;
@@ -352,19 +379,28 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
                skb = buf;
                len -= sizeof(struct virtio_net_hdr);
                skb_trim(skb, len);
+       } else if (vi->mergeable_rx_bufs) {
+               struct page *page = virt_to_head_page(buf);
+               skb = page_to_skb(rq, page,
+                                 (char *)buf - (char *)page_address(page),
+                                 len, MAX_PACKET_LEN);
+               if (unlikely(!skb)) {
+                       dev->stats.rx_dropped++;
+                       put_page(page);
+                       return;
+               }
+               if (receive_mergeable(rq, skb)) {
+                       dev_kfree_skb(skb);
+                       return;
+               }
        } else {
                page = buf;
-               skb = page_to_skb(rq, page, len);
+               skb = page_to_skb(rq, page, 0, len, PAGE_SIZE);
                if (unlikely(!skb)) {
                        dev->stats.rx_dropped++;
                        give_pages(rq, page);
                        return;
                }
-               if (vi->mergeable_rx_bufs)
-                       if (receive_mergeable(rq, skb)) {
-                               dev_kfree_skb(skb);
-                               return;
-                       }
        }
 
        hdr = skb_vnet_hdr(skb);
@@ -501,18 +537,28 @@ static int add_recvbuf_big(struct receive_queue *rq, gfp_t gfp)
 
 static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp)
 {
-       struct page *page;
+       struct virtnet_info *vi = rq->vq->vdev->priv;
+       char *buf = NULL;
        int err;
 
-       page = get_a_page(rq, gfp);
-       if (!page)
+       if (gfp & __GFP_WAIT) {
+               if (skb_page_frag_refill(MAX_PACKET_LEN, &vi->alloc_frag,
+                                        gfp)) {
+                       buf = (char *)page_address(vi->alloc_frag.page) +
+                             vi->alloc_frag.offset;
+                       get_page(vi->alloc_frag.page);
+                       vi->alloc_frag.offset += MAX_PACKET_LEN;
+               }
+       } else {
+               buf = netdev_alloc_frag(MAX_PACKET_LEN);
+       }
+       if (!buf)
                return -ENOMEM;
 
-       sg_init_one(rq->sg, page_address(page), PAGE_SIZE);
-
-       err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, page, gfp);
+       sg_init_one(rq->sg, buf, MAX_PACKET_LEN);
+       err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, buf, gfp);
        if (err < 0)
-               give_pages(rq, page);
+               put_page(virt_to_head_page(buf));
 
        return err;
 }
@@ -938,7 +984,9 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
                return -EINVAL;
        } else {
                vi->curr_queue_pairs = queue_pairs;
-               schedule_delayed_work(&vi->refill, 0);
+               /* virtnet_open() will refill when device is going to up. */
+               if (dev->flags & IFF_UP)
+                       schedule_delayed_work(&vi->refill, 0);
        }
 
        return 0;
@@ -1063,7 +1111,6 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev,
 static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu)
 {
        int i;
-       int cpu;
 
        if (vi->affinity_hint_set) {
                for (i = 0; i < vi->max_queue_pairs; i++) {
@@ -1073,16 +1120,6 @@ static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu)
 
                vi->affinity_hint_set = false;
        }
-
-       i = 0;
-       for_each_online_cpu(cpu) {
-               if (cpu == hcpu) {
-                       *per_cpu_ptr(vi->vq_index, cpu) = -1;
-               } else {
-                       *per_cpu_ptr(vi->vq_index, cpu) =
-                               ++i % vi->curr_queue_pairs;
-               }
-       }
 }
 
 static void virtnet_set_affinity(struct virtnet_info *vi)
@@ -1104,7 +1141,7 @@ static void virtnet_set_affinity(struct virtnet_info *vi)
        for_each_online_cpu(cpu) {
                virtqueue_set_affinity(vi->rq[i].vq, cpu);
                virtqueue_set_affinity(vi->sq[i].vq, cpu);
-               *per_cpu_ptr(vi->vq_index, cpu) = i;
+               netif_set_xps_queue(vi->dev, cpumask_of(cpu), i);
                i++;
        }
 
@@ -1128,6 +1165,7 @@ static int virtnet_cpu_callback(struct notifier_block *nfb,
        default:
                break;
        }
+
        return NOTIFY_OK;
 }
 
@@ -1217,28 +1255,6 @@ static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
-/* To avoid contending a lock hold by a vcpu who would exit to host, select the
- * txq based on the processor id.
- */
-static u16 virtnet_select_queue(struct net_device *dev, struct sk_buff *skb)
-{
-       int txq;
-       struct virtnet_info *vi = netdev_priv(dev);
-
-       if (skb_rx_queue_recorded(skb)) {
-               txq = skb_get_rx_queue(skb);
-       } else {
-               txq = *__this_cpu_ptr(vi->vq_index);
-               if (txq == -1)
-                       txq = 0;
-       }
-
-       while (unlikely(txq >= dev->real_num_tx_queues))
-               txq -= dev->real_num_tx_queues;
-
-       return txq;
-}
-
 static const struct net_device_ops virtnet_netdev = {
        .ndo_open            = virtnet_open,
        .ndo_stop            = virtnet_close,
@@ -1250,7 +1266,6 @@ static const struct net_device_ops virtnet_netdev = {
        .ndo_get_stats64     = virtnet_stats,
        .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid,
-       .ndo_select_queue     = virtnet_select_queue,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = virtnet_netpoll,
 #endif
@@ -1333,8 +1348,10 @@ static void free_unused_bufs(struct virtnet_info *vi)
                struct virtqueue *vq = vi->rq[i].vq;
 
                while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) {
-                       if (vi->mergeable_rx_bufs || vi->big_packets)
+                       if (vi->big_packets)
                                give_pages(&vi->rq[i], buf);
+                       else if (vi->mergeable_rx_bufs)
+                               put_page(virt_to_head_page(buf));
                        else
                                dev_kfree_skb(buf);
                        --vi->rq[i].num;
@@ -1559,10 +1576,6 @@ static int virtnet_probe(struct virtio_device *vdev)
        if (vi->stats == NULL)
                goto free;
 
-       vi->vq_index = alloc_percpu(int);
-       if (vi->vq_index == NULL)
-               goto free_stats;
-
        mutex_init(&vi->config_lock);
        vi->config_enable = true;
        INIT_WORK(&vi->config_work, virtnet_config_changed_work);
@@ -1589,7 +1602,7 @@ static int virtnet_probe(struct virtio_device *vdev)
        /* Allocate/initialize the rx/tx queues, and invoke find_vqs */
        err = init_vqs(vi);
        if (err)
-               goto free_index;
+               goto free_stats;
 
        netif_set_real_num_tx_queues(dev, 1);
        netif_set_real_num_rx_queues(dev, 1);
@@ -1640,8 +1653,8 @@ free_recv_bufs:
 free_vqs:
        cancel_delayed_work_sync(&vi->refill);
        virtnet_del_vqs(vi);
-free_index:
-       free_percpu(vi->vq_index);
+       if (vi->alloc_frag.page)
+               put_page(vi->alloc_frag.page);
 free_stats:
        free_percpu(vi->stats);
 free:
@@ -1675,10 +1688,11 @@ static void virtnet_remove(struct virtio_device *vdev)
        unregister_netdev(vi->dev);
 
        remove_vq_common(vi);
+       if (vi->alloc_frag.page)
+               put_page(vi->alloc_frag.page);
 
        flush_work(&vi->config_work);
 
-       free_percpu(vi->vq_index);
        free_percpu(vi->stats);
        free_netdev(vi->dev);
 }
@@ -1689,6 +1703,8 @@ static int virtnet_freeze(struct virtio_device *vdev)
        struct virtnet_info *vi = vdev->priv;
        int i;
 
+       unregister_hotcpu_notifier(&vi->nb);
+
        /* Prevent config work handler from accessing the device */
        mutex_lock(&vi->config_lock);
        vi->config_enable = false;
@@ -1733,7 +1749,13 @@ static int virtnet_restore(struct virtio_device *vdev)
        vi->config_enable = true;
        mutex_unlock(&vi->config_lock);
 
+       rtnl_lock();
        virtnet_set_queues(vi, vi->curr_queue_pairs);
+       rtnl_unlock();
+
+       err = register_hotcpu_notifier(&vi->nb);
+       if (err)
+               return err;
 
        return 0;
 }
index a03f358..12040a3 100644 (file)
@@ -410,9 +410,9 @@ int
 vmxnet3_create_queues(struct vmxnet3_adapter *adapter,
                      u32 tx_ring_size, u32 rx_ring_size, u32 rx_ring2_size);
 
-extern void vmxnet3_set_ethtool_ops(struct net_device *netdev);
+void vmxnet3_set_ethtool_ops(struct net_device *netdev);
 
-extern struct rtnl_link_stats64 *
+struct rtnl_link_stats64 *
 vmxnet3_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats);
 
 extern char vmxnet3_driver_name[];
index d1292fe..78df8f3 100644 (file)
 
 #define VXLAN_N_VID    (1u << 24)
 #define VXLAN_VID_MASK (VXLAN_N_VID - 1)
-/* IP header + UDP + VXLAN + Ethernet header */
-#define VXLAN_HEADROOM (20 + 8 + 8 + 14)
-/* IPv6 header + UDP + VXLAN + Ethernet header */
-#define VXLAN6_HEADROOM (40 + 8 + 8 + 14)
 #define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
 
 #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
@@ -952,8 +948,7 @@ void vxlan_sock_release(struct vxlan_sock *vs)
 
        spin_lock(&vn->sock_lock);
        hlist_del_rcu(&vs->hlist);
-       smp_wmb();
-       vs->sock->sk->sk_user_data = NULL;
+       rcu_assign_sk_user_data(vs->sock->sk, NULL);
        vxlan_notify_del_rx_port(sk);
        spin_unlock(&vn->sock_lock);
 
@@ -1048,8 +1043,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
        port = inet_sk(sk)->inet_sport;
 
-       smp_read_barrier_depends();
-       vs = (struct vxlan_sock *)sk->sk_user_data;
+       vs = rcu_dereference_sk_user_data(sk);
        if (!vs)
                goto drop;
 
@@ -2089,7 +2083,7 @@ static void vxlan_setup(struct net_device *dev)
        vxlan->age_timer.function = vxlan_cleanup;
        vxlan->age_timer.data = (unsigned long) vxlan;
 
-       inet_get_local_port_range(&low, &high);
+       inet_get_local_port_range(dev_net(dev), &low, &high);
        vxlan->port_min = low;
        vxlan->port_max = high;
        vxlan->dst_port = htons(vxlan_port);
@@ -2182,7 +2176,7 @@ static void vxlan_del_work(struct work_struct *work)
  * could be used for both IPv4 and IPv6 communications, but
  * users may set bindv6only=1.
  */
-static int create_v6_sock(struct net *net, __be16 port, struct socket **psock)
+static struct socket *create_v6_sock(struct net *net, __be16 port)
 {
        struct sock *sk;
        struct socket *sock;
@@ -2195,7 +2189,7 @@ static int create_v6_sock(struct net *net, __be16 port, struct socket **psock)
        rc = sock_create_kern(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock);
        if (rc < 0) {
                pr_debug("UDPv6 socket create failed\n");
-               return rc;
+               return ERR_PTR(rc);
        }
 
        /* Put in proper namespace */
@@ -2210,28 +2204,27 @@ static int create_v6_sock(struct net *net, __be16 port, struct socket **psock)
                pr_debug("bind for UDPv6 socket %pI6:%u (%d)\n",
                         &vxlan_addr.sin6_addr, ntohs(vxlan_addr.sin6_port), rc);
                sk_release_kernel(sk);
-               return rc;
+               return ERR_PTR(rc);
        }
        /* At this point, IPv6 module should have been loaded in
         * sock_create_kern().
         */
        BUG_ON(!ipv6_stub);
 
-       *psock = sock;
        /* Disable multicast loopback */
        inet_sk(sk)->mc_loop = 0;
-       return 0;
+       return sock;
 }
 
 #else
 
-static int create_v6_sock(struct net *net, __be16 port, struct socket **psock)
+static struct socket *create_v6_sock(struct net *net, __be16 port)
 {
-               return -EPFNOSUPPORT;
+               return ERR_PTR(-EPFNOSUPPORT);
 }
 #endif
 
-static int create_v4_sock(struct net *net, __be16 port, struct socket **psock)
+static struct socket *create_v4_sock(struct net *net, __be16 port)
 {
        struct sock *sk;
        struct socket *sock;
@@ -2246,7 +2239,7 @@ static int create_v4_sock(struct net *net, __be16 port, struct socket **psock)
        rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
        if (rc < 0) {
                pr_debug("UDP socket create failed\n");
-               return rc;
+               return ERR_PTR(rc);
        }
 
        /* Put in proper namespace */
@@ -2259,13 +2252,12 @@ static int create_v4_sock(struct net *net, __be16 port, struct socket **psock)
                pr_debug("bind for UDP socket %pI4:%u (%d)\n",
                         &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc);
                sk_release_kernel(sk);
-               return rc;
+               return ERR_PTR(rc);
        }
 
-       *psock = sock;
        /* Disable multicast loopback */
        inet_sk(sk)->mc_loop = 0;
-       return 0;
+       return sock;
 }
 
 /* Create new listen socket if needed */
@@ -2276,7 +2268,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        struct vxlan_sock *vs;
        struct socket *sock;
        struct sock *sk;
-       int rc = 0;
        unsigned int h;
 
        vs = kmalloc(sizeof(*vs), GFP_KERNEL);
@@ -2289,12 +2280,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        INIT_WORK(&vs->del_work, vxlan_del_work);
 
        if (ipv6)
-               rc = create_v6_sock(net, port, &sock);
+               sock = create_v6_sock(net, port);
        else
-               rc = create_v4_sock(net, port, &sock);
-       if (rc < 0) {
+               sock = create_v4_sock(net, port);
+       if (IS_ERR(sock)) {
                kfree(vs);
-               return ERR_PTR(rc);
+               return ERR_CAST(sock);
        }
 
        vs->sock = sock;
@@ -2302,8 +2293,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        atomic_set(&vs->refcnt, 1);
        vs->rcv = rcv;
        vs->data = data;
-       smp_wmb();
-       vs->sock->sk->sk_user_data = vs;
+       rcu_assign_sk_user_data(vs->sock->sk, vs);
 
        spin_lock(&vn->sock_lock);
        hlist_add_head_rcu(&vs->hlist, vs_head(net, port));
index 3f0c4f2..bcfff0d 100644 (file)
@@ -1972,6 +1972,7 @@ fst_get_iface(struct fst_card_info *card, struct fst_port_info *port,
        }
 
        i = port->index;
+       memset(&sync, 0, sizeof(sync));
        sync.clock_rate = FST_RDL(card, portConfig[i].lineSpeed);
        /* Lucky card and linux use same encoding here */
        sync.clock_type = FST_RDB(card, portConfig[i].internalClock) ==
index 3d80e42..3d74166 100644 (file)
@@ -220,7 +220,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq)
        /* We want a fast IRQ for this device. Actually we'd like an even faster
           IRQ ;) - This is one driver RtLinux is made for */
 
-       if (request_irq(irq, z8530_interrupt, IRQF_DISABLED,
+       if (request_irq(irq, z8530_interrupt, 0,
                        "Hostess SV11", sv) < 0) {
                pr_warn("IRQ %d already in use\n", irq);
                goto err_irq;
index 5bbcb5e..388ddf6 100644 (file)
@@ -148,10 +148,6 @@ static int  enslave( struct net_device *, struct net_device * );
 static int  emancipate( struct net_device * );
 #endif
 
-#ifdef __i386__
-#define ASM_CRC 1
-#endif
-
 static const char  version[] =
        "Granch SBNI12 driver ver 5.0.1  Jun 22 2001  Denis I.Timofeev.\n";
 
@@ -1551,88 +1547,6 @@ __setup( "sbni=", sbni_setup );
 
 /* -------------------------------------------------------------------------- */
 
-#ifdef ASM_CRC
-
-static u32
-calc_crc32( u32  crc,  u8  *p,  u32  len )
-{
-       register u32  _crc;
-       _crc = crc;
-       
-       __asm__ __volatile__ (
-               "xorl   %%ebx, %%ebx\n"
-               "movl   %2, %%esi\n" 
-               "movl   %3, %%ecx\n" 
-               "movl   $crc32tab, %%edi\n"
-               "shrl   $2, %%ecx\n"
-               "jz     1f\n"
-
-               ".align 4\n"
-       "0:\n"
-               "movb   %%al, %%bl\n"
-               "movl   (%%esi), %%edx\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "shrl   $8, %%edx\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "shrl   $8, %%edx\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "movb   %%dh, %%dl\n" 
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   %%dl, %%bl\n"
-               "addl   $4, %%esi\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "decl   %%ecx\n"
-               "jnz    0b\n"
-
-       "1:\n"
-               "movl   %3, %%ecx\n"
-               "andl   $3, %%ecx\n"
-               "jz     2f\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   (%%esi), %%bl\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "decl   %%ecx\n"
-               "jz     2f\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   1(%%esi), %%bl\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-
-               "decl   %%ecx\n"
-               "jz     2f\n"
-
-               "movb   %%al, %%bl\n"
-               "shrl   $8, %%eax\n"
-               "xorb   2(%%esi), %%bl\n"
-               "xorl   (%%edi,%%ebx,4), %%eax\n"
-       "2:\n"
-               : "=a" (_crc)
-               : "0" (_crc), "g" (p), "g" (len)
-               : "bx", "cx", "dx", "si", "di"
-       );
-
-       return  _crc;
-}
-
-#else  /* ASM_CRC */
-
 static u32
 calc_crc32( u32  crc,  u8  *p,  u32  len )
 {
@@ -1642,9 +1556,6 @@ calc_crc32( u32  crc,  u8  *p,  u32  len )
        return  crc;
 }
 
-#endif /* ASM_CRC */
-
-
 static u32  crc32tab[] __attribute__ ((aligned(8))) = {
        0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
        0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
index 4f77484..27860b4 100644 (file)
@@ -266,7 +266,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq,
        /* We want a fast IRQ for this device. Actually we'd like an even faster
           IRQ ;) - This is one driver RtLinux is made for */
 
-       if (request_irq(irq, z8530_interrupt, IRQF_DISABLED,
+       if (request_irq(irq, z8530_interrupt, 0,
                        "SeaLevel", dev) < 0) {
                pr_warn("IRQ %d already in use\n", irq);
                goto err_request_irq;
index 6a24a5a..4c0a697 100644 (file)
@@ -355,6 +355,7 @@ static int wanxl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        ifr->ifr_settings.size = size; /* data size wanted */
                        return -ENOBUFS;
                }
+               memset(&line, 0, sizeof(line));
                line.clock_type = get_status(port)->clocking;
                line.clock_rate = 0;
                line.loopback = 0;
index 8f0fc2e..f57ee67 100644 (file)
@@ -41,6 +41,6 @@ struct x25_asy {
 
 #define X25_ASY_MAGIC 0x5303
 
-extern int x25_asy_init(struct net_device *dev);
+int x25_asy_init(struct net_device *dev);
 
 #endif /* _LINUX_X25_ASY.H */
index f29d554..2416a9d 100644 (file)
@@ -395,20 +395,19 @@ struct z8530_dev
 extern u8 z8530_dead_port[];
 extern u8 z8530_hdlc_kilostream_85230[];
 extern u8 z8530_hdlc_kilostream[];
-extern irqreturn_t z8530_interrupt(int, void *);
-extern void z8530_describe(struct z8530_dev *, char *mapping, unsigned long io);
-extern int z8530_init(struct z8530_dev *);
-extern int z8530_shutdown(struct z8530_dev *);
-extern int z8530_sync_open(struct net_device *, struct z8530_channel *);
-extern int z8530_sync_close(struct net_device *, struct z8530_channel *);
-extern int z8530_sync_dma_open(struct net_device *, struct z8530_channel *);
-extern int z8530_sync_dma_close(struct net_device *, struct z8530_channel *);
-extern int z8530_sync_txdma_open(struct net_device *, struct z8530_channel *);
-extern int z8530_sync_txdma_close(struct net_device *, struct z8530_channel *);
-extern int z8530_channel_load(struct z8530_channel *, u8 *);
-extern netdev_tx_t z8530_queue_xmit(struct z8530_channel *c,
-                                         struct sk_buff *skb);
-extern void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb);
+irqreturn_t z8530_interrupt(int, void *);
+void z8530_describe(struct z8530_dev *, char *mapping, unsigned long io);
+int z8530_init(struct z8530_dev *);
+int z8530_shutdown(struct z8530_dev *);
+int z8530_sync_open(struct net_device *, struct z8530_channel *);
+int z8530_sync_close(struct net_device *, struct z8530_channel *);
+int z8530_sync_dma_open(struct net_device *, struct z8530_channel *);
+int z8530_sync_dma_close(struct net_device *, struct z8530_channel *);
+int z8530_sync_txdma_open(struct net_device *, struct z8530_channel *);
+int z8530_sync_txdma_close(struct net_device *, struct z8530_channel *);
+int z8530_channel_load(struct z8530_channel *, u8 *);
+netdev_tx_t z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb);
+void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb);
 
 
 /*
index 9f1e947..649ecad 100644 (file)
@@ -256,21 +256,20 @@ void i2400mu_init(struct i2400mu *i2400mu)
        i2400mu->rx_size_auto_shrink = 1;
 }
 
-extern int i2400mu_notification_setup(struct i2400mu *);
-extern void i2400mu_notification_release(struct i2400mu *);
+int i2400mu_notification_setup(struct i2400mu *);
+void i2400mu_notification_release(struct i2400mu *);
 
-extern int i2400mu_rx_setup(struct i2400mu *);
-extern void i2400mu_rx_release(struct i2400mu *);
-extern void i2400mu_rx_kick(struct i2400mu *);
+int i2400mu_rx_setup(struct i2400mu *);
+void i2400mu_rx_release(struct i2400mu *);
+void i2400mu_rx_kick(struct i2400mu *);
 
-extern int i2400mu_tx_setup(struct i2400mu *);
-extern void i2400mu_tx_release(struct i2400mu *);
-extern void i2400mu_bus_tx_kick(struct i2400m *);
+int i2400mu_tx_setup(struct i2400mu *);
+void i2400mu_tx_release(struct i2400mu *);
+void i2400mu_bus_tx_kick(struct i2400m *);
 
-extern ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *,
-                                      const struct i2400m_bootrom_header *,
-                                      size_t, int);
-extern ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *,
-                                          struct i2400m_bootrom_header *,
-                                          size_t);
+ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *,
+                               const struct i2400m_bootrom_header *, size_t,
+                               int);
+ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *,
+                                   struct i2400m_bootrom_header *, size_t);
 #endif /* #ifndef __I2400M_USB_H__ */
index 79c6505..5a34e72 100644 (file)
@@ -710,18 +710,18 @@ enum i2400m_bri {
        I2400M_BRI_MAC_REINIT = 1 << 3,
 };
 
-extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *);
-extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri);
-extern int i2400m_read_mac_addr(struct i2400m *);
-extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri);
-extern int i2400m_is_boot_barker(struct i2400m *, const void *, size_t);
+void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *);
+int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri);
+int i2400m_read_mac_addr(struct i2400m *);
+int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri);
+int i2400m_is_boot_barker(struct i2400m *, const void *, size_t);
 static inline
 int i2400m_is_d2h_barker(const void *buf)
 {
        const __le32 *barker = buf;
        return le32_to_cpu(*barker) == I2400M_D2H_MSG_BARKER;
 }
-extern void i2400m_unknown_barker(struct i2400m *, const void *, size_t);
+void i2400m_unknown_barker(struct i2400m *, const void *, size_t);
 
 /* Make/grok boot-rom header commands */
 
@@ -789,32 +789,31 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr)
 /*
  * Driver / device setup and internal functions
  */
-extern void i2400m_init(struct i2400m *);
-extern int i2400m_reset(struct i2400m *, enum i2400m_reset_type);
-extern void i2400m_netdev_setup(struct net_device *net_dev);
-extern int i2400m_sysfs_setup(struct device_driver *);
-extern void i2400m_sysfs_release(struct device_driver *);
-extern int i2400m_tx_setup(struct i2400m *);
-extern void i2400m_wake_tx_work(struct work_struct *);
-extern void i2400m_tx_release(struct i2400m *);
-
-extern int i2400m_rx_setup(struct i2400m *);
-extern void i2400m_rx_release(struct i2400m *);
-
-extern void i2400m_fw_cache(struct i2400m *);
-extern void i2400m_fw_uncache(struct i2400m *);
-
-extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned,
-                         const void *, int);
-extern void i2400m_net_erx(struct i2400m *, struct sk_buff *,
-                          enum i2400m_cs);
-extern void i2400m_net_wake_stop(struct i2400m *);
+void i2400m_init(struct i2400m *);
+int i2400m_reset(struct i2400m *, enum i2400m_reset_type);
+void i2400m_netdev_setup(struct net_device *net_dev);
+int i2400m_sysfs_setup(struct device_driver *);
+void i2400m_sysfs_release(struct device_driver *);
+int i2400m_tx_setup(struct i2400m *);
+void i2400m_wake_tx_work(struct work_struct *);
+void i2400m_tx_release(struct i2400m *);
+
+int i2400m_rx_setup(struct i2400m *);
+void i2400m_rx_release(struct i2400m *);
+
+void i2400m_fw_cache(struct i2400m *);
+void i2400m_fw_uncache(struct i2400m *);
+
+void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *,
+                  int);
+void i2400m_net_erx(struct i2400m *, struct sk_buff *, enum i2400m_cs);
+void i2400m_net_wake_stop(struct i2400m *);
 enum i2400m_pt;
-extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt);
+int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt);
 
 #ifdef CONFIG_DEBUG_FS
-extern int i2400m_debugfs_add(struct i2400m *);
-extern void i2400m_debugfs_rm(struct i2400m *);
+int i2400m_debugfs_add(struct i2400m *);
+void i2400m_debugfs_rm(struct i2400m *);
 #else
 static inline int i2400m_debugfs_add(struct i2400m *i2400m)
 {
@@ -824,8 +823,8 @@ static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {}
 #endif
 
 /* Initialize/shutdown the device */
-extern int i2400m_dev_initialize(struct i2400m *);
-extern void i2400m_dev_shutdown(struct i2400m *);
+int i2400m_dev_initialize(struct i2400m *);
+void i2400m_dev_shutdown(struct i2400m *);
 
 extern struct attribute_group i2400m_dev_attr_group;
 
@@ -873,21 +872,21 @@ void i2400m_put(struct i2400m *i2400m)
        dev_put(i2400m->wimax_dev.net_dev);
 }
 
-extern int i2400m_dev_reset_handle(struct i2400m *, const char *);
-extern int i2400m_pre_reset(struct i2400m *);
-extern int i2400m_post_reset(struct i2400m *);
-extern void i2400m_error_recovery(struct i2400m *);
+int i2400m_dev_reset_handle(struct i2400m *, const char *);
+int i2400m_pre_reset(struct i2400m *);
+int i2400m_post_reset(struct i2400m *);
+void i2400m_error_recovery(struct i2400m *);
 
 /*
  * _setup()/_release() are called by the probe/disconnect functions of
  * the bus-specific drivers.
  */
-extern int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags);
-extern void i2400m_release(struct i2400m *);
+int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags);
+void i2400m_release(struct i2400m *);
 
-extern int i2400m_rx(struct i2400m *, struct sk_buff *);
-extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *);
-extern void i2400m_tx_msg_sent(struct i2400m *);
+int i2400m_rx(struct i2400m *, struct sk_buff *);
+struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *);
+void i2400m_tx_msg_sent(struct i2400m *);
 
 
 /*
@@ -900,20 +899,19 @@ struct device *i2400m_dev(struct i2400m *i2400m)
        return i2400m->wimax_dev.net_dev->dev.parent;
 }
 
-extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *,
-                                  char *, size_t);
-extern int i2400m_msg_size_check(struct i2400m *,
-                                const struct i2400m_l3l4_hdr *, size_t);
-extern struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t);
-extern void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int);
-extern void i2400m_report_hook(struct i2400m *,
-                              const struct i2400m_l3l4_hdr *, size_t);
-extern void i2400m_report_hook_work(struct work_struct *);
-extern int i2400m_cmd_enter_powersave(struct i2400m *);
-extern int i2400m_cmd_exit_idle(struct i2400m *);
-extern struct sk_buff *i2400m_get_device_info(struct i2400m *);
-extern int i2400m_firmware_check(struct i2400m *);
-extern int i2400m_set_idle_timeout(struct i2400m *, unsigned);
+int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, char *, size_t);
+int i2400m_msg_size_check(struct i2400m *, const struct i2400m_l3l4_hdr *,
+                         size_t);
+struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t);
+void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int);
+void i2400m_report_hook(struct i2400m *, const struct i2400m_l3l4_hdr *,
+                       size_t);
+void i2400m_report_hook_work(struct work_struct *);
+int i2400m_cmd_enter_powersave(struct i2400m *);
+int i2400m_cmd_exit_idle(struct i2400m *);
+struct sk_buff *i2400m_get_device_info(struct i2400m *);
+int i2400m_firmware_check(struct i2400m *);
+int i2400m_set_idle_timeout(struct i2400m *, unsigned);
 
 static inline
 struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep)
@@ -921,10 +919,9 @@ struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep)
        return &iface->cur_altsetting->endpoint[ep].desc;
 }
 
-extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *,
-                                     enum wimax_rf_state);
-extern void i2400m_report_tlv_rf_switches_status(
-       struct i2400m *, const struct i2400m_tlv_rf_switches_status *);
+int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, enum wimax_rf_state);
+void i2400m_report_tlv_rf_switches_status(struct i2400m *,
+                                         const struct i2400m_tlv_rf_switches_status *);
 
 /*
  * Helpers for firmware backwards compatibility
@@ -968,8 +965,8 @@ void __i2400m_msleep(unsigned ms)
 
 
 /* module initialization helpers */
-extern int i2400m_barker_db_init(const char *);
-extern void i2400m_barker_db_exit(void);
+int i2400m_barker_db_init(const char *);
+void i2400m_barker_db_exit(void);
 
 
 
index f9a24e5..cfce83e 100644 (file)
@@ -1924,7 +1924,6 @@ static int adm8211_probe(struct pci_dev *pdev,
        pci_iounmap(pdev, priv->map);
 
  err_free_dev:
-       pci_set_drvdata(pdev, NULL);
        ieee80211_free_hw(dev);
 
  err_free_reg:
index 7fe1964..edf4b57 100644 (file)
@@ -5570,7 +5570,6 @@ static void airo_pci_remove(struct pci_dev *pdev)
        airo_print_info(dev->name, "Unregistering...");
        stop_airo_card(dev, 1);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
index 1abf1d4..c63d115 100644 (file)
@@ -25,6 +25,23 @@ config ATH_DEBUG
          Say Y, if you want to debug atheros wireless drivers.
          Right now only ath9k makes use of this.
 
+config ATH_REG_DYNAMIC_USER_REG_HINTS
+       bool "Atheros dynamic user regulatory hints"
+       depends on CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+         Say N. This should only be enabled in countries where
+         this feature is explicitly allowed and only on cards that
+         specifically have been tested for this.
+
+config ATH_REG_DYNAMIC_USER_CERT_TESTING
+       bool "Atheros dynamic user regulatory testing"
+       depends on ATH_REG_DYNAMIC_USER_REG_HINTS && CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+         Say N. This should only be enabled on systems
+         undergoing certification testing.
+
 source "drivers/net/wireless/ath/ath5k/Kconfig"
 source "drivers/net/wireless/ath/ath9k/Kconfig"
 source "drivers/net/wireless/ath/carl9170/Kconfig"
@@ -32,5 +49,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig"
 source "drivers/net/wireless/ath/ar5523/Kconfig"
 source "drivers/net/wireless/ath/wil6210/Kconfig"
 source "drivers/net/wireless/ath/ath10k/Kconfig"
+source "drivers/net/wireless/ath/wcn36xx/Kconfig"
 
 endif
index fb05cfd..7d023b0 100644 (file)
@@ -5,13 +5,16 @@ obj-$(CONFIG_ATH6KL)          += ath6kl/
 obj-$(CONFIG_AR5523)           += ar5523/
 obj-$(CONFIG_WIL6210)          += wil6210/
 obj-$(CONFIG_ATH10K)           += ath10k/
+obj-$(CONFIG_WCN36XX)          += wcn36xx/
 
 obj-$(CONFIG_ATH_COMMON)       += ath.o
 
 ath-objs :=    main.o \
                regd.o \
                hw.o \
-               key.o
+               key.o \
+               dfs_pattern_detector.o \
+               dfs_pri_detector.o
 
 ath-$(CONFIG_ATH_DEBUG) += debug.o
 ccflags-y += -D__CHECK_ENDIAN__
index 17d7fec..280fc3d 100644 (file)
@@ -1762,6 +1762,7 @@ static struct usb_device_id ar5523_id_table[] = {
        AR5523_DEVICE_UX(0x2001, 0x3a00),       /* Dlink / DWLAG132 */
        AR5523_DEVICE_UG(0x2001, 0x3a02),       /* Dlink / DWLG132 */
        AR5523_DEVICE_UX(0x2001, 0x3a04),       /* Dlink / DWLAG122 */
+       AR5523_DEVICE_UG(0x07d1, 0x3a07),       /* D-Link / WUA-2340 rev A1 */
        AR5523_DEVICE_UG(0x1690, 0x0712),       /* Gigaset / AR5523 */
        AR5523_DEVICE_UG(0x1690, 0x0710),       /* Gigaset / SMCWUSBTG */
        AR5523_DEVICE_UG(0x129b, 0x160c),       /* Gigaset / USB stick 108
index 744da6d..a1f0996 100644 (file)
@@ -22,7 +22,8 @@
 
 void ath10k_bmi_start(struct ath10k *ar)
 {
-       ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n");
+
        ar->bmi.done_sent = false;
 }
 
@@ -32,8 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar)
        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n");
+
        if (ar->bmi.done_sent) {
-               ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
+               ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n");
                return 0;
        }
 
@@ -46,7 +49,6 @@ int ath10k_bmi_done(struct ath10k *ar)
                return ret;
        }
 
-       ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
        return 0;
 }
 
@@ -59,6 +61,8 @@ int ath10k_bmi_get_target_info(struct ath10k *ar,
        u32 resplen = sizeof(resp.get_target_info);
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n");
+
        if (ar->bmi.done_sent) {
                ath10k_warn("BMI Get Target Info Command disallowed\n");
                return -EBUSY;
@@ -80,6 +84,7 @@ int ath10k_bmi_get_target_info(struct ath10k *ar,
 
        target_info->version = __le32_to_cpu(resp.get_target_info.version);
        target_info->type    = __le32_to_cpu(resp.get_target_info.type);
+
        return 0;
 }
 
@@ -92,15 +97,14 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
        u32 rxlen;
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
+                  address, length);
+
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
                return -EBUSY;
        }
 
-       ath10k_dbg(ATH10K_DBG_CORE,
-                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
-                  __func__, ar, address, length);
-
        while (length) {
                rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
 
@@ -133,15 +137,14 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
        u32 txlen;
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
+                  address, length);
+
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
                return -EBUSY;
        }
 
-       ath10k_dbg(ATH10K_DBG_CORE,
-                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
-                  __func__, ar, address, length);
-
        while (length) {
                txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
 
@@ -180,15 +183,14 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
        u32 resplen = sizeof(resp.execute);
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
+                  address, *param);
+
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
                return -EBUSY;
        }
 
-       ath10k_dbg(ATH10K_DBG_CORE,
-                  "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
-                  __func__, ar, address, *param);
-
        cmd.id            = __cpu_to_le32(BMI_EXECUTE);
        cmd.execute.addr  = __cpu_to_le32(address);
        cmd.execute.param = __cpu_to_le32(*param);
@@ -216,6 +218,9 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
        u32 txlen;
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
+                  buffer, length);
+
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
                return -EBUSY;
@@ -250,6 +255,9 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
+                  address);
+
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
                return -EBUSY;
@@ -275,6 +283,10 @@ int ath10k_bmi_fast_download(struct ath10k *ar,
        u32 trailer_len = length - head_len;
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BMI,
+                  "bmi fast download address 0x%x buffer 0x%p length %d\n",
+                  address, buffer, length);
+
        ret = ath10k_bmi_lz_stream_start(ar, address);
        if (ret)
                return ret;
index f8b969f..e46951b 100644 (file)
@@ -76,36 +76,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
                                                      u32 ce_ctrl_addr,
                                                      unsigned int n)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       void __iomem *indicator_addr;
-
-       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
-               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
-               return;
-       }
-
-       /* workaround for QCA988x_1.0 HW CE */
-       indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS;
-
-       if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) {
-               iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr);
-       } else {
-               unsigned long irq_flags;
-               local_irq_save(irq_flags);
-               iowrite32(1, indicator_addr);
-
-               /*
-                * PCIE write waits for ACK in IPQ8K, there is no
-                * need to read back value.
-                */
-               (void)ioread32(indicator_addr);
-               (void)ioread32(indicator_addr); /* conservative */
-
-               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
-
-               iowrite32(0, indicator_addr);
-               local_irq_restore(irq_flags);
-       }
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
 }
 
 static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
@@ -285,7 +256,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
  * ath10k_ce_sendlist_send.
  * The caller takes responsibility for any needed locking.
  */
-static int ath10k_ce_send_nolock(struct ce_state *ce_state,
+static int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
                                 void *per_transfer_context,
                                 u32 buffer,
                                 unsigned int nbytes,
@@ -293,7 +264,7 @@ static int ath10k_ce_send_nolock(struct ce_state *ce_state,
                                 unsigned int flags)
 {
        struct ath10k *ar = ce_state->ar;
-       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ath10k_ce_ring *src_ring = ce_state->src_ring;
        struct ce_desc *desc, *sdesc;
        unsigned int nentries_mask = src_ring->nentries_mask;
        unsigned int sw_index = src_ring->sw_index;
@@ -306,11 +277,13 @@ static int ath10k_ce_send_nolock(struct ce_state *ce_state,
                ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
                            __func__, nbytes, ce_state->src_sz_max);
 
-       ath10k_pci_wake(ar);
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return ret;
 
        if (unlikely(CE_RING_DELTA(nentries_mask,
                                   write_index, sw_index - 1) <= 0)) {
-               ret = -EIO;
+               ret = -ENOSR;
                goto exit;
        }
 
@@ -346,7 +319,7 @@ exit:
        return ret;
 }
 
-int ath10k_ce_send(struct ce_state *ce_state,
+int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
                   void *per_transfer_context,
                   u32 buffer,
                   unsigned int nbytes,
@@ -365,77 +338,26 @@ int ath10k_ce_send(struct ce_state *ce_state,
        return ret;
 }
 
-void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
-                               unsigned int nbytes, u32 flags)
+int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
 {
-       unsigned int num_items = sendlist->num_items;
-       struct ce_sendlist_item *item;
-
-       item = &sendlist->item[num_items];
-       item->data = buffer;
-       item->u.nbytes = nbytes;
-       item->flags = flags;
-       sendlist->num_items++;
-}
-
-int ath10k_ce_sendlist_send(struct ce_state *ce_state,
-                           void *per_transfer_context,
-                           struct ce_sendlist *sendlist,
-                           unsigned int transfer_id)
-{
-       struct ce_ring_state *src_ring = ce_state->src_ring;
-       struct ce_sendlist_item *item;
-       struct ath10k *ar = ce_state->ar;
+       struct ath10k *ar = pipe->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       unsigned int nentries_mask = src_ring->nentries_mask;
-       unsigned int num_items = sendlist->num_items;
-       unsigned int sw_index;
-       unsigned int write_index;
-       int i, delta, ret = -ENOMEM;
+       int delta;
 
        spin_lock_bh(&ar_pci->ce_lock);
-
-       sw_index = src_ring->sw_index;
-       write_index = src_ring->write_index;
-
-       delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
-
-       if (delta >= num_items) {
-               /*
-                * Handle all but the last item uniformly.
-                */
-               for (i = 0; i < num_items - 1; i++) {
-                       item = &sendlist->item[i];
-                       ret = ath10k_ce_send_nolock(ce_state,
-                                                   CE_SENDLIST_ITEM_CTXT,
-                                                   (u32) item->data,
-                                                   item->u.nbytes, transfer_id,
-                                                   item->flags |
-                                                   CE_SEND_FLAG_GATHER);
-                       if (ret)
-                               ath10k_warn("CE send failed for item: %d\n", i);
-               }
-               /*
-                * Provide valid context pointer for final item.
-                */
-               item = &sendlist->item[i];
-               ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
-                                           (u32) item->data, item->u.nbytes,
-                                           transfer_id, item->flags);
-               if (ret)
-                       ath10k_warn("CE send failed for last item: %d\n", i);
-       }
-
+       delta = CE_RING_DELTA(pipe->src_ring->nentries_mask,
+                             pipe->src_ring->write_index,
+                             pipe->src_ring->sw_index - 1);
        spin_unlock_bh(&ar_pci->ce_lock);
 
-       return ret;
+       return delta;
 }
 
-int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
                               void *per_recv_context,
                               u32 buffer)
 {
-       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
        u32 ctrl_addr = ce_state->ctrl_addr;
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -448,7 +370,9 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
        write_index = dest_ring->write_index;
        sw_index = dest_ring->sw_index;
 
-       ath10k_pci_wake(ar);
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               goto out;
 
        if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
                struct ce_desc *base = dest_ring->base_addr_owner_space;
@@ -470,6 +394,8 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
                ret = -EIO;
        }
        ath10k_pci_sleep(ar);
+
+out:
        spin_unlock_bh(&ar_pci->ce_lock);
 
        return ret;
@@ -479,14 +405,14 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
  * Guts of ath10k_ce_completed_recv_next.
  * The caller takes responsibility for any necessary locking.
  */
-static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
+static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
                                                void **per_transfer_contextp,
                                                u32 *bufferp,
                                                unsigned int *nbytesp,
                                                unsigned int *transfer_idp,
                                                unsigned int *flagsp)
 {
-       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
        unsigned int nentries_mask = dest_ring->nentries_mask;
        unsigned int sw_index = dest_ring->sw_index;
 
@@ -535,7 +461,7 @@ static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
        return 0;
 }
 
-int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
                                  void **per_transfer_contextp,
                                  u32 *bufferp,
                                  unsigned int *nbytesp,
@@ -556,11 +482,11 @@ int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
        return ret;
 }
 
-int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
                               void **per_transfer_contextp,
                               u32 *bufferp)
 {
-       struct ce_ring_state *dest_ring;
+       struct ath10k_ce_ring *dest_ring;
        unsigned int nentries_mask;
        unsigned int sw_index;
        unsigned int write_index;
@@ -612,19 +538,20 @@ int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
  * Guts of ath10k_ce_completed_send_next.
  * The caller takes responsibility for any necessary locking.
  */
-static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
+static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
                                                void **per_transfer_contextp,
                                                u32 *bufferp,
                                                unsigned int *nbytesp,
                                                unsigned int *transfer_idp)
 {
-       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ath10k_ce_ring *src_ring = ce_state->src_ring;
        u32 ctrl_addr = ce_state->ctrl_addr;
        struct ath10k *ar = ce_state->ar;
        unsigned int nentries_mask = src_ring->nentries_mask;
        unsigned int sw_index = src_ring->sw_index;
+       struct ce_desc *sdesc, *sbase;
        unsigned int read_index;
-       int ret = -EIO;
+       int ret;
 
        if (src_ring->hw_index == sw_index) {
                /*
@@ -634,48 +561,54 @@ static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
                 * the SW has really caught up to the HW, or if the cached
                 * value of the HW index has become stale.
                 */
-               ath10k_pci_wake(ar);
+
+               ret = ath10k_pci_wake(ar);
+               if (ret)
+                       return ret;
+
                src_ring->hw_index =
                        ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
                src_ring->hw_index &= nentries_mask;
+
                ath10k_pci_sleep(ar);
        }
+
        read_index = src_ring->hw_index;
 
-       if ((read_index != sw_index) && (read_index != 0xffffffff)) {
-               struct ce_desc *sbase = src_ring->shadow_base;
-               struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
+       if ((read_index == sw_index) || (read_index == 0xffffffff))
+               return -EIO;
 
-               /* Return data from completed source descriptor */
-               *bufferp = __le32_to_cpu(sdesc->addr);
-               *nbytesp = __le16_to_cpu(sdesc->nbytes);
-               *transfer_idp = MS(__le16_to_cpu(sdesc->flags),
-                                               CE_DESC_FLAGS_META_DATA);
+       sbase = src_ring->shadow_base;
+       sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
 
-               if (per_transfer_contextp)
-                       *per_transfer_contextp =
-                               src_ring->per_transfer_context[sw_index];
+       /* Return data from completed source descriptor */
+       *bufferp = __le32_to_cpu(sdesc->addr);
+       *nbytesp = __le16_to_cpu(sdesc->nbytes);
+       *transfer_idp = MS(__le16_to_cpu(sdesc->flags),
+                          CE_DESC_FLAGS_META_DATA);
 
-               /* sanity */
-               src_ring->per_transfer_context[sw_index] = NULL;
+       if (per_transfer_contextp)
+               *per_transfer_contextp =
+                       src_ring->per_transfer_context[sw_index];
 
-               /* Update sw_index */
-               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
-               src_ring->sw_index = sw_index;
-               ret = 0;
-       }
+       /* sanity */
+       src_ring->per_transfer_context[sw_index] = NULL;
 
-       return ret;
+       /* Update sw_index */
+       sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+       src_ring->sw_index = sw_index;
+
+       return 0;
 }
 
 /* NB: Modeled after ath10k_ce_completed_send_next */
-int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
                               void **per_transfer_contextp,
                               u32 *bufferp,
                               unsigned int *nbytesp,
                               unsigned int *transfer_idp)
 {
-       struct ce_ring_state *src_ring;
+       struct ath10k_ce_ring *src_ring;
        unsigned int nentries_mask;
        unsigned int sw_index;
        unsigned int write_index;
@@ -727,7 +660,7 @@ int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
        return ret;
 }
 
-int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
                                  void **per_transfer_contextp,
                                  u32 *bufferp,
                                  unsigned int *nbytesp,
@@ -756,53 +689,29 @@ int ath10k_ce_completed_send_next(struct ce_state *ce_state,
 void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
        u32 ctrl_addr = ce_state->ctrl_addr;
-       void *transfer_context;
-       u32 buf;
-       unsigned int nbytes;
-       unsigned int id;
-       unsigned int flags;
+       int ret;
+
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return;
 
-       ath10k_pci_wake(ar);
        spin_lock_bh(&ar_pci->ce_lock);
 
        /* Clear the copy-complete interrupts that will be handled here. */
        ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
                                          HOST_IS_COPY_COMPLETE_MASK);
 
-       if (ce_state->recv_cb) {
-               /*
-                * Pop completed recv buffers and call the registered
-                * recv callback for each
-                */
-               while (ath10k_ce_completed_recv_next_nolock(ce_state,
-                                                           &transfer_context,
-                                                           &buf, &nbytes,
-                                                           &id, &flags) == 0) {
-                       spin_unlock_bh(&ar_pci->ce_lock);
-                       ce_state->recv_cb(ce_state, transfer_context, buf,
-                                         nbytes, id, flags);
-                       spin_lock_bh(&ar_pci->ce_lock);
-               }
-       }
+       spin_unlock_bh(&ar_pci->ce_lock);
 
-       if (ce_state->send_cb) {
-               /*
-                * Pop completed send buffers and call the registered
-                * send callback for each
-                */
-               while (ath10k_ce_completed_send_next_nolock(ce_state,
-                                                           &transfer_context,
-                                                           &buf,
-                                                           &nbytes,
-                                                           &id) == 0) {
-                       spin_unlock_bh(&ar_pci->ce_lock);
-                       ce_state->send_cb(ce_state, transfer_context,
-                                         buf, nbytes, id);
-                       spin_lock_bh(&ar_pci->ce_lock);
-               }
-       }
+       if (ce_state->recv_cb)
+               ce_state->recv_cb(ce_state);
+
+       if (ce_state->send_cb)
+               ce_state->send_cb(ce_state);
+
+       spin_lock_bh(&ar_pci->ce_lock);
 
        /*
         * Misc CE interrupts are not being handled, but still need
@@ -823,10 +732,13 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
 void ath10k_ce_per_engine_service_any(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int ce_id;
+       int ce_id, ret;
        u32 intr_summary;
 
-       ath10k_pci_wake(ar);
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return;
+
        intr_summary = CE_INTERRUPT_SUMMARY(ar);
 
        for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) {
@@ -849,13 +761,16 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
  *
  * Called with ce_lock held.
  */
-static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
+static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state,
                                                int disable_copy_compl_intr)
 {
        u32 ctrl_addr = ce_state->ctrl_addr;
        struct ath10k *ar = ce_state->ar;
+       int ret;
 
-       ath10k_pci_wake(ar);
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return;
 
        if ((!disable_copy_compl_intr) &&
            (ce_state->send_cb || ce_state->recv_cb))
@@ -871,11 +786,14 @@ static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
 void ath10k_ce_disable_interrupts(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int ce_id;
+       int ce_id, ret;
+
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return;
 
-       ath10k_pci_wake(ar);
        for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) {
-               struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+               struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
                u32 ctrl_addr = ce_state->ctrl_addr;
 
                ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
@@ -883,12 +801,8 @@ void ath10k_ce_disable_interrupts(struct ath10k *ar)
        ath10k_pci_sleep(ar);
 }
 
-void ath10k_ce_send_cb_register(struct ce_state *ce_state,
-                               void (*send_cb) (struct ce_state *ce_state,
-                                                void *transfer_context,
-                                                u32 buffer,
-                                                unsigned int nbytes,
-                                                unsigned int transfer_id),
+void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
+                               void (*send_cb)(struct ath10k_ce_pipe *),
                                int disable_interrupts)
 {
        struct ath10k *ar = ce_state->ar;
@@ -900,13 +814,8 @@ void ath10k_ce_send_cb_register(struct ce_state *ce_state,
        spin_unlock_bh(&ar_pci->ce_lock);
 }
 
-void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
-                               void (*recv_cb) (struct ce_state *ce_state,
-                                                void *transfer_context,
-                                                u32 buffer,
-                                                unsigned int nbytes,
-                                                unsigned int transfer_id,
-                                                unsigned int flags))
+void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
+                               void (*recv_cb)(struct ath10k_ce_pipe *))
 {
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -919,11 +828,11 @@ void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
 
 static int ath10k_ce_init_src_ring(struct ath10k *ar,
                                   unsigned int ce_id,
-                                  struct ce_state *ce_state,
+                                  struct ath10k_ce_pipe *ce_state,
                                   const struct ce_attr *attr)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_ring_state *src_ring;
+       struct ath10k_ce_ring *src_ring;
        unsigned int nentries = attr->src_nentries;
        unsigned int ce_nbytes;
        u32 ctrl_addr = ath10k_ce_base_address(ce_id);
@@ -937,19 +846,18 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
                return 0;
        }
 
-       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
        ptr = kzalloc(ce_nbytes, GFP_KERNEL);
        if (ptr == NULL)
                return -ENOMEM;
 
-       ce_state->src_ring = (struct ce_ring_state *)ptr;
+       ce_state->src_ring = (struct ath10k_ce_ring *)ptr;
        src_ring = ce_state->src_ring;
 
-       ptr += sizeof(struct ce_ring_state);
+       ptr += sizeof(struct ath10k_ce_ring);
        src_ring->nentries = nentries;
        src_ring->nentries_mask = nentries - 1;
 
-       ath10k_pci_wake(ar);
        src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
        src_ring->sw_index &= src_ring->nentries_mask;
        src_ring->hw_index = src_ring->sw_index;
@@ -957,7 +865,6 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
        src_ring->write_index =
                ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
        src_ring->write_index &= src_ring->nentries_mask;
-       ath10k_pci_sleep(ar);
 
        src_ring->per_transfer_context = (void **)ptr;
 
@@ -970,6 +877,12 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
                                     (nentries * sizeof(struct ce_desc) +
                                      CE_DESC_RING_ALIGN),
                                     &base_addr);
+       if (!src_ring->base_addr_owner_space_unaligned) {
+               kfree(ce_state->src_ring);
+               ce_state->src_ring = NULL;
+               return -ENOMEM;
+       }
+
        src_ring->base_addr_ce_space_unaligned = base_addr;
 
        src_ring->base_addr_owner_space = PTR_ALIGN(
@@ -986,12 +899,21 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
        src_ring->shadow_base_unaligned =
                kmalloc((nentries * sizeof(struct ce_desc) +
                         CE_DESC_RING_ALIGN), GFP_KERNEL);
+       if (!src_ring->shadow_base_unaligned) {
+               pci_free_consistent(ar_pci->pdev,
+                                   (nentries * sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   src_ring->base_addr_owner_space,
+                                   src_ring->base_addr_ce_space);
+               kfree(ce_state->src_ring);
+               ce_state->src_ring = NULL;
+               return -ENOMEM;
+       }
 
        src_ring->shadow_base = PTR_ALIGN(
                        src_ring->shadow_base_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       ath10k_pci_wake(ar);
        ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr,
                                         src_ring->base_addr_ce_space);
        ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries);
@@ -999,18 +921,21 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
        ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0);
        ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
        ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
-       ath10k_pci_sleep(ar);
+
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot ce src ring id %d entries %d base_addr %p\n",
+                  ce_id, nentries, src_ring->base_addr_owner_space);
 
        return 0;
 }
 
 static int ath10k_ce_init_dest_ring(struct ath10k *ar,
                                    unsigned int ce_id,
-                                   struct ce_state *ce_state,
+                                   struct ath10k_ce_pipe *ce_state,
                                    const struct ce_attr *attr)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_ring_state *dest_ring;
+       struct ath10k_ce_ring *dest_ring;
        unsigned int nentries = attr->dest_nentries;
        unsigned int ce_nbytes;
        u32 ctrl_addr = ath10k_ce_base_address(ce_id);
@@ -1024,25 +949,23 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
                return 0;
        }
 
-       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
        ptr = kzalloc(ce_nbytes, GFP_KERNEL);
        if (ptr == NULL)
                return -ENOMEM;
 
-       ce_state->dest_ring = (struct ce_ring_state *)ptr;
+       ce_state->dest_ring = (struct ath10k_ce_ring *)ptr;
        dest_ring = ce_state->dest_ring;
 
-       ptr += sizeof(struct ce_ring_state);
+       ptr += sizeof(struct ath10k_ce_ring);
        dest_ring->nentries = nentries;
        dest_ring->nentries_mask = nentries - 1;
 
-       ath10k_pci_wake(ar);
        dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
        dest_ring->sw_index &= dest_ring->nentries_mask;
        dest_ring->write_index =
                ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
        dest_ring->write_index &= dest_ring->nentries_mask;
-       ath10k_pci_sleep(ar);
 
        dest_ring->per_transfer_context = (void **)ptr;
 
@@ -1055,6 +978,12 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
                                     (nentries * sizeof(struct ce_desc) +
                                      CE_DESC_RING_ALIGN),
                                     &base_addr);
+       if (!dest_ring->base_addr_owner_space_unaligned) {
+               kfree(ce_state->dest_ring);
+               ce_state->dest_ring = NULL;
+               return -ENOMEM;
+       }
+
        dest_ring->base_addr_ce_space_unaligned = base_addr;
 
        /*
@@ -1071,44 +1000,35 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
                        dest_ring->base_addr_ce_space_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       ath10k_pci_wake(ar);
        ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr,
                                          dest_ring->base_addr_ce_space);
        ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries);
        ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0);
        ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
        ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
-       ath10k_pci_sleep(ar);
+
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot ce dest ring id %d entries %d base_addr %p\n",
+                  ce_id, nentries, dest_ring->base_addr_owner_space);
 
        return 0;
 }
 
-static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
+static struct ath10k_ce_pipe *ath10k_ce_init_state(struct ath10k *ar,
                                             unsigned int ce_id,
                                             const struct ce_attr *attr)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_state *ce_state = NULL;
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
        u32 ctrl_addr = ath10k_ce_base_address(ce_id);
 
        spin_lock_bh(&ar_pci->ce_lock);
 
-       if (!ar_pci->ce_id_to_state[ce_id]) {
-               ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC);
-               if (ce_state == NULL) {
-                       spin_unlock_bh(&ar_pci->ce_lock);
-                       return NULL;
-               }
-
-               ar_pci->ce_id_to_state[ce_id] = ce_state;
-               ce_state->ar = ar;
-               ce_state->id = ce_id;
-               ce_state->ctrl_addr = ctrl_addr;
-               ce_state->state = CE_RUNNING;
-               /* Save attribute flags */
-               ce_state->attr_flags = attr->flags;
-               ce_state->src_sz_max = attr->src_sz_max;
-       }
+       ce_state->ar = ar;
+       ce_state->id = ce_id;
+       ce_state->ctrl_addr = ctrl_addr;
+       ce_state->attr_flags = attr->flags;
+       ce_state->src_sz_max = attr->src_sz_max;
 
        spin_unlock_bh(&ar_pci->ce_lock);
 
@@ -1122,12 +1042,17 @@ static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
  * initialization. It may be that only one side or the other is
  * initialized by software/firmware.
  */
-struct ce_state *ath10k_ce_init(struct ath10k *ar,
+struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
                                unsigned int ce_id,
                                const struct ce_attr *attr)
 {
-       struct ce_state *ce_state;
+       struct ath10k_ce_pipe *ce_state;
        u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       int ret;
+
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return NULL;
 
        ce_state = ath10k_ce_init_state(ar, ce_id, attr);
        if (!ce_state) {
@@ -1136,40 +1061,38 @@ struct ce_state *ath10k_ce_init(struct ath10k *ar,
        }
 
        if (attr->src_nentries) {
-               if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) {
-                       ath10k_err("Failed to initialize CE src ring for ID: %d\n",
-                                  ce_id);
+               ret = ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr);
+               if (ret) {
+                       ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n",
+                                  ce_id, ret);
                        ath10k_ce_deinit(ce_state);
                        return NULL;
                }
        }
 
        if (attr->dest_nentries) {
-               if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) {
-                       ath10k_err("Failed to initialize CE dest ring for ID: %d\n",
-                                  ce_id);
+               ret = ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr);
+               if (ret) {
+                       ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n",
+                                  ce_id, ret);
                        ath10k_ce_deinit(ce_state);
                        return NULL;
                }
        }
 
        /* Enable CE error interrupts */
-       ath10k_pci_wake(ar);
        ath10k_ce_error_intr_enable(ar, ctrl_addr);
+
        ath10k_pci_sleep(ar);
 
        return ce_state;
 }
 
-void ath10k_ce_deinit(struct ce_state *ce_state)
+void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state)
 {
-       unsigned int ce_id = ce_state->id;
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-       ce_state->state = CE_UNUSED;
-       ar_pci->ce_id_to_state[ce_id] = NULL;
-
        if (ce_state->src_ring) {
                kfree(ce_state->src_ring->shadow_base_unaligned);
                pci_free_consistent(ar_pci->pdev,
@@ -1190,5 +1113,7 @@ void ath10k_ce_deinit(struct ce_state *ce_state)
                                    ce_state->dest_ring->base_addr_ce_space);
                kfree(ce_state->dest_ring);
        }
-       kfree(ce_state);
+
+       ce_state->src_ring = NULL;
+       ce_state->dest_ring = NULL;
 }
index c17f07c..15d45b5 100644 (file)
@@ -27,7 +27,6 @@
 
 /* Descriptor rings must be aligned to this boundary */
 #define CE_DESC_RING_ALIGN     8
-#define CE_SENDLIST_ITEMS_MAX  12
 #define CE_SEND_FLAG_GATHER    0x00010000
 
 /*
  * how to use copy engines.
  */
 
-struct ce_state;
+struct ath10k_ce_pipe;
 
 
-/* Copy Engine operational state */
-enum ce_op_state {
-       CE_UNUSED,
-       CE_PAUSED,
-       CE_RUNNING,
-};
-
 #define CE_DESC_FLAGS_GATHER         (1 << 0)
 #define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
 #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
@@ -57,8 +49,7 @@ struct ce_desc {
        __le16 flags; /* %CE_DESC_FLAGS_ */
 };
 
-/* Copy Engine Ring internal state */
-struct ce_ring_state {
+struct ath10k_ce_ring {
        /* Number of entries in this ring; must be power of 2 */
        unsigned int nentries;
        unsigned int nentries_mask;
@@ -116,49 +107,20 @@ struct ce_ring_state {
        void **per_transfer_context;
 };
 
-/* Copy Engine internal state */
-struct ce_state {
+struct ath10k_ce_pipe {
        struct ath10k *ar;
        unsigned int id;
 
        unsigned int attr_flags;
 
        u32 ctrl_addr;
-       enum ce_op_state state;
-
-       void (*send_cb) (struct ce_state *ce_state,
-                        void *per_transfer_send_context,
-                        u32 buffer,
-                        unsigned int nbytes,
-                        unsigned int transfer_id);
-       void (*recv_cb) (struct ce_state *ce_state,
-                        void *per_transfer_recv_context,
-                        u32 buffer,
-                        unsigned int nbytes,
-                        unsigned int transfer_id,
-                        unsigned int flags);
-
-       unsigned int src_sz_max;
-       struct ce_ring_state *src_ring;
-       struct ce_ring_state *dest_ring;
-};
 
-struct ce_sendlist_item {
-       /* e.g. buffer or desc list */
-       dma_addr_t data;
-       union {
-               /* simple buffer */
-               unsigned int nbytes;
-               /* Rx descriptor list */
-               unsigned int ndesc;
-       } u;
-       /* externally-specified flags; OR-ed with internal flags */
-       u32 flags;
-};
+       void (*send_cb)(struct ath10k_ce_pipe *);
+       void (*recv_cb)(struct ath10k_ce_pipe *);
 
-struct ce_sendlist {
-       unsigned int num_items;
-       struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
+       unsigned int src_sz_max;
+       struct ath10k_ce_ring *src_ring;
+       struct ath10k_ce_ring *dest_ring;
 };
 
 /* Copy Engine settable attributes */
@@ -182,7 +144,7 @@ struct ce_attr;
  *
  * Implementation note: pushes 1 buffer to Source ring
  */
-int ath10k_ce_send(struct ce_state *ce_state,
+int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
                   void *per_transfer_send_context,
                   u32 buffer,
                   unsigned int nbytes,
@@ -190,36 +152,11 @@ int ath10k_ce_send(struct ce_state *ce_state,
                   unsigned int transfer_id,
                   unsigned int flags);
 
-void ath10k_ce_send_cb_register(struct ce_state *ce_state,
-                               void (*send_cb) (struct ce_state *ce_state,
-                                                void *transfer_context,
-                                                u32 buffer,
-                                                unsigned int nbytes,
-                                                unsigned int transfer_id),
+void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
+                               void (*send_cb)(struct ath10k_ce_pipe *),
                                int disable_interrupts);
 
-/* Append a simple buffer (address/length) to a sendlist. */
-void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
-                               u32 buffer,
-                               unsigned int nbytes,
-                               /* OR-ed with internal flags */
-                               u32 flags);
-
-/*
- * Queue a "sendlist" of buffers to be sent using gather to a single
- * anonymous destination buffer
- *   ce         - which copy engine to use
- *   sendlist        - list of simple buffers to send using gather
- *   transfer_id     - arbitrary ID; reflected to destination
- * Returns 0 on success; otherwise an error status.
- *
- * Implemenation note: Pushes multiple buffers with Gather to Source ring.
- */
-int ath10k_ce_sendlist_send(struct ce_state *ce_state,
-                           void *per_transfer_send_context,
-                           struct ce_sendlist *sendlist,
-                           /* 14 bits */
-                           unsigned int transfer_id);
+int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe);
 
 /*==================Recv=======================*/
 
@@ -233,17 +170,12 @@ int ath10k_ce_sendlist_send(struct ce_state *ce_state,
  *
  * Implemenation note: Pushes a buffer to Dest ring.
  */
-int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
                               void *per_transfer_recv_context,
                               u32 buffer);
 
-void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
-                               void (*recv_cb) (struct ce_state *ce_state,
-                                                void *transfer_context,
-                                                u32 buffer,
-                                                unsigned int nbytes,
-                                                unsigned int transfer_id,
-                                                unsigned int flags));
+void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
+                               void (*recv_cb)(struct ath10k_ce_pipe *));
 
 /* recv flags */
 /* Data is byte-swapped */
@@ -253,7 +185,7 @@ void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
  * Supply data for the next completed unprocessed receive descriptor.
  * Pops buffer from Dest ring.
  */
-int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
                                  void **per_transfer_contextp,
                                  u32 *bufferp,
                                  unsigned int *nbytesp,
@@ -263,7 +195,7 @@ int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
  * Supply data for the next completed unprocessed send descriptor.
  * Pops 1 completed send buffer from Source ring.
  */
-int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
                           void **per_transfer_contextp,
                           u32 *bufferp,
                           unsigned int *nbytesp,
@@ -272,7 +204,7 @@ int ath10k_ce_completed_send_next(struct ce_state *ce_state,
 /*==================CE Engine Initialization=======================*/
 
 /* Initialize an instance of a CE */
-struct ce_state *ath10k_ce_init(struct ath10k *ar,
+struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
                                unsigned int ce_id,
                                const struct ce_attr *attr);
 
@@ -282,7 +214,7 @@ struct ce_state *ath10k_ce_init(struct ath10k *ar,
  * receive buffers.  Target DMA must be stopped before using
  * this API.
  */
-int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
                               void **per_transfer_contextp,
                               u32 *bufferp);
 
@@ -291,13 +223,13 @@ int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
  * pending sends.  Target DMA must be stopped before using
  * this API.
  */
-int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
                               void **per_transfer_contextp,
                               u32 *bufferp,
                               unsigned int *nbytesp,
                               unsigned int *transfer_idp);
 
-void ath10k_ce_deinit(struct ce_state *ce_state);
+void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state);
 
 /*==================CE Interrupt Handlers====================*/
 void ath10k_ce_per_engine_service_any(struct ath10k *ar);
@@ -322,9 +254,6 @@ struct ce_attr {
        /* CE_ATTR_* values */
        unsigned int flags;
 
-       /* currently not in use */
-       unsigned int priority;
-
        /* #entries in source ring - Must be a power of 2 */
        unsigned int src_nentries;
 
@@ -336,21 +265,8 @@ struct ce_attr {
 
        /* #entries in destination ring - Must be a power of 2 */
        unsigned int dest_nentries;
-
-       /* Future use */
-       void *reserved;
 };
 
-/*
- * When using sendlist_send to transfer multiple buffer fragments, the
- * transfer context of each fragment, except last one, will be filled
- * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
- * each fragment done with send and the transfer context would be
- * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
- * status of a send completion.
- */
-#define CE_SENDLIST_ITEM_CTXT  ((void *)0xcecebeef)
-
 #define SR_BA_ADDRESS          0x0000
 #define SR_SIZE_ADDRESS                0x0004
 #define DR_BA_ADDRESS          0x0008
index 7226c23..1129994 100644 (file)
@@ -38,17 +38,6 @@ MODULE_PARM_DESC(uart_print, "Uart target debugging");
 MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
 
 static const struct ath10k_hw_params ath10k_hw_params_list[] = {
-       {
-               .id = QCA988X_HW_1_0_VERSION,
-               .name = "qca988x hw1.0",
-               .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR,
-               .fw = {
-                       .dir = QCA988X_HW_1_0_FW_DIR,
-                       .fw = QCA988X_HW_1_0_FW_FILE,
-                       .otp = QCA988X_HW_1_0_OTP_FILE,
-                       .board = QCA988X_HW_1_0_BOARD_DATA_FILE,
-               },
-       },
        {
                .id = QCA988X_HW_2_0_VERSION,
                .name = "qca988x hw2.0",
@@ -64,33 +53,12 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
 
 static void ath10k_send_suspend_complete(struct ath10k *ar)
 {
-       ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n");
 
        ar->is_target_paused = true;
        wake_up(&ar->event_queue);
 }
 
-static int ath10k_check_fw_version(struct ath10k *ar)
-{
-       char version[32];
-
-       if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
-           ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
-           ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
-           ar->fw_version_build >= SUPPORTED_FW_BUILD)
-               return 0;
-
-       snprintf(version, sizeof(version), "%u.%u.%u.%u",
-                SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
-                SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
-
-       ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
-                   ar->hw->wiphy->fw_version);
-       ath10k_warn("Please upgrade to version %s (or newer)\n", version);
-
-       return 0;
-}
-
 static int ath10k_init_connect_htc(struct ath10k *ar)
 {
        int status;
@@ -112,7 +80,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
                goto timeout;
        }
 
-       ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n");
        return 0;
 
 timeout:
@@ -200,8 +168,7 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
        return fw;
 }
 
-static int ath10k_push_board_ext_data(struct ath10k *ar,
-                                     const struct firmware *fw)
+static int ath10k_push_board_ext_data(struct ath10k *ar)
 {
        u32 board_data_size = QCA988X_BOARD_DATA_SZ;
        u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
@@ -214,21 +181,21 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
                return ret;
        }
 
-       ath10k_dbg(ATH10K_DBG_CORE,
-                  "ath10k: Board extended Data download addr: 0x%x\n",
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot push board extended data addr 0x%x\n",
                   board_ext_data_addr);
 
        if (board_ext_data_addr == 0)
                return 0;
 
-       if (fw->size != (board_data_size + board_ext_data_size)) {
+       if (ar->board_len != (board_data_size + board_ext_data_size)) {
                ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
-                          fw->size, board_data_size, board_ext_data_size);
+                          ar->board_len, board_data_size, board_ext_data_size);
                return -EINVAL;
        }
 
        ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
-                                     fw->data + board_data_size,
+                                     ar->board_data + board_data_size,
                                      board_ext_data_size);
        if (ret) {
                ath10k_err("could not write board ext data (%d)\n", ret);
@@ -247,12 +214,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
-       const struct firmware *fw = ar->board_data;
        u32 board_data_size = QCA988X_BOARD_DATA_SZ;
        u32 address;
        int ret;
 
-       ret = ath10k_push_board_ext_data(ar, fw);
+       ret = ath10k_push_board_ext_data(ar);
        if (ret) {
                ath10k_err("could not push board ext data (%d)\n", ret);
                goto exit;
@@ -264,8 +230,9 @@ static int ath10k_download_board_data(struct ath10k *ar)
                goto exit;
        }
 
-       ret = ath10k_bmi_write_memory(ar, address, fw->data,
-                                     min_t(u32, board_data_size, fw->size));
+       ret = ath10k_bmi_write_memory(ar, address, ar->board_data,
+                                     min_t(u32, board_data_size,
+                                           ar->board_len));
        if (ret) {
                ath10k_err("could not write board data (%d)\n", ret);
                goto exit;
@@ -283,17 +250,16 @@ exit:
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-       const struct firmware *fw = ar->otp;
        u32 address = ar->hw_params.patch_load_addr;
        u32 exec_param;
        int ret;
 
        /* OTP is optional */
 
-       if (!ar->otp)
+       if (!ar->otp_data || !ar->otp_len)
                return 0;
 
-       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
        if (ret) {
                ath10k_err("could not write otp (%d)\n", ret);
                goto exit;
@@ -312,13 +278,13 @@ exit:
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-       const struct firmware *fw = ar->firmware;
        u32 address;
        int ret;
 
        address = ar->hw_params.patch_load_addr;
 
-       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data,
+                                      ar->firmware_len);
        if (ret) {
                ath10k_err("could not write fw (%d)\n", ret);
                goto exit;
@@ -330,8 +296,8 @@ exit:
 
 static void ath10k_core_free_firmware_files(struct ath10k *ar)
 {
-       if (ar->board_data && !IS_ERR(ar->board_data))
-               release_firmware(ar->board_data);
+       if (ar->board && !IS_ERR(ar->board))
+               release_firmware(ar->board);
 
        if (ar->otp && !IS_ERR(ar->otp))
                release_firmware(ar->otp);
@@ -339,12 +305,20 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
        if (ar->firmware && !IS_ERR(ar->firmware))
                release_firmware(ar->firmware);
 
+       ar->board = NULL;
        ar->board_data = NULL;
+       ar->board_len = 0;
+
        ar->otp = NULL;
+       ar->otp_data = NULL;
+       ar->otp_len = 0;
+
        ar->firmware = NULL;
+       ar->firmware_data = NULL;
+       ar->firmware_len = 0;
 }
 
-static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
 {
        int ret = 0;
 
@@ -358,15 +332,18 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
                return -EINVAL;
        }
 
-       ar->board_data = ath10k_fetch_fw_file(ar,
-                                             ar->hw_params.fw.dir,
-                                             ar->hw_params.fw.board);
-       if (IS_ERR(ar->board_data)) {
-               ret = PTR_ERR(ar->board_data);
+       ar->board = ath10k_fetch_fw_file(ar,
+                                        ar->hw_params.fw.dir,
+                                        ar->hw_params.fw.board);
+       if (IS_ERR(ar->board)) {
+               ret = PTR_ERR(ar->board);
                ath10k_err("could not fetch board data (%d)\n", ret);
                goto err;
        }
 
+       ar->board_data = ar->board->data;
+       ar->board_len = ar->board->size;
+
        ar->firmware = ath10k_fetch_fw_file(ar,
                                            ar->hw_params.fw.dir,
                                            ar->hw_params.fw.fw);
@@ -376,6 +353,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
                goto err;
        }
 
+       ar->firmware_data = ar->firmware->data;
+       ar->firmware_len = ar->firmware->size;
+
        /* OTP may be undefined. If so, don't fetch it at all */
        if (ar->hw_params.fw.otp == NULL)
                return 0;
@@ -389,6 +369,172 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
                goto err;
        }
 
+       ar->otp_data = ar->otp->data;
+       ar->otp_len = ar->otp->size;
+
+       return 0;
+
+err:
+       ath10k_core_free_firmware_files(ar);
+       return ret;
+}
+
+static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
+{
+       size_t magic_len, len, ie_len;
+       int ie_id, i, index, bit, ret;
+       struct ath10k_fw_ie *hdr;
+       const u8 *data;
+       __le32 *timestamp;
+
+       /* first fetch the firmware file (firmware-*.bin) */
+       ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name);
+       if (IS_ERR(ar->firmware)) {
+               ath10k_err("Could not fetch firmware file '%s': %ld\n",
+                          name, PTR_ERR(ar->firmware));
+               return PTR_ERR(ar->firmware);
+       }
+
+       data = ar->firmware->data;
+       len = ar->firmware->size;
+
+       /* magic also includes the null byte, check that as well */
+       magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
+
+       if (len < magic_len) {
+               ath10k_err("firmware image too small to contain magic: %zu\n",
+                          len);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
+               ath10k_err("Invalid firmware magic\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* jump over the padding */
+       magic_len = ALIGN(magic_len, 4);
+
+       len -= magic_len;
+       data += magic_len;
+
+       /* loop elements */
+       while (len > sizeof(struct ath10k_fw_ie)) {
+               hdr = (struct ath10k_fw_ie *)data;
+
+               ie_id = le32_to_cpu(hdr->id);
+               ie_len = le32_to_cpu(hdr->len);
+
+               len -= sizeof(*hdr);
+               data += sizeof(*hdr);
+
+               if (len < ie_len) {
+                       ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n",
+                                  ie_id, len, ie_len);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               switch (ie_id) {
+               case ATH10K_FW_IE_FW_VERSION:
+                       if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1)
+                               break;
+
+                       memcpy(ar->hw->wiphy->fw_version, data, ie_len);
+                       ar->hw->wiphy->fw_version[ie_len] = '\0';
+
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found fw version %s\n",
+                                   ar->hw->wiphy->fw_version);
+                       break;
+               case ATH10K_FW_IE_TIMESTAMP:
+                       if (ie_len != sizeof(u32))
+                               break;
+
+                       timestamp = (__le32 *)data;
+
+                       ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n",
+                                  le32_to_cpup(timestamp));
+                       break;
+               case ATH10K_FW_IE_FEATURES:
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found firmware features ie (%zd B)\n",
+                                  ie_len);
+
+                       for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) {
+                               index = i / 8;
+                               bit = i % 8;
+
+                               if (index == ie_len)
+                                       break;
+
+                               if (data[index] & (1 << bit))
+                                       __set_bit(i, ar->fw_features);
+                       }
+
+                       ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "",
+                                       ar->fw_features,
+                                       sizeof(ar->fw_features));
+                       break;
+               case ATH10K_FW_IE_FW_IMAGE:
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found fw image ie (%zd B)\n",
+                                  ie_len);
+
+                       ar->firmware_data = data;
+                       ar->firmware_len = ie_len;
+
+                       break;
+               case ATH10K_FW_IE_OTP_IMAGE:
+                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                  "found otp image ie (%zd B)\n",
+                                  ie_len);
+
+                       ar->otp_data = data;
+                       ar->otp_len = ie_len;
+
+                       break;
+               default:
+                       ath10k_warn("Unknown FW IE: %u\n",
+                                   le32_to_cpu(hdr->id));
+                       break;
+               }
+
+               /* jump over the padding */
+               ie_len = ALIGN(ie_len, 4);
+
+               len -= ie_len;
+               data += ie_len;
+       }
+
+       if (!ar->firmware_data || !ar->firmware_len) {
+               ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n",
+                           name);
+               ret = -ENOMEDIUM;
+               goto err;
+       }
+
+       /* now fetch the board file */
+       if (ar->hw_params.fw.board == NULL) {
+               ath10k_err("board data file not defined");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ar->board = ath10k_fetch_fw_file(ar,
+                                        ar->hw_params.fw.dir,
+                                        ar->hw_params.fw.board);
+       if (IS_ERR(ar->board)) {
+               ret = PTR_ERR(ar->board);
+               ath10k_err("could not fetch board data (%d)\n", ret);
+               goto err;
+       }
+
+       ar->board_data = ar->board->data;
+       ar->board_len = ar->board->size;
+
        return 0;
 
 err:
@@ -396,6 +542,28 @@ err:
        return ret;
 }
 
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE);
+       if (ret == 0) {
+               ar->fw_api = 2;
+               goto out;
+       }
+
+       ret = ath10k_core_fetch_firmware_api_1(ar);
+       if (ret)
+               return ret;
+
+       ar->fw_api = 1;
+
+out:
+       ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
+
+       return 0;
+}
+
 static int ath10k_init_download_firmware(struct ath10k *ar)
 {
        int ret;
@@ -446,6 +614,13 @@ static int ath10k_init_uart(struct ath10k *ar)
                return ret;
        }
 
+       /* Set the UART baud rate to 19200. */
+       ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200);
+       if (ret) {
+               ath10k_warn("could not set the baud rate (%d)\n", ret);
+               return ret;
+       }
+
        ath10k_info("UART prints enabled\n");
        return 0;
 }
@@ -545,6 +720,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
        INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
        skb_queue_head_init(&ar->offchan_tx_queue);
 
+       INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
+       skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+
        init_waitqueue_head(&ar->event_queue);
 
        INIT_WORK(&ar->restart_work, ath10k_core_restart);
@@ -559,6 +737,8 @@ EXPORT_SYMBOL(ath10k_core_create);
 
 void ath10k_core_destroy(struct ath10k *ar)
 {
+       ath10k_debug_destroy(ar);
+
        flush_workqueue(ar->workqueue);
        destroy_workqueue(ar->workqueue);
 
@@ -570,6 +750,8 @@ int ath10k_core_start(struct ath10k *ar)
 {
        int status;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ath10k_bmi_start(ar);
 
        if (ath10k_init_configure_target(ar)) {
@@ -620,10 +802,6 @@ int ath10k_core_start(struct ath10k *ar)
 
        ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
 
-       status = ath10k_check_fw_version(ar);
-       if (status)
-               goto err_disconnect_htc;
-
        status = ath10k_wmi_cmd_init(ar);
        if (status) {
                ath10k_err("could not send WMI init command (%d)\n", status);
@@ -641,7 +819,12 @@ int ath10k_core_start(struct ath10k *ar)
        if (status)
                goto err_disconnect_htc;
 
+       status = ath10k_debug_start(ar);
+       if (status)
+               goto err_disconnect_htc;
+
        ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+       INIT_LIST_HEAD(&ar->arvifs);
 
        return 0;
 
@@ -658,6 +841,9 @@ EXPORT_SYMBOL(ath10k_core_start);
 
 void ath10k_core_stop(struct ath10k *ar)
 {
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ath10k_debug_stop(ar);
        ath10k_htc_stop(&ar->htc);
        ath10k_htt_detach(&ar->htt);
        ath10k_wmi_detach(ar);
@@ -704,23 +890,65 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                return ret;
        }
 
+       mutex_lock(&ar->conf_mutex);
+
        ret = ath10k_core_start(ar);
        if (ret) {
                ath10k_err("could not init core (%d)\n", ret);
                ath10k_core_free_firmware_files(ar);
                ath10k_hif_power_down(ar);
+               mutex_unlock(&ar->conf_mutex);
                return ret;
        }
 
        ath10k_core_stop(ar);
+
+       mutex_unlock(&ar->conf_mutex);
+
        ath10k_hif_power_down(ar);
        return 0;
 }
 
-int ath10k_core_register(struct ath10k *ar)
+static int ath10k_core_check_chip_id(struct ath10k *ar)
+{
+       u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n",
+                  ar->chip_id, hw_revision);
+
+       /* Check that we are not using hw1.0 (some of them have same pci id
+        * as hw2.0) before doing anything else as ath10k crashes horribly
+        * due to missing hw1.0 workarounds. */
+       switch (hw_revision) {
+       case QCA988X_HW_1_0_CHIP_ID_REV:
+               ath10k_err("ERROR: qca988x hw1.0 is not supported\n");
+               return -EOPNOTSUPP;
+
+       case QCA988X_HW_2_0_CHIP_ID_REV:
+               /* known hardware revision, continue normally */
+               return 0;
+
+       default:
+               ath10k_warn("Warning: hardware revision unknown (0x%x), expect problems\n",
+                           ar->chip_id);
+               return 0;
+       }
+
+       return 0;
+}
+
+int ath10k_core_register(struct ath10k *ar, u32 chip_id)
 {
        int status;
 
+       ar->chip_id = chip_id;
+
+       status = ath10k_core_check_chip_id(ar);
+       if (status) {
+               ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id);
+               return status;
+       }
+
        status = ath10k_core_probe_fw(ar);
        if (status) {
                ath10k_err("could not probe fw (%d)\n", status);
@@ -755,6 +983,7 @@ void ath10k_core_unregister(struct ath10k *ar)
         * Otherwise we will fail to submit commands to FW and mac80211 will be
         * unhappy about callback failures. */
        ath10k_mac_unregister(ar);
+
        ath10k_core_free_firmware_files(ar);
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
index e4bba56..0934f76 100644 (file)
 /* Antenna noise floor */
 #define ATH10K_DEFAULT_NOISE_FLOOR -95
 
+#define ATH10K_MAX_NUM_MGMT_PENDING 16
+
 struct ath10k;
 
 struct ath10k_skb_cb {
        dma_addr_t paddr;
        bool is_mapped;
        bool is_aborted;
+       u8 vdev_id;
 
        struct {
-               u8 vdev_id;
-               u16 msdu_id;
                u8 tid;
                bool is_offchan;
-               bool is_conf;
-               bool discard;
-               bool no_ack;
-               u8 refcount;
-               struct sk_buff *txfrag;
-               struct sk_buff *msdu;
-       } __packed htt;
 
-       /* 4 bytes left on 64bit arch */
+               u8 frag_len;
+               u8 pad_len;
+       } __packed htt;
 } __packed;
 
 static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
@@ -108,15 +104,26 @@ struct ath10k_bmi {
        bool done_sent;
 };
 
+#define ATH10K_MAX_MEM_REQS 16
+
+struct ath10k_mem_chunk {
+       void *vaddr;
+       dma_addr_t paddr;
+       u32 len;
+       u32 req_id;
+};
+
 struct ath10k_wmi {
        enum ath10k_htc_ep_id eid;
        struct completion service_ready;
        struct completion unified_ready;
-       atomic_t pending_tx_count;
-       wait_queue_head_t wq;
+       wait_queue_head_t tx_credits_wq;
+       struct wmi_cmd_map *cmd;
+       struct wmi_vdev_param_map *vdev_param;
+       struct wmi_pdev_param_map *pdev_param;
 
-       struct sk_buff_head wmi_event_list;
-       struct work_struct wmi_event_work;
+       u32 num_mem_chunks;
+       struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS];
 };
 
 struct ath10k_peer_stat {
@@ -198,17 +205,22 @@ struct ath10k_peer {
 #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
 
 struct ath10k_vif {
+       struct list_head list;
+
        u32 vdev_id;
        enum wmi_vdev_type vdev_type;
        enum wmi_vdev_subtype vdev_subtype;
        u32 beacon_interval;
        u32 dtim_period;
+       struct sk_buff *beacon;
 
        struct ath10k *ar;
        struct ieee80211_vif *vif;
 
+       struct work_struct wep_key_work;
        struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
-       u8 def_wep_key_index;
+       u8 def_wep_key_idx;
+       u8 def_wep_key_newidx;
 
        u16 tx_seq_no;
 
@@ -246,6 +258,9 @@ struct ath10k_debug {
        u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
 
        struct completion event_stats_compl;
+
+       unsigned long htt_stats_mask;
+       struct delayed_work htt_stats_dwork;
 };
 
 enum ath10k_state {
@@ -270,12 +285,27 @@ enum ath10k_state {
        ATH10K_STATE_WEDGED,
 };
 
+enum ath10k_fw_features {
+       /* wmi_mgmt_rx_hdr contains extra RSSI information */
+       ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0,
+
+       /* firmware from 10X branch */
+       ATH10K_FW_FEATURE_WMI_10X = 1,
+
+       /* firmware support tx frame management over WMI, otherwise it's HTT */
+       ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2,
+
+       /* keep last */
+       ATH10K_FW_FEATURE_COUNT,
+};
+
 struct ath10k {
        struct ath_common ath_common;
        struct ieee80211_hw *hw;
        struct device *dev;
        u8 mac_addr[ETH_ALEN];
 
+       u32 chip_id;
        u32 target_version;
        u8 fw_version_major;
        u32 fw_version_minor;
@@ -288,6 +318,8 @@ struct ath10k {
        u32 vht_cap_info;
        u32 num_rf_chains;
 
+       DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT);
+
        struct targetdef *targetdef;
        struct hostdef *hostdef;
 
@@ -319,9 +351,19 @@ struct ath10k {
                } fw;
        } hw_params;
 
-       const struct firmware *board_data;
+       const struct firmware *board;
+       const void *board_data;
+       size_t board_len;
+
        const struct firmware *otp;
+       const void *otp_data;
+       size_t otp_len;
+
        const struct firmware *firmware;
+       const void *firmware_data;
+       size_t firmware_len;
+
+       int fw_api;
 
        struct {
                struct completion started;
@@ -364,6 +406,7 @@ struct ath10k {
        /* protects shared structure data */
        spinlock_t data_lock;
 
+       struct list_head arvifs;
        struct list_head peers;
        wait_queue_head_t peer_mapping_wq;
 
@@ -372,6 +415,9 @@ struct ath10k {
        struct completion offchan_tx_completed;
        struct sk_buff *offchan_tx_skb;
 
+       struct work_struct wmi_mgmt_tx_work;
+       struct sk_buff_head wmi_mgmt_tx_queue;
+
        enum ath10k_state state;
 
        struct work_struct restart_work;
@@ -393,7 +439,7 @@ void ath10k_core_destroy(struct ath10k *ar);
 
 int ath10k_core_start(struct ath10k *ar);
 void ath10k_core_stop(struct ath10k *ar);
-int ath10k_core_register(struct ath10k *ar);
+int ath10k_core_register(struct ath10k *ar, u32 chip_id);
 void ath10k_core_unregister(struct ath10k *ar);
 
 #endif /* _CORE_H_ */
index 3d65594..760ff22 100644 (file)
@@ -21,6 +21,9 @@
 #include "core.h"
 #include "debug.h"
 
+/* ms */
+#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
+
 static int ath10k_printk(const char *level, const char *fmt, ...)
 {
        struct va_format vaf;
@@ -260,7 +263,6 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        }
 
        spin_unlock_bh(&ar->data_lock);
-       mutex_unlock(&ar->conf_mutex);
        complete(&ar->debug.event_stats_compl);
 }
 
@@ -499,6 +501,144 @@ static const struct file_operations fops_simulate_fw_crash = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       unsigned int len;
+       char buf[50];
+
+       len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_chip_id = {
+       .read = ath10k_read_chip_id,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static int ath10k_debug_htt_stats_req(struct ath10k *ar)
+{
+       u64 cookie;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ar->debug.htt_stats_mask == 0)
+               /* htt stats are disabled */
+               return 0;
+
+       if (ar->state != ATH10K_STATE_ON)
+               return 0;
+
+       cookie = get_jiffies_64();
+
+       ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,
+                                      cookie);
+       if (ret) {
+               ath10k_warn("failed to send htt stats request: %d\n", ret);
+               return ret;
+       }
+
+       queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,
+                          msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));
+
+       return 0;
+}
+
+static void ath10k_debug_htt_stats_dwork(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k,
+                                        debug.htt_stats_dwork.work);
+
+       mutex_lock(&ar->conf_mutex);
+
+       ath10k_debug_htt_stats_req(ar);
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static ssize_t ath10k_read_htt_stats_mask(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_htt_stats_mask(struct file *file,
+                                            const char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       unsigned long mask;
+       int ret;
+
+       ret = kstrtoul_from_user(user_buf, count, 0, &mask);
+       if (ret)
+               return ret;
+
+       /* max 8 bit masks (for now) */
+       if (mask > 0xff)
+               return -E2BIG;
+
+       mutex_lock(&ar->conf_mutex);
+
+       ar->debug.htt_stats_mask = mask;
+
+       ret = ath10k_debug_htt_stats_req(ar);
+       if (ret)
+               goto out;
+
+       ret = count;
+
+out:
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
+static const struct file_operations fops_htt_stats_mask = {
+       .read = ath10k_read_htt_stats_mask,
+       .write = ath10k_write_htt_stats_mask,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+int ath10k_debug_start(struct ath10k *ar)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_debug_htt_stats_req(ar);
+       if (ret)
+               /* continue normally anyway, this isn't serious */
+               ath10k_warn("failed to start htt stats workqueue: %d\n", ret);
+
+       return 0;
+}
+
+void ath10k_debug_stop(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* Must not use _sync to avoid deadlock, we do that in
+        * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
+        * warning from del_timer(). */
+       if (ar->debug.htt_stats_mask != 0)
+               cancel_delayed_work(&ar->debug.htt_stats_dwork);
+}
+
 int ath10k_debug_create(struct ath10k *ar)
 {
        ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -507,6 +647,9 @@ int ath10k_debug_create(struct ath10k *ar)
        if (!ar->debug.debugfs_phy)
                return -ENOMEM;
 
+       INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
+                         ath10k_debug_htt_stats_dwork);
+
        init_completion(&ar->debug.event_stats_compl);
 
        debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
@@ -518,8 +661,20 @@ int ath10k_debug_create(struct ath10k *ar)
        debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_simulate_fw_crash);
 
+       debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
+                           ar, &fops_chip_id);
+
+       debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
+                           ar, &fops_htt_stats_mask);
+
        return 0;
 }
+
+void ath10k_debug_destroy(struct ath10k *ar)
+{
+       cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
+}
+
 #endif /* CONFIG_ATH10K_DEBUGFS */
 
 #ifdef CONFIG_ATH10K_DEBUG
index 168140c..3cfe3ee 100644 (file)
@@ -27,22 +27,26 @@ enum ath10k_debug_mask {
        ATH10K_DBG_HTC          = 0x00000004,
        ATH10K_DBG_HTT          = 0x00000008,
        ATH10K_DBG_MAC          = 0x00000010,
-       ATH10K_DBG_CORE         = 0x00000020,
+       ATH10K_DBG_BOOT         = 0x00000020,
        ATH10K_DBG_PCI_DUMP     = 0x00000040,
        ATH10K_DBG_HTT_DUMP     = 0x00000080,
        ATH10K_DBG_MGMT         = 0x00000100,
        ATH10K_DBG_DATA         = 0x00000200,
+       ATH10K_DBG_BMI          = 0x00000400,
        ATH10K_DBG_ANY          = 0xffffffff,
 };
 
 extern unsigned int ath10k_debug_mask;
 
-extern __printf(1, 2) int ath10k_info(const char *fmt, ...);
-extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
-extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
+__printf(1, 2) int ath10k_info(const char *fmt, ...);
+__printf(1, 2) int ath10k_err(const char *fmt, ...);
+__printf(1, 2) int ath10k_warn(const char *fmt, ...);
 
 #ifdef CONFIG_ATH10K_DEBUGFS
+int ath10k_debug_start(struct ath10k *ar);
+void ath10k_debug_stop(struct ath10k *ar);
 int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_destroy(struct ath10k *ar);
 void ath10k_debug_read_service_map(struct ath10k *ar,
                                   void *service_map,
                                   size_t map_size);
@@ -50,11 +54,24 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
                                    struct wmi_stats_event *ev);
 
 #else
+static inline int ath10k_debug_start(struct ath10k *ar)
+{
+       return 0;
+}
+
+static inline void ath10k_debug_stop(struct ath10k *ar)
+{
+}
+
 static inline int ath10k_debug_create(struct ath10k *ar)
 {
        return 0;
 }
 
+static inline void ath10k_debug_destroy(struct ath10k *ar)
+{
+}
+
 static inline void ath10k_debug_read_service_map(struct ath10k *ar,
                                                 void *service_map,
                                                 size_t map_size)
@@ -68,7 +85,7 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
 #endif /* CONFIG_ATH10K_DEBUGFS */
 
 #ifdef CONFIG_ATH10K_DEBUG
-extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
+__printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
                                      const char *fmt, ...);
 void ath10k_dbg_dump(enum ath10k_debug_mask mask,
                     const char *msg, const char *prefix,
index ef3329e..3118d75 100644 (file)
@@ -103,10 +103,10 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
        struct ath10k_htc_hdr *hdr;
 
        hdr = (struct ath10k_htc_hdr *)skb->data;
-       memset(hdr, 0, sizeof(*hdr));
 
        hdr->eid = ep->eid;
        hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
+       hdr->flags = 0;
 
        spin_lock_bh(&ep->htc->tx_lock);
        hdr->seq_no = ep->seq_no++;
@@ -117,134 +117,13 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
        spin_unlock_bh(&ep->htc->tx_lock);
 }
 
-static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
-                               struct ath10k_htc_ep *ep,
-                               struct sk_buff *skb,
-                               u8 credits)
-{
-       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
-       int ret;
-
-       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
-                  ep->eid, skb);
-
-       ath10k_htc_prepare_tx_skb(ep, skb);
-
-       ret = ath10k_skb_map(htc->ar->dev, skb);
-       if (ret)
-               goto err;
-
-       ret = ath10k_hif_send_head(htc->ar,
-                                  ep->ul_pipe_id,
-                                  ep->eid,
-                                  skb->len,
-                                  skb);
-       if (unlikely(ret))
-               goto err;
-
-       return 0;
-err:
-       ath10k_warn("HTC issue failed: %d\n", ret);
-
-       spin_lock_bh(&htc->tx_lock);
-       ep->tx_credits += credits;
-       spin_unlock_bh(&htc->tx_lock);
-
-       /* this is the simplest way to handle out-of-resources for non-credit
-        * based endpoints. credit based endpoints can still get -ENOSR, but
-        * this is highly unlikely as credit reservation should prevent that */
-       if (ret == -ENOSR) {
-               spin_lock_bh(&htc->tx_lock);
-               __skb_queue_head(&ep->tx_queue, skb);
-               spin_unlock_bh(&htc->tx_lock);
-
-               return ret;
-       }
-
-       skb_cb->is_aborted = true;
-       ath10k_htc_notify_tx_completion(ep, skb);
-
-       return ret;
-}
-
-static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
-                                                      struct ath10k_htc_ep *ep,
-                                                      u8 *credits)
-{
-       struct sk_buff *skb;
-       struct ath10k_skb_cb *skb_cb;
-       int credits_required;
-       int remainder;
-       unsigned int transfer_len;
-
-       lockdep_assert_held(&htc->tx_lock);
-
-       skb = __skb_dequeue(&ep->tx_queue);
-       if (!skb)
-               return NULL;
-
-       skb_cb = ATH10K_SKB_CB(skb);
-       transfer_len = skb->len;
-
-       if (likely(transfer_len <= htc->target_credit_size)) {
-               credits_required = 1;
-       } else {
-               /* figure out how many credits this message requires */
-               credits_required = transfer_len / htc->target_credit_size;
-               remainder = transfer_len % htc->target_credit_size;
-
-               if (remainder)
-                       credits_required++;
-       }
-
-       ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
-                  credits_required, ep->tx_credits);
-
-       if (ep->tx_credits < credits_required) {
-               __skb_queue_head(&ep->tx_queue, skb);
-               return NULL;
-       }
-
-       ep->tx_credits -= credits_required;
-       *credits = credits_required;
-       return skb;
-}
-
-static void ath10k_htc_send_work(struct work_struct *work)
-{
-       struct ath10k_htc_ep *ep = container_of(work,
-                                       struct ath10k_htc_ep, send_work);
-       struct ath10k_htc *htc = ep->htc;
-       struct sk_buff *skb;
-       u8 credits = 0;
-       int ret;
-
-       while (true) {
-               if (ep->ul_is_polled)
-                       ath10k_htc_send_complete_check(ep, 0);
-
-               spin_lock_bh(&htc->tx_lock);
-               if (ep->tx_credit_flow_enabled)
-                       skb = ath10k_htc_get_skb_credit_based(htc, ep,
-                                                             &credits);
-               else
-                       skb = __skb_dequeue(&ep->tx_queue);
-               spin_unlock_bh(&htc->tx_lock);
-
-               if (!skb)
-                       break;
-
-               ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
-               if (ret == -ENOSR)
-                       break;
-       }
-}
-
 int ath10k_htc_send(struct ath10k_htc *htc,
                    enum ath10k_htc_ep_id eid,
                    struct sk_buff *skb)
 {
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+       int credits = 0;
+       int ret;
 
        if (htc->ar->state == ATH10K_STATE_WEDGED)
                return -ECOMM;
@@ -254,18 +133,55 @@ int ath10k_htc_send(struct ath10k_htc *htc,
                return -ENOENT;
        }
 
+       /* FIXME: This looks ugly, can we fix it? */
        spin_lock_bh(&htc->tx_lock);
        if (htc->stopped) {
                spin_unlock_bh(&htc->tx_lock);
                return -ESHUTDOWN;
        }
+       spin_unlock_bh(&htc->tx_lock);
 
-       __skb_queue_tail(&ep->tx_queue, skb);
        skb_push(skb, sizeof(struct ath10k_htc_hdr));
-       spin_unlock_bh(&htc->tx_lock);
 
-       queue_work(htc->ar->workqueue, &ep->send_work);
+       if (ep->tx_credit_flow_enabled) {
+               credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
+               spin_lock_bh(&htc->tx_lock);
+               if (ep->tx_credits < credits) {
+                       spin_unlock_bh(&htc->tx_lock);
+                       ret = -EAGAIN;
+                       goto err_pull;
+               }
+               ep->tx_credits -= credits;
+               spin_unlock_bh(&htc->tx_lock);
+       }
+
+       ath10k_htc_prepare_tx_skb(ep, skb);
+
+       ret = ath10k_skb_map(htc->ar->dev, skb);
+       if (ret)
+               goto err_credits;
+
+       ret = ath10k_hif_send_head(htc->ar, ep->ul_pipe_id, ep->eid,
+                                  skb->len, skb);
+       if (ret)
+               goto err_unmap;
+
        return 0;
+
+err_unmap:
+       ath10k_skb_unmap(htc->ar->dev, skb);
+err_credits:
+       if (ep->tx_credit_flow_enabled) {
+               spin_lock_bh(&htc->tx_lock);
+               ep->tx_credits += credits;
+               spin_unlock_bh(&htc->tx_lock);
+
+               if (ep->ep_ops.ep_tx_credits)
+                       ep->ep_ops.ep_tx_credits(htc->ar);
+       }
+err_pull:
+       skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+       return ret;
 }
 
 static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
@@ -278,39 +194,9 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
        ath10k_htc_notify_tx_completion(ep, skb);
        /* the skb now belongs to the completion handler */
 
-       /* note: when using TX credit flow, the re-checking of queues happens
-        * when credits flow back from the target.  in the non-TX credit case,
-        * we recheck after the packet completes */
-       spin_lock_bh(&htc->tx_lock);
-       if (!ep->tx_credit_flow_enabled && !htc->stopped)
-               queue_work(ar->workqueue, &ep->send_work);
-       spin_unlock_bh(&htc->tx_lock);
-
        return 0;
 }
 
-/* flush endpoint TX queue */
-static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
-                                        struct ath10k_htc_ep *ep)
-{
-       struct sk_buff *skb;
-       struct ath10k_skb_cb *skb_cb;
-
-       spin_lock_bh(&htc->tx_lock);
-       for (;;) {
-               skb = __skb_dequeue(&ep->tx_queue);
-               if (!skb)
-                       break;
-
-               skb_cb = ATH10K_SKB_CB(skb);
-               skb_cb->is_aborted = true;
-               ath10k_htc_notify_tx_completion(ep, skb);
-       }
-       spin_unlock_bh(&htc->tx_lock);
-
-       cancel_work_sync(&ep->send_work);
-}
-
 /***********/
 /* Receive */
 /***********/
@@ -340,8 +226,11 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc,
                ep = &htc->endpoint[report->eid];
                ep->tx_credits += report->credits;
 
-               if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
-                       queue_work(htc->ar->workqueue, &ep->send_work);
+               if (ep->ep_ops.ep_tx_credits) {
+                       spin_unlock_bh(&htc->tx_lock);
+                       ep->ep_ops.ep_tx_credits(htc->ar);
+                       spin_lock_bh(&htc->tx_lock);
+               }
        }
        spin_unlock_bh(&htc->tx_lock);
 }
@@ -599,10 +488,8 @@ static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
                ep->max_ep_message_len = 0;
                ep->max_tx_queue_depth = 0;
                ep->eid = i;
-               skb_queue_head_init(&ep->tx_queue);
                ep->htc = htc;
                ep->tx_credit_flow_enabled = true;
-               INIT_WORK(&ep->send_work, ath10k_htc_send_work);
        }
 }
 
@@ -752,8 +639,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
        tx_alloc = ath10k_htc_get_credit_allocation(htc,
                                                    conn_req->service_id);
        if (!tx_alloc)
-               ath10k_dbg(ATH10K_DBG_HTC,
-                          "HTC Service %s does not allocate target credits\n",
+               ath10k_dbg(ATH10K_DBG_BOOT,
+                          "boot htc service %s does not allocate target credits\n",
                           htc_service_name(conn_req->service_id));
 
        skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
@@ -772,16 +659,16 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 
        flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
 
-       req_msg = &msg->connect_service;
-       req_msg->flags = __cpu_to_le16(flags);
-       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
-
        /* Only enable credit flow control for WMI ctrl service */
        if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
                flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
                disable_credit_flow_ctrl = true;
        }
 
+       req_msg = &msg->connect_service;
+       req_msg->flags = __cpu_to_le16(flags);
+       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
        INIT_COMPLETION(htc->ctl_resp);
 
        status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
@@ -873,19 +760,19 @@ setup:
        if (status)
                return status;
 
-       ath10k_dbg(ATH10K_DBG_HTC,
-                  "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
                   htc_service_name(ep->service_id), ep->ul_pipe_id,
                   ep->dl_pipe_id, ep->eid);
 
-       ath10k_dbg(ATH10K_DBG_HTC,
-                  "EP %d UL polled: %d, DL polled: %d\n",
+       ath10k_dbg(ATH10K_DBG_BOOT,
+                  "boot htc ep %d ul polled %d dl polled %d\n",
                   ep->eid, ep->ul_is_polled, ep->dl_is_polled);
 
        if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
                ep->tx_credit_flow_enabled = false;
-               ath10k_dbg(ATH10K_DBG_HTC,
-                          "HTC service: %s eid: %d TX flow control disabled\n",
+               ath10k_dbg(ATH10K_DBG_BOOT,
+                          "boot htc service '%s' eid %d TX flow control disabled\n",
                           htc_service_name(ep->service_id), assigned_eid);
        }
 
@@ -945,18 +832,10 @@ int ath10k_htc_start(struct ath10k_htc *htc)
  */
 void ath10k_htc_stop(struct ath10k_htc *htc)
 {
-       int i;
-       struct ath10k_htc_ep *ep;
-
        spin_lock_bh(&htc->tx_lock);
        htc->stopped = true;
        spin_unlock_bh(&htc->tx_lock);
 
-       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
-               ep = &htc->endpoint[i];
-               ath10k_htc_flush_endpoint_tx(htc, ep);
-       }
-
        ath10k_hif_stop(htc->ar);
 }
 
index e1dd8c7..4716d33 100644 (file)
@@ -276,6 +276,7 @@ struct ath10k_htc_ops {
 struct ath10k_htc_ep_ops {
        void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
        void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
+       void (*ep_tx_credits)(struct ath10k *);
 };
 
 /* service connection information */
@@ -315,15 +316,11 @@ struct ath10k_htc_ep {
        int ul_is_polled; /* call HIF to get tx completions */
        int dl_is_polled; /* call HIF to fetch rx (not implemented) */
 
-       struct sk_buff_head tx_queue;
-
        u8 seq_no; /* for debugging */
        int tx_credits;
        int tx_credit_size;
        int tx_credits_per_max_message;
        bool tx_credit_flow_enabled;
-
-       struct work_struct send_work;
 };
 
 struct ath10k_htc_svc_tx_credits {
index 39342c5..5f7eeeb 100644 (file)
@@ -104,21 +104,16 @@ err_htc_attach:
 
 static int ath10k_htt_verify_version(struct ath10k_htt *htt)
 {
-       ath10k_dbg(ATH10K_DBG_HTT,
-                  "htt target version %d.%d; host version %d.%d\n",
-                   htt->target_version_major,
-                   htt->target_version_minor,
-                   HTT_CURRENT_VERSION_MAJOR,
-                   HTT_CURRENT_VERSION_MINOR);
-
-       if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) {
-               ath10k_err("htt major versions are incompatible!\n");
+       ath10k_info("htt target version %d.%d\n",
+                   htt->target_version_major, htt->target_version_minor);
+
+       if (htt->target_version_major != 2 &&
+           htt->target_version_major != 3) {
+               ath10k_err("unsupported htt major version %d. supported versions are 2 and 3\n",
+                          htt->target_version_major);
                return -ENOTSUPP;
        }
 
-       if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR)
-               ath10k_warn("htt minor version differ but still compatible\n");
-
        return 0;
 }
 
index 318be46..1a337e9 100644 (file)
 #define _HTT_H_
 
 #include <linux/bug.h>
+#include <linux/interrupt.h>
 
 #include "htc.h"
 #include "rx_desc.h"
 
-#define HTT_CURRENT_VERSION_MAJOR      2
-#define HTT_CURRENT_VERSION_MINOR      1
-
 enum htt_dbg_stats_type {
        HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0,
        HTT_DBG_STATS_RX_REORDER    = 1 << 1,
@@ -45,6 +43,9 @@ enum htt_h2t_msg_type { /* host-to-target */
        HTT_H2T_MSG_TYPE_SYNC               = 4,
        HTT_H2T_MSG_TYPE_AGGR_CFG           = 5,
        HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6,
+
+       /* This command is used for sending management frames in HTT < 3.0.
+        * HTT >= 3.0 uses TX_FRM for everything. */
        HTT_H2T_MSG_TYPE_MGMT_TX            = 7,
 
        HTT_H2T_NUM_MSGS /* keep this last */
@@ -1268,6 +1269,7 @@ struct ath10k_htt {
        /* set if host-fw communication goes haywire
         * used to avoid further failures */
        bool rx_confused;
+       struct tasklet_struct rx_replenish_task;
 };
 
 #define RX_HTT_HDR_STATUS_LEN 64
@@ -1308,6 +1310,10 @@ struct htt_rx_desc {
 #define HTT_RX_BUF_SIZE 1920
 #define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
 
+/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle
+ * aggregated traffic more nicely. */
+#define ATH10K_HTT_MAX_NUM_REFILL 16
+
 /*
  * DMA_MAP expects the buffer to be an integral number of cache lines.
  * Rather than checking the actual cache line size, this code makes a
@@ -1327,6 +1333,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt);
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
 int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
 
 void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
index e784c40..90d4f74 100644 (file)
@@ -20,6 +20,7 @@
 #include "htt.h"
 #include "txrx.h"
 #include "debug.h"
+#include "trace.h"
 
 #include <linux/log2.h>
 
 /* when under memory pressure rx ring refill may fail and needs a retry */
 #define HTT_RX_RING_REFILL_RETRY_MS 50
 
+
+static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
+
+
 static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
 {
        int size;
@@ -177,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
 
 static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
 {
-       int ret, num_to_fill;
+       int ret, num_deficit, num_to_fill;
 
+       /* Refilling the whole RX ring buffer proves to be a bad idea. The
+        * reason is RX may take up significant amount of CPU cycles and starve
+        * other tasks, e.g. TX on an ethernet device while acting as a bridge
+        * with ath10k wlan interface. This ended up with very poor performance
+        * once CPU the host system was overwhelmed with RX on ath10k.
+        *
+        * By limiting the number of refills the replenishing occurs
+        * progressively. This in turns makes use of the fact tasklets are
+        * processed in FIFO order. This means actual RX processing can starve
+        * out refilling. If there's not enough buffers on RX ring FW will not
+        * report RX until it is refilled with enough buffers. This
+        * automatically balances load wrt to CPU power.
+        *
+        * This probably comes at a cost of lower maximum throughput but
+        * improves the avarage and stability. */
        spin_lock_bh(&htt->rx_ring.lock);
-       num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
+       num_deficit -= num_to_fill;
        ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
        if (ret == -ENOMEM) {
                /*
@@ -191,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
                 */
                mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
                          msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+       } else if (num_deficit > 0) {
+               tasklet_schedule(&htt->rx_replenish_task);
        }
        spin_unlock_bh(&htt->rx_ring.lock);
 }
@@ -212,6 +236,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
        int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
 
        del_timer_sync(&htt->rx_ring.refill_retry_timer);
+       tasklet_kill(&htt->rx_replenish_task);
 
        while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
                struct sk_buff *skb =
@@ -441,6 +466,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        return msdu_chaining;
 }
 
+static void ath10k_htt_rx_replenish_task(unsigned long ptr)
+{
+       struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
 int ath10k_htt_rx_attach(struct ath10k_htt *htt)
 {
        dma_addr_t paddr;
@@ -501,7 +532,10 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
        if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
                goto err_fill_ring;
 
-       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+       tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
+                    (unsigned long)htt);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
                   htt->rx_ring.size, htt->rx_ring.fill_level);
        return 0;
 
@@ -590,134 +624,144 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
        return false;
 }
 
-static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
-                       struct htt_rx_info *info)
+struct rfc1042_hdr {
+       u8 llc_dsap;
+       u8 llc_ssap;
+       u8 llc_ctrl;
+       u8 snap_oui[3];
+       __be16 snap_type;
+} __packed;
+
+struct amsdu_subframe_hdr {
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       __be16 len;
+} __packed;
+
+static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+                               struct htt_rx_info *info)
 {
        struct htt_rx_desc *rxd;
-       struct sk_buff *amsdu;
        struct sk_buff *first;
-       struct ieee80211_hdr *hdr;
        struct sk_buff *skb = info->skb;
        enum rx_msdu_decap_format fmt;
        enum htt_rx_mpdu_encrypt_type enctype;
+       struct ieee80211_hdr *hdr;
+       u8 hdr_buf[64], addr[ETH_ALEN], *qos;
        unsigned int hdr_len;
-       int crypto_len;
 
        rxd = (void *)skb->data - sizeof(*rxd);
-       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-                       RX_MSDU_START_INFO1_DECAP_FORMAT);
        enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
                        RX_MPDU_START_INFO0_ENCRYPT_TYPE);
 
-       /* FIXME: No idea what assumptions are safe here. Need logs */
-       if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
-           (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
-               ath10k_htt_rx_free_msdu_chain(skb->next);
-               skb->next = NULL;
-               return -ENOTSUPP;
-       }
+       hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       memcpy(hdr_buf, hdr, hdr_len);
+       hdr = (struct ieee80211_hdr *)hdr_buf;
 
-       /* A-MSDU max is a little less than 8K */
-       amsdu = dev_alloc_skb(8*1024);
-       if (!amsdu) {
-               ath10k_warn("A-MSDU allocation failed\n");
-               ath10k_htt_rx_free_msdu_chain(skb->next);
-               skb->next = NULL;
-               return -ENOMEM;
-       }
-
-       if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
-               int hdrlen;
-
-               hdr = (void *)rxd->rx_hdr_status;
-               hdrlen = ieee80211_hdrlen(hdr->frame_control);
-               memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
-       }
+       /* FIXME: Hopefully this is a temporary measure.
+        *
+        * Reporting individual A-MSDU subframes means each reported frame
+        * shares the same sequence number.
+        *
+        * mac80211 drops frames it recognizes as duplicates, i.e.
+        * retransmission flag is set and sequence number matches sequence
+        * number from a previous frame (as per IEEE 802.11-2012: 9.3.2.10
+        * "Duplicate detection and recovery")
+        *
+        * To avoid frames being dropped clear retransmission flag for all
+        * received A-MSDUs.
+        *
+        * Worst case: actual duplicate frames will be reported but this should
+        * still be handled gracefully by other OSI/ISO layers. */
+       hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_RETRY);
 
        first = skb;
        while (skb) {
                void *decap_hdr;
-               int decap_len = 0;
+               int len;
 
                rxd = (void *)skb->data - sizeof(*rxd);
                fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-                               RX_MSDU_START_INFO1_DECAP_FORMAT);
+                        RX_MSDU_START_INFO1_DECAP_FORMAT);
                decap_hdr = (void *)rxd->rx_hdr_status;
 
-               if (skb == first) {
-                       /* We receive linked A-MSDU subframe skbuffs. The
-                        * first one contains the original 802.11 header (and
-                        * possible crypto param) in the RX descriptor. The
-                        * A-MSDU subframe header follows that. Each part is
-                        * aligned to 4 byte boundary. */
-
-                       hdr = (void *)amsdu->data;
-                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
-                       crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
-
-                       decap_hdr += roundup(hdr_len, 4);
-                       decap_hdr += roundup(crypto_len, 4);
-               }
+               skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
 
-               if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
-                       /* Ethernet2 decap inserts ethernet header in place of
-                        * A-MSDU subframe header. */
-                       skb_pull(skb, 6 + 6 + 2);
-
-                       /* A-MSDU subframe header length */
-                       decap_len += 6 + 6 + 2;
-
-                       /* Ethernet2 decap also strips the LLC/SNAP so we need
-                        * to re-insert it. The LLC/SNAP follows A-MSDU
-                        * subframe header. */
-                       /* FIXME: Not all LLCs are 8 bytes long */
-                       decap_len += 8;
-
-                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               /* First frame in an A-MSDU chain has more decapped data. */
+               if (skb == first) {
+                       len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+                       len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
+                                       4);
+                       decap_hdr += len;
                }
 
-               if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
-                       /* Native Wifi decap inserts regular 802.11 header
-                        * in place of A-MSDU subframe header. */
+               switch (fmt) {
+               case RX_MSDU_DECAP_RAW:
+                       /* remove trailing FCS */
+                       skb_trim(skb, skb->len - FCS_LEN);
+                       break;
+               case RX_MSDU_DECAP_NATIVE_WIFI:
+                       /* pull decapped header and copy DA */
                        hdr = (struct ieee80211_hdr *)skb->data;
-                       skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN);
+                       skb_pull(skb, hdr_len);
 
-                       /* A-MSDU subframe header length */
-                       decap_len += 6 + 6 + 2;
+                       /* push original 802.11 header */
+                       hdr = (struct ieee80211_hdr *)hdr_buf;
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
 
-                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
-               }
+                       /* original A-MSDU header has the bit set but we're
+                        * not including A-MSDU subframe header */
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       qos = ieee80211_get_qos_ctl(hdr);
+                       qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
 
-               if (fmt == RX_MSDU_DECAP_RAW)
-                       skb_trim(skb, skb->len - 4); /* remove FCS */
+                       /* original 802.11 header has a different DA */
+                       memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN);
+                       break;
+               case RX_MSDU_DECAP_ETHERNET2_DIX:
+                       /* strip ethernet header and insert decapped 802.11
+                        * header, amsdu subframe header and rfc1042 header */
 
-               memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+                       len = 0;
+                       len += sizeof(struct rfc1042_hdr);
+                       len += sizeof(struct amsdu_subframe_hdr);
 
-               /* A-MSDU subframes are padded to 4bytes
-                * but relative to first subframe, not the whole MPDU */
-               if (skb->next && ((decap_len + skb->len) & 3)) {
-                       int padlen = 4 - ((decap_len + skb->len) & 3);
-                       memset(skb_put(amsdu, padlen), 0, padlen);
+                       skb_pull(skb, sizeof(struct ethhdr));
+                       memcpy(skb_push(skb, len), decap_hdr, len);
+                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+                       break;
+               case RX_MSDU_DECAP_8023_SNAP_LLC:
+                       /* insert decapped 802.11 header making a singly
+                        * A-MSDU */
+                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+                       break;
                }
 
+               info->skb = skb;
+               info->encrypt_type = enctype;
                skb = skb->next;
-       }
+               info->skb->next = NULL;
 
-       info->skb = amsdu;
-       info->encrypt_type = enctype;
-
-       ath10k_htt_rx_free_msdu_chain(first);
+               ath10k_process_rx(htt->ar, info);
+       }
 
-       return 0;
+       /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
+        * monitor interface active for sniffing purposes. */
 }
 
-static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
 {
        struct sk_buff *skb = info->skb;
        struct htt_rx_desc *rxd;
        struct ieee80211_hdr *hdr;
        enum rx_msdu_decap_format fmt;
        enum htt_rx_mpdu_encrypt_type enctype;
+       int hdr_len;
+       void *rfc1042;
 
        /* This shouldn't happen. If it does than it may be a FW bug. */
        if (skb->next) {
@@ -731,49 +775,53 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
                        RX_MSDU_START_INFO1_DECAP_FORMAT);
        enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
                        RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-       hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+       hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+       skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
 
        switch (fmt) {
        case RX_MSDU_DECAP_RAW:
                /* remove trailing FCS */
-               skb_trim(skb, skb->len - 4);
+               skb_trim(skb, skb->len - FCS_LEN);
                break;
        case RX_MSDU_DECAP_NATIVE_WIFI:
-               /* nothing to do here */
+               /* Pull decapped header */
+               hdr = (struct ieee80211_hdr *)skb->data;
+               hdr_len = ieee80211_hdrlen(hdr->frame_control);
+               skb_pull(skb, hdr_len);
+
+               /* Push original header */
+               hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+               hdr_len = ieee80211_hdrlen(hdr->frame_control);
+               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
                break;
        case RX_MSDU_DECAP_ETHERNET2_DIX:
-               /* macaddr[6] + macaddr[6] + ethertype[2] */
-               skb_pull(skb, 6 + 6 + 2);
-               break;
-       case RX_MSDU_DECAP_8023_SNAP_LLC:
-               /* macaddr[6] + macaddr[6] + len[2] */
-               /* we don't need this for non-A-MSDU */
-               skb_pull(skb, 6 + 6 + 2);
-               break;
-       }
+               /* strip ethernet header and insert decapped 802.11 header and
+                * rfc1042 header */
 
-       if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
-               void *llc;
-               int llclen;
+               rfc1042 = hdr;
+               rfc1042 += roundup(hdr_len, 4);
+               rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
 
-               llclen = 8;
-               llc  = hdr;
-               llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
-               llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
-
-               skb_push(skb, llclen);
-               memcpy(skb->data, llc, llclen);
-       }
+               skb_pull(skb, sizeof(struct ethhdr));
+               memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
+                      rfc1042, sizeof(struct rfc1042_hdr));
+               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               break;
+       case RX_MSDU_DECAP_8023_SNAP_LLC:
+               /* remove A-MSDU subframe header and insert
+                * decapped 802.11 header. rfc1042 header is already there */
 
-       if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
-               int len = ieee80211_hdrlen(hdr->frame_control);
-               skb_push(skb, len);
-               memcpy(skb->data, hdr, len);
+               skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
+               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               break;
        }
 
        info->skb = skb;
        info->encrypt_type = enctype;
-       return 0;
+
+       ath10k_process_rx(htt->ar, info);
 }
 
 static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
@@ -845,8 +893,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
        int fw_desc_len;
        u8 *fw_desc;
        int i, j;
-       int ret;
-       int ip_summed;
 
        memset(&info, 0, sizeof(info));
 
@@ -921,11 +967,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                continue;
                        }
 
-                       /* The skb is not yet processed and it may be
-                        * reallocated. Since the offload is in the original
-                        * skb extract the checksum now and assign it later */
-                       ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
-
                        info.skb     = msdu_head;
                        info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
                        info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
@@ -938,28 +979,13 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
 
                        if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-                               ret = ath10k_htt_rx_amsdu(htt, &info);
+                               ath10k_htt_rx_amsdu(htt, &info);
                        else
-                               ret = ath10k_htt_rx_msdu(htt, &info);
-
-                       if (ret && !info.fcs_err) {
-                               ath10k_warn("error processing msdus %d\n", ret);
-                               dev_kfree_skb_any(info.skb);
-                               continue;
-                       }
-
-                       if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
-                               ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
-
-                       info.skb->ip_summed = ip_summed;
-
-                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
-                                       info.skb->data, info.skb->len);
-                       ath10k_process_rx(htt->ar, &info);
+                               ath10k_htt_rx_msdu(htt, &info);
                }
        }
 
-       ath10k_htt_rx_msdu_buff_replenish(htt);
+       tasklet_schedule(&htt->rx_replenish_task);
 }
 
 static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
@@ -1131,7 +1157,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
                        break;
                }
 
-               ath10k_txrx_tx_completed(htt, &tx_done);
+               ath10k_txrx_tx_unref(htt, &tx_done);
                break;
        }
        case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
@@ -1165,7 +1191,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
                for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
                        msdu_id = resp->data_tx_completion.msdus[i];
                        tx_done.msdu_id = __le16_to_cpu(msdu_id);
-                       ath10k_txrx_tx_completed(htt, &tx_done);
+                       ath10k_txrx_tx_unref(htt, &tx_done);
                }
                break;
        }
@@ -1190,8 +1216,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
        case HTT_T2H_MSG_TYPE_TEST:
                /* FIX THIS */
                break;
-       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
        case HTT_T2H_MSG_TYPE_STATS_CONF:
+               trace_ath10k_htt_stats(skb->data, skb->len);
+               break;
+       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
        case HTT_T2H_MSG_TYPE_RX_ADDBA:
        case HTT_T2H_MSG_TYPE_RX_DELBA:
        case HTT_T2H_MSG_TYPE_RX_FLUSH:
index 656c254..d9335e9 100644 (file)
@@ -96,7 +96,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
        htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
                                                                   pipe);
 
-       ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
+       ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
                   htt->max_num_pending_tx);
 
        htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
@@ -117,7 +117,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
 
 static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
 {
-       struct sk_buff *txdesc;
+       struct htt_tx_done tx_done = {0};
        int msdu_id;
 
        /* No locks needed. Called after communication with the device has
@@ -127,18 +127,13 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
                if (!test_bit(msdu_id, htt->used_msdu_ids))
                        continue;
 
-               txdesc = htt->pending_tx[msdu_id];
-               if (!txdesc)
-                       continue;
-
                ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
                           msdu_id);
 
-               if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
-                       ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
+               tx_done.discard = 1;
+               tx_done.msdu_id = msdu_id;
 
-               ATH10K_SKB_CB(txdesc)->htt.discard = true;
-               ath10k_txrx_tx_unref(htt, txdesc);
+               ath10k_txrx_tx_unref(htt, &tx_done);
        }
 }
 
@@ -152,26 +147,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
 
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
-       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
-       struct ath10k_htt *htt = &ar->htt;
-
-       if (skb_cb->htt.is_conf) {
-               dev_kfree_skb_any(skb);
-               return;
-       }
-
-       if (skb_cb->is_aborted) {
-               skb_cb->htt.discard = true;
-
-               /* if the skbuff is aborted we need to make sure we'll free up
-                * the tx resources, we can't simply run tx_unref() 2 times
-                * because if htt tx completion came in earlier we'd access
-                * unallocated memory */
-               if (skb_cb->htt.refcount > 1)
-                       skb_cb->htt.refcount = 1;
-       }
-
-       ath10k_txrx_tx_unref(htt, skb);
+       dev_kfree_skb_any(skb);
 }
 
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
@@ -192,10 +168,48 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
        cmd = (struct htt_cmd *)skb->data;
        cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
 
-       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie)
+{
+       struct htt_stats_req *req;
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       int len = 0, ret;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->stats_req);
+
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_STATS_REQ;
+
+       req = &cmd->stats_req;
+
+       memset(req, 0, sizeof(*req));
+
+       /* currently we support only max 8 bit masks so no need to worry
+        * about endian support */
+       req->upload_types[0] = mask;
+       req->reset_types[0] = mask;
+       req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID;
+       req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff);
+       req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32);
 
        ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
        if (ret) {
+               ath10k_warn("failed to send htt type stats request: %d", ret);
                dev_kfree_skb_any(skb);
                return ret;
        }
@@ -279,8 +293,6 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
 
 #undef desc_offset
 
-       ATH10K_SKB_CB(skb)->htt.is_conf = true;
-
        ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
        if (ret) {
                dev_kfree_skb_any(skb);
@@ -293,10 +305,10 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 {
        struct device *dev = htt->ar->dev;
-       struct ath10k_skb_cb *skb_cb;
        struct sk_buff *txdesc = NULL;
        struct htt_cmd *cmd;
-       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
+       u8 vdev_id = skb_cb->vdev_id;
        int len = 0;
        int msdu_id = -1;
        int res;
@@ -304,30 +316,30 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 
        res = ath10k_htt_tx_inc_pending(htt);
        if (res)
-               return res;
+               goto err;
 
        len += sizeof(cmd->hdr);
        len += sizeof(cmd->mgmt_tx);
 
-       txdesc = ath10k_htc_alloc_skb(len);
-       if (!txdesc) {
-               res = -ENOMEM;
-               goto err;
-       }
-
        spin_lock_bh(&htt->tx_lock);
-       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
-       if (msdu_id < 0) {
+       res = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (res < 0) {
                spin_unlock_bh(&htt->tx_lock);
-               res = msdu_id;
-               goto err;
+               goto err_tx_dec;
        }
-       htt->pending_tx[msdu_id] = txdesc;
+       msdu_id = res;
+       htt->pending_tx[msdu_id] = msdu;
        spin_unlock_bh(&htt->tx_lock);
 
+       txdesc = ath10k_htc_alloc_skb(len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err_free_msdu_id;
+       }
+
        res = ath10k_skb_map(dev, msdu);
        if (res)
-               goto err;
+               goto err_free_txdesc;
 
        skb_put(txdesc, len);
        cmd = (struct htt_cmd *)txdesc->data;
@@ -339,31 +351,27 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        memcpy(cmd->mgmt_tx.hdr, msdu->data,
               min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
 
-       /* refcount is decremented by HTC and HTT completions until it reaches
-        * zero and is freed */
-       skb_cb = ATH10K_SKB_CB(txdesc);
-       skb_cb->htt.msdu_id = msdu_id;
-       skb_cb->htt.refcount = 2;
-       skb_cb->htt.msdu = msdu;
+       skb_cb->htt.frag_len = 0;
+       skb_cb->htt.pad_len = 0;
 
        res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
        if (res)
-               goto err;
+               goto err_unmap_msdu;
 
        return 0;
 
-err:
+err_unmap_msdu:
        ath10k_skb_unmap(dev, msdu);
-
-       if (txdesc)
-               dev_kfree_skb_any(txdesc);
-       if (msdu_id >= 0) {
-               spin_lock_bh(&htt->tx_lock);
-               htt->pending_tx[msdu_id] = NULL;
-               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
-               spin_unlock_bh(&htt->tx_lock);
-       }
+err_free_txdesc:
+       dev_kfree_skb_any(txdesc);
+err_free_msdu_id:
+       spin_lock_bh(&htt->tx_lock);
+       htt->pending_tx[msdu_id] = NULL;
+       ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+       spin_unlock_bh(&htt->tx_lock);
+err_tx_dec:
        ath10k_htt_tx_dec_pending(htt);
+err:
        return res;
 }
 
@@ -373,13 +381,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        struct htt_cmd *cmd;
        struct htt_data_tx_desc_frag *tx_frags;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
-       struct ath10k_skb_cb *skb_cb;
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
        struct sk_buff *txdesc = NULL;
-       struct sk_buff *txfrag = NULL;
-       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       bool use_frags;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id;
        u8 tid;
-       int prefetch_len, desc_len, frag_len;
-       dma_addr_t frags_paddr;
+       int prefetch_len, desc_len;
        int msdu_id = -1;
        int res;
        u8 flags0;
@@ -387,69 +394,82 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 
        res = ath10k_htt_tx_inc_pending(htt);
        if (res)
-               return res;
+               goto err;
+
+       spin_lock_bh(&htt->tx_lock);
+       res = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (res < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               goto err_tx_dec;
+       }
+       msdu_id = res;
+       htt->pending_tx[msdu_id] = msdu;
+       spin_unlock_bh(&htt->tx_lock);
 
        prefetch_len = min(htt->prefetch_len, msdu->len);
        prefetch_len = roundup(prefetch_len, 4);
 
        desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
-       frag_len = sizeof(*tx_frags) * 2;
 
        txdesc = ath10k_htc_alloc_skb(desc_len);
        if (!txdesc) {
                res = -ENOMEM;
-               goto err;
+               goto err_free_msdu_id;
        }
 
-       txfrag = dev_alloc_skb(frag_len);
-       if (!txfrag) {
-               res = -ENOMEM;
-               goto err;
-       }
+       /* Since HTT 3.0 there is no separate mgmt tx command. However in case
+        * of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
+        * fragment list host driver specifies directly frame pointer. */
+       use_frags = htt->target_version_major < 3 ||
+                   !ieee80211_is_mgmt(hdr->frame_control);
 
        if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
                ath10k_warn("htt alignment check failed. dropping packet.\n");
                res = -EIO;
-               goto err;
+               goto err_free_txdesc;
        }
 
-       spin_lock_bh(&htt->tx_lock);
-       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
-       if (msdu_id < 0) {
-               spin_unlock_bh(&htt->tx_lock);
-               res = msdu_id;
-               goto err;
+       if (use_frags) {
+               skb_cb->htt.frag_len = sizeof(*tx_frags) * 2;
+               skb_cb->htt.pad_len = (unsigned long)msdu->data -
+                                     round_down((unsigned long)msdu->data, 4);
+
+               skb_push(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
+       } else {
+               skb_cb->htt.frag_len = 0;
+               skb_cb->htt.pad_len = 0;
        }
-       htt->pending_tx[msdu_id] = txdesc;
-       spin_unlock_bh(&htt->tx_lock);
 
        res = ath10k_skb_map(dev, msdu);
        if (res)
-               goto err;
-
-       /* tx fragment list must be terminated with zero-entry */
-       skb_put(txfrag, frag_len);
-       tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
-       tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
-       tx_frags[0].len   = __cpu_to_le32(msdu->len);
-       tx_frags[1].paddr = __cpu_to_le32(0);
-       tx_frags[1].len   = __cpu_to_le32(0);
-
-       res = ath10k_skb_map(dev, txfrag);
-       if (res)
-               goto err;
+               goto err_pull_txfrag;
+
+       if (use_frags) {
+               dma_sync_single_for_cpu(dev, skb_cb->paddr, msdu->len,
+                                       DMA_TO_DEVICE);
+
+               /* tx fragment list must be terminated with zero-entry */
+               tx_frags = (struct htt_data_tx_desc_frag *)msdu->data;
+               tx_frags[0].paddr = __cpu_to_le32(skb_cb->paddr +
+                                                 skb_cb->htt.frag_len +
+                                                 skb_cb->htt.pad_len);
+               tx_frags[0].len   = __cpu_to_le32(msdu->len -
+                                                 skb_cb->htt.frag_len -
+                                                 skb_cb->htt.pad_len);
+               tx_frags[1].paddr = __cpu_to_le32(0);
+               tx_frags[1].len   = __cpu_to_le32(0);
+
+               dma_sync_single_for_device(dev, skb_cb->paddr, msdu->len,
+                                          DMA_TO_DEVICE);
+       }
 
-       ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n",
-                  (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr,
+       ath10k_dbg(ATH10K_DBG_HTT, "msdu 0x%llx\n",
                   (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
-       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
-                       txfrag->data, frag_len);
        ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
                        msdu->data, msdu->len);
 
        skb_put(txdesc, desc_len);
        cmd = (struct htt_cmd *)txdesc->data;
-       memset(cmd, 0, desc_len);
 
        tid = ATH10K_SKB_CB(msdu)->htt.tid;
 
@@ -459,8 +479,13 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        if (!ieee80211_has_protected(hdr->frame_control))
                flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
        flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
-       flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
-                    HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+
+       if (use_frags)
+               flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+                            HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+       else
+               flags0 |= SM(ATH10K_HW_TXRX_MGMT,
+                            HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
 
        flags1  = 0;
        flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
@@ -468,45 +493,37 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
        flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
 
-       frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
-
        cmd->hdr.msg_type        = HTT_H2T_MSG_TYPE_TX_FRM;
        cmd->data_tx.flags0      = flags0;
        cmd->data_tx.flags1      = __cpu_to_le16(flags1);
-       cmd->data_tx.len         = __cpu_to_le16(msdu->len);
+       cmd->data_tx.len         = __cpu_to_le16(msdu->len -
+                                                skb_cb->htt.frag_len -
+                                                skb_cb->htt.pad_len);
        cmd->data_tx.id          = __cpu_to_le16(msdu_id);
-       cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+       cmd->data_tx.frags_paddr = __cpu_to_le32(skb_cb->paddr);
        cmd->data_tx.peerid      = __cpu_to_le32(HTT_INVALID_PEERID);
 
-       memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
-
-       /* refcount is decremented by HTC and HTT completions until it reaches
-        * zero and is freed */
-       skb_cb = ATH10K_SKB_CB(txdesc);
-       skb_cb->htt.msdu_id = msdu_id;
-       skb_cb->htt.refcount = 2;
-       skb_cb->htt.txfrag = txfrag;
-       skb_cb->htt.msdu = msdu;
+       memcpy(cmd->data_tx.prefetch, hdr, prefetch_len);
 
        res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
        if (res)
-               goto err;
+               goto err_unmap_msdu;
 
        return 0;
-err:
-       if (txfrag)
-               ath10k_skb_unmap(dev, txfrag);
-       if (txdesc)
-               dev_kfree_skb_any(txdesc);
-       if (txfrag)
-               dev_kfree_skb_any(txfrag);
-       if (msdu_id >= 0) {
-               spin_lock_bh(&htt->tx_lock);
-               htt->pending_tx[msdu_id] = NULL;
-               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
-               spin_unlock_bh(&htt->tx_lock);
-       }
-       ath10k_htt_tx_dec_pending(htt);
+
+err_unmap_msdu:
        ath10k_skb_unmap(dev, msdu);
+err_pull_txfrag:
+       skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
+err_free_txdesc:
+       dev_kfree_skb_any(txdesc);
+err_free_msdu_id:
+       spin_lock_bh(&htt->tx_lock);
+       htt->pending_tx[msdu_id] = NULL;
+       ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+       spin_unlock_bh(&htt->tx_lock);
+err_tx_dec:
+       ath10k_htt_tx_dec_pending(htt);
+err:
        return res;
 }
index 44ed5af..8aeb46d 100644 (file)
 
 #include "targaddrs.h"
 
-/* Supported FW version */
-#define SUPPORTED_FW_MAJOR     1
-#define SUPPORTED_FW_MINOR     0
-#define SUPPORTED_FW_RELEASE   0
-#define SUPPORTED_FW_BUILD     629
-
-/* QCA988X 1.0 definitions */
-#define QCA988X_HW_1_0_VERSION         0x4000002c
-#define QCA988X_HW_1_0_FW_DIR          "ath10k/QCA988X/hw1.0"
-#define QCA988X_HW_1_0_FW_FILE         "firmware.bin"
-#define QCA988X_HW_1_0_OTP_FILE                "otp.bin"
-#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin"
-#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234
+/* QCA988X 1.0 definitions (unsupported) */
+#define QCA988X_HW_1_0_CHIP_ID_REV     0x0
 
 /* QCA988X 2.0 definitions */
 #define QCA988X_HW_2_0_VERSION         0x4100016c
+#define QCA988X_HW_2_0_CHIP_ID_REV     0x2
 #define QCA988X_HW_2_0_FW_DIR          "ath10k/QCA988X/hw2.0"
 #define QCA988X_HW_2_0_FW_FILE         "firmware.bin"
 #define QCA988X_HW_2_0_OTP_FILE                "otp.bin"
 #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
 #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
 
+#define ATH10K_FW_API2_FILE            "firmware-2.bin"
+
+/* includes also the null byte */
+#define ATH10K_FIRMWARE_MAGIC               "QCA-ATH10K"
+
+struct ath10k_fw_ie {
+       __le32 id;
+       __le32 len;
+       u8 data[0];
+};
+
+enum ath10k_fw_ie_type {
+       ATH10K_FW_IE_FW_VERSION = 0,
+       ATH10K_FW_IE_TIMESTAMP = 1,
+       ATH10K_FW_IE_FEATURES = 2,
+       ATH10K_FW_IE_FW_IMAGE = 3,
+       ATH10K_FW_IE_OTP_IMAGE = 4,
+};
+
 /* Known pecularities:
  *  - current FW doesn't support raw rx mode (last tested v599)
  *  - current FW dumps upon raw tx mode (last tested v599)
@@ -53,6 +62,9 @@ enum ath10k_hw_txrx_mode {
        ATH10K_HW_TXRX_RAW = 0,
        ATH10K_HW_TXRX_NATIVE_WIFI = 1,
        ATH10K_HW_TXRX_ETHERNET = 2,
+
+       /* Valid for HTT >= 3.0. Used for management frames in TX_FRM. */
+       ATH10K_HW_TXRX_MGMT = 3,
 };
 
 enum ath10k_mcast2ucast_mode {
@@ -60,6 +72,7 @@ enum ath10k_mcast2ucast_mode {
        ATH10K_MCAST2UCAST_ENABLED = 1,
 };
 
+/* Target specific defines for MAIN firmware */
 #define TARGET_NUM_VDEVS                       8
 #define TARGET_NUM_PEER_AST                    2
 #define TARGET_NUM_WDS_ENTRIES                 32
@@ -75,7 +88,11 @@ enum ath10k_mcast2ucast_mode {
 #define TARGET_RX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
 #define TARGET_RX_TIMEOUT_LO_PRI               100
 #define TARGET_RX_TIMEOUT_HI_PRI               40
-#define TARGET_RX_DECAP_MODE                   ATH10K_HW_TXRX_ETHERNET
+
+/* Native Wifi decap mode is used to align IP frames to 4-byte boundaries and
+ * avoid a very expensive re-alignment in mac80211. */
+#define TARGET_RX_DECAP_MODE                   ATH10K_HW_TXRX_NATIVE_WIFI
+
 #define TARGET_SCAN_MAX_PENDING_REQS           4
 #define TARGET_BMISS_OFFLOAD_MAX_VDEV          3
 #define TARGET_ROAM_OFFLOAD_MAX_VDEV           3
@@ -90,6 +107,36 @@ enum ath10k_mcast2ucast_mode {
 #define TARGET_NUM_MSDU_DESC                   (1024 + 400)
 #define TARGET_MAX_FRAG_ENTRIES                        0
 
+/* Target specific defines for 10.X firmware */
+#define TARGET_10X_NUM_VDEVS                   16
+#define TARGET_10X_NUM_PEER_AST                        2
+#define TARGET_10X_NUM_WDS_ENTRIES             32
+#define TARGET_10X_DMA_BURST_SIZE              0
+#define TARGET_10X_MAC_AGGR_DELIM              0
+#define TARGET_10X_AST_SKID_LIMIT              16
+#define TARGET_10X_NUM_PEERS                   (128 + (TARGET_10X_NUM_VDEVS))
+#define TARGET_10X_NUM_OFFLOAD_PEERS           0
+#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS    0
+#define TARGET_10X_NUM_PEER_KEYS               2
+#define TARGET_10X_NUM_TIDS                    256
+#define TARGET_10X_TX_CHAIN_MASK               (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_10X_RX_CHAIN_MASK               (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_10X_RX_TIMEOUT_LO_PRI           100
+#define TARGET_10X_RX_TIMEOUT_HI_PRI           40
+#define TARGET_10X_RX_DECAP_MODE               ATH10K_HW_TXRX_NATIVE_WIFI
+#define TARGET_10X_SCAN_MAX_PENDING_REQS       4
+#define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV      2
+#define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV       2
+#define TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES        8
+#define TARGET_10X_GTK_OFFLOAD_MAX_VDEV                3
+#define TARGET_10X_NUM_MCAST_GROUPS            0
+#define TARGET_10X_NUM_MCAST_TABLE_ELEMS       0
+#define TARGET_10X_MCAST2UCAST_MODE            ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_10X_TX_DBG_LOG_SIZE             1024
+#define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1
+#define TARGET_10X_VOW_CONFIG                  0
+#define TARGET_10X_NUM_MSDU_DESC               (1024 + 400)
+#define TARGET_10X_MAX_FRAG_ENTRIES            0
 
 /* Number of Copy Engines supported */
 #define CE_COUNT 8
@@ -169,6 +216,10 @@ enum ath10k_mcast2ucast_mode {
 #define SOC_LPO_CAL_ENABLE_LSB                 20
 #define SOC_LPO_CAL_ENABLE_MASK                        0x00100000
 
+#define SOC_CHIP_ID_ADDRESS                    0x000000ec
+#define SOC_CHIP_ID_REV_LSB                    8
+#define SOC_CHIP_ID_REV_MASK                   0x00000f00
+
 #define WLAN_RESET_CONTROL_COLD_RST_MASK       0x00000008
 #define WLAN_RESET_CONTROL_WARM_RST_MASK       0x00000004
 #define WLAN_SYSTEM_SLEEP_DISABLE_LSB          0
index cf2ba4d..0b1cc51 100644 (file)
@@ -334,25 +334,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 
 static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
 {
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param;
+
        if (value != 0xFFFFFFFF)
                value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
                              ATH10K_RTS_MAX);
 
-       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-                                        WMI_VDEV_PARAM_RTS_THRESHOLD,
-                                        value);
+       vdev_param = ar->wmi.vdev_param->rts_threshold;
+       return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
 }
 
 static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
 {
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param;
+
        if (value != 0xFFFFFFFF)
                value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
                                ATH10K_FRAGMT_THRESHOLD_MIN,
                                ATH10K_FRAGMT_THRESHOLD_MAX);
 
-       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-                                        WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
-                                        value);
+       vdev_param = ar->wmi.vdev_param->fragmentation_threshold;
+       return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
 }
 
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
@@ -460,6 +464,11 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
                arg.ssid_len = arvif->vif->bss_conf.ssid_len;
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac vdev %d start center_freq %d phymode %s\n",
+                  arg.vdev_id, arg.channel.freq,
+                  ath10k_wmi_phymode_str(arg.channel.mode));
+
        ret = ath10k_wmi_vdev_start(ar, &arg);
        if (ret) {
                ath10k_warn("WMI vdev start failed: ret %d\n", ret);
@@ -503,13 +512,10 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 {
        struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
        struct wmi_vdev_start_request_arg arg = {};
-       enum nl80211_channel_type type;
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
-
        arg.vdev_id = vdev_id;
        arg.channel.freq = channel->center_freq;
        arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
@@ -560,12 +566,9 @@ static int ath10k_monitor_stop(struct ath10k *ar)
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       /* For some reasons, ath10k_wmi_vdev_down() here couse
-        * often ath10k_wmi_vdev_stop() to fail. Next we could
-        * not run monitor vdev and driver reload
-        * required. Don't see such problems we skip
-        * ath10k_wmi_vdev_down() here.
-        */
+       ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev down failed: %d\n", ret);
 
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
@@ -607,7 +610,7 @@ static int ath10k_monitor_create(struct ath10k *ar)
                goto vdev_fail;
        }
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
                   ar->monitor_vdev_id);
 
        ar->monitor_present = true;
@@ -639,7 +642,7 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
        ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
        ar->monitor_present = false;
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
                   ar->monitor_vdev_id);
        return ret;
 }
@@ -668,13 +671,14 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                            arvif->vdev_id);
                return;
        }
-       ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
 }
 
 static void ath10k_control_ibss(struct ath10k_vif *arvif,
                                struct ieee80211_bss_conf *info,
                                const u8 self_peer[ETH_ALEN])
 {
+       u32 vdev_param;
        int ret = 0;
 
        lockdep_assert_held(&arvif->ar->conf_mutex);
@@ -708,8 +712,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
                return;
        }
 
-       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_ATIM_WINDOW,
+       vdev_param = arvif->ar->wmi.vdev_param->atim_window;
+       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
                                        ATH10K_DEFAULT_ATIM);
        if (ret)
                ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
@@ -719,47 +723,45 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 /*
  * Review this when mac80211 gains per-interface powersave support.
  */
-static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 {
-       struct ath10k_generic_iter *ar_iter = data;
-       struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_conf *conf = &ar->hw->conf;
        enum wmi_sta_powersave_param param;
        enum wmi_sta_ps_mode psmode;
        int ret;
 
        lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
+       if (arvif->vif->type != NL80211_IFTYPE_STATION)
+               return 0;
 
        if (conf->flags & IEEE80211_CONF_PS) {
                psmode = WMI_STA_PS_MODE_ENABLED;
                param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
 
-               ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
-                                                 arvif->vdev_id,
-                                                 param,
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
                                                  conf->dynamic_ps_timeout);
                if (ret) {
                        ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
                                    arvif->vdev_id);
-                       return;
+                       return ret;
                }
-
-               ar_iter->ret = ret;
        } else {
                psmode = WMI_STA_PS_MODE_DISABLED;
        }
 
-       ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
-                                            psmode);
-       if (ar_iter->ret)
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
+                  arvif->vdev_id, psmode ? "enable" : "disable");
+
+       ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
+       if (ret) {
                ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
                            psmode, arvif->vdev_id);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
-                          psmode, arvif->vdev_id);
+               return ret;
+       }
+
+       return 0;
 }
 
 /**********************/
@@ -949,7 +951,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        arg->peer_ht_rates.num_rates = n;
        arg->peer_num_spatial_streams = max((n+7) / 8, 1);
 
-       ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
+                  arg->addr,
                   arg->peer_ht_rates.num_rates,
                   arg->peer_num_spatial_streams);
 }
@@ -969,11 +972,11 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
                arg->peer_flags |= WMI_PEER_QOS;
 
        if (sta->wme && sta->uapsd_queues) {
-               ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+               ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
                           sta->uapsd_queues, sta->max_sp);
 
                arg->peer_flags |= WMI_PEER_APSD;
-               arg->peer_flags |= WMI_RC_UAPSD_FLAG;
+               arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
 
                if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
                        uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
@@ -1028,14 +1031,27 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
                                    struct wmi_peer_assoc_complete_arg *arg)
 {
        const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       u8 ampdu_factor;
 
        if (!vht_cap->vht_supported)
                return;
 
        arg->peer_flags |= WMI_PEER_VHT;
-
        arg->peer_vht_caps = vht_cap->cap;
 
+
+       ampdu_factor = (vht_cap->cap &
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+                      IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+       /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
+        * zero in VHT IE. Using it would result in degraded throughput.
+        * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
+        * it if VHT max_mpdu is smaller. */
+       arg->peer_max_mpdu = max(arg->peer_max_mpdu,
+                                (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+                                       ampdu_factor)) - 1);
+
        if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
                arg->peer_flags |= WMI_PEER_80MHZ;
 
@@ -1048,7 +1064,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
        arg->peer_vht_rates.tx_mcs_set =
                __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
 
-       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n",
+                  sta->addr, arg->peer_max_mpdu, arg->peer_flags);
 }
 
 static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
@@ -1076,8 +1093,6 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
 {
        enum wmi_phy_mode phymode = MODE_UNKNOWN;
 
-       /* FIXME: add VHT */
-
        switch (ar->hw->conf.chandef.chan->band) {
        case IEEE80211_BAND_2GHZ:
                if (sta->ht_cap.ht_supported) {
@@ -1091,7 +1106,17 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
 
                break;
        case IEEE80211_BAND_5GHZ:
-               if (sta->ht_cap.ht_supported) {
+               /*
+                * Check VHT first.
+                */
+               if (sta->vht_cap.vht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+                               phymode = MODE_11AC_VHT80;
+                       else if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11AC_VHT40;
+                       else if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
+                               phymode = MODE_11AC_VHT20;
+               } else if (sta->ht_cap.ht_supported) {
                        if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
                                phymode = MODE_11NA_HT40;
                        else
@@ -1105,30 +1130,32 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
                break;
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
+                  sta->addr, ath10k_wmi_phymode_str(phymode));
+
        arg->peer_phymode = phymode;
        WARN_ON(phymode == MODE_UNKNOWN);
 }
 
-static int ath10k_peer_assoc(struct ath10k *ar,
-                            struct ath10k_vif *arvif,
-                            struct ieee80211_sta *sta,
-                            struct ieee80211_bss_conf *bss_conf)
+static int ath10k_peer_assoc_prepare(struct ath10k *ar,
+                                    struct ath10k_vif *arvif,
+                                    struct ieee80211_sta *sta,
+                                    struct ieee80211_bss_conf *bss_conf,
+                                    struct wmi_peer_assoc_complete_arg *arg)
 {
-       struct wmi_peer_assoc_complete_arg arg;
-
        lockdep_assert_held(&ar->conf_mutex);
 
-       memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+       memset(arg, 0, sizeof(*arg));
 
-       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
-       ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
-       ath10k_peer_assoc_h_rates(ar, sta, &arg);
-       ath10k_peer_assoc_h_ht(ar, sta, &arg);
-       ath10k_peer_assoc_h_vht(ar, sta, &arg);
-       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
-       ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg);
+       ath10k_peer_assoc_h_crypto(ar, arvif, arg);
+       ath10k_peer_assoc_h_rates(ar, sta, arg);
+       ath10k_peer_assoc_h_ht(ar, sta, arg);
+       ath10k_peer_assoc_h_vht(ar, sta, arg);
+       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg);
+       ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg);
 
-       return ath10k_wmi_peer_assoc(ar, &arg);
+       return 0;
 }
 
 /* can be called only in mac80211 callbacks due to `key_count` usage */
@@ -1138,6 +1165,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_peer_assoc_complete_arg peer_arg;
        struct ieee80211_sta *ap_sta;
        int ret;
 
@@ -1153,24 +1181,33 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                return;
        }
 
-       ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+       ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
+                                       bss_conf, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+               ath10k_warn("Peer assoc prepare failed for %pM\n: %d",
+                           bss_conf->bssid, ret);
                rcu_read_unlock();
                return;
        }
 
        rcu_read_unlock();
 
+       ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+       if (ret) {
+               ath10k_warn("Peer assoc failed for %pM\n: %d",
+                           bss_conf->bssid, ret);
+               return;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac vdev %d up (associated) bssid %pM aid %d\n",
+                  arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+
        ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
                                 bss_conf->bssid);
        if (ret)
                ath10k_warn("VDEV: %d up failed: ret %d\n",
                            arvif->vdev_id, ret);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "VDEV: %d associated, BSSID: %pM, AID: %d\n",
-                          arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
 }
 
 /*
@@ -1191,10 +1228,11 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
         * No idea why this happens, even though VDEV-DOWN is supposed
         * to be analogous to link down, so just stop the VDEV.
         */
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n",
+                  arvif->vdev_id);
+
+       /* FIXME: check return value */
        ret = ath10k_vdev_stop(arvif);
-       if (!ret)
-               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
-                          arvif->vdev_id);
 
        /*
         * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
@@ -1203,26 +1241,33 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
         * interfaces as it expects there is no rx when no interface is
         * running.
         */
-       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
-       if (ret)
-               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
-                          arvif->vdev_id, ret);
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
 
-       ath10k_wmi_flush_tx(ar);
+       /* FIXME: why don't we print error if wmi call fails? */
+       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
 
-       arvif->def_wep_key_index = 0;
+       arvif->def_wep_key_idx = 0;
 }
 
 static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
                                struct ieee80211_sta *sta)
 {
+       struct wmi_peer_assoc_complete_arg peer_arg;
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+       ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
+       if (ret) {
+               ath10k_warn("WMI peer assoc prepare failed for %pM\n",
+                           sta->addr);
+               return ret;
+       }
+
+       ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
-               ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+               ath10k_warn("Peer assoc failed for STA %pM\n: %d",
+                           sta->addr, ret);
                return ret;
        }
 
@@ -1333,8 +1378,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
                                continue;
 
                        ath10k_dbg(ATH10K_DBG_WMI,
-                                  "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
-                                  __func__, ch - arg.channels, arg.n_channels,
+                                  "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+                                   ch - arg.channels, arg.n_channels,
                                   ch->freq, ch->max_power, ch->max_reg_power,
                                   ch->max_antenna_gain, ch->mode);
 
@@ -1391,6 +1436,33 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 /* TX handlers */
 /***************/
 
+static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr)
+{
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               return HTT_DATA_TX_EXT_TID_MGMT;
+
+       if (!ieee80211_is_data_qos(hdr->frame_control))
+               return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+
+       if (!is_unicast_ether_addr(ieee80211_get_DA(hdr)))
+               return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+
+       return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
+}
+
+static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar,
+                                 struct ieee80211_tx_info *info)
+{
+       if (info->control.vif)
+               return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
+
+       if (ar->monitor_enabled)
+               return ar->monitor_vdev_id;
+
+       ath10k_warn("could not resolve vdev id\n");
+       return 0;
+}
+
 /*
  * Frames sent to the FW have to be in "Native Wifi" format.
  * Strip the QoS field from the 802.11 header.
@@ -1411,6 +1483,30 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
        skb_pull(skb, IEEE80211_QOS_CTL_LEN);
 }
 
+static void ath10k_tx_wep_key_work(struct work_struct *work)
+{
+       struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+                                               wep_key_work);
+       int ret, keyidx = arvif->def_wep_key_newidx;
+
+       if (arvif->def_wep_key_idx == keyidx)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+                  arvif->vdev_id, keyidx);
+
+       ret = ath10k_wmi_vdev_set_param(arvif->ar,
+                                       arvif->vdev_id,
+                                       arvif->ar->wmi.vdev_param->def_keyid,
+                                       keyidx);
+       if (ret) {
+               ath10k_warn("could not update wep keyidx (%d)\n", ret);
+               return;
+       }
+
+       arvif->def_wep_key_idx = keyidx;
+}
+
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1419,11 +1515,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
        struct ath10k *ar = arvif->ar;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ieee80211_key_conf *key = info->control.hw_key;
-       int ret;
-
-       /* TODO AP mode should be implemented */
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
 
        if (!ieee80211_has_protected(hdr->frame_control))
                return;
@@ -1435,20 +1526,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
            key->cipher != WLAN_CIPHER_SUITE_WEP104)
                return;
 
-       if (key->keyidx == arvif->def_wep_key_index)
+       if (key->keyidx == arvif->def_wep_key_idx)
                return;
 
-       ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
-
-       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_DEF_KEYID,
-                                       key->keyidx);
-       if (ret) {
-               ath10k_warn("could not update wep keyidx (%d)\n", ret);
-               return;
-       }
-
-       arvif->def_wep_key_index = key->keyidx;
+       /* FIXME: Most likely a few frames will be TXed with an old key. Simply
+        * queueing frames until key index is updated is not an option because
+        * sk_buff may need more processing to be done, e.g. offchannel */
+       arvif->def_wep_key_newidx = key->keyidx;
+       ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
 }
 
 static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
@@ -1478,19 +1563,42 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
 static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       int ret;
+       int ret = 0;
 
-       if (ieee80211_is_mgmt(hdr->frame_control))
-               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
-       else if (ieee80211_is_nullfunc(hdr->frame_control))
+       if (ar->htt.target_version_major >= 3) {
+               /* Since HTT 3.0 there is no separate mgmt tx command */
+               ret = ath10k_htt_tx(&ar->htt, skb);
+               goto exit;
+       }
+
+       if (ieee80211_is_mgmt(hdr->frame_control)) {
+               if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+                            ar->fw_features)) {
+                       if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
+                           ATH10K_MAX_NUM_MGMT_PENDING) {
+                               ath10k_warn("wmi mgmt_tx queue limit reached\n");
+                               ret = -EBUSY;
+                               goto exit;
+                       }
+
+                       skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb);
+                       ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
+               } else {
+                       ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
+               }
+       } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+                            ar->fw_features) &&
+                  ieee80211_is_nullfunc(hdr->frame_control)) {
                /* FW does not report tx status properly for NullFunc frames
                 * unless they are sent through mgmt tx path. mac80211 sends
-                * those frames when it detects link/beacon loss and depends on
-                * the tx status to be correct. */
+                * those frames when it detects link/beacon loss and depends
+                * on the tx status to be correct. */
                ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
-       else
+       } else {
                ret = ath10k_htt_tx(&ar->htt, skb);
+       }
 
+exit:
        if (ret) {
                ath10k_warn("tx failed (%d). dropping packet.\n", ret);
                ieee80211_free_txskb(ar->hw, skb);
@@ -1534,18 +1642,19 @@ void ath10k_offchan_tx_work(struct work_struct *work)
 
                mutex_lock(&ar->conf_mutex);
 
-               ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+               ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n",
                           skb);
 
                hdr = (struct ieee80211_hdr *)skb->data;
                peer_addr = ieee80211_get_DA(hdr);
-               vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+               vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
 
                spin_lock_bh(&ar->data_lock);
                peer = ath10k_peer_find(ar, vdev_id, peer_addr);
                spin_unlock_bh(&ar->data_lock);
 
                if (peer)
+                       /* FIXME: should this use ath10k_warn()? */
                        ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
                                   peer_addr, vdev_id);
 
@@ -1580,6 +1689,36 @@ void ath10k_offchan_tx_work(struct work_struct *work)
        }
 }
 
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+               if (!skb)
+                       break;
+
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
+       struct sk_buff *skb;
+       int ret;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
+               if (!skb)
+                       break;
+
+               ret = ath10k_wmi_mgmt_tx(ar, skb);
+               if (ret)
+                       ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
+       }
+}
+
 /************/
 /* Scanning */
 /************/
@@ -1643,8 +1782,6 @@ static int ath10k_abort_scan(struct ath10k *ar)
                return -EIO;
        }
 
-       ath10k_wmi_flush_tx(ar);
-
        ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
        if (ret == 0)
                ath10k_warn("timed out while waiting for scan to stop\n");
@@ -1678,10 +1815,6 @@ static int ath10k_start_scan(struct ath10k *ar,
        if (ret)
                return ret;
 
-       /* make sure we submit the command so the completion
-       * timeout makes sense */
-       ath10k_wmi_flush_tx(ar);
-
        ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
        if (ret == 0) {
                ath10k_abort_scan(ar);
@@ -1709,16 +1842,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ath10k *ar = hw->priv;
-       struct ath10k_vif *arvif = NULL;
-       u32 vdev_id = 0;
-       u8 tid;
-
-       if (info->control.vif) {
-               arvif = ath10k_vif_to_arvif(info->control.vif);
-               vdev_id = arvif->vdev_id;
-       } else if (ar->monitor_enabled) {
-               vdev_id = ar->monitor_vdev_id;
-       }
+       u8 tid, vdev_id;
 
        /* We should disable CCK RATE due to P2P */
        if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
@@ -1726,12 +1850,8 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 
        /* we must calculate tid before we apply qos workaround
         * as we'd lose the qos control field */
-       tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-       if (ieee80211_is_data_qos(hdr->frame_control) &&
-           is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
-               u8 *qc = ieee80211_get_qos_ctl(hdr);
-               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-       }
+       tid = ath10k_tx_h_get_tid(hdr);
+       vdev_id = ath10k_tx_h_get_vdev_id(ar, info);
 
        /* it makes no sense to process injected frames like that */
        if (info->control.vif &&
@@ -1742,14 +1862,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
                ath10k_tx_h_seq_no(skb);
        }
 
-       memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
-       ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
+       ATH10K_SKB_CB(skb)->vdev_id = vdev_id;
+       ATH10K_SKB_CB(skb)->htt.is_offchan = false;
        ATH10K_SKB_CB(skb)->htt.tid = tid;
 
        if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
                spin_lock_bh(&ar->data_lock);
                ATH10K_SKB_CB(skb)->htt.is_offchan = true;
-               ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+               ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
                spin_unlock_bh(&ar->data_lock);
 
                ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
@@ -1771,6 +1891,7 @@ void ath10k_halt(struct ath10k *ar)
 
        del_timer_sync(&ar->scan.timeout);
        ath10k_offchan_tx_purge(ar);
+       ath10k_mgmt_over_wmi_tx_purge(ar);
        ath10k_peer_cleanup_all(ar);
        ath10k_core_stop(ar);
        ath10k_hif_power_down(ar);
@@ -1817,12 +1938,12 @@ static int ath10k_start(struct ieee80211_hw *hw)
        else if (ar->state == ATH10K_STATE_RESTARTING)
                ar->state = ATH10K_STATE_RESTARTED;
 
-       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1);
        if (ret)
                ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
                            ret);
 
-       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 0);
        if (ret)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
@@ -1847,32 +1968,29 @@ static void ath10k_stop(struct ieee80211_hw *hw)
        ar->state = ATH10K_STATE_OFF;
        mutex_unlock(&ar->conf_mutex);
 
+       ath10k_mgmt_over_wmi_tx_purge(ar);
+
        cancel_work_sync(&ar->offchan_tx_work);
+       cancel_work_sync(&ar->wmi_mgmt_tx_work);
        cancel_work_sync(&ar->restart_work);
 }
 
-static void ath10k_config_ps(struct ath10k *ar)
+static int ath10k_config_ps(struct ath10k *ar)
 {
-       struct ath10k_generic_iter ar_iter;
+       struct ath10k_vif *arvif;
+       int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       /* During HW reconfiguration mac80211 reports all interfaces that were
-        * running until reconfiguration was started. Since FW doesn't have any
-        * vdevs at this point we must not iterate over this interface list.
-        * This setting will be updated upon add_interface(). */
-       if (ar->state == ATH10K_STATE_RESTARTED)
-               return;
-
-       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-       ar_iter.ar = ar;
-
-       ieee80211_iterate_active_interfaces_atomic(
-               ar->hw, IEEE80211_IFACE_ITER_NORMAL,
-               ath10k_ps_iter, &ar_iter);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ret = ath10k_mac_vif_setup_ps(arvif);
+               if (ret) {
+                       ath10k_warn("could not setup powersave (%d)\n", ret);
+                       break;
+               }
+       }
 
-       if (ar_iter.ret)
-               ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+       return ret;
 }
 
 static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
@@ -1884,7 +2002,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
        mutex_lock(&ar->conf_mutex);
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
+               ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n",
                           conf->chandef.chan->center_freq);
                spin_lock_bh(&ar->data_lock);
                ar->rx_channel = conf->chandef.chan;
@@ -1901,7 +2019,6 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                        ret = ath10k_monitor_destroy(ar);
        }
 
-       ath10k_wmi_flush_tx(ar);
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -1922,6 +2039,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        int ret = 0;
        u32 value;
        int bit;
+       u32 vdev_param;
 
        mutex_lock(&ar->conf_mutex);
 
@@ -1930,21 +2048,22 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        arvif->ar = ar;
        arvif->vif = vif;
 
+       INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
+
        if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
                ath10k_warn("Only one monitor interface allowed\n");
                ret = -EBUSY;
-               goto exit;
+               goto err;
        }
 
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
                ret = -EBUSY;
-               goto exit;
+               goto err;
        }
 
        arvif->vdev_id = bit - 1;
        arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
-       ar->free_vdev_map &= ~(1 << arvif->vdev_id);
 
        if (ar->p2p)
                arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
@@ -1973,32 +2092,41 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                break;
        }
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n",
                   arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
 
        ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
                                     arvif->vdev_subtype, vif->addr);
        if (ret) {
                ath10k_warn("WMI vdev create failed: ret %d\n", ret);
-               goto exit;
+               goto err;
        }
 
-       ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
-                                       arvif->def_wep_key_index);
-       if (ret)
+       ar->free_vdev_map &= ~BIT(arvif->vdev_id);
+       list_add(&arvif->list, &ar->arvifs);
+
+       vdev_param = ar->wmi.vdev_param->def_keyid;
+       ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
+                                       arvif->def_wep_key_idx);
+       if (ret) {
                ath10k_warn("Failed to set default keyid: %d\n", ret);
+               goto err_vdev_delete;
+       }
 
-       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+       vdev_param = ar->wmi.vdev_param->tx_encap_type;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                        ATH10K_HW_TXRX_NATIVE_WIFI);
-       if (ret)
+       /* 10.X firmware does not support this VDEV parameter. Do not warn */
+       if (ret && ret != -EOPNOTSUPP) {
                ath10k_warn("Failed to set TX encap: %d\n", ret);
+               goto err_vdev_delete;
+       }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
                if (ret) {
                        ath10k_warn("Failed to create peer for AP: %d\n", ret);
-                       goto exit;
+                       goto err_vdev_delete;
                }
        }
 
@@ -2007,39 +2135,62 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
-               if (ret)
+               if (ret) {
                        ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+                       goto err_peer_delete;
+               }
 
                param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
                value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
-               if (ret)
+               if (ret) {
                        ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+                       goto err_peer_delete;
+               }
 
                param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
                value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
-               if (ret)
+               if (ret) {
                        ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+                       goto err_peer_delete;
+               }
        }
 
        ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
-       if (ret)
+       if (ret) {
                ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
                            arvif->vdev_id, ret);
+               goto err_peer_delete;
+       }
 
        ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
-       if (ret)
+       if (ret) {
                ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
                            arvif->vdev_id, ret);
+               goto err_peer_delete;
+       }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                ar->monitor_present = true;
 
-exit:
        mutex_unlock(&ar->conf_mutex);
+       return 0;
+
+err_peer_delete:
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
+               ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr);
+
+err_vdev_delete:
+       ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+       ar->free_vdev_map &= ~BIT(arvif->vdev_id);
+       list_del(&arvif->list);
+
+err:
+       mutex_unlock(&ar->conf_mutex);
+
        return ret;
 }
 
@@ -2052,9 +2203,17 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&ar->conf_mutex);
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+       cancel_work_sync(&arvif->wep_key_work);
+
+       spin_lock_bh(&ar->data_lock);
+       if (arvif->beacon) {
+               dev_kfree_skb_any(arvif->beacon);
+               arvif->beacon = NULL;
+       }
+       spin_unlock_bh(&ar->data_lock);
 
        ar->free_vdev_map |= 1 << (arvif->vdev_id);
+       list_del(&arvif->list);
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
@@ -2064,6 +2223,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
                kfree(arvif->u.ap.noa_data);
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev delete %d (remove interface)\n",
+                  arvif->vdev_id);
+
        ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
        if (ret)
                ath10k_warn("WMI vdev delete failed: %d\n", ret);
@@ -2105,18 +2267,20 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw,
 
        if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
            !ar->monitor_enabled) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n",
+                          ar->monitor_vdev_id);
+
                ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
                if (ret)
                        ath10k_warn("Unable to start monitor mode\n");
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
        } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
                   ar->monitor_enabled) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n",
+                          ar->monitor_vdev_id);
+
                ret = ath10k_monitor_stop(ar);
                if (ret)
                        ath10k_warn("Unable to stop monitor mode\n");
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2130,6 +2294,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret = 0;
+       u32 vdev_param, pdev_param;
 
        mutex_lock(&ar->conf_mutex);
 
@@ -2138,44 +2303,44 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_INT) {
                arvif->beacon_interval = info->beacon_int;
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_BEACON_INTERVAL,
+               vdev_param = ar->wmi.vdev_param->beacon_interval;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                arvif->beacon_interval);
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d beacon_interval %d\n",
+                          arvif->vdev_id, arvif->beacon_interval);
+
                if (ret)
                        ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Beacon interval: %d set for VDEV: %d\n",
-                                  arvif->beacon_interval, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_BEACON) {
-               ret = ath10k_wmi_pdev_set_param(ar,
-                                               WMI_PDEV_PARAM_BEACON_TX_MODE,
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "vdev %d set beacon tx mode to staggered\n",
+                          arvif->vdev_id);
+
+               pdev_param = ar->wmi.pdev_param->beacon_tx_mode;
+               ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
                                                WMI_BEACON_STAGGERED_MODE);
                if (ret)
                        ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set staggered beacon mode for VDEV: %d\n",
-                                  arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_BEACON_INFO) {
                arvif->dtim_period = info->dtim_period;
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_DTIM_PERIOD,
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d dtim_period %d\n",
+                          arvif->vdev_id, arvif->dtim_period);
+
+               vdev_param = ar->wmi.vdev_param->dtim_period;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                arvif->dtim_period);
                if (ret)
                        ath10k_warn("Failed to set dtim period for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set dtim period: %d for VDEV: %d\n",
-                                  arvif->dtim_period, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_SSID &&
@@ -2188,16 +2353,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BSSID) {
                if (!is_zero_ether_addr(info->bssid)) {
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "mac vdev %d create peer %pM\n",
+                                  arvif->vdev_id, info->bssid);
+
                        ret = ath10k_peer_create(ar, arvif->vdev_id,
                                                 info->bssid);
                        if (ret)
                                ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
                                            info->bssid, arvif->vdev_id);
-                       else
-                               ath10k_dbg(ATH10K_DBG_MAC,
-                                          "Added peer: %pM for VDEV: %d\n",
-                                          info->bssid, arvif->vdev_id);
-
 
                        if (vif->type == NL80211_IFTYPE_STATION) {
                                /*
@@ -2207,11 +2371,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                                memcpy(arvif->u.sta.bssid, info->bssid,
                                       ETH_ALEN);
 
+                               ath10k_dbg(ATH10K_DBG_MAC,
+                                          "mac vdev %d start %pM\n",
+                                          arvif->vdev_id, info->bssid);
+
+                               /* FIXME: check return value */
                                ret = ath10k_vdev_start(arvif);
-                               if (!ret)
-                                       ath10k_dbg(ATH10K_DBG_MAC,
-                                                  "VDEV: %d started with BSSID: %pM\n",
-                                                  arvif->vdev_id, info->bssid);
                        }
 
                        /*
@@ -2235,16 +2400,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                else
                        cts_prot = 0;
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_ENABLE_RTSCTS,
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
+                          arvif->vdev_id, cts_prot);
+
+               vdev_param = ar->wmi.vdev_param->enable_rtscts;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                cts_prot);
                if (ret)
                        ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set CTS prot: %d for VDEV: %d\n",
-                                  cts_prot, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_ERP_SLOT) {
@@ -2255,16 +2419,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                else
                        slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_SLOT_TIME,
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
+                          arvif->vdev_id, slottime);
+
+               vdev_param = ar->wmi.vdev_param->slot_time;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                slottime);
                if (ret)
                        ath10k_warn("Failed to set erp slot for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set slottime: %d for VDEV: %d\n",
-                                  slottime, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -2274,16 +2437,16 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                else
                        preamble = WMI_VDEV_PREAMBLE_LONG;
 
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
-                                               WMI_VDEV_PARAM_PREAMBLE,
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d preamble %dn",
+                          arvif->vdev_id, preamble);
+
+               vdev_param = ar->wmi.vdev_param->preamble;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                preamble);
                if (ret)
                        ath10k_warn("Failed to set preamble for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set preamble: %d for VDEV: %d\n",
-                                  preamble, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_ASSOC) {
@@ -2474,27 +2637,26 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New station addition.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d peer create %pM (new sta)\n",
+                          arvif->vdev_id, sta->addr);
+
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
                        ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
                                    sta->addr, arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Added peer: %pM for VDEV: %d\n",
-                                  sta->addr, arvif->vdev_id);
        } else if ((old_state == IEEE80211_STA_NONE &&
                    new_state == IEEE80211_STA_NOTEXIST)) {
                /*
                 * Existing station deletion.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d peer delete %pM (sta gone)\n",
+                          arvif->vdev_id, sta->addr);
                ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
                if (ret)
                        ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
                                    sta->addr, arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Removed peer: %pM for VDEV: %d\n",
-                                  sta->addr, arvif->vdev_id);
 
                if (vif->type == NL80211_IFTYPE_STATION)
                        ath10k_bss_disassoc(hw, vif);
@@ -2505,14 +2667,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New association.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
+                          sta->addr);
+
                ret = ath10k_station_assoc(ar, arvif, sta);
                if (ret)
                        ath10k_warn("Failed to associate station: %pM\n",
                                    sta->addr);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Station %pM moved to assoc state\n",
-                                  sta->addr);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH &&
                   (vif->type == NL80211_IFTYPE_AP ||
@@ -2520,14 +2681,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * Disassociation.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
+                          sta->addr);
+
                ret = ath10k_station_disassoc(ar, arvif, sta);
                if (ret)
                        ath10k_warn("Failed to disassociate station: %pM\n",
                                    sta->addr);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Station %pM moved to disassociated state\n",
-                                  sta->addr);
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2732,88 +2892,51 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
  * Both RTS and Fragmentation threshold are interface-specific
  * in ath10k, but device-specific in mac80211.
  */
-static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath10k_generic_iter *ar_iter = data;
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-       u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
-
-       lockdep_assert_held(&arvif->ar->conf_mutex);
-
-       /* During HW reconfiguration mac80211 reports all interfaces that were
-        * running until reconfiguration was started. Since FW doesn't have any
-        * vdevs at this point we must not iterate over this interface list.
-        * This setting will be updated upon add_interface(). */
-       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
-               return;
-
-       ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
-       if (ar_iter->ret)
-               ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
-                           arvif->vdev_id);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "Set RTS threshold: %d for VDEV: %d\n",
-                          rts, arvif->vdev_id);
-}
 
 static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 {
-       struct ath10k_generic_iter ar_iter;
        struct ath10k *ar = hw->priv;
-
-       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-       ar_iter.ar = ar;
+       struct ath10k_vif *arvif;
+       int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces_atomic(
-               hw, IEEE80211_IFACE_ITER_NORMAL,
-               ath10k_set_rts_iter, &ar_iter);
-       mutex_unlock(&ar->conf_mutex);
-
-       return ar_iter.ret;
-}
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n",
+                          arvif->vdev_id, value);
 
-static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath10k_generic_iter *ar_iter = data;
-       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
-       u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
-
-       lockdep_assert_held(&arvif->ar->conf_mutex);
-
-       /* During HW reconfiguration mac80211 reports all interfaces that were
-        * running until reconfiguration was started. Since FW doesn't have any
-        * vdevs at this point we must not iterate over this interface list.
-        * This setting will be updated upon add_interface(). */
-       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
-               return;
+               ret = ath10k_mac_set_rts(arvif, value);
+               if (ret) {
+                       ath10k_warn("could not set rts threshold for vdev %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       break;
+               }
+       }
+       mutex_unlock(&ar->conf_mutex);
 
-       ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
-       if (ar_iter->ret)
-               ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
-                           arvif->vdev_id);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "Set frag threshold: %d for VDEV: %d\n",
-                          frag, arvif->vdev_id);
+       return ret;
 }
 
 static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 {
-       struct ath10k_generic_iter ar_iter;
        struct ath10k *ar = hw->priv;
-
-       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-       ar_iter.ar = ar;
+       struct ath10k_vif *arvif;
+       int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces_atomic(
-               hw, IEEE80211_IFACE_ITER_NORMAL,
-               ath10k_set_frag_iter, &ar_iter);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
+                          arvif->vdev_id, value);
+
+               ret = ath10k_mac_set_rts(arvif, value);
+               if (ret) {
+                       ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       break;
+               }
+       }
        mutex_unlock(&ar->conf_mutex);
 
-       return ar_iter.ret;
+       return ret;
 }
 
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
@@ -2836,8 +2959,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
                        bool empty;
 
                        spin_lock_bh(&ar->htt.tx_lock);
-                       empty = bitmap_empty(ar->htt.used_msdu_ids,
-                                            ar->htt.max_num_pending_tx);
+                       empty = (ar->htt.num_pending_tx == 0);
                        spin_unlock_bh(&ar->htt.tx_lock);
 
                        skip = (ar->state == ATH10K_STATE_WEDGED);
@@ -3326,6 +3448,10 @@ int ath10k_mac_register(struct ath10k *ar)
                        IEEE80211_HW_WANT_MONITOR_VIF |
                        IEEE80211_HW_AP_LINK_PS;
 
+       /* MSDU can have HTT TX fragment pushed in front. The additional 4
+        * bytes is used for padding/alignment if necessary. */
+       ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
+
        if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
                ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
 
index 6fce9bf..ba10219 100644 (file)
@@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
+void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
 void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
index e2f9ef5..f8d59c7 100644 (file)
@@ -36,11 +36,9 @@ static unsigned int ath10k_target_ps;
 module_param(ath10k_target_ps, uint, 0644);
 MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
 
-#define QCA988X_1_0_DEVICE_ID  (0xabcd)
 #define QCA988X_2_0_DEVICE_ID  (0x003c)
 
 static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
-       { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */
        { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
        {0}
 };
@@ -50,9 +48,9 @@ static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
 
 static void ath10k_pci_process_ce(struct ath10k *ar);
 static int ath10k_pci_post_rx(struct ath10k *ar);
-static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
                                             int num);
-static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
+static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
 static void ath10k_pci_device_reset(struct ath10k *ar);
 static int ath10k_pci_reset_target(struct ath10k *ar);
@@ -60,43 +58,145 @@ static int ath10k_pci_start_intr(struct ath10k *ar);
 static void ath10k_pci_stop_intr(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
-       /* host->target HTC control and raw streams */
-       { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
-       /* could be moved to share CE3 */
-       /* target->host HTT + HTC control */
-       { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,},
-       /* target->host WMI */
-       { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
-       /* host->target WMI */
-       { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
-       /* host->target HTT */
-       { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0,
-                   CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
-       /* unused */
-       { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
-       /* Target autonomous hif_memcpy */
-       { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
-       /* ce_diag, the Diagnostic Window */
-       { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+       /* CE0: host->target HTC control and raw streams */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 16,
+               .src_sz_max = 256,
+               .dest_nentries = 0,
+       },
+
+       /* CE1: target->host HTT + HTC control */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 512,
+               .dest_nentries = 512,
+       },
+
+       /* CE2: target->host WMI */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 32,
+       },
+
+       /* CE3: host->target WMI */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 32,
+               .src_sz_max = 2048,
+               .dest_nentries = 0,
+       },
+
+       /* CE4: host->target HTT */
+       {
+               .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+               .src_nentries = CE_HTT_H2T_MSG_SRC_NENTRIES,
+               .src_sz_max = 256,
+               .dest_nentries = 0,
+       },
+
+       /* CE5: unused */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 0,
+               .dest_nentries = 0,
+       },
+
+       /* CE6: target autonomous hif_memcpy */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 0,
+               .dest_nentries = 0,
+       },
+
+       /* CE7: ce_diag, the Diagnostic Window */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 2,
+               .src_sz_max = DIAG_TRANSFER_LIMIT,
+               .dest_nentries = 2,
+       },
 };
 
 /* Target firmware's Copy Engine configuration. */
 static const struct ce_pipe_config target_ce_config_wlan[] = {
-       /* host->target HTC control and raw streams */
-       { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
-       /* target->host HTT + HTC control */
-       { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,},
-       /* target->host WMI */
-       { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
-       /* host->target WMI */
-       { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
-       /* host->target HTT */
-       { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+       /* CE0: host->target HTC control and raw streams */
+       {
+               .pipenum = 0,
+               .pipedir = PIPEDIR_OUT,
+               .nentries = 32,
+               .nbytes_max = 256,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
+       /* CE1: target->host HTT + HTC control */
+       {
+               .pipenum = 1,
+               .pipedir = PIPEDIR_IN,
+               .nentries = 32,
+               .nbytes_max = 512,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
+       /* CE2: target->host WMI */
+       {
+               .pipenum = 2,
+               .pipedir = PIPEDIR_IN,
+               .nentries = 32,
+               .nbytes_max = 2048,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
+       /* CE3: host->target WMI */
+       {
+               .pipenum = 3,
+               .pipedir = PIPEDIR_OUT,
+               .nentries = 32,
+               .nbytes_max = 2048,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
+       /* CE4: host->target HTT */
+       {
+               .pipenum = 4,
+               .pipedir = PIPEDIR_OUT,
+               .nentries = 256,
+               .nbytes_max = 256,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
        /* NB: 50% of src nentries, since tx has 2 frags */
-       /* unused */
-       { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
-       /* Reserved for target autonomous hif_memcpy */
-       { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+
+       /* CE5: unused */
+       {
+               .pipenum = 5,
+               .pipedir = PIPEDIR_OUT,
+               .nentries = 32,
+               .nbytes_max = 2048,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
+       /* CE6: Reserved for target autonomous hif_memcpy */
+       {
+               .pipenum = 6,
+               .pipedir = PIPEDIR_INOUT,
+               .nentries = 32,
+               .nbytes_max = 4096,
+               .flags = CE_ATTR_FLAGS,
+               .reserved = 0,
+       },
+
        /* CE7 used only by Host */
 };
 
@@ -114,7 +214,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
        unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
        unsigned int id;
        unsigned int flags;
-       struct ce_state *ce_diag;
+       struct ath10k_ce_pipe *ce_diag;
        /* Host buffer address in CE space */
        u32 ce_data;
        dma_addr_t ce_data_base = 0;
@@ -278,7 +378,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
        unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
        unsigned int id;
        unsigned int flags;
-       struct ce_state *ce_diag;
+       struct ath10k_ce_pipe *ce_diag;
        void *data_buf = NULL;
        u32 ce_data;    /* Host buffer address in CE space */
        dma_addr_t ce_data_base = 0;
@@ -437,7 +537,7 @@ static void ath10k_pci_wait(struct ath10k *ar)
                ath10k_warn("Unable to wakeup target\n");
 }
 
-void ath10k_do_pci_wake(struct ath10k *ar)
+int ath10k_do_pci_wake(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *pci_addr = ar_pci->mem;
@@ -453,18 +553,19 @@ void ath10k_do_pci_wake(struct ath10k *ar)
        atomic_inc(&ar_pci->keep_awake_count);
 
        if (ar_pci->verified_awake)
-               return;
+               return 0;
 
        for (;;) {
                if (ath10k_pci_target_is_awake(ar)) {
                        ar_pci->verified_awake = true;
-                       break;
+                       return 0;
                }
 
                if (tot_delay > PCIE_WAKE_TIMEOUT) {
-                       ath10k_warn("target takes too long to wake up (awake count %d)\n",
+                       ath10k_warn("target took longer %d us to wake up (awake count %d)\n",
+                                   PCIE_WAKE_TIMEOUT,
                                    atomic_read(&ar_pci->keep_awake_count));
-                       break;
+                       return -ETIMEDOUT;
                }
 
                udelay(curr_delay);
@@ -493,7 +594,7 @@ void ath10k_do_pci_sleep(struct ath10k *ar)
  * FIXME: Handle OOM properly.
  */
 static inline
-struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info)
+struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info)
 {
        struct ath10k_pci_compl *compl = NULL;
 
@@ -511,39 +612,28 @@ exit:
 }
 
 /* Called by lower (CE) layer when a send to Target completes. */
-static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
-                                   void *transfer_context,
-                                   u32 ce_data,
-                                   unsigned int nbytes,
-                                   unsigned int transfer_id)
+static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
 {
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_pipe *pipe_info =  &ar_pci->pipe_info[ce_state->id];
        struct ath10k_pci_compl *compl;
-       bool process = false;
-
-       do {
-               /*
-                * For the send completion of an item in sendlist, just
-                * increment num_sends_allowed. The upper layer callback will
-                * be triggered when last fragment is done with send.
-                */
-               if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
-                       spin_lock_bh(&pipe_info->pipe_lock);
-                       pipe_info->num_sends_allowed++;
-                       spin_unlock_bh(&pipe_info->pipe_lock);
-                       continue;
-               }
+       void *transfer_context;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int transfer_id;
 
+       while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
+                                            &ce_data, &nbytes,
+                                            &transfer_id) == 0) {
                compl = get_free_compl(pipe_info);
                if (!compl)
                        break;
 
-               compl->send_or_recv = HIF_CE_COMPLETE_SEND;
+               compl->state = ATH10K_PCI_COMPL_SEND;
                compl->ce_state = ce_state;
                compl->pipe_info = pipe_info;
-               compl->transfer_context = transfer_context;
+               compl->skb = transfer_context;
                compl->nbytes = nbytes;
                compl->transfer_id = transfer_id;
                compl->flags = 0;
@@ -554,46 +644,36 @@ static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
                spin_lock_bh(&ar_pci->compl_lock);
                list_add_tail(&compl->list, &ar_pci->compl_process);
                spin_unlock_bh(&ar_pci->compl_lock);
-
-               process = true;
-       } while (ath10k_ce_completed_send_next(ce_state,
-                                                          &transfer_context,
-                                                          &ce_data, &nbytes,
-                                                          &transfer_id) == 0);
-
-       /*
-        * If only some of the items within a sendlist have completed,
-        * don't invoke completion processing until the entire sendlist
-        * has been sent.
-        */
-       if (!process)
-               return;
+       }
 
        ath10k_pci_process_ce(ar);
 }
 
 /* Called by lower (CE) layer when data is received from the Target. */
-static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
-                                   void *transfer_context, u32 ce_data,
-                                   unsigned int nbytes,
-                                   unsigned int transfer_id,
-                                   unsigned int flags)
+static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
 {
        struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_pipe *pipe_info =  &ar_pci->pipe_info[ce_state->id];
        struct ath10k_pci_compl *compl;
        struct sk_buff *skb;
+       void *transfer_context;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int transfer_id;
+       unsigned int flags;
 
-       do {
+       while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
+                                            &ce_data, &nbytes, &transfer_id,
+                                            &flags) == 0) {
                compl = get_free_compl(pipe_info);
                if (!compl)
                        break;
 
-               compl->send_or_recv = HIF_CE_COMPLETE_RECV;
+               compl->state = ATH10K_PCI_COMPL_RECV;
                compl->ce_state = ce_state;
                compl->pipe_info = pipe_info;
-               compl->transfer_context = transfer_context;
+               compl->skb = transfer_context;
                compl->nbytes = nbytes;
                compl->transfer_id = transfer_id;
                compl->flags = flags;
@@ -608,12 +688,7 @@ static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
                spin_lock_bh(&ar_pci->compl_lock);
                list_add_tail(&compl->list, &ar_pci->compl_process);
                spin_unlock_bh(&ar_pci->compl_lock);
-
-       } while (ath10k_ce_completed_recv_next(ce_state,
-                                                          &transfer_context,
-                                                          &ce_data, &nbytes,
-                                                          &transfer_id,
-                                                          &flags) == 0);
+       }
 
        ath10k_pci_process_ce(ar);
 }
@@ -625,15 +700,12 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
 {
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]);
-       struct ce_state *ce_hdl = pipe_info->ce_hdl;
-       struct ce_sendlist sendlist;
+       struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe_id]);
+       struct ath10k_ce_pipe *ce_hdl = pipe_info->ce_hdl;
        unsigned int len;
        u32 flags = 0;
        int ret;
 
-       memset(&sendlist, 0, sizeof(struct ce_sendlist));
-
        len = min(bytes, nbuf->len);
        bytes -= len;
 
@@ -648,19 +720,8 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
                        "ath10k tx: data: ",
                        nbuf->data, nbuf->len);
 
-       ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
-
-       /* Make sure we have resources to handle this request */
-       spin_lock_bh(&pipe_info->pipe_lock);
-       if (!pipe_info->num_sends_allowed) {
-               ath10k_warn("Pipe: %d is full\n", pipe_id);
-               spin_unlock_bh(&pipe_info->pipe_lock);
-               return -ENOSR;
-       }
-       pipe_info->num_sends_allowed--;
-       spin_unlock_bh(&pipe_info->pipe_lock);
-
-       ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
+       ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id,
+                            flags);
        if (ret)
                ath10k_warn("CE send failed: %p\n", nbuf);
 
@@ -670,14 +731,7 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
 static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]);
-       int ret;
-
-       spin_lock_bh(&pipe_info->pipe_lock);
-       ret = pipe_info->num_sends_allowed;
-       spin_unlock_bh(&pipe_info->pipe_lock);
-
-       return ret;
+       return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl);
 }
 
 static void ath10k_pci_hif_dump_area(struct ath10k *ar)
@@ -764,9 +818,9 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
 static int ath10k_pci_start_ce(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_state *ce_diag = ar_pci->ce_diag;
+       struct ath10k_ce_pipe *ce_diag = ar_pci->ce_diag;
        const struct ce_attr *attr;
-       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_pipe *pipe_info;
        struct ath10k_pci_compl *compl;
        int i, pipe_num, completions, disable_interrupts;
 
@@ -792,7 +846,6 @@ static int ath10k_pci_start_ce(struct ath10k *ar)
                                                   ath10k_pci_ce_send_done,
                                                   disable_interrupts);
                        completions += attr->src_nentries;
-                       pipe_info->num_sends_allowed = attr->src_nentries - 1;
                }
 
                if (attr->dest_nentries) {
@@ -805,15 +858,14 @@ static int ath10k_pci_start_ce(struct ath10k *ar)
                        continue;
 
                for (i = 0; i < completions; i++) {
-                       compl = kmalloc(sizeof(struct ath10k_pci_compl),
-                                       GFP_KERNEL);
+                       compl = kmalloc(sizeof(*compl), GFP_KERNEL);
                        if (!compl) {
                                ath10k_warn("No memory for completion state\n");
                                ath10k_pci_stop_ce(ar);
                                return -ENOMEM;
                        }
 
-                       compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+                       compl->state = ATH10K_PCI_COMPL_FREE;
                        list_add_tail(&compl->list, &pipe_info->compl_free);
                }
        }
@@ -840,7 +892,7 @@ static void ath10k_pci_stop_ce(struct ath10k *ar)
         * their associated resources */
        spin_lock_bh(&ar_pci->compl_lock);
        list_for_each_entry(compl, &ar_pci->compl_process, list) {
-               skb = (struct sk_buff *)compl->transfer_context;
+               skb = compl->skb;
                ATH10K_SKB_CB(skb)->is_aborted = true;
        }
        spin_unlock_bh(&ar_pci->compl_lock);
@@ -850,7 +902,7 @@ static void ath10k_pci_cleanup_ce(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_pci_compl *compl, *tmp;
-       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_pipe *pipe_info;
        struct sk_buff *netbuf;
        int pipe_num;
 
@@ -861,7 +913,7 @@ static void ath10k_pci_cleanup_ce(struct ath10k *ar)
 
        list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
                list_del(&compl->list);
-               netbuf = (struct sk_buff *)compl->transfer_context;
+               netbuf = compl->skb;
                dev_kfree_skb_any(netbuf);
                kfree(compl);
        }
@@ -912,12 +964,14 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
                list_del(&compl->list);
                spin_unlock_bh(&ar_pci->compl_lock);
 
-               if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
+               switch (compl->state) {
+               case ATH10K_PCI_COMPL_SEND:
                        cb->tx_completion(ar,
-                                         compl->transfer_context,
+                                         compl->skb,
                                          compl->transfer_id);
                        send_done = 1;
-               } else {
+                       break;
+               case ATH10K_PCI_COMPL_RECV:
                        ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
                        if (ret) {
                                ath10k_warn("Unable to post recv buffer for pipe: %d\n",
@@ -925,7 +979,7 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
                                break;
                        }
 
-                       skb = (struct sk_buff *)compl->transfer_context;
+                       skb = compl->skb;
                        nbytes = compl->nbytes;
 
                        ath10k_dbg(ATH10K_DBG_PCI,
@@ -944,16 +998,23 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
                                            nbytes,
                                            skb->len + skb_tailroom(skb));
                        }
+                       break;
+               case ATH10K_PCI_COMPL_FREE:
+                       ath10k_warn("free completion cannot be processed\n");
+                       break;
+               default:
+                       ath10k_warn("invalid completion state (%d)\n",
+                                   compl->state);
+                       break;
                }
 
-               compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+               compl->state = ATH10K_PCI_COMPL_FREE;
 
                /*
                 * Add completion back to the pipe's free list.
                 */
                spin_lock_bh(&compl->pipe_info->pipe_lock);
                list_add_tail(&compl->list, &compl->pipe_info->compl_free);
-               compl->pipe_info->num_sends_allowed += send_done;
                spin_unlock_bh(&compl->pipe_info->pipe_lock);
        }
 
@@ -1037,12 +1098,12 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
                                                 &dl_is_polled);
 }
 
-static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
                                   int num)
 {
        struct ath10k *ar = pipe_info->hif_ce_state;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_state *ce_state = pipe_info->ce_hdl;
+       struct ath10k_ce_pipe *ce_state = pipe_info->ce_hdl;
        struct sk_buff *skb;
        dma_addr_t ce_data;
        int i, ret = 0;
@@ -1097,7 +1158,7 @@ err:
 static int ath10k_pci_post_rx(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_pipe *pipe_info;
        const struct ce_attr *attr;
        int pipe_num, ret = 0;
 
@@ -1147,11 +1208,11 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
        return 0;
 }
 
-static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
 {
        struct ath10k *ar;
        struct ath10k_pci *ar_pci;
-       struct ce_state *ce_hdl;
+       struct ath10k_ce_pipe *ce_hdl;
        u32 buf_sz;
        struct sk_buff *netbuf;
        u32 ce_data;
@@ -1179,11 +1240,11 @@ static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
        }
 }
 
-static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
 {
        struct ath10k *ar;
        struct ath10k_pci *ar_pci;
-       struct ce_state *ce_hdl;
+       struct ath10k_ce_pipe *ce_hdl;
        struct sk_buff *netbuf;
        u32 ce_data;
        unsigned int nbytes;
@@ -1206,15 +1267,14 @@ static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
 
        while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
                                          &ce_data, &nbytes, &id) == 0) {
-               if (netbuf != CE_SENDLIST_ITEM_CTXT)
-                       /*
-                        * Indicate the completion to higer layer to free
-                        * the buffer
-                        */
-                       ATH10K_SKB_CB(netbuf)->is_aborted = true;
-                       ar_pci->msg_callbacks_current.tx_completion(ar,
-                                                                   netbuf,
-                                                                   id);
+               /*
+                * Indicate the completion to higer layer to free
+                * the buffer
+                */
+               ATH10K_SKB_CB(netbuf)->is_aborted = true;
+               ar_pci->msg_callbacks_current.tx_completion(ar,
+                                                           netbuf,
+                                                           id);
        }
 }
 
@@ -1232,7 +1292,7 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
        int pipe_num;
 
        for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
-               struct hif_ce_pipe_info *pipe_info;
+               struct ath10k_pci_pipe *pipe_info;
 
                pipe_info = &ar_pci->pipe_info[pipe_num];
                ath10k_pci_rx_pipe_cleanup(pipe_info);
@@ -1243,7 +1303,7 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
 static void ath10k_pci_ce_deinit(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_pipe *pipe_info;
        int pipe_num;
 
        for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
@@ -1293,8 +1353,10 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
                                           void *resp, u32 *resp_len)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl;
-       struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl;
+       struct ath10k_pci_pipe *pci_tx = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
+       struct ath10k_pci_pipe *pci_rx = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
+       struct ath10k_ce_pipe *ce_tx = pci_tx->ce_hdl;
+       struct ath10k_ce_pipe *ce_rx = pci_rx->ce_hdl;
        dma_addr_t req_paddr = 0;
        dma_addr_t resp_paddr = 0;
        struct bmi_xfer xfer = {};
@@ -1378,13 +1440,16 @@ err_dma:
        return ret;
 }
 
-static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
-                                    void *transfer_context,
-                                    u32 data,
-                                    unsigned int nbytes,
-                                    unsigned int transfer_id)
+static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
 {
-       struct bmi_xfer *xfer = transfer_context;
+       struct bmi_xfer *xfer;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int transfer_id;
+
+       if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data,
+                                         &nbytes, &transfer_id))
+               return;
 
        if (xfer->wait_for_resp)
                return;
@@ -1392,14 +1457,17 @@ static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
        complete(&xfer->done);
 }
 
-static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state,
-                                    void *transfer_context,
-                                    u32 data,
-                                    unsigned int nbytes,
-                                    unsigned int transfer_id,
-                                    unsigned int flags)
+static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
 {
-       struct bmi_xfer *xfer = transfer_context;
+       struct bmi_xfer *xfer;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int transfer_id;
+       unsigned int flags;
+
+       if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data,
+                                         &nbytes, &transfer_id, &flags))
+               return;
 
        if (!xfer->wait_for_resp) {
                ath10k_warn("unexpected: BMI data received; ignoring\n");
@@ -1679,7 +1747,7 @@ static int ath10k_pci_init_config(struct ath10k *ar)
 static int ath10k_pci_ce_init(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_pipe *pipe_info;
        const struct ce_attr *attr;
        int pipe_num;
 
@@ -1895,7 +1963,7 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
 {
-       struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr;
+       struct ath10k_pci_pipe *pipe = (struct ath10k_pci_pipe *)ptr;
        struct ath10k_pci *ar_pci = pipe->ar_pci;
 
        ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num);
@@ -2212,18 +2280,13 @@ static int ath10k_pci_reset_target(struct ath10k *ar)
 
 static void ath10k_pci_device_reset(struct ath10k *ar)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       void __iomem *mem = ar_pci->mem;
        int i;
        u32 val;
 
        if (!SOC_GLOBAL_RESET_ADDRESS)
                return;
 
-       if (!mem)
-               return;
-
-       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS,
+       ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS,
                               PCIE_SOC_WAKE_V_MASK);
        for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
                if (ath10k_pci_target_is_awake(ar))
@@ -2232,12 +2295,12 @@ static void ath10k_pci_device_reset(struct ath10k *ar)
        }
 
        /* Put Target, including PCIe, into RESET. */
-       val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS);
+       val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS);
        val |= 1;
-       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+       ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val);
 
        for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
-               if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+               if (ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) &
                                          RTC_STATE_COLD_RESET_MASK)
                        break;
                msleep(1);
@@ -2245,16 +2308,16 @@ static void ath10k_pci_device_reset(struct ath10k *ar)
 
        /* Pull Target, including PCIe, out of RESET. */
        val &= ~1;
-       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+       ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val);
 
        for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
-               if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+               if (!(ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) &
                                            RTC_STATE_COLD_RESET_MASK))
                        break;
                msleep(1);
        }
 
-       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
+       ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
 }
 
 static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
@@ -2267,13 +2330,10 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
 
                switch (i) {
                case ATH10K_PCI_FEATURE_MSI_X:
-                       ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
-                       break;
-               case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND:
-                       ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
+                       ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n");
                        break;
                case ATH10K_PCI_FEATURE_SOC_POWER_SAVE:
-                       ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n");
+                       ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n");
                        break;
                }
        }
@@ -2286,7 +2346,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        int ret = 0;
        struct ath10k *ar;
        struct ath10k_pci *ar_pci;
-       u32 lcr_val;
+       u32 lcr_val, chip_id;
 
        ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
 
@@ -2298,9 +2358,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        ar_pci->dev = &pdev->dev;
 
        switch (pci_dev->device) {
-       case QCA988X_1_0_DEVICE_ID:
-               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features);
-               break;
        case QCA988X_2_0_DEVICE_ID:
                set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
                break;
@@ -2322,10 +2379,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                goto err_ar_pci;
        }
 
-       /* Enable QCA988X_1.0 HW workarounds */
-       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features))
-               spin_lock_init(&ar_pci->hw_v1_workaround_lock);
-
        ar_pci->ar = ar;
        ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
        atomic_set(&ar_pci->keep_awake_count, 0);
@@ -2395,9 +2448,20 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        spin_lock_init(&ar_pci->ce_lock);
 
-       ar_pci->cacheline_sz = dma_get_cache_alignment();
+       ret = ath10k_do_pci_wake(ar);
+       if (ret) {
+               ath10k_err("Failed to get chip id: %d\n", ret);
+               return ret;
+       }
+
+       chip_id = ath10k_pci_read32(ar,
+                                   RTC_SOC_BASE_ADDRESS + SOC_CHIP_ID_ADDRESS);
+
+       ath10k_do_pci_sleep(ar);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
 
-       ret = ath10k_core_register(ar);
+       ret = ath10k_core_register(ar, chip_id);
        if (ret) {
                ath10k_err("could not register driver core (%d)\n", ret);
                goto err_iomap;
@@ -2414,7 +2478,6 @@ err_region:
 err_device:
        pci_disable_device(pdev);
 err_ar:
-       pci_set_drvdata(pdev, NULL);
        ath10k_core_destroy(ar);
 err_ar_pci:
        /* call HIF PCI free here */
@@ -2442,7 +2505,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
 
        ath10k_core_unregister(ar);
 
-       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ar_pci->mem);
        pci_release_region(pdev, BAR_NUM);
        pci_clear_master(pdev);
@@ -2483,9 +2545,6 @@ module_exit(ath10k_pci_exit);
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
 MODULE_LICENSE("Dual BSD/GPL");
-MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE);
-MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE);
-MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
index 871bb33..52fb7b9 100644 (file)
@@ -43,22 +43,23 @@ struct bmi_xfer {
        u32 resp_len;
 };
 
+enum ath10k_pci_compl_state {
+       ATH10K_PCI_COMPL_FREE = 0,
+       ATH10K_PCI_COMPL_SEND,
+       ATH10K_PCI_COMPL_RECV,
+};
+
 struct ath10k_pci_compl {
        struct list_head list;
-       int send_or_recv;
-       struct ce_state *ce_state;
-       struct hif_ce_pipe_info *pipe_info;
-       void *transfer_context;
+       enum ath10k_pci_compl_state state;
+       struct ath10k_ce_pipe *ce_state;
+       struct ath10k_pci_pipe *pipe_info;
+       struct sk_buff *skb;
        unsigned int nbytes;
        unsigned int transfer_id;
        unsigned int flags;
 };
 
-/* compl_state.send_or_recv */
-#define HIF_CE_COMPLETE_FREE 0
-#define HIF_CE_COMPLETE_SEND 1
-#define HIF_CE_COMPLETE_RECV 2
-
 /*
  * PCI-specific Target state
  *
@@ -152,17 +153,16 @@ struct service_to_pipe {
 
 enum ath10k_pci_features {
        ATH10K_PCI_FEATURE_MSI_X                = 0,
-       ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND    = 1,
-       ATH10K_PCI_FEATURE_SOC_POWER_SAVE       = 2,
+       ATH10K_PCI_FEATURE_SOC_POWER_SAVE       = 1,
 
        /* keep last */
        ATH10K_PCI_FEATURE_COUNT
 };
 
 /* Per-pipe state. */
-struct hif_ce_pipe_info {
+struct ath10k_pci_pipe {
        /* Handle of underlying Copy Engine */
-       struct ce_state *ce_hdl;
+       struct ath10k_ce_pipe *ce_hdl;
 
        /* Our pipe number; facilitiates use of pipe_info ptrs. */
        u8 pipe_num;
@@ -178,9 +178,6 @@ struct hif_ce_pipe_info {
        /* List of free CE completion slots */
        struct list_head compl_free;
 
-       /* Limit the number of outstanding send requests. */
-       int num_sends_allowed;
-
        struct ath10k_pci *ar_pci;
        struct tasklet_struct intr;
 };
@@ -190,7 +187,6 @@ struct ath10k_pci {
        struct device *dev;
        struct ath10k *ar;
        void __iomem *mem;
-       int cacheline_sz;
 
        DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
 
@@ -219,7 +215,7 @@ struct ath10k_pci {
 
        bool compl_processing;
 
-       struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX];
+       struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
 
        struct ath10k_hif_cb msg_callbacks_current;
 
@@ -227,16 +223,13 @@ struct ath10k_pci {
        u32 fw_indicator_address;
 
        /* Copy Engine used for Diagnostic Accesses */
-       struct ce_state *ce_diag;
+       struct ath10k_ce_pipe *ce_diag;
 
        /* FIXME: document what this really protects */
        spinlock_t ce_lock;
 
        /* Map CE id to ce_state */
-       struct ce_state *ce_id_to_state[CE_COUNT_MAX];
-
-       /* makes sure that dummy reads are atomic */
-       spinlock_t hw_v1_workaround_lock;
+       struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
 };
 
 static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
@@ -244,14 +237,18 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
        return ar->hif.priv;
 }
 
-static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr)
+static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr)
 {
-       return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
 }
 
-static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val)
+static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val)
 {
-       iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
 }
 
 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
@@ -310,23 +307,8 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
                                      u32 value)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       void __iomem *addr = ar_pci->mem;
-
-       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
-               unsigned long irq_flags;
 
-               spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
-
-               ioread32(addr+offset+4); /* 3rd read prior to write */
-               ioread32(addr+offset+4); /* 2nd read prior to write */
-               ioread32(addr+offset+4); /* 1st read prior to write */
-               iowrite32(value, addr+offset);
-
-               spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock,
-                                      irq_flags);
-       } else {
-               iowrite32(value, addr+offset);
-       }
+       iowrite32(value, ar_pci->mem + offset);
 }
 
 static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
@@ -336,15 +318,17 @@ static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
        return ioread32(ar_pci->mem + offset);
 }
 
-void ath10k_do_pci_wake(struct ath10k *ar);
+int ath10k_do_pci_wake(struct ath10k *ar);
 void ath10k_do_pci_sleep(struct ath10k *ar);
 
-static inline void ath10k_pci_wake(struct ath10k *ar)
+static inline int ath10k_pci_wake(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
        if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
-               ath10k_do_pci_wake(ar);
+               return ath10k_do_pci_wake(ar);
+
+       return 0;
 }
 
 static inline void ath10k_pci_sleep(struct ath10k *ar)
index bfec6c8..1c584c4 100644 (file)
@@ -422,10 +422,30 @@ struct rx_mpdu_end {
 #define RX_MSDU_START_INFO1_IP_FRAG             (1 << 14)
 #define RX_MSDU_START_INFO1_TCP_ONLY_ACK        (1 << 15)
 
+/* The decapped header (rx_hdr_status) contains the following:
+ *  a) 802.11 header
+ *  [padding to 4 bytes]
+ *  b) HW crypto parameter
+ *     - 0 bytes for no security
+ *     - 4 bytes for WEP
+ *     - 8 bytes for TKIP, AES
+ *  [padding to 4 bytes]
+ *  c) A-MSDU subframe header (14 bytes) if appliable
+ *  d) LLC/SNAP (RFC1042, 8 bytes)
+ *
+ * In case of A-MSDU only first frame in sequence contains (a) and (b). */
 enum rx_msdu_decap_format {
-       RX_MSDU_DECAP_RAW           = 0,
-       RX_MSDU_DECAP_NATIVE_WIFI   = 1,
+       RX_MSDU_DECAP_RAW = 0,
+
+       /* Note: QoS frames are reported as non-QoS. The rx_hdr_status in
+        * htt_rx_desc contains the original decapped 802.11 header. */
+       RX_MSDU_DECAP_NATIVE_WIFI = 1,
+
+       /* Payload contains an ethernet header (struct ethhdr). */
        RX_MSDU_DECAP_ETHERNET2_DIX = 2,
+
+       /* Payload contains two 48-bit addresses and 2-byte length (14 bytes
+        * total), followed by an RFC1042 header (8 bytes). */
        RX_MSDU_DECAP_8023_SNAP_LLC = 3
 };
 
index 85e806b..90817dd 100644 (file)
@@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump,
 );
 
 TRACE_EVENT(ath10k_wmi_cmd,
-       TP_PROTO(int id, void *buf, size_t buf_len),
+       TP_PROTO(int id, void *buf, size_t buf_len, int ret),
 
-       TP_ARGS(id, buf, buf_len),
+       TP_ARGS(id, buf, buf_len, ret),
 
        TP_STRUCT__entry(
                __field(unsigned int, id)
                __field(size_t, buf_len)
                __dynamic_array(u8, buf, buf_len)
+               __field(int, ret)
        ),
 
        TP_fast_assign(
                __entry->id = id;
                __entry->buf_len = buf_len;
+               __entry->ret = ret;
                memcpy(__get_dynamic_array(buf), buf, buf_len);
        ),
 
        TP_printk(
-               "id %d len %zu",
+               "id %d len %zu ret %d",
                __entry->id,
-               __entry->buf_len
+               __entry->buf_len,
+               __entry->ret
        )
 );
 
@@ -158,6 +161,27 @@ TRACE_EVENT(ath10k_wmi_event,
        )
 );
 
+TRACE_EVENT(ath10k_htt_stats,
+       TP_PROTO(void *buf, size_t buf_len),
+
+       TP_ARGS(buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "len %zu",
+               __entry->buf_len
+       )
+);
+
 #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
 
 /* we don't want to use include/trace/events */
index 68b6fae..5ae373a 100644 (file)
@@ -44,40 +44,39 @@ out:
        spin_unlock_bh(&ar->data_lock);
 }
 
-void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
+                         const struct htt_tx_done *tx_done)
 {
        struct device *dev = htt->ar->dev;
        struct ieee80211_tx_info *info;
-       struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag;
-       struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *msdu;
        int ret;
 
-       if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0)
-               return;
-
-       ATH10K_SKB_CB(txdesc)->htt.refcount--;
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
+                  tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
 
-       if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+       if (tx_done->msdu_id >= htt->max_num_pending_tx) {
+               ath10k_warn("warning: msdu_id %d too big, ignoring\n",
+                           tx_done->msdu_id);
                return;
-
-       if (txfrag) {
-               ret = ath10k_skb_unmap(dev, txfrag);
-               if (ret)
-                       ath10k_warn("txfrag unmap failed (%d)\n", ret);
-
-               dev_kfree_skb_any(txfrag);
        }
 
+       msdu = htt->pending_tx[tx_done->msdu_id];
+       skb_cb = ATH10K_SKB_CB(msdu);
+
        ret = ath10k_skb_unmap(dev, msdu);
        if (ret)
                ath10k_warn("data skb unmap failed (%d)\n", ret);
 
+       if (skb_cb->htt.frag_len)
+               skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len);
+
        ath10k_report_offchan_tx(htt->ar, msdu);
 
        info = IEEE80211_SKB_CB(msdu);
-       memset(&info->status, 0, sizeof(info->status));
 
-       if (ATH10K_SKB_CB(txdesc)->htt.discard) {
+       if (tx_done->discard) {
                ieee80211_free_txskb(htt->ar->hw, msdu);
                goto exit;
        }
@@ -85,7 +84,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
        if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
                info->flags |= IEEE80211_TX_STAT_ACK;
 
-       if (ATH10K_SKB_CB(txdesc)->htt.no_ack)
+       if (tx_done->no_ack)
                info->flags &= ~IEEE80211_TX_STAT_ACK;
 
        ieee80211_tx_status(htt->ar->hw, msdu);
@@ -93,36 +92,12 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
 
 exit:
        spin_lock_bh(&htt->tx_lock);
-       htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
-       ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
+       htt->pending_tx[tx_done->msdu_id] = NULL;
+       ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
        __ath10k_htt_tx_dec_pending(htt);
-       if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx))
+       if (htt->num_pending_tx == 0)
                wake_up(&htt->empty_tx_wq);
        spin_unlock_bh(&htt->tx_lock);
-
-       dev_kfree_skb_any(txdesc);
-}
-
-void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
-                             const struct htt_tx_done *tx_done)
-{
-       struct sk_buff *txdesc;
-
-       ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
-                  tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
-
-       if (tx_done->msdu_id >= htt->max_num_pending_tx) {
-               ath10k_warn("warning: msdu_id %d too big, ignoring\n",
-                           tx_done->msdu_id);
-               return;
-       }
-
-       txdesc = htt->pending_tx[tx_done->msdu_id];
-
-       ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard;
-       ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack;
-
-       ath10k_txrx_tx_unref(htt, txdesc);
 }
 
 static const u8 rx_legacy_rate_idx[] = {
@@ -293,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
                   status->vht_nss,
                   status->freq,
                   status->band);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
+                       info->skb->data, info->skb->len);
 
        ieee80211_rx(ar->hw, info->skb);
 }
index e78632a..356dc9c 100644 (file)
@@ -19,9 +19,8 @@
 
 #include "htt.h"
 
-void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc);
-void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
-                             const struct htt_tx_done *tx_done);
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
+                         const struct htt_tx_done *tx_done);
 void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info);
 
 struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
index 55f90c7..ccf3597 100644 (file)
 #include "wmi.h"
 #include "mac.h"
 
-void ath10k_wmi_flush_tx(struct ath10k *ar)
-{
-       int ret;
-
-       lockdep_assert_held(&ar->conf_mutex);
-
-       if (ar->state == ATH10K_STATE_WEDGED) {
-               ath10k_warn("wmi flush skipped - device is wedged anyway\n");
-               return;
-       }
-
-       ret = wait_event_timeout(ar->wmi.wq,
-                                atomic_read(&ar->wmi.pending_tx_count) == 0,
-                                5*HZ);
-       if (atomic_read(&ar->wmi.pending_tx_count) == 0)
-               return;
-
-       if (ret == 0)
-               ret = -ETIMEDOUT;
-
-       if (ret < 0)
-               ath10k_warn("wmi flush failed (%d)\n", ret);
-}
+/* MAIN WMI cmd track */
+static struct wmi_cmd_map wmi_cmd_map = {
+       .init_cmdid = WMI_INIT_CMDID,
+       .start_scan_cmdid = WMI_START_SCAN_CMDID,
+       .stop_scan_cmdid = WMI_STOP_SCAN_CMDID,
+       .scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID,
+       .scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID,
+       .pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID,
+       .pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID,
+       .pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID,
+       .pdev_pktlog_enable_cmdid = WMI_PDEV_PKTLOG_ENABLE_CMDID,
+       .pdev_pktlog_disable_cmdid = WMI_PDEV_PKTLOG_DISABLE_CMDID,
+       .pdev_set_wmm_params_cmdid = WMI_PDEV_SET_WMM_PARAMS_CMDID,
+       .pdev_set_ht_cap_ie_cmdid = WMI_PDEV_SET_HT_CAP_IE_CMDID,
+       .pdev_set_vht_cap_ie_cmdid = WMI_PDEV_SET_VHT_CAP_IE_CMDID,
+       .pdev_set_dscp_tid_map_cmdid = WMI_PDEV_SET_DSCP_TID_MAP_CMDID,
+       .pdev_set_quiet_mode_cmdid = WMI_PDEV_SET_QUIET_MODE_CMDID,
+       .pdev_green_ap_ps_enable_cmdid = WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       .pdev_get_tpc_config_cmdid = WMI_PDEV_GET_TPC_CONFIG_CMDID,
+       .pdev_set_base_macaddr_cmdid = WMI_PDEV_SET_BASE_MACADDR_CMDID,
+       .vdev_create_cmdid = WMI_VDEV_CREATE_CMDID,
+       .vdev_delete_cmdid = WMI_VDEV_DELETE_CMDID,
+       .vdev_start_request_cmdid = WMI_VDEV_START_REQUEST_CMDID,
+       .vdev_restart_request_cmdid = WMI_VDEV_RESTART_REQUEST_CMDID,
+       .vdev_up_cmdid = WMI_VDEV_UP_CMDID,
+       .vdev_stop_cmdid = WMI_VDEV_STOP_CMDID,
+       .vdev_down_cmdid = WMI_VDEV_DOWN_CMDID,
+       .vdev_set_param_cmdid = WMI_VDEV_SET_PARAM_CMDID,
+       .vdev_install_key_cmdid = WMI_VDEV_INSTALL_KEY_CMDID,
+       .peer_create_cmdid = WMI_PEER_CREATE_CMDID,
+       .peer_delete_cmdid = WMI_PEER_DELETE_CMDID,
+       .peer_flush_tids_cmdid = WMI_PEER_FLUSH_TIDS_CMDID,
+       .peer_set_param_cmdid = WMI_PEER_SET_PARAM_CMDID,
+       .peer_assoc_cmdid = WMI_PEER_ASSOC_CMDID,
+       .peer_add_wds_entry_cmdid = WMI_PEER_ADD_WDS_ENTRY_CMDID,
+       .peer_remove_wds_entry_cmdid = WMI_PEER_REMOVE_WDS_ENTRY_CMDID,
+       .peer_mcast_group_cmdid = WMI_PEER_MCAST_GROUP_CMDID,
+       .bcn_tx_cmdid = WMI_BCN_TX_CMDID,
+       .pdev_send_bcn_cmdid = WMI_PDEV_SEND_BCN_CMDID,
+       .bcn_tmpl_cmdid = WMI_BCN_TMPL_CMDID,
+       .bcn_filter_rx_cmdid = WMI_BCN_FILTER_RX_CMDID,
+       .prb_req_filter_rx_cmdid = WMI_PRB_REQ_FILTER_RX_CMDID,
+       .mgmt_tx_cmdid = WMI_MGMT_TX_CMDID,
+       .prb_tmpl_cmdid = WMI_PRB_TMPL_CMDID,
+       .addba_clear_resp_cmdid = WMI_ADDBA_CLEAR_RESP_CMDID,
+       .addba_send_cmdid = WMI_ADDBA_SEND_CMDID,
+       .addba_status_cmdid = WMI_ADDBA_STATUS_CMDID,
+       .delba_send_cmdid = WMI_DELBA_SEND_CMDID,
+       .addba_set_resp_cmdid = WMI_ADDBA_SET_RESP_CMDID,
+       .send_singleamsdu_cmdid = WMI_SEND_SINGLEAMSDU_CMDID,
+       .sta_powersave_mode_cmdid = WMI_STA_POWERSAVE_MODE_CMDID,
+       .sta_powersave_param_cmdid = WMI_STA_POWERSAVE_PARAM_CMDID,
+       .sta_mimo_ps_mode_cmdid = WMI_STA_MIMO_PS_MODE_CMDID,
+       .pdev_dfs_enable_cmdid = WMI_PDEV_DFS_ENABLE_CMDID,
+       .pdev_dfs_disable_cmdid = WMI_PDEV_DFS_DISABLE_CMDID,
+       .roam_scan_mode = WMI_ROAM_SCAN_MODE,
+       .roam_scan_rssi_threshold = WMI_ROAM_SCAN_RSSI_THRESHOLD,
+       .roam_scan_period = WMI_ROAM_SCAN_PERIOD,
+       .roam_scan_rssi_change_threshold = WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       .roam_ap_profile = WMI_ROAM_AP_PROFILE,
+       .ofl_scan_add_ap_profile = WMI_ROAM_AP_PROFILE,
+       .ofl_scan_remove_ap_profile = WMI_OFL_SCAN_REMOVE_AP_PROFILE,
+       .ofl_scan_period = WMI_OFL_SCAN_PERIOD,
+       .p2p_dev_set_device_info = WMI_P2P_DEV_SET_DEVICE_INFO,
+       .p2p_dev_set_discoverability = WMI_P2P_DEV_SET_DISCOVERABILITY,
+       .p2p_go_set_beacon_ie = WMI_P2P_GO_SET_BEACON_IE,
+       .p2p_go_set_probe_resp_ie = WMI_P2P_GO_SET_PROBE_RESP_IE,
+       .p2p_set_vendor_ie_data_cmdid = WMI_P2P_SET_VENDOR_IE_DATA_CMDID,
+       .ap_ps_peer_param_cmdid = WMI_AP_PS_PEER_PARAM_CMDID,
+       .ap_ps_peer_uapsd_coex_cmdid = WMI_AP_PS_PEER_UAPSD_COEX_CMDID,
+       .peer_rate_retry_sched_cmdid = WMI_PEER_RATE_RETRY_SCHED_CMDID,
+       .wlan_profile_trigger_cmdid = WMI_WLAN_PROFILE_TRIGGER_CMDID,
+       .wlan_profile_set_hist_intvl_cmdid =
+                               WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       .wlan_profile_get_profile_data_cmdid =
+                               WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       .wlan_profile_enable_profile_id_cmdid =
+                               WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       .wlan_profile_list_profile_id_cmdid =
+                               WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+       .pdev_suspend_cmdid = WMI_PDEV_SUSPEND_CMDID,
+       .pdev_resume_cmdid = WMI_PDEV_RESUME_CMDID,
+       .add_bcn_filter_cmdid = WMI_ADD_BCN_FILTER_CMDID,
+       .rmv_bcn_filter_cmdid = WMI_RMV_BCN_FILTER_CMDID,
+       .wow_add_wake_pattern_cmdid = WMI_WOW_ADD_WAKE_PATTERN_CMDID,
+       .wow_del_wake_pattern_cmdid = WMI_WOW_DEL_WAKE_PATTERN_CMDID,
+       .wow_enable_disable_wake_event_cmdid =
+                               WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       .wow_enable_cmdid = WMI_WOW_ENABLE_CMDID,
+       .wow_hostwakeup_from_sleep_cmdid = WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+       .rtt_measreq_cmdid = WMI_RTT_MEASREQ_CMDID,
+       .rtt_tsf_cmdid = WMI_RTT_TSF_CMDID,
+       .vdev_spectral_scan_configure_cmdid =
+                               WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+       .vdev_spectral_scan_enable_cmdid = WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+       .request_stats_cmdid = WMI_REQUEST_STATS_CMDID,
+       .set_arp_ns_offload_cmdid = WMI_SET_ARP_NS_OFFLOAD_CMDID,
+       .network_list_offload_config_cmdid =
+                               WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID,
+       .gtk_offload_cmdid = WMI_GTK_OFFLOAD_CMDID,
+       .csa_offload_enable_cmdid = WMI_CSA_OFFLOAD_ENABLE_CMDID,
+       .csa_offload_chanswitch_cmdid = WMI_CSA_OFFLOAD_CHANSWITCH_CMDID,
+       .chatter_set_mode_cmdid = WMI_CHATTER_SET_MODE_CMDID,
+       .peer_tid_addba_cmdid = WMI_PEER_TID_ADDBA_CMDID,
+       .peer_tid_delba_cmdid = WMI_PEER_TID_DELBA_CMDID,
+       .sta_dtim_ps_method_cmdid = WMI_STA_DTIM_PS_METHOD_CMDID,
+       .sta_uapsd_auto_trig_cmdid = WMI_STA_UAPSD_AUTO_TRIG_CMDID,
+       .sta_keepalive_cmd = WMI_STA_KEEPALIVE_CMD,
+       .echo_cmdid = WMI_ECHO_CMDID,
+       .pdev_utf_cmdid = WMI_PDEV_UTF_CMDID,
+       .dbglog_cfg_cmdid = WMI_DBGLOG_CFG_CMDID,
+       .pdev_qvit_cmdid = WMI_PDEV_QVIT_CMDID,
+       .pdev_ftm_intg_cmdid = WMI_PDEV_FTM_INTG_CMDID,
+       .vdev_set_keepalive_cmdid = WMI_VDEV_SET_KEEPALIVE_CMDID,
+       .vdev_get_keepalive_cmdid = WMI_VDEV_GET_KEEPALIVE_CMDID,
+       .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID,
+       .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
+       .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
+};
+
+/* 10.X WMI cmd track */
+static struct wmi_cmd_map wmi_10x_cmd_map = {
+       .init_cmdid = WMI_10X_INIT_CMDID,
+       .start_scan_cmdid = WMI_10X_START_SCAN_CMDID,
+       .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID,
+       .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID,
+       .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+       .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
+       .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID,
+       .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID,
+       .pdev_pktlog_enable_cmdid = WMI_10X_PDEV_PKTLOG_ENABLE_CMDID,
+       .pdev_pktlog_disable_cmdid = WMI_10X_PDEV_PKTLOG_DISABLE_CMDID,
+       .pdev_set_wmm_params_cmdid = WMI_10X_PDEV_SET_WMM_PARAMS_CMDID,
+       .pdev_set_ht_cap_ie_cmdid = WMI_10X_PDEV_SET_HT_CAP_IE_CMDID,
+       .pdev_set_vht_cap_ie_cmdid = WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID,
+       .pdev_set_dscp_tid_map_cmdid = WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID,
+       .pdev_set_quiet_mode_cmdid = WMI_10X_PDEV_SET_QUIET_MODE_CMDID,
+       .pdev_green_ap_ps_enable_cmdid = WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       .pdev_get_tpc_config_cmdid = WMI_10X_PDEV_GET_TPC_CONFIG_CMDID,
+       .pdev_set_base_macaddr_cmdid = WMI_10X_PDEV_SET_BASE_MACADDR_CMDID,
+       .vdev_create_cmdid = WMI_10X_VDEV_CREATE_CMDID,
+       .vdev_delete_cmdid = WMI_10X_VDEV_DELETE_CMDID,
+       .vdev_start_request_cmdid = WMI_10X_VDEV_START_REQUEST_CMDID,
+       .vdev_restart_request_cmdid = WMI_10X_VDEV_RESTART_REQUEST_CMDID,
+       .vdev_up_cmdid = WMI_10X_VDEV_UP_CMDID,
+       .vdev_stop_cmdid = WMI_10X_VDEV_STOP_CMDID,
+       .vdev_down_cmdid = WMI_10X_VDEV_DOWN_CMDID,
+       .vdev_set_param_cmdid = WMI_10X_VDEV_SET_PARAM_CMDID,
+       .vdev_install_key_cmdid = WMI_10X_VDEV_INSTALL_KEY_CMDID,
+       .peer_create_cmdid = WMI_10X_PEER_CREATE_CMDID,
+       .peer_delete_cmdid = WMI_10X_PEER_DELETE_CMDID,
+       .peer_flush_tids_cmdid = WMI_10X_PEER_FLUSH_TIDS_CMDID,
+       .peer_set_param_cmdid = WMI_10X_PEER_SET_PARAM_CMDID,
+       .peer_assoc_cmdid = WMI_10X_PEER_ASSOC_CMDID,
+       .peer_add_wds_entry_cmdid = WMI_10X_PEER_ADD_WDS_ENTRY_CMDID,
+       .peer_remove_wds_entry_cmdid = WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID,
+       .peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID,
+       .bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID,
+       .pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID,
+       .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+       .bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID,
+       .prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID,
+       .mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID,
+       .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
+       .addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID,
+       .addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID,
+       .addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID,
+       .delba_send_cmdid = WMI_10X_DELBA_SEND_CMDID,
+       .addba_set_resp_cmdid = WMI_10X_ADDBA_SET_RESP_CMDID,
+       .send_singleamsdu_cmdid = WMI_10X_SEND_SINGLEAMSDU_CMDID,
+       .sta_powersave_mode_cmdid = WMI_10X_STA_POWERSAVE_MODE_CMDID,
+       .sta_powersave_param_cmdid = WMI_10X_STA_POWERSAVE_PARAM_CMDID,
+       .sta_mimo_ps_mode_cmdid = WMI_10X_STA_MIMO_PS_MODE_CMDID,
+       .pdev_dfs_enable_cmdid = WMI_10X_PDEV_DFS_ENABLE_CMDID,
+       .pdev_dfs_disable_cmdid = WMI_10X_PDEV_DFS_DISABLE_CMDID,
+       .roam_scan_mode = WMI_10X_ROAM_SCAN_MODE,
+       .roam_scan_rssi_threshold = WMI_10X_ROAM_SCAN_RSSI_THRESHOLD,
+       .roam_scan_period = WMI_10X_ROAM_SCAN_PERIOD,
+       .roam_scan_rssi_change_threshold =
+                               WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       .roam_ap_profile = WMI_10X_ROAM_AP_PROFILE,
+       .ofl_scan_add_ap_profile = WMI_10X_OFL_SCAN_ADD_AP_PROFILE,
+       .ofl_scan_remove_ap_profile = WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE,
+       .ofl_scan_period = WMI_10X_OFL_SCAN_PERIOD,
+       .p2p_dev_set_device_info = WMI_10X_P2P_DEV_SET_DEVICE_INFO,
+       .p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY,
+       .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE,
+       .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
+       .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
+       .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED,
+       .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
+       .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
+       .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
+       .wlan_profile_set_hist_intvl_cmdid =
+                               WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       .wlan_profile_get_profile_data_cmdid =
+                               WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       .wlan_profile_enable_profile_id_cmdid =
+                               WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       .wlan_profile_list_profile_id_cmdid =
+                               WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+       .pdev_suspend_cmdid = WMI_10X_PDEV_SUSPEND_CMDID,
+       .pdev_resume_cmdid = WMI_10X_PDEV_RESUME_CMDID,
+       .add_bcn_filter_cmdid = WMI_10X_ADD_BCN_FILTER_CMDID,
+       .rmv_bcn_filter_cmdid = WMI_10X_RMV_BCN_FILTER_CMDID,
+       .wow_add_wake_pattern_cmdid = WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID,
+       .wow_del_wake_pattern_cmdid = WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID,
+       .wow_enable_disable_wake_event_cmdid =
+                               WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       .wow_enable_cmdid = WMI_10X_WOW_ENABLE_CMDID,
+       .wow_hostwakeup_from_sleep_cmdid =
+                               WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+       .rtt_measreq_cmdid = WMI_10X_RTT_MEASREQ_CMDID,
+       .rtt_tsf_cmdid = WMI_10X_RTT_TSF_CMDID,
+       .vdev_spectral_scan_configure_cmdid =
+                               WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+       .vdev_spectral_scan_enable_cmdid =
+                               WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+       .request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID,
+       .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
+       .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
+       .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
+       .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
+       .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
+       .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
+       .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
+       .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
+       .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
+       .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
+       .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
+       .echo_cmdid = WMI_10X_ECHO_CMDID,
+       .pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID,
+       .dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID,
+       .pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID,
+       .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
+       .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+       .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
+       .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
+       .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
+       .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
+};
+
+/* MAIN WMI VDEV param map */
+static struct wmi_vdev_param_map wmi_vdev_param_map = {
+       .rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD,
+       .fragmentation_threshold = WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       .beacon_interval = WMI_VDEV_PARAM_BEACON_INTERVAL,
+       .listen_interval = WMI_VDEV_PARAM_LISTEN_INTERVAL,
+       .multicast_rate = WMI_VDEV_PARAM_MULTICAST_RATE,
+       .mgmt_tx_rate = WMI_VDEV_PARAM_MGMT_TX_RATE,
+       .slot_time = WMI_VDEV_PARAM_SLOT_TIME,
+       .preamble = WMI_VDEV_PARAM_PREAMBLE,
+       .swba_time = WMI_VDEV_PARAM_SWBA_TIME,
+       .wmi_vdev_stats_update_period = WMI_VDEV_STATS_UPDATE_PERIOD,
+       .wmi_vdev_pwrsave_ageout_time = WMI_VDEV_PWRSAVE_AGEOUT_TIME,
+       .wmi_vdev_host_swba_interval = WMI_VDEV_HOST_SWBA_INTERVAL,
+       .dtim_period = WMI_VDEV_PARAM_DTIM_PERIOD,
+       .wmi_vdev_oc_scheduler_air_time_limit =
+                                       WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       .wds = WMI_VDEV_PARAM_WDS,
+       .atim_window = WMI_VDEV_PARAM_ATIM_WINDOW,
+       .bmiss_count_max = WMI_VDEV_PARAM_BMISS_COUNT_MAX,
+       .bmiss_first_bcnt = WMI_VDEV_PARAM_BMISS_FIRST_BCNT,
+       .bmiss_final_bcnt = WMI_VDEV_PARAM_BMISS_FINAL_BCNT,
+       .feature_wmm = WMI_VDEV_PARAM_FEATURE_WMM,
+       .chwidth = WMI_VDEV_PARAM_CHWIDTH,
+       .chextoffset = WMI_VDEV_PARAM_CHEXTOFFSET,
+       .disable_htprotection = WMI_VDEV_PARAM_DISABLE_HTPROTECTION,
+       .sta_quickkickout = WMI_VDEV_PARAM_STA_QUICKKICKOUT,
+       .mgmt_rate = WMI_VDEV_PARAM_MGMT_RATE,
+       .protection_mode = WMI_VDEV_PARAM_PROTECTION_MODE,
+       .fixed_rate = WMI_VDEV_PARAM_FIXED_RATE,
+       .sgi = WMI_VDEV_PARAM_SGI,
+       .ldpc = WMI_VDEV_PARAM_LDPC,
+       .tx_stbc = WMI_VDEV_PARAM_TX_STBC,
+       .rx_stbc = WMI_VDEV_PARAM_RX_STBC,
+       .intra_bss_fwd = WMI_VDEV_PARAM_INTRA_BSS_FWD,
+       .def_keyid = WMI_VDEV_PARAM_DEF_KEYID,
+       .nss = WMI_VDEV_PARAM_NSS,
+       .bcast_data_rate = WMI_VDEV_PARAM_BCAST_DATA_RATE,
+       .mcast_data_rate = WMI_VDEV_PARAM_MCAST_DATA_RATE,
+       .mcast_indicate = WMI_VDEV_PARAM_MCAST_INDICATE,
+       .dhcp_indicate = WMI_VDEV_PARAM_DHCP_INDICATE,
+       .unknown_dest_indicate = WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+       .ap_keepalive_min_idle_inactive_time_secs =
+                       WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_idle_inactive_time_secs =
+                       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_unresponsive_time_secs =
+                       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+       .ap_enable_nawds = WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
+       .mcast2ucast_set = WMI_VDEV_PARAM_UNSUPPORTED,
+       .enable_rtscts = WMI_VDEV_PARAM_ENABLE_RTSCTS,
+       .txbf = WMI_VDEV_PARAM_TXBF,
+       .packet_powersave = WMI_VDEV_PARAM_PACKET_POWERSAVE,
+       .drop_unencry = WMI_VDEV_PARAM_DROP_UNENCRY,
+       .tx_encap_type = WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+       .ap_detect_out_of_sync_sleeping_sta_time_secs =
+                                       WMI_VDEV_PARAM_UNSUPPORTED,
+};
+
+/* 10.X WMI VDEV param map */
+static struct wmi_vdev_param_map wmi_10x_vdev_param_map = {
+       .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD,
+       .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+       .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+       .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+       .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+       .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME,
+       .preamble = WMI_10X_VDEV_PARAM_PREAMBLE,
+       .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME,
+       .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+       .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+       .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+       .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+       .wmi_vdev_oc_scheduler_air_time_limit =
+                               WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       .wds = WMI_10X_VDEV_PARAM_WDS,
+       .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+       .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+       .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+       .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
+       .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM,
+       .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH,
+       .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+       .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+       .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+       .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE,
+       .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+       .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE,
+       .sgi = WMI_10X_VDEV_PARAM_SGI,
+       .ldpc = WMI_10X_VDEV_PARAM_LDPC,
+       .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC,
+       .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC,
+       .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+       .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID,
+       .nss = WMI_10X_VDEV_PARAM_NSS,
+       .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+       .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+       .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+       .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+       .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+       .ap_keepalive_min_idle_inactive_time_secs =
+               WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_idle_inactive_time_secs =
+               WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+       .ap_keepalive_max_unresponsive_time_secs =
+               WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+       .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+       .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+       .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+       .txbf = WMI_VDEV_PARAM_UNSUPPORTED,
+       .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
+       .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
+       .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
+       .ap_detect_out_of_sync_sleeping_sta_time_secs =
+               WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
+static struct wmi_pdev_param_map wmi_pdev_param_map = {
+       .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK,
+       .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK,
+       .txpower_limit2g = WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+       .txpower_limit5g = WMI_PDEV_PARAM_TXPOWER_LIMIT5G,
+       .txpower_scale = WMI_PDEV_PARAM_TXPOWER_SCALE,
+       .beacon_gen_mode = WMI_PDEV_PARAM_BEACON_GEN_MODE,
+       .beacon_tx_mode = WMI_PDEV_PARAM_BEACON_TX_MODE,
+       .resmgr_offchan_mode = WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       .protection_mode = WMI_PDEV_PARAM_PROTECTION_MODE,
+       .dynamic_bw = WMI_PDEV_PARAM_DYNAMIC_BW,
+       .non_agg_sw_retry_th = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       .agg_sw_retry_th = WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
+       .sta_kickout_th = WMI_PDEV_PARAM_STA_KICKOUT_TH,
+       .ac_aggrsize_scaling = WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       .ltr_enable = WMI_PDEV_PARAM_LTR_ENABLE,
+       .ltr_ac_latency_be = WMI_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       .ltr_ac_latency_bk = WMI_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       .ltr_ac_latency_vi = WMI_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       .ltr_ac_latency_vo = WMI_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       .ltr_ac_latency_timeout = WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       .ltr_sleep_override = WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       .ltr_rx_override = WMI_PDEV_PARAM_LTR_RX_OVERRIDE,
+       .ltr_tx_activity_timeout = WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       .l1ss_enable = WMI_PDEV_PARAM_L1SS_ENABLE,
+       .dsleep_enable = WMI_PDEV_PARAM_DSLEEP_ENABLE,
+       .pcielp_txbuf_flush = WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+       .pcielp_txbuf_watermark = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+       .pdev_stats_update_period = WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       .vdev_stats_update_period = WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       .peer_stats_update_period = WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       .pmf_qos = WMI_PDEV_PARAM_PMF_QOS,
+       .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+       .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
+       .dcs = WMI_PDEV_PARAM_DCS,
+       .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE,
+       .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD,
+       .ani_listen_period = WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       .ani_ofdm_level = WMI_PDEV_PARAM_ANI_OFDM_LEVEL,
+       .ani_cck_level = WMI_PDEV_PARAM_ANI_CCK_LEVEL,
+       .dyntxchain = WMI_PDEV_PARAM_DYNTXCHAIN,
+       .proxy_sta = WMI_PDEV_PARAM_PROXY_STA,
+       .idle_ps_config = WMI_PDEV_PARAM_IDLE_PS_CONFIG,
+       .power_gating_sleep = WMI_PDEV_PARAM_POWER_GATING_SLEEP,
+       .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED,
+       .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED,
+       .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED,
+};
+
+static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+       .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK,
+       .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+       .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+       .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+       .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+       .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+       .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+       .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+       .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+       .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+       .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+       .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE,
+       .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+       .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+       .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+       .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED,
+       .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       .bcnflt_stats_update_period =
+                               WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
+       .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
+       .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+       .dcs = WMI_10X_PDEV_PARAM_DCS,
+       .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
+       .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+       .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+       .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+       .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+       .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED,
+       .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED,
+       .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED,
+       .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+       .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
+       .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
+};
 
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
 {
@@ -85,18 +526,14 @@ static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
 static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
        dev_kfree_skb(skb);
-
-       if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0)
-               wake_up(&ar->wmi.wq);
 }
 
-/* WMI command API */
-static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
-                              enum wmi_cmd_id cmd_id)
+static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
+                                     u32 cmd_id)
 {
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
        struct wmi_cmd_hdr *cmd_hdr;
-       int status;
+       int ret;
        u32 cmd = 0;
 
        if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
@@ -107,25 +544,146 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
        cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
        cmd_hdr->cmd_id = __cpu_to_le32(cmd);
 
-       if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
-           WMI_MAX_PENDING_TX_COUNT) {
-               /* avoid using up memory when FW hangs */
-               atomic_dec(&ar->wmi.pending_tx_count);
-               return -EBUSY;
+       memset(skb_cb, 0, sizeof(*skb_cb));
+       ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
+       trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret);
+
+       if (ret)
+               goto err_pull;
+
+       return 0;
+
+err_pull:
+       skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+       return ret;
+}
+
+static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
+{
+       struct wmi_bcn_tx_arg arg = {0};
+       int ret;
+
+       lockdep_assert_held(&arvif->ar->data_lock);
+
+       if (arvif->beacon == NULL)
+               return;
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.tx_rate = 0;
+       arg.tx_power = 0;
+       arg.bcn = arvif->beacon->data;
+       arg.bcn_len = arvif->beacon->len;
+
+       ret = ath10k_wmi_beacon_send_nowait(arvif->ar, &arg);
+       if (ret)
+               return;
+
+       dev_kfree_skb_any(arvif->beacon);
+       arvif->beacon = NULL;
+}
+
+static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       ath10k_wmi_tx_beacon_nowait(arvif);
+}
+
+static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar)
+{
+       spin_lock_bh(&ar->data_lock);
+       ieee80211_iterate_active_interfaces_atomic(ar->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  ath10k_wmi_tx_beacons_iter,
+                                                  NULL);
+       spin_unlock_bh(&ar->data_lock);
+}
+
+static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
+{
+       /* try to send pending beacons first. they take priority */
+       ath10k_wmi_tx_beacons_nowait(ar);
+
+       wake_up(&ar->wmi.tx_credits_wq);
+}
+
+static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
+                              u32 cmd_id)
+{
+       int ret = -EOPNOTSUPP;
+
+       might_sleep();
+
+       if (cmd_id == WMI_CMD_UNSUPPORTED) {
+               ath10k_warn("wmi command %d is not supported by firmware\n",
+                           cmd_id);
+               return ret;
        }
 
-       memset(skb_cb, 0, sizeof(*skb_cb));
+       wait_event_timeout(ar->wmi.tx_credits_wq, ({
+               /* try to send pending beacons first. they take priority */
+               ath10k_wmi_tx_beacons_nowait(ar);
 
-       trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
+               ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
+               (ret != -EAGAIN);
+       }), 3*HZ);
 
-       status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
-       if (status) {
+       if (ret)
                dev_kfree_skb_any(skb);
-               atomic_dec(&ar->wmi.pending_tx_count);
-               return status;
+
+       return ret;
+}
+
+int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
+{
+       int ret = 0;
+       struct wmi_mgmt_tx_cmd *cmd;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *wmi_skb;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       int len;
+       u16 fc;
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = le16_to_cpu(hdr->frame_control);
+
+       if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
+               return -EINVAL;
+
+       len = sizeof(cmd->hdr) + skb->len;
+       len = round_up(len, 4);
+
+       wmi_skb = ath10k_wmi_alloc_skb(len);
+       if (!wmi_skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
+
+       cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
+       cmd->hdr.tx_rate = 0;
+       cmd->hdr.tx_power = 0;
+       cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len));
+
+       memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
+       memcpy(cmd->buf, skb->data, skb->len);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
+                  wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
+                  fc & IEEE80211_FCTL_STYPE);
+
+       /* Send the management frame buffer to the target */
+       ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
        }
 
-       return 0;
+       /* TODO: report tx status to mac80211 - temporary just ACK */
+       info->flags |= IEEE80211_TX_STAT_ACK;
+       ieee80211_tx_status_irqsafe(ar->hw, skb);
+
+       return ret;
 }
 
 static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
@@ -315,7 +873,9 @@ static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band)
 
 static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 {
-       struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data;
+       struct wmi_mgmt_rx_event_v1 *ev_v1;
+       struct wmi_mgmt_rx_event_v2 *ev_v2;
+       struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr;
        u32 rx_status;
@@ -325,13 +885,24 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        u32 rate;
        u32 buf_len;
        u16 fc;
+       int pull_len;
+
+       if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
+               ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
+               ev_hdr = &ev_v2->hdr.v1;
+               pull_len = sizeof(*ev_v2);
+       } else {
+               ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data;
+               ev_hdr = &ev_v1->hdr;
+               pull_len = sizeof(*ev_v1);
+       }
 
-       channel   = __le32_to_cpu(event->hdr.channel);
-       buf_len   = __le32_to_cpu(event->hdr.buf_len);
-       rx_status = __le32_to_cpu(event->hdr.status);
-       snr       = __le32_to_cpu(event->hdr.snr);
-       phy_mode  = __le32_to_cpu(event->hdr.phy_mode);
-       rate      = __le32_to_cpu(event->hdr.rate);
+       channel   = __le32_to_cpu(ev_hdr->channel);
+       buf_len   = __le32_to_cpu(ev_hdr->buf_len);
+       rx_status = __le32_to_cpu(ev_hdr->status);
+       snr       = __le32_to_cpu(ev_hdr->snr);
+       phy_mode  = __le32_to_cpu(ev_hdr->phy_mode);
+       rate      = __le32_to_cpu(ev_hdr->rate);
 
        memset(status, 0, sizeof(*status));
 
@@ -358,7 +929,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
        status->rate_idx = get_rate_idx(rate, status->band);
 
-       skb_pull(skb, sizeof(event->hdr));
+       skb_pull(skb, pull_len);
 
        hdr = (struct ieee80211_hdr *)skb->data;
        fc = le16_to_cpu(hdr->frame_control);
@@ -734,10 +1305,8 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
        int i = -1;
        struct wmi_bcn_info *bcn_info;
        struct ath10k_vif *arvif;
-       struct wmi_bcn_tx_arg arg;
        struct sk_buff *bcn;
        int vdev_id = 0;
-       int ret;
 
        ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
 
@@ -794,17 +1363,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
                ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
 
-               arg.vdev_id = arvif->vdev_id;
-               arg.tx_rate = 0;
-               arg.tx_power = 0;
-               arg.bcn = bcn->data;
-               arg.bcn_len = bcn->len;
+               spin_lock_bh(&ar->data_lock);
+               if (arvif->beacon) {
+                       ath10k_warn("SWBA overrun on vdev %d\n",
+                                   arvif->vdev_id);
+                       dev_kfree_skb_any(arvif->beacon);
+               }
 
-               ret = ath10k_wmi_beacon_send(ar, &arg);
-               if (ret)
-                       ath10k_warn("could not send beacon (%d)\n", ret);
+               arvif->beacon = bcn;
 
-               dev_kfree_skb_any(bcn);
+               ath10k_wmi_tx_beacon_nowait(arvif);
+               spin_unlock_bh(&ar->data_lock);
        }
 }
 
@@ -919,6 +1488,55 @@ static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
        ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
 }
 
+static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
+}
+
+static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+                                     u32 num_units, u32 unit_len)
+{
+       dma_addr_t paddr;
+       u32 pool_size;
+       int idx = ar->wmi.num_mem_chunks;
+
+       pool_size = num_units * round_up(unit_len, 4);
+
+       if (!pool_size)
+               return -EINVAL;
+
+       ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev,
+                                                          pool_size,
+                                                          &paddr,
+                                                          GFP_ATOMIC);
+       if (!ar->wmi.mem_chunks[idx].vaddr) {
+               ath10k_warn("failed to allocate memory chunk\n");
+               return -ENOMEM;
+       }
+
+       memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size);
+
+       ar->wmi.mem_chunks[idx].paddr = paddr;
+       ar->wmi.mem_chunks[idx].len = pool_size;
+       ar->wmi.mem_chunks[idx].req_id = req_id;
+       ar->wmi.num_mem_chunks++;
+
+       return 0;
+}
+
 static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
                                              struct sk_buff *skb)
 {
@@ -943,6 +1561,10 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
        ar->phy_capability = __le32_to_cpu(ev->phy_capability);
        ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
 
+       /* only manually set fw features when not using FW IE format */
+       if (ar->fw_api == 1 && ar->fw_version_build > 636)
+               set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features);
+
        if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
                ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
                            ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
@@ -987,6 +1609,108 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
        complete(&ar->wmi.service_ready);
 }
 
+static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
+                                                 struct sk_buff *skb)
+{
+       u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
+       int ret;
+       struct wmi_service_ready_event_10x *ev = (void *)skb->data;
+
+       if (skb->len < sizeof(*ev)) {
+               ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+                           skb->len, sizeof(*ev));
+               return;
+       }
+
+       ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+       ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+       ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+       ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+       ar->fw_version_major =
+               (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+       ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+       ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+       ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+
+       if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+               ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
+                           ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+               ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+       }
+
+       ar->ath_common.regulatory.current_rd =
+               __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+
+       ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+                                     sizeof(ev->wmi_service_bitmap));
+
+       if (strlen(ar->hw->wiphy->fw_version) == 0) {
+               snprintf(ar->hw->wiphy->fw_version,
+                        sizeof(ar->hw->wiphy->fw_version),
+                        "%u.%u",
+                        ar->fw_version_major,
+                        ar->fw_version_minor);
+       }
+
+       num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs);
+
+       if (num_mem_reqs > ATH10K_MAX_MEM_REQS) {
+               ath10k_warn("requested memory chunks number (%d) exceeds the limit\n",
+                           num_mem_reqs);
+               return;
+       }
+
+       if (!num_mem_reqs)
+               goto exit;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n",
+                  num_mem_reqs);
+
+       for (i = 0; i < num_mem_reqs; ++i) {
+               req_id = __le32_to_cpu(ev->mem_reqs[i].req_id);
+               num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
+               unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size);
+               num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info);
+
+               if (num_unit_info & NUM_UNITS_IS_NUM_PEERS)
+                       /* number of units to allocate is number of
+                        * peers, 1 extra for self peer on target */
+                       /* this needs to be tied, host and target
+                        * can get out of sync */
+                       num_units = TARGET_10X_NUM_PEERS + 1;
+               else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS)
+                       num_units = TARGET_10X_NUM_VDEVS + 1;
+
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n",
+                          req_id,
+                          __le32_to_cpu(ev->mem_reqs[i].num_units),
+                          num_unit_info,
+                          unit_size,
+                          num_units);
+
+               ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units,
+                                               unit_size);
+               if (ret)
+                       return;
+       }
+
+exit:
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->abi_version),
+                  __le32_to_cpu(ev->phy_capability),
+                  __le32_to_cpu(ev->ht_cap_info),
+                  __le32_to_cpu(ev->vht_cap_info),
+                  __le32_to_cpu(ev->vht_supp_mcs),
+                  __le32_to_cpu(ev->sys_cap_info),
+                  __le32_to_cpu(ev->num_mem_reqs),
+                  __le32_to_cpu(ev->num_rf_chains));
+
+       complete(&ar->wmi.service_ready);
+}
+
 static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
@@ -1007,7 +1731,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
        return 0;
 }
 
-static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
+static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_event_id id;
@@ -1126,64 +1850,158 @@ static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
        dev_kfree_skb(skb);
 }
 
-static void ath10k_wmi_event_work(struct work_struct *work)
+static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
-       struct ath10k *ar = container_of(work, struct ath10k,
-                                        wmi.wmi_event_work);
-       struct sk_buff *skb;
+       struct wmi_cmd_hdr *cmd_hdr;
+       enum wmi_10x_event_id id;
+       u16 len;
 
-       for (;;) {
-               skb = skb_dequeue(&ar->wmi.wmi_event_list);
-               if (!skb)
-                       break;
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
 
-               ath10k_wmi_event_process(ar, skb);
-       }
-}
+       if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return;
 
-static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
-{
-       struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
-       enum wmi_event_id event_id;
+       len = skb->len;
 
-       event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+       trace_ath10k_wmi_event(id, skb->data, skb->len);
 
-       /* some events require to be handled ASAP
-        * thus can't be defered to a worker thread */
-       switch (event_id) {
-       case WMI_HOST_SWBA_EVENTID:
-       case WMI_MGMT_RX_EVENTID:
-               ath10k_wmi_event_process(ar, skb);
+       switch (id) {
+       case WMI_10X_MGMT_RX_EVENTID:
+               ath10k_wmi_event_mgmt_rx(ar, skb);
+               /* mgmt_rx() owns the skb now! */
                return;
+       case WMI_10X_SCAN_EVENTID:
+               ath10k_wmi_event_scan(ar, skb);
+               break;
+       case WMI_10X_CHAN_INFO_EVENTID:
+               ath10k_wmi_event_chan_info(ar, skb);
+               break;
+       case WMI_10X_ECHO_EVENTID:
+               ath10k_wmi_event_echo(ar, skb);
+               break;
+       case WMI_10X_DEBUG_MESG_EVENTID:
+               ath10k_wmi_event_debug_mesg(ar, skb);
+               break;
+       case WMI_10X_UPDATE_STATS_EVENTID:
+               ath10k_wmi_event_update_stats(ar, skb);
+               break;
+       case WMI_10X_VDEV_START_RESP_EVENTID:
+               ath10k_wmi_event_vdev_start_resp(ar, skb);
+               break;
+       case WMI_10X_VDEV_STOPPED_EVENTID:
+               ath10k_wmi_event_vdev_stopped(ar, skb);
+               break;
+       case WMI_10X_PEER_STA_KICKOUT_EVENTID:
+               ath10k_wmi_event_peer_sta_kickout(ar, skb);
+               break;
+       case WMI_10X_HOST_SWBA_EVENTID:
+               ath10k_wmi_event_host_swba(ar, skb);
+               break;
+       case WMI_10X_TBTTOFFSET_UPDATE_EVENTID:
+               ath10k_wmi_event_tbttoffset_update(ar, skb);
+               break;
+       case WMI_10X_PHYERR_EVENTID:
+               ath10k_wmi_event_phyerr(ar, skb);
+               break;
+       case WMI_10X_ROAM_EVENTID:
+               ath10k_wmi_event_roam(ar, skb);
+               break;
+       case WMI_10X_PROFILE_MATCH:
+               ath10k_wmi_event_profile_match(ar, skb);
+               break;
+       case WMI_10X_DEBUG_PRINT_EVENTID:
+               ath10k_wmi_event_debug_print(ar, skb);
+               break;
+       case WMI_10X_PDEV_QVIT_EVENTID:
+               ath10k_wmi_event_pdev_qvit(ar, skb);
+               break;
+       case WMI_10X_WLAN_PROFILE_DATA_EVENTID:
+               ath10k_wmi_event_wlan_profile_data(ar, skb);
+               break;
+       case WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_measurement_report(ar, skb);
+               break;
+       case WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_tsf_measurement_report(ar, skb);
+               break;
+       case WMI_10X_RTT_ERROR_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_error_report(ar, skb);
+               break;
+       case WMI_10X_WOW_WAKEUP_HOST_EVENTID:
+               ath10k_wmi_event_wow_wakeup_host(ar, skb);
+               break;
+       case WMI_10X_DCS_INTERFERENCE_EVENTID:
+               ath10k_wmi_event_dcs_interference(ar, skb);
+               break;
+       case WMI_10X_PDEV_TPC_CONFIG_EVENTID:
+               ath10k_wmi_event_pdev_tpc_config(ar, skb);
+               break;
+       case WMI_10X_INST_RSSI_STATS_EVENTID:
+               ath10k_wmi_event_inst_rssi_stats(ar, skb);
+               break;
+       case WMI_10X_VDEV_STANDBY_REQ_EVENTID:
+               ath10k_wmi_event_vdev_standby_req(ar, skb);
+               break;
+       case WMI_10X_VDEV_RESUME_REQ_EVENTID:
+               ath10k_wmi_event_vdev_resume_req(ar, skb);
+               break;
+       case WMI_10X_SERVICE_READY_EVENTID:
+               ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+               break;
+       case WMI_10X_READY_EVENTID:
+               ath10k_wmi_ready_event_rx(ar, skb);
+               break;
        default:
+               ath10k_warn("Unknown eventid: %d\n", id);
                break;
        }
 
-       skb_queue_tail(&ar->wmi.wmi_event_list, skb);
-       queue_work(ar->workqueue, &ar->wmi.wmi_event_work);
+       dev_kfree_skb(skb);
+}
+
+
+static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               ath10k_wmi_10x_process_rx(ar, skb);
+       else
+               ath10k_wmi_main_process_rx(ar, skb);
 }
 
 /* WMI Initialization functions */
 int ath10k_wmi_attach(struct ath10k *ar)
 {
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               ar->wmi.cmd = &wmi_10x_cmd_map;
+               ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+               ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+       } else {
+               ar->wmi.cmd = &wmi_cmd_map;
+               ar->wmi.vdev_param = &wmi_vdev_param_map;
+               ar->wmi.pdev_param = &wmi_pdev_param_map;
+       }
+
        init_completion(&ar->wmi.service_ready);
        init_completion(&ar->wmi.unified_ready);
-       init_waitqueue_head(&ar->wmi.wq);
-
-       skb_queue_head_init(&ar->wmi.wmi_event_list);
-       INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
+       init_waitqueue_head(&ar->wmi.tx_credits_wq);
 
        return 0;
 }
 
 void ath10k_wmi_detach(struct ath10k *ar)
 {
-       /* HTC should've drained the packets already */
-       if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0))
-               ath10k_warn("there are still pending packets\n");
+       int i;
+
+       /* free the host memory chunks requested by firmware */
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+               dma_free_coherent(ar->dev,
+                                 ar->wmi.mem_chunks[i].len,
+                                 ar->wmi.mem_chunks[i].vaddr,
+                                 ar->wmi.mem_chunks[i].paddr);
+       }
 
-       cancel_work_sync(&ar->wmi.wmi_event_work);
-       skb_queue_purge(&ar->wmi.wmi_event_list);
+       ar->wmi.num_mem_chunks = 0;
 }
 
 int ath10k_wmi_connect_htc_service(struct ath10k *ar)
@@ -1198,6 +2016,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
        /* these fields are the same for all service endpoints */
        conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
        conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
+       conn_req.ep_ops.ep_tx_credits = ath10k_wmi_op_ep_tx_credits;
 
        /* connect to control service */
        conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
@@ -1234,7 +2053,8 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                   "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
                   rd, rd2g, rd5g, ctl2g, ctl5g);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_regdomain_cmdid);
 }
 
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
@@ -1264,7 +2084,8 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                   "wmi set channel mode %d freq %d\n",
                   arg->mode, arg->freq);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_channel_cmdid);
 }
 
 int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
@@ -1279,7 +2100,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
        cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
        cmd->suspend_opt = WMI_PDEV_SUSPEND;
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
 }
 
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
@@ -1290,15 +2111,19 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
        if (skb == NULL)
                return -ENOMEM;
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
 }
 
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
-                             u32 value)
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
 {
        struct wmi_pdev_set_param_cmd *cmd;
        struct sk_buff *skb;
 
+       if (id == WMI_PDEV_PARAM_UNSUPPORTED) {
+               ath10k_warn("pdev param %d not supported by firmware\n", id);
+               return -EOPNOTSUPP;
+       }
+
        skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
        if (!skb)
                return -ENOMEM;
@@ -1309,15 +2134,16 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
                   id, value);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
 }
 
-int ath10k_wmi_cmd_init(struct ath10k *ar)
+static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
 {
        struct wmi_init_cmd *cmd;
        struct sk_buff *buf;
        struct wmi_resource_config config = {};
-       u32 val;
+       u32 len, val;
+       int i;
 
        config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
        config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
@@ -1370,23 +2196,158 @@ int ath10k_wmi_cmd_init(struct ath10k *ar)
        config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
        config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
 
-       buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       len = sizeof(*cmd) +
+             (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+       buf = ath10k_wmi_alloc_skb(len);
        if (!buf)
                return -ENOMEM;
 
        cmd = (struct wmi_init_cmd *)buf->data;
-       cmd->num_host_mem_chunks = 0;
+
+       if (ar->wmi.num_mem_chunks == 0) {
+               cmd->num_host_mem_chunks = 0;
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+                  __cpu_to_le32(ar->wmi.num_mem_chunks));
+
+       cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+               cmd->host_mem_chunks[i].ptr =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+               cmd->host_mem_chunks[i].size =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+               cmd->host_mem_chunks[i].req_id =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "wmi chunk %d len %d requested, addr 0x%x\n",
+                          i,
+                          cmd->host_mem_chunks[i].size,
+                          cmd->host_mem_chunks[i].ptr);
+       }
+out:
        memcpy(&cmd->resource_config, &config, sizeof(config));
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
-       return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID);
+       return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
 }
 
-static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg)
+static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
+{
+       struct wmi_init_cmd_10x *cmd;
+       struct sk_buff *buf;
+       struct wmi_resource_config_10x config = {};
+       u32 len, val;
+       int i;
+
+       config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
+       config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
+       config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS);
+       config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS);
+       config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT);
+       config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK);
+       config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK);
+       config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI);
+       config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE);
+
+       config.scan_max_pending_reqs =
+               __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS);
+
+       config.bmiss_offload_max_vdev =
+               __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_vdev =
+               __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_ap_profiles =
+               __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES);
+
+       config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS);
+       config.num_mcast_table_elems =
+               __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS);
+
+       config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE);
+       config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE);
+       config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES);
+       config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE);
+       config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM);
+
+       val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
+       config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
+
+       config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG);
+
+       config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC);
+       config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES);
+
+       len = sizeof(*cmd) +
+             (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+       buf = ath10k_wmi_alloc_skb(len);
+       if (!buf)
+               return -ENOMEM;
+
+       cmd = (struct wmi_init_cmd_10x *)buf->data;
+
+       if (ar->wmi.num_mem_chunks == 0) {
+               cmd->num_host_mem_chunks = 0;
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+                  __cpu_to_le32(ar->wmi.num_mem_chunks));
+
+       cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+               cmd->host_mem_chunks[i].ptr =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+               cmd->host_mem_chunks[i].size =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+               cmd->host_mem_chunks[i].req_id =
+                       __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "wmi chunk %d len %d requested, addr 0x%x\n",
+                          i,
+                          cmd->host_mem_chunks[i].size,
+                          cmd->host_mem_chunks[i].ptr);
+       }
+out:
+       memcpy(&cmd->resource_config, &config, sizeof(config));
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n");
+       return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
+}
+
+int ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+       int ret;
+
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               ret = ath10k_wmi_10x_cmd_init(ar);
+       else
+               ret = ath10k_wmi_main_cmd_init(ar);
+
+       return ret;
+}
+
+static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
+                                         const struct wmi_start_scan_arg *arg)
 {
        int len;
 
-       len = sizeof(struct wmi_start_scan_cmd);
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               len = sizeof(struct wmi_start_scan_cmd_10x);
+       else
+               len = sizeof(struct wmi_start_scan_cmd);
 
        if (arg->ie_len) {
                if (!arg->ie)
@@ -1446,7 +2407,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
        int len = 0;
        int i;
 
-       len = ath10k_wmi_start_scan_calc_len(arg);
+       len = ath10k_wmi_start_scan_calc_len(ar, arg);
        if (len < 0)
                return len; /* len contains error code here */
 
@@ -1478,7 +2439,14 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
        cmd->scan_ctrl_flags    = __cpu_to_le32(arg->scan_ctrl_flags);
 
        /* TLV list starts after fields included in the struct */
-       off = sizeof(*cmd);
+       /* There's just one filed that differes the two start_scan
+        * structures - burst_duration, which we are not using btw,
+          no point to make the split here, just shift the buffer to fit with
+          given FW */
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               off = sizeof(struct wmi_start_scan_cmd_10x);
+       else
+               off = sizeof(struct wmi_start_scan_cmd);
 
        if (arg->n_channels) {
                channels = (void *)skb->data + off;
@@ -1540,7 +2508,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar,
        }
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
-       return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
 }
 
 void ath10k_wmi_start_scan_init(struct ath10k *ar,
@@ -1556,7 +2524,7 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar,
        arg->repeat_probe_time = 0;
        arg->probe_spacing_time = 0;
        arg->idle_time = 0;
-       arg->max_scan_time = 5000;
+       arg->max_scan_time = 20000;
        arg->probe_delay = 5;
        arg->notify_scan_events = WMI_SCAN_EVENT_STARTED
                | WMI_SCAN_EVENT_COMPLETED
@@ -1600,7 +2568,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
                   arg->req_id, arg->req_type, arg->u.scan_id);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
 }
 
 int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
@@ -1625,7 +2593,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
                   "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
                   vdev_id, type, subtype, macaddr);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
 }
 
 int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
@@ -1643,20 +2611,20 @@ int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
        ath10k_dbg(ATH10K_DBG_WMI,
                   "WMI vdev delete id %d\n", vdev_id);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
 }
 
 static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
                                const struct wmi_vdev_start_request_arg *arg,
-                               enum wmi_cmd_id cmd_id)
+                               u32 cmd_id)
 {
        struct wmi_vdev_start_request_cmd *cmd;
        struct sk_buff *skb;
        const char *cmdname;
        u32 flags = 0;
 
-       if (cmd_id != WMI_VDEV_START_REQUEST_CMDID &&
-           cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID)
+       if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
+           cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
                return -EINVAL;
        if (WARN_ON(arg->ssid && arg->ssid_len == 0))
                return -EINVAL;
@@ -1665,9 +2633,9 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
        if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
                return -EINVAL;
 
-       if (cmd_id == WMI_VDEV_START_REQUEST_CMDID)
+       if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid)
                cmdname = "start";
-       else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID)
+       else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid)
                cmdname = "restart";
        else
                return -EINVAL; /* should not happen, we already check cmd_id */
@@ -1718,15 +2686,17 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 int ath10k_wmi_vdev_start(struct ath10k *ar,
                          const struct wmi_vdev_start_request_arg *arg)
 {
-       return ath10k_wmi_vdev_start_restart(ar, arg,
-                                            WMI_VDEV_START_REQUEST_CMDID);
+       u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid;
+
+       return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 }
 
 int ath10k_wmi_vdev_restart(struct ath10k *ar,
                     const struct wmi_vdev_start_request_arg *arg)
 {
-       return ath10k_wmi_vdev_start_restart(ar, arg,
-                                            WMI_VDEV_RESTART_REQUEST_CMDID);
+       u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid;
+
+       return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 }
 
 int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
@@ -1743,7 +2713,7 @@ int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
 }
 
 int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
@@ -1758,13 +2728,13 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
        cmd = (struct wmi_vdev_up_cmd *)skb->data;
        cmd->vdev_id       = __cpu_to_le32(vdev_id);
        cmd->vdev_assoc_id = __cpu_to_le32(aid);
-       memcpy(&cmd->vdev_bssid.addr, bssid, 6);
+       memcpy(&cmd->vdev_bssid.addr, bssid, ETH_ALEN);
 
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
                   vdev_id, aid, bssid);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
 }
 
 int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
@@ -1782,15 +2752,22 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi mgmt vdev down id 0x%x\n", vdev_id);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
 }
 
 int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-                             enum wmi_vdev_param param_id, u32 param_value)
+                             u32 param_id, u32 param_value)
 {
        struct wmi_vdev_set_param_cmd *cmd;
        struct sk_buff *skb;
 
+       if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) {
+               ath10k_dbg(ATH10K_DBG_WMI,
+                          "vdev param %d not supported by firmware\n",
+                           param_id);
+               return -EOPNOTSUPP;
+       }
+
        skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
        if (!skb)
                return -ENOMEM;
@@ -1804,7 +2781,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
                   "wmi vdev id 0x%x set param %d value %d\n",
                   vdev_id, param_id, param_value);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
 }
 
 int ath10k_wmi_vdev_install_key(struct ath10k *ar,
@@ -1839,7 +2816,8 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi vdev install key idx %d cipher %d len %d\n",
                   arg->key_idx, arg->key_cipher, arg->key_len);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->vdev_install_key_cmdid);
 }
 
 int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
@@ -1859,7 +2837,7 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer create vdev_id %d peer_addr %pM\n",
                   vdev_id, peer_addr);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
 }
 
 int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
@@ -1879,7 +2857,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer delete vdev_id %d peer_addr %pM\n",
                   vdev_id, peer_addr);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
 }
 
 int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
@@ -1900,7 +2878,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
                   vdev_id, peer_addr, tid_bitmap);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
 }
 
 int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
@@ -1918,13 +2896,13 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
        cmd->vdev_id     = __cpu_to_le32(vdev_id);
        cmd->param_id    = __cpu_to_le32(param_id);
        cmd->param_value = __cpu_to_le32(param_value);
-       memcpy(&cmd->peer_macaddr.addr, peer_addr, 6);
+       memcpy(&cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
 
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi vdev %d peer 0x%pM set param %d value %d\n",
                   vdev_id, peer_addr, param_id, param_value);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
 }
 
 int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
@@ -1945,7 +2923,8 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
                   "wmi set powersave id 0x%x mode %d\n",
                   vdev_id, psmode);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->sta_powersave_mode_cmdid);
 }
 
 int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
@@ -1967,7 +2946,8 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi sta ps param vdev_id 0x%x param %d value %d\n",
                   vdev_id, param_id, value);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->sta_powersave_param_cmdid);
 }
 
 int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
@@ -1993,7 +2973,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
                   "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
                   vdev_id, param_id, value, mac);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->ap_ps_peer_param_cmdid);
 }
 
 int ath10k_wmi_scan_chan_list(struct ath10k *ar,
@@ -2046,7 +3027,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
                ci->flags            |= __cpu_to_le32(flags);
        }
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
 }
 
 int ath10k_wmi_peer_assoc(struct ath10k *ar,
@@ -2105,10 +3086,11 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
        ath10k_dbg(ATH10K_DBG_WMI,
                   "wmi peer assoc vdev %d addr %pM\n",
                   arg->vdev_id, arg->addr);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 }
 
-int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
+int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
+                                 const struct wmi_bcn_tx_arg *arg)
 {
        struct wmi_bcn_tx_cmd *cmd;
        struct sk_buff *skb;
@@ -2124,7 +3106,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
        cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
        memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
 
-       return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID);
+       return ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid);
 }
 
 static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
@@ -2155,7 +3137,8 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
        ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
-       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_wmm_params_cmdid);
 }
 
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
@@ -2171,7 +3154,7 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
        cmd->stats_id = __cpu_to_le32(stats_id);
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
 }
 
 int ath10k_wmi_force_fw_hang(struct ath10k *ar,
@@ -2190,5 +3173,5 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar,
 
        ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
                   type, delay_ms);
-       return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
 }
index 2c5a4f8..78c991a 100644 (file)
@@ -208,6 +208,118 @@ struct wmi_mac_addr {
        (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
        } while (0)
 
+struct wmi_cmd_map {
+       u32 init_cmdid;
+       u32 start_scan_cmdid;
+       u32 stop_scan_cmdid;
+       u32 scan_chan_list_cmdid;
+       u32 scan_sch_prio_tbl_cmdid;
+       u32 pdev_set_regdomain_cmdid;
+       u32 pdev_set_channel_cmdid;
+       u32 pdev_set_param_cmdid;
+       u32 pdev_pktlog_enable_cmdid;
+       u32 pdev_pktlog_disable_cmdid;
+       u32 pdev_set_wmm_params_cmdid;
+       u32 pdev_set_ht_cap_ie_cmdid;
+       u32 pdev_set_vht_cap_ie_cmdid;
+       u32 pdev_set_dscp_tid_map_cmdid;
+       u32 pdev_set_quiet_mode_cmdid;
+       u32 pdev_green_ap_ps_enable_cmdid;
+       u32 pdev_get_tpc_config_cmdid;
+       u32 pdev_set_base_macaddr_cmdid;
+       u32 vdev_create_cmdid;
+       u32 vdev_delete_cmdid;
+       u32 vdev_start_request_cmdid;
+       u32 vdev_restart_request_cmdid;
+       u32 vdev_up_cmdid;
+       u32 vdev_stop_cmdid;
+       u32 vdev_down_cmdid;
+       u32 vdev_set_param_cmdid;
+       u32 vdev_install_key_cmdid;
+       u32 peer_create_cmdid;
+       u32 peer_delete_cmdid;
+       u32 peer_flush_tids_cmdid;
+       u32 peer_set_param_cmdid;
+       u32 peer_assoc_cmdid;
+       u32 peer_add_wds_entry_cmdid;
+       u32 peer_remove_wds_entry_cmdid;
+       u32 peer_mcast_group_cmdid;
+       u32 bcn_tx_cmdid;
+       u32 pdev_send_bcn_cmdid;
+       u32 bcn_tmpl_cmdid;
+       u32 bcn_filter_rx_cmdid;
+       u32 prb_req_filter_rx_cmdid;
+       u32 mgmt_tx_cmdid;
+       u32 prb_tmpl_cmdid;
+       u32 addba_clear_resp_cmdid;
+       u32 addba_send_cmdid;
+       u32 addba_status_cmdid;
+       u32 delba_send_cmdid;
+       u32 addba_set_resp_cmdid;
+       u32 send_singleamsdu_cmdid;
+       u32 sta_powersave_mode_cmdid;
+       u32 sta_powersave_param_cmdid;
+       u32 sta_mimo_ps_mode_cmdid;
+       u32 pdev_dfs_enable_cmdid;
+       u32 pdev_dfs_disable_cmdid;
+       u32 roam_scan_mode;
+       u32 roam_scan_rssi_threshold;
+       u32 roam_scan_period;
+       u32 roam_scan_rssi_change_threshold;
+       u32 roam_ap_profile;
+       u32 ofl_scan_add_ap_profile;
+       u32 ofl_scan_remove_ap_profile;
+       u32 ofl_scan_period;
+       u32 p2p_dev_set_device_info;
+       u32 p2p_dev_set_discoverability;
+       u32 p2p_go_set_beacon_ie;
+       u32 p2p_go_set_probe_resp_ie;
+       u32 p2p_set_vendor_ie_data_cmdid;
+       u32 ap_ps_peer_param_cmdid;
+       u32 ap_ps_peer_uapsd_coex_cmdid;
+       u32 peer_rate_retry_sched_cmdid;
+       u32 wlan_profile_trigger_cmdid;
+       u32 wlan_profile_set_hist_intvl_cmdid;
+       u32 wlan_profile_get_profile_data_cmdid;
+       u32 wlan_profile_enable_profile_id_cmdid;
+       u32 wlan_profile_list_profile_id_cmdid;
+       u32 pdev_suspend_cmdid;
+       u32 pdev_resume_cmdid;
+       u32 add_bcn_filter_cmdid;
+       u32 rmv_bcn_filter_cmdid;
+       u32 wow_add_wake_pattern_cmdid;
+       u32 wow_del_wake_pattern_cmdid;
+       u32 wow_enable_disable_wake_event_cmdid;
+       u32 wow_enable_cmdid;
+       u32 wow_hostwakeup_from_sleep_cmdid;
+       u32 rtt_measreq_cmdid;
+       u32 rtt_tsf_cmdid;
+       u32 vdev_spectral_scan_configure_cmdid;
+       u32 vdev_spectral_scan_enable_cmdid;
+       u32 request_stats_cmdid;
+       u32 set_arp_ns_offload_cmdid;
+       u32 network_list_offload_config_cmdid;
+       u32 gtk_offload_cmdid;
+       u32 csa_offload_enable_cmdid;
+       u32 csa_offload_chanswitch_cmdid;
+       u32 chatter_set_mode_cmdid;
+       u32 peer_tid_addba_cmdid;
+       u32 peer_tid_delba_cmdid;
+       u32 sta_dtim_ps_method_cmdid;
+       u32 sta_uapsd_auto_trig_cmdid;
+       u32 sta_keepalive_cmd;
+       u32 echo_cmdid;
+       u32 pdev_utf_cmdid;
+       u32 dbglog_cfg_cmdid;
+       u32 pdev_qvit_cmdid;
+       u32 pdev_ftm_intg_cmdid;
+       u32 vdev_set_keepalive_cmdid;
+       u32 vdev_get_keepalive_cmdid;
+       u32 force_fw_hang_cmdid;
+       u32 gpio_config_cmdid;
+       u32 gpio_output_cmdid;
+};
+
 /*
  * wmi command groups.
  */
@@ -247,7 +359,9 @@ enum wmi_cmd_group {
 #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
 #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
 
-/* Command IDs and commande events. */
+#define WMI_CMD_UNSUPPORTED 0
+
+/* Command IDs and command events for MAIN FW. */
 enum wmi_cmd_id {
        WMI_INIT_CMDID = 0x1,
 
@@ -488,6 +602,217 @@ enum wmi_event_id {
        WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
 };
 
+/* Command IDs and command events for 10.X firmware */
+enum wmi_10x_cmd_id {
+       WMI_10X_START_CMDID = 0x9000,
+       WMI_10X_END_CMDID = 0x9FFF,
+
+       /* initialize the wlan sub system */
+       WMI_10X_INIT_CMDID,
+
+       /* Scan specific commands */
+
+       WMI_10X_START_SCAN_CMDID = WMI_10X_START_CMDID,
+       WMI_10X_STOP_SCAN_CMDID,
+       WMI_10X_SCAN_CHAN_LIST_CMDID,
+       WMI_10X_ECHO_CMDID,
+
+       /* PDEV(physical device) specific commands */
+       WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
+       WMI_10X_PDEV_SET_CHANNEL_CMDID,
+       WMI_10X_PDEV_SET_PARAM_CMDID,
+       WMI_10X_PDEV_PKTLOG_ENABLE_CMDID,
+       WMI_10X_PDEV_PKTLOG_DISABLE_CMDID,
+       WMI_10X_PDEV_SET_WMM_PARAMS_CMDID,
+       WMI_10X_PDEV_SET_HT_CAP_IE_CMDID,
+       WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID,
+       WMI_10X_PDEV_SET_BASE_MACADDR_CMDID,
+       WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID,
+       WMI_10X_PDEV_SET_QUIET_MODE_CMDID,
+       WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       WMI_10X_PDEV_GET_TPC_CONFIG_CMDID,
+
+       /* VDEV(virtual device) specific commands */
+       WMI_10X_VDEV_CREATE_CMDID,
+       WMI_10X_VDEV_DELETE_CMDID,
+       WMI_10X_VDEV_START_REQUEST_CMDID,
+       WMI_10X_VDEV_RESTART_REQUEST_CMDID,
+       WMI_10X_VDEV_UP_CMDID,
+       WMI_10X_VDEV_STOP_CMDID,
+       WMI_10X_VDEV_DOWN_CMDID,
+       WMI_10X_VDEV_STANDBY_RESPONSE_CMDID,
+       WMI_10X_VDEV_RESUME_RESPONSE_CMDID,
+       WMI_10X_VDEV_SET_PARAM_CMDID,
+       WMI_10X_VDEV_INSTALL_KEY_CMDID,
+
+       /* peer specific commands */
+       WMI_10X_PEER_CREATE_CMDID,
+       WMI_10X_PEER_DELETE_CMDID,
+       WMI_10X_PEER_FLUSH_TIDS_CMDID,
+       WMI_10X_PEER_SET_PARAM_CMDID,
+       WMI_10X_PEER_ASSOC_CMDID,
+       WMI_10X_PEER_ADD_WDS_ENTRY_CMDID,
+       WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID,
+       WMI_10X_PEER_MCAST_GROUP_CMDID,
+
+       /* beacon/management specific commands */
+
+       WMI_10X_BCN_TX_CMDID,
+       WMI_10X_BCN_PRB_TMPL_CMDID,
+       WMI_10X_BCN_FILTER_RX_CMDID,
+       WMI_10X_PRB_REQ_FILTER_RX_CMDID,
+       WMI_10X_MGMT_TX_CMDID,
+
+       /* commands to directly control ba negotiation directly from host. */
+       WMI_10X_ADDBA_CLEAR_RESP_CMDID,
+       WMI_10X_ADDBA_SEND_CMDID,
+       WMI_10X_ADDBA_STATUS_CMDID,
+       WMI_10X_DELBA_SEND_CMDID,
+       WMI_10X_ADDBA_SET_RESP_CMDID,
+       WMI_10X_SEND_SINGLEAMSDU_CMDID,
+
+       /* Station power save specific config */
+       WMI_10X_STA_POWERSAVE_MODE_CMDID,
+       WMI_10X_STA_POWERSAVE_PARAM_CMDID,
+       WMI_10X_STA_MIMO_PS_MODE_CMDID,
+
+       /* set debug log config */
+       WMI_10X_DBGLOG_CFG_CMDID,
+
+       /* DFS-specific commands */
+       WMI_10X_PDEV_DFS_ENABLE_CMDID,
+       WMI_10X_PDEV_DFS_DISABLE_CMDID,
+
+       /* QVIT specific command id */
+       WMI_10X_PDEV_QVIT_CMDID,
+
+       /* Offload Scan and Roaming related  commands */
+       WMI_10X_ROAM_SCAN_MODE,
+       WMI_10X_ROAM_SCAN_RSSI_THRESHOLD,
+       WMI_10X_ROAM_SCAN_PERIOD,
+       WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       WMI_10X_ROAM_AP_PROFILE,
+       WMI_10X_OFL_SCAN_ADD_AP_PROFILE,
+       WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE,
+       WMI_10X_OFL_SCAN_PERIOD,
+
+       /* P2P specific commands */
+       WMI_10X_P2P_DEV_SET_DEVICE_INFO,
+       WMI_10X_P2P_DEV_SET_DISCOVERABILITY,
+       WMI_10X_P2P_GO_SET_BEACON_IE,
+       WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
+
+       /* AP power save specific config */
+       WMI_10X_AP_PS_PEER_PARAM_CMDID,
+       WMI_10X_AP_PS_PEER_UAPSD_COEX_CMDID,
+
+       /* Rate-control specific commands */
+       WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
+
+       /* WLAN Profiling commands. */
+       WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
+       WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+
+       /* Suspend resume command Ids */
+       WMI_10X_PDEV_SUSPEND_CMDID,
+       WMI_10X_PDEV_RESUME_CMDID,
+
+       /* Beacon filter commands */
+       WMI_10X_ADD_BCN_FILTER_CMDID,
+       WMI_10X_RMV_BCN_FILTER_CMDID,
+
+       /* WOW Specific WMI commands*/
+       WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID,
+       WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID,
+       WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       WMI_10X_WOW_ENABLE_CMDID,
+       WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+
+       /* RTT measurement related cmd */
+       WMI_10X_RTT_MEASREQ_CMDID,
+       WMI_10X_RTT_TSF_CMDID,
+
+       /* transmit beacon by value */
+       WMI_10X_PDEV_SEND_BCN_CMDID,
+
+       /* F/W stats */
+       WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
+       WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+       WMI_10X_REQUEST_STATS_CMDID,
+
+       /* GPIO Configuration */
+       WMI_10X_GPIO_CONFIG_CMDID,
+       WMI_10X_GPIO_OUTPUT_CMDID,
+
+       WMI_10X_PDEV_UTF_CMDID = WMI_10X_END_CMDID - 1,
+};
+
+enum wmi_10x_event_id {
+       WMI_10X_SERVICE_READY_EVENTID = 0x8000,
+       WMI_10X_READY_EVENTID,
+       WMI_10X_START_EVENTID = 0x9000,
+       WMI_10X_END_EVENTID = 0x9FFF,
+
+       /* Scan specific events */
+       WMI_10X_SCAN_EVENTID = WMI_10X_START_EVENTID,
+       WMI_10X_ECHO_EVENTID,
+       WMI_10X_DEBUG_MESG_EVENTID,
+       WMI_10X_UPDATE_STATS_EVENTID,
+
+       /* Instantaneous RSSI event */
+       WMI_10X_INST_RSSI_STATS_EVENTID,
+
+       /* VDEV specific events */
+       WMI_10X_VDEV_START_RESP_EVENTID,
+       WMI_10X_VDEV_STANDBY_REQ_EVENTID,
+       WMI_10X_VDEV_RESUME_REQ_EVENTID,
+       WMI_10X_VDEV_STOPPED_EVENTID,
+
+       /* peer  specific events */
+       WMI_10X_PEER_STA_KICKOUT_EVENTID,
+
+       /* beacon/mgmt specific events */
+       WMI_10X_HOST_SWBA_EVENTID,
+       WMI_10X_TBTTOFFSET_UPDATE_EVENTID,
+       WMI_10X_MGMT_RX_EVENTID,
+
+       /* Channel stats event */
+       WMI_10X_CHAN_INFO_EVENTID,
+
+       /* PHY Error specific WMI event */
+       WMI_10X_PHYERR_EVENTID,
+
+       /* Roam event to trigger roaming on host */
+       WMI_10X_ROAM_EVENTID,
+
+       /* matching AP found from list of profiles */
+       WMI_10X_PROFILE_MATCH,
+
+       /* debug print message used for tracing FW code while debugging */
+       WMI_10X_DEBUG_PRINT_EVENTID,
+       /* VI spoecific event */
+       WMI_10X_PDEV_QVIT_EVENTID,
+       /* FW code profile data in response to profile request */
+       WMI_10X_WLAN_PROFILE_DATA_EVENTID,
+
+       /*RTT related event ID*/
+       WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID,
+       WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID,
+       WMI_10X_RTT_ERROR_REPORT_EVENTID,
+
+       WMI_10X_WOW_WAKEUP_HOST_EVENTID,
+       WMI_10X_DCS_INTERFERENCE_EVENTID,
+
+       /* TPC config for the current operating channel */
+       WMI_10X_PDEV_TPC_CONFIG_EVENTID,
+
+       WMI_10X_GPIO_INPUT_EVENTID,
+       WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1,
+};
+
 enum wmi_phy_mode {
        MODE_11A        = 0,   /* 11a Mode */
        MODE_11G        = 1,   /* 11b/g Mode */
@@ -508,6 +833,48 @@ enum wmi_phy_mode {
        MODE_MAX        = 14
 };
 
+static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode)
+{
+       switch (mode) {
+       case MODE_11A:
+               return "11a";
+       case MODE_11G:
+               return "11g";
+       case MODE_11B:
+               return "11b";
+       case MODE_11GONLY:
+               return "11gonly";
+       case MODE_11NA_HT20:
+               return "11na-ht20";
+       case MODE_11NG_HT20:
+               return "11ng-ht20";
+       case MODE_11NA_HT40:
+               return "11na-ht40";
+       case MODE_11NG_HT40:
+               return "11ng-ht40";
+       case MODE_11AC_VHT20:
+               return "11ac-vht20";
+       case MODE_11AC_VHT40:
+               return "11ac-vht40";
+       case MODE_11AC_VHT80:
+               return "11ac-vht80";
+       case MODE_11AC_VHT20_2G:
+               return "11ac-vht20-2g";
+       case MODE_11AC_VHT40_2G:
+               return "11ac-vht40-2g";
+       case MODE_11AC_VHT80_2G:
+               return "11ac-vht80-2g";
+       case MODE_UNKNOWN:
+               /* skip */
+               break;
+
+               /* no default handler to allow compiler to check that the
+                * enum is fully handled */
+       };
+
+       return "<unknown>";
+}
+
 #define WMI_CHAN_LIST_TAG      0x1
 #define WMI_SSID_LIST_TAG      0x2
 #define WMI_BSSID_LIST_TAG     0x3
@@ -763,13 +1130,45 @@ struct wmi_service_ready_event {
        struct wlan_host_mem_req mem_reqs[1];
 } __packed;
 
-/*
- * status consists of  upper 16 bits fo int status and lower 16 bits of
- * module ID that retuned status
- */
-#define WLAN_INIT_STATUS_SUCCESS   0x0
-#define WLAN_GET_INIT_STATUS_REASON(status)    ((status) & 0xffff)
-#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
+/* This is the definition from 10.X firmware branch */
+struct wmi_service_ready_event_10x {
+       __le32 sw_version;
+       __le32 abi_version;
+
+       /* WMI_PHY_CAPABILITY */
+       __le32 phy_capability;
+
+       /* Maximum number of frag table entries that SW will populate less 1 */
+       __le32 max_frag_entry;
+       __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+       __le32 num_rf_chains;
+
+       /*
+        * The following field is only valid for service type
+        * WMI_SERVICE_11AC
+        */
+       __le32 ht_cap_info; /* WMI HT Capability */
+       __le32 vht_cap_info; /* VHT capability info field of 802.11ac */
+       __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */
+       __le32 hw_min_tx_power;
+       __le32 hw_max_tx_power;
+
+       struct hal_reg_capabilities hal_reg_capabilities;
+
+       __le32 sys_cap_info;
+       __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */
+
+       /*
+        * request to host to allocate a chuck of memory and pss it down to FW
+        * via WM_INIT. FW uses this as FW extesnsion memory for saving its
+        * data structures. Only valid for low latency interfaces like PCIE
+        * where FW can access this memory directly (or) by DMA.
+        */
+       __le32 num_mem_reqs;
+
+       struct wlan_host_mem_req mem_reqs[1];
+} __packed;
+
 
 #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
 #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
@@ -978,6 +1377,192 @@ struct wmi_resource_config {
        __le32 max_frag_entries;
 } __packed;
 
+struct wmi_resource_config_10x {
+       /* number of virtual devices (VAPs) to support */
+       __le32 num_vdevs;
+
+       /* number of peer nodes to support */
+       __le32 num_peers;
+
+       /* number of keys per peer */
+       __le32 num_peer_keys;
+
+       /* total number of TX/RX data TIDs */
+       __le32 num_tids;
+
+       /*
+        * max skid for resolving hash collisions
+        *
+        *   The address search table is sparse, so that if two MAC addresses
+        *   result in the same hash value, the second of these conflicting
+        *   entries can slide to the next index in the address search table,
+        *   and use it, if it is unoccupied.  This ast_skid_limit parameter
+        *   specifies the upper bound on how many subsequent indices to search
+        *   over to find an unoccupied space.
+        */
+       __le32 ast_skid_limit;
+
+       /*
+        * the nominal chain mask for transmit
+        *
+        *   The chain mask may be modified dynamically, e.g. to operate AP
+        *   tx with a reduced number of chains if no clients are associated.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of tx chains.
+        */
+       __le32 tx_chain_mask;
+
+       /*
+        * the nominal chain mask for receive
+        *
+        *   The chain mask may be modified dynamically, e.g. for a client
+        *   to use a reduced number of chains for receive if the traffic to
+        *   the client is low enough that it doesn't require downlink MIMO
+        *   or antenna diversity.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of rx chains.
+        */
+       __le32 rx_chain_mask;
+
+       /*
+        * what rx reorder timeout (ms) to use for the AC
+        *
+        *   Each WMM access class (voice, video, best-effort, background) will
+        *   have its own timeout value to dictate how long to wait for missing
+        *   rx MPDUs to arrive before flushing subsequent MPDUs that have
+        *   already been received.
+        *   This parameter specifies the timeout in milliseconds for each
+        *   class.
+        */
+       __le32 rx_timeout_pri_vi;
+       __le32 rx_timeout_pri_vo;
+       __le32 rx_timeout_pri_be;
+       __le32 rx_timeout_pri_bk;
+
+       /*
+        * what mode the rx should decap packets to
+        *
+        *   MAC can decap to RAW (no decap), native wifi or Ethernet types
+        *   THis setting also determines the default TX behavior, however TX
+        *   behavior can be modified on a per VAP basis during VAP init
+        */
+       __le32 rx_decap_mode;
+
+       /* what is the maximum scan requests than can be queued */
+       __le32 scan_max_pending_reqs;
+
+       /* maximum VDEV that could use BMISS offload */
+       __le32 bmiss_offload_max_vdev;
+
+       /* maximum VDEV that could use offload roaming */
+       __le32 roam_offload_max_vdev;
+
+       /* maximum AP profiles that would push to offload roaming */
+       __le32 roam_offload_max_ap_profiles;
+
+       /*
+        * how many groups to use for mcast->ucast conversion
+        *
+        *   The target's WAL maintains a table to hold information regarding
+        *   which peers belong to a given multicast group, so that if
+        *   multicast->unicast conversion is enabled, the target can convert
+        *   multicast tx frames to a series of unicast tx frames, to each
+        *   peer within the multicast group.
+            This num_mcast_groups configuration parameter tells the target how
+        *   many multicast groups to provide storage for within its multicast
+        *   group membership table.
+        */
+       __le32 num_mcast_groups;
+
+       /*
+        * size to alloc for the mcast membership table
+        *
+        *   This num_mcast_table_elems configuration parameter tells the
+        *   target how many peer elements it needs to provide storage for in
+        *   its multicast group membership table.
+        *   These multicast group membership table elements are shared by the
+        *   multicast groups stored within the table.
+        */
+       __le32 num_mcast_table_elems;
+
+       /*
+        * whether/how to do multicast->unicast conversion
+        *
+        *   This configuration parameter specifies whether the target should
+        *   perform multicast --> unicast conversion on transmit, and if so,
+        *   what to do if it finds no entries in its multicast group
+        *   membership table for the multicast IP address in the tx frame.
+        *   Configuration value:
+        *   0 -> Do not perform multicast to unicast conversion.
+        *   1 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        drop the frame.
+        *   2 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        transmit the frame as multicast.
+        */
+       __le32 mcast2ucast_mode;
+
+       /*
+        * how much memory to allocate for a tx PPDU dbg log
+        *
+        *   This parameter controls how much memory the target will allocate
+        *   to store a log of tx PPDU meta-information (how large the PPDU
+        *   was, when it was sent, whether it was successful, etc.)
+        */
+       __le32 tx_dbg_log_size;
+
+       /* how many AST entries to be allocated for WDS */
+       __le32 num_wds_entries;
+
+       /*
+        * MAC DMA burst size, e.g., For target PCI limit can be
+        * 0 -default, 1 256B
+        */
+       __le32 dma_burst_size;
+
+       /*
+        * Fixed delimiters to be inserted after every MPDU to
+        * account for interface latency to avoid underrun.
+        */
+       __le32 mac_aggr_delim;
+
+       /*
+        *   determine whether target is responsible for detecting duplicate
+        *   non-aggregate MPDU and timing out stale fragments.
+        *
+        *   A-MPDU reordering is always performed on the target.
+        *
+        *   0: target responsible for frag timeout and dup checking
+        *   1: host responsible for frag timeout and dup checking
+        */
+       __le32 rx_skip_defrag_timeout_dup_detection_check;
+
+       /*
+        * Configuration for VoW :
+        * No of Video Nodes to be supported
+        * and Max no of descriptors for each Video link (node).
+        */
+       __le32 vow_config;
+
+       /* Number of msdu descriptors target should use */
+       __le32 num_msdu_desc;
+
+       /*
+        * Max. number of Tx fragments per MSDU
+        *  This parameter controls the max number of Tx fragments per MSDU.
+        *  This is sent by the target as part of the WMI_SERVICE_READY event
+        *  and is overriden by the OS shim as required.
+        */
+       __le32 max_frag_entries;
+} __packed;
+
+
+#define NUM_UNITS_IS_NUM_VDEVS   0x1
+#define NUM_UNITS_IS_NUM_PEERS   0x2
+
 /* strucutre describing host memory chunk. */
 struct host_memory_chunk {
        /* id of the request that is passed up in service ready */
@@ -999,6 +1584,18 @@ struct wmi_init_cmd {
        struct host_memory_chunk host_mem_chunks[1];
 } __packed;
 
+/* _10x stucture is from 10.X FW API */
+struct wmi_init_cmd_10x {
+       struct wmi_resource_config_10x resource_config;
+       __le32 num_host_mem_chunks;
+
+       /*
+        * variable number of host memory chunks.
+        * This should be the last element in the structure
+        */
+       struct host_memory_chunk host_mem_chunks[1];
+} __packed;
+
 /* TLV for channel list */
 struct wmi_chan_list {
        __le32 tag; /* WMI_CHAN_LIST_TAG */
@@ -1118,6 +1715,88 @@ struct wmi_start_scan_cmd {
         */
 } __packed;
 
+/* This is the definition from 10.X firmware branch */
+struct wmi_start_scan_cmd_10x {
+       /* Scan ID */
+       __le32 scan_id;
+
+       /* Scan requestor ID */
+       __le32 scan_req_id;
+
+       /* VDEV id(interface) that is requesting scan */
+       __le32 vdev_id;
+
+       /* Scan Priority, input to scan scheduler */
+       __le32 scan_priority;
+
+       /* Scan events subscription */
+       __le32 notify_scan_events;
+
+       /* dwell time in msec on active channels */
+       __le32 dwell_time_active;
+
+       /* dwell time in msec on passive channels */
+       __le32 dwell_time_passive;
+
+       /*
+        * min time in msec on the BSS channel,only valid if atleast one
+        * VDEV is active
+        */
+       __le32 min_rest_time;
+
+       /*
+        * max rest time in msec on the BSS channel,only valid if at least
+        * one VDEV is active
+        */
+       /*
+        * the scanner will rest on the bss channel at least min_rest_time
+        * after min_rest_time the scanner will start checking for tx/rx
+        * activity on all VDEVs. if there is no activity the scanner will
+        * switch to off channel. if there is activity the scanner will let
+        * the radio on the bss channel until max_rest_time expires.at
+        * max_rest_time scanner will switch to off channel irrespective of
+        * activity. activity is determined by the idle_time parameter.
+        */
+       __le32 max_rest_time;
+
+       /*
+        * time before sending next set of probe requests.
+        * The scanner keeps repeating probe requests transmission with
+        * period specified by repeat_probe_time.
+        * The number of probe requests specified depends on the ssid_list
+        * and bssid_list
+        */
+       __le32 repeat_probe_time;
+
+       /* time in msec between 2 consequetive probe requests with in a set. */
+       __le32 probe_spacing_time;
+
+       /*
+        * data inactivity time in msec on bss channel that will be used by
+        * scanner for measuring the inactivity.
+        */
+       __le32 idle_time;
+
+       /* maximum time in msec allowed for scan  */
+       __le32 max_scan_time;
+
+       /*
+        * delay in msec before sending first probe request after switching
+        * to a channel
+        */
+       __le32 probe_delay;
+
+       /* Scan control flags */
+       __le32 scan_ctrl_flags;
+
+       /*
+        * TLV (tag length value )  paramerters follow the scan_cmd structure.
+        * TLV can contain channel list, bssid list, ssid list and
+        * ie. the TLV tags are defined above;
+        */
+} __packed;
+
+
 struct wmi_ssid_arg {
        int len;
        const u8 *ssid;
@@ -1268,7 +1947,7 @@ struct wmi_scan_event {
  * good idea to pass all the fields in the RX status
  * descriptor up to the host.
  */
-struct wmi_mgmt_rx_hdr {
+struct wmi_mgmt_rx_hdr_v1 {
        __le32 channel;
        __le32 snr;
        __le32 rate;
@@ -1277,8 +1956,18 @@ struct wmi_mgmt_rx_hdr {
        __le32 status; /* %WMI_RX_STATUS_ */
 } __packed;
 
-struct wmi_mgmt_rx_event {
-       struct wmi_mgmt_rx_hdr hdr;
+struct wmi_mgmt_rx_hdr_v2 {
+       struct wmi_mgmt_rx_hdr_v1 v1;
+       __le32 rssi_ctl[4];
+} __packed;
+
+struct wmi_mgmt_rx_event_v1 {
+       struct wmi_mgmt_rx_hdr_v1 hdr;
+       u8 buf[0];
+} __packed;
+
+struct wmi_mgmt_rx_event_v2 {
+       struct wmi_mgmt_rx_hdr_v2 hdr;
        u8 buf[0];
 } __packed;
 
@@ -1465,6 +2154,60 @@ struct wmi_csa_event {
 #define VDEV_DEFAULT_STATS_UPDATE_PERIOD    500
 #define PEER_DEFAULT_STATS_UPDATE_PERIOD    500
 
+struct wmi_pdev_param_map {
+       u32 tx_chain_mask;
+       u32 rx_chain_mask;
+       u32 txpower_limit2g;
+       u32 txpower_limit5g;
+       u32 txpower_scale;
+       u32 beacon_gen_mode;
+       u32 beacon_tx_mode;
+       u32 resmgr_offchan_mode;
+       u32 protection_mode;
+       u32 dynamic_bw;
+       u32 non_agg_sw_retry_th;
+       u32 agg_sw_retry_th;
+       u32 sta_kickout_th;
+       u32 ac_aggrsize_scaling;
+       u32 ltr_enable;
+       u32 ltr_ac_latency_be;
+       u32 ltr_ac_latency_bk;
+       u32 ltr_ac_latency_vi;
+       u32 ltr_ac_latency_vo;
+       u32 ltr_ac_latency_timeout;
+       u32 ltr_sleep_override;
+       u32 ltr_rx_override;
+       u32 ltr_tx_activity_timeout;
+       u32 l1ss_enable;
+       u32 dsleep_enable;
+       u32 pcielp_txbuf_flush;
+       u32 pcielp_txbuf_watermark;
+       u32 pcielp_txbuf_tmo_en;
+       u32 pcielp_txbuf_tmo_value;
+       u32 pdev_stats_update_period;
+       u32 vdev_stats_update_period;
+       u32 peer_stats_update_period;
+       u32 bcnflt_stats_update_period;
+       u32 pmf_qos;
+       u32 arp_ac_override;
+       u32 arpdhcp_ac_override;
+       u32 dcs;
+       u32 ani_enable;
+       u32 ani_poll_period;
+       u32 ani_listen_period;
+       u32 ani_ofdm_level;
+       u32 ani_cck_level;
+       u32 dyntxchain;
+       u32 proxy_sta;
+       u32 idle_ps_config;
+       u32 power_gating_sleep;
+       u32 fast_channel_reset;
+       u32 burst_dur;
+       u32 burst_enable;
+};
+
+#define WMI_PDEV_PARAM_UNSUPPORTED 0
+
 enum wmi_pdev_param {
        /* TX chian mask */
        WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
@@ -1564,6 +2307,97 @@ enum wmi_pdev_param {
        WMI_PDEV_PARAM_POWER_GATING_SLEEP,
 };
 
+enum wmi_10x_pdev_param {
+       /* TX chian mask */
+       WMI_10X_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+       /* RX chian mask */
+       WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
+       /* TX power limit for 2G Radio */
+       WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
+       /* TX power limit for 5G Radio */
+       WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
+       /* TX power scale */
+       WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
+       /* Beacon generation mode . 0: host, 1: target   */
+       WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
+       /* Beacon generation mode . 0: staggered 1: bursted   */
+       WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
+       /*
+        * Resource manager off chan mode .
+        * 0: turn off off chan mode. 1: turn on offchan mode
+        */
+       WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       /*
+        * Protection mode:
+        * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
+        */
+       WMI_10X_PDEV_PARAM_PROTECTION_MODE,
+       /* Dynamic bandwidth 0: disable 1: enable */
+       WMI_10X_PDEV_PARAM_DYNAMIC_BW,
+       /* Non aggregrate/ 11g sw retry threshold.0-disable */
+       WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       /* aggregrate sw retry threshold. 0-disable*/
+       WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
+       /* Station kickout threshold (non of consecutive failures).0-disable */
+       WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
+       /* Aggerate size scaling configuration per AC */
+       WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       /* LTR enable */
+       WMI_10X_PDEV_PARAM_LTR_ENABLE,
+       /* LTR latency for BE, in us */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       /* LTR latency for BK, in us */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       /* LTR latency for VI, in us */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       /* LTR latency for VO, in us  */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       /* LTR AC latency timeout, in ms */
+       WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       /* LTR platform latency override, in us */
+       WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       /* LTR-RX override, in us */
+       WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
+       /* Tx activity timeout for LTR, in us */
+       WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       /* L1SS state machine enable */
+       WMI_10X_PDEV_PARAM_L1SS_ENABLE,
+       /* Deep sleep state machine enable */
+       WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
+       /* pdev level stats update period in ms */
+       WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       /* vdev level stats update period in ms */
+       WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       /* peer level stats update period in ms */
+       WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       /* beacon filter status update period */
+       WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */
+       WMI_10X_PDEV_PARAM_PMF_QOS,
+       /* Access category on which ARP and DHCP frames are sent */
+       WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+       /* DCS configuration */
+       WMI_10X_PDEV_PARAM_DCS,
+       /* Enable/Disable ANI on target */
+       WMI_10X_PDEV_PARAM_ANI_ENABLE,
+       /* configure the ANI polling period */
+       WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
+       /* configure the ANI listening period */
+       WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       /* configure OFDM immunity level */
+       WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
+       /* configure CCK immunity level */
+       WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
+       /* Enable/Disable CDD for 1x1 STAs in rate control module */
+       WMI_10X_PDEV_PARAM_DYNTXCHAIN,
+       /* Enable/Disable Fast channel reset*/
+       WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+       /* Set Bursting DUR */
+       WMI_10X_PDEV_PARAM_BURST_DUR,
+       /* Set Bursting Enable*/
+       WMI_10X_PDEV_PARAM_BURST_ENABLE,
+};
+
 struct wmi_pdev_set_param_cmd {
        __le32 param_id;
        __le32 param_value;
@@ -2088,6 +2922,61 @@ enum wmi_rate_preamble {
 /* Value to disable fixed rate setting */
 #define WMI_FIXED_RATE_NONE    (0xff)
 
+struct wmi_vdev_param_map {
+       u32 rts_threshold;
+       u32 fragmentation_threshold;
+       u32 beacon_interval;
+       u32 listen_interval;
+       u32 multicast_rate;
+       u32 mgmt_tx_rate;
+       u32 slot_time;
+       u32 preamble;
+       u32 swba_time;
+       u32 wmi_vdev_stats_update_period;
+       u32 wmi_vdev_pwrsave_ageout_time;
+       u32 wmi_vdev_host_swba_interval;
+       u32 dtim_period;
+       u32 wmi_vdev_oc_scheduler_air_time_limit;
+       u32 wds;
+       u32 atim_window;
+       u32 bmiss_count_max;
+       u32 bmiss_first_bcnt;
+       u32 bmiss_final_bcnt;
+       u32 feature_wmm;
+       u32 chwidth;
+       u32 chextoffset;
+       u32 disable_htprotection;
+       u32 sta_quickkickout;
+       u32 mgmt_rate;
+       u32 protection_mode;
+       u32 fixed_rate;
+       u32 sgi;
+       u32 ldpc;
+       u32 tx_stbc;
+       u32 rx_stbc;
+       u32 intra_bss_fwd;
+       u32 def_keyid;
+       u32 nss;
+       u32 bcast_data_rate;
+       u32 mcast_data_rate;
+       u32 mcast_indicate;
+       u32 dhcp_indicate;
+       u32 unknown_dest_indicate;
+       u32 ap_keepalive_min_idle_inactive_time_secs;
+       u32 ap_keepalive_max_idle_inactive_time_secs;
+       u32 ap_keepalive_max_unresponsive_time_secs;
+       u32 ap_enable_nawds;
+       u32 mcast2ucast_set;
+       u32 enable_rtscts;
+       u32 txbf;
+       u32 packet_powersave;
+       u32 drop_unencry;
+       u32 tx_encap_type;
+       u32 ap_detect_out_of_sync_sleeping_sta_time_secs;
+};
+
+#define WMI_VDEV_PARAM_UNSUPPORTED 0
+
 /* the definition of different VDEV parameters */
 enum wmi_vdev_param {
        /* RTS Threshold */
@@ -2219,6 +3108,121 @@ enum wmi_vdev_param {
        WMI_VDEV_PARAM_TX_ENCAP_TYPE,
 };
 
+/* the definition of different VDEV parameters */
+enum wmi_10x_vdev_param {
+       /* RTS Threshold */
+       WMI_10X_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+       /* Fragmentation threshold */
+       WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       /* beacon interval in TUs */
+       WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
+       /* Listen interval in TUs */
+       WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
+       /* muticast rate in Mbps */
+       WMI_10X_VDEV_PARAM_MULTICAST_RATE,
+       /* management frame rate in Mbps */
+       WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
+       /* slot time (long vs short) */
+       WMI_10X_VDEV_PARAM_SLOT_TIME,
+       /* preamble (long vs short) */
+       WMI_10X_VDEV_PARAM_PREAMBLE,
+       /* SWBA time (time before tbtt in msec) */
+       WMI_10X_VDEV_PARAM_SWBA_TIME,
+       /* time period for updating VDEV stats */
+       WMI_10X_VDEV_STATS_UPDATE_PERIOD,
+       /* age out time in msec for frames queued for station in power save */
+       WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
+       /*
+        * Host SWBA interval (time in msec before tbtt for SWBA event
+        * generation).
+        */
+       WMI_10X_VDEV_HOST_SWBA_INTERVAL,
+       /* DTIM period (specified in units of num beacon intervals) */
+       WMI_10X_VDEV_PARAM_DTIM_PERIOD,
+       /*
+        * scheduler air time limit for this VDEV. used by off chan
+        * scheduler.
+        */
+       WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       /* enable/dsiable WDS for this VDEV  */
+       WMI_10X_VDEV_PARAM_WDS,
+       /* ATIM Window */
+       WMI_10X_VDEV_PARAM_ATIM_WINDOW,
+       /* BMISS max */
+       WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
+       /* WMM enables/disabled */
+       WMI_10X_VDEV_PARAM_FEATURE_WMM,
+       /* Channel width */
+       WMI_10X_VDEV_PARAM_CHWIDTH,
+       /* Channel Offset */
+       WMI_10X_VDEV_PARAM_CHEXTOFFSET,
+       /* Disable HT Protection */
+       WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
+       /* Quick STA Kickout */
+       WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
+       /* Rate to be used with Management frames */
+       WMI_10X_VDEV_PARAM_MGMT_RATE,
+       /* Protection Mode */
+       WMI_10X_VDEV_PARAM_PROTECTION_MODE,
+       /* Fixed rate setting */
+       WMI_10X_VDEV_PARAM_FIXED_RATE,
+       /* Short GI Enable/Disable */
+       WMI_10X_VDEV_PARAM_SGI,
+       /* Enable LDPC */
+       WMI_10X_VDEV_PARAM_LDPC,
+       /* Enable Tx STBC */
+       WMI_10X_VDEV_PARAM_TX_STBC,
+       /* Enable Rx STBC */
+       WMI_10X_VDEV_PARAM_RX_STBC,
+       /* Intra BSS forwarding  */
+       WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
+       /* Setting Default xmit key for Vdev */
+       WMI_10X_VDEV_PARAM_DEF_KEYID,
+       /* NSS width */
+       WMI_10X_VDEV_PARAM_NSS,
+       /* Set the custom rate for the broadcast data frames */
+       WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
+       /* Set the custom rate (rate-code) for multicast data frames */
+       WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
+       /* Tx multicast packet indicate Enable/Disable */
+       WMI_10X_VDEV_PARAM_MCAST_INDICATE,
+       /* Tx DHCP packet indicate Enable/Disable */
+       WMI_10X_VDEV_PARAM_DHCP_INDICATE,
+       /* Enable host inspection of Tx unicast packet to unknown destination */
+       WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+
+       /* The minimum amount of time AP begins to consider STA inactive */
+       WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered inactive when there is no recent
+        * TX/RX activity and no downlink frames are buffered for it. Once a
+        * STA exceeds the maximum idle inactive time, the AP will send an
+        * 802.11 data-null as a keep alive to verify the STA is still
+        * associated. If the STA does ACK the data-null, or if the data-null
+        * is buffered and the STA does not retrieve it, the STA will be
+        * considered unresponsive
+        * (see WMI_10X_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS).
+        */
+       WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered unresponsive if there is no recent
+        * TX/RX activity and downlink frames are buffered for it. Once a STA
+        * exceeds the maximum unresponsive time, the AP will send a
+        * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */
+       WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+
+       /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */
+       WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
+
+       WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
+       /* Enable/Disable RTS-CTS */
+       WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+
+       WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+};
+
 /* slot time long */
 #define WMI_VDEV_SLOT_TIME_LONG                0x1
 /* slot time short */
@@ -3000,7 +4004,6 @@ struct wmi_force_fw_hang_cmd {
 
 #define WMI_MAX_EVENT 0x1000
 /* Maximum number of pending TXed WMI packets */
-#define WMI_MAX_PENDING_TX_COUNT 128
 #define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
 
 /* By default disable power save for IBSS */
@@ -3013,7 +4016,6 @@ int ath10k_wmi_attach(struct ath10k *ar);
 void ath10k_wmi_detach(struct ath10k *ar);
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
 int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
-void ath10k_wmi_flush_tx(struct ath10k *ar);
 
 int ath10k_wmi_connect_htc_service(struct ath10k *ar);
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
@@ -3022,8 +4024,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
 int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                                  u16 rd5g, u16 ctl2g, u16 ctl5g);
-int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
-                             u32 value);
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value);
 int ath10k_wmi_cmd_init(struct ath10k *ar);
 int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
 void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
@@ -3043,7 +4044,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
                       const u8 *bssid);
 int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
 int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
-                             enum wmi_vdev_param param_id, u32 param_value);
+                             u32 param_id, u32 param_value);
 int ath10k_wmi_vdev_install_key(struct ath10k *ar,
                                const struct wmi_vdev_install_key_arg *arg);
 int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
@@ -3066,11 +4067,13 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
                               enum wmi_ap_ps_peer_param param_id, u32 value);
 int ath10k_wmi_scan_chan_list(struct ath10k *ar,
                              const struct wmi_scan_chan_list_arg *arg);
-int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
+int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
+                                 const struct wmi_bcn_tx_arg *arg);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
                        const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
 int ath10k_wmi_force_fw_hang(struct ath10k *ar,
                             enum wmi_force_fw_hang_type type, u32 delay_ms);
+int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
 
 #endif /* _WMI_H_ */
index e9bc9e6..79bffe1 100644 (file)
@@ -37,12 +37,9 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
 {
        struct ath5k_hw *ah = common->priv;
        struct platform_device *pdev = to_platform_device(ah->dev);
-       struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+       struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
        u16 *eeprom, *eeprom_end;
 
-
-
-       bcfg = pdev->dev.platform_data;
        eeprom = (u16 *) bcfg->radio;
        eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ;
 
@@ -57,7 +54,7 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
 int ath5k_hw_read_srev(struct ath5k_hw *ah)
 {
        struct platform_device *pdev = to_platform_device(ah->dev);
-       struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+       struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
        ah->ah_mac_srev = bcfg->devid;
        return 0;
 }
@@ -65,7 +62,7 @@ int ath5k_hw_read_srev(struct ath5k_hw *ah)
 static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
 {
        struct platform_device *pdev = to_platform_device(ah->dev);
-       struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+       struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
        u8 *cfg_mac;
 
        if (to_platform_device(ah->dev)->id == 0)
@@ -87,7 +84,7 @@ static const struct ath_bus_ops ath_ahb_bus_ops = {
 /*Initialization*/
 static int ath_ahb_probe(struct platform_device *pdev)
 {
-       struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+       struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
        struct ath5k_hw *ah;
        struct ieee80211_hw *hw;
        struct resource *res;
@@ -96,7 +93,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
        int ret = 0;
        u32 reg;
 
-       if (!pdev->dev.platform_data) {
+       if (!dev_get_platdata(&pdev->dev)) {
                dev_err(&pdev->dev, "no platform data specified\n");
                ret = -EINVAL;
                goto err_out;
@@ -193,7 +190,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
 
 static int ath_ahb_remove(struct platform_device *pdev)
 {
-       struct ar231x_board_config *bcfg = pdev->dev.platform_data;
+       struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
        struct ieee80211_hw *hw = platform_get_drvdata(pdev);
        struct ath5k_hw *ah;
        u32 reg;
index 98a8861..05debf7 100644 (file)
@@ -22,8 +22,7 @@
 
 #define ATH6KL_MAX_IE                  256
 
-extern __printf(2, 3)
-int ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
 
 /*
  * Reflects the version of binary interface exposed by ATH6KL target
index 74369de..ca9ba00 100644 (file)
@@ -50,11 +50,10 @@ enum ATH6K_DEBUG_MASK {
 };
 
 extern unsigned int debug_mask;
-extern __printf(2, 3)
-int ath6kl_printk(const char *level, const char *fmt, ...);
-extern __printf(1, 2) int ath6kl_info(const char *fmt, ...);
-extern __printf(1, 2) int ath6kl_err(const char *fmt, ...);
-extern __printf(1, 2) int ath6kl_warn(const char *fmt, ...);
+__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(1, 2) int ath6kl_info(const char *fmt, ...);
+__printf(1, 2) int ath6kl_err(const char *fmt, ...);
+__printf(1, 2) int ath6kl_warn(const char *fmt, ...);
 
 enum ath6kl_war {
        ATH6KL_WAR_INVALID_RATE,
index a2c8ff8..14cab14 100644 (file)
@@ -60,7 +60,7 @@
 /* disable credit flow control on a specific service */
 #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL          (1 << 3)
 #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT    8
-#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK     0xFF00
+#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK     0xFF00U
 
 /* connect response status codes */
 #define HTC_SERVICE_SUCCESS      0
index 7944c25..32f139e 100644 (file)
@@ -84,6 +84,26 @@ config ATH9K_DFS_CERTIFIED
          developed. At this point enabling this option won't do anything
          except increase code size.
 
+config ATH9K_TX99
+       bool "Atheros ath9k TX99 testing support"
+       depends on CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+         Say N. This should only be enabled on systems undergoing
+         certification testing and evaluation in a controlled environment.
+         Enabling this will only enable TX99 support, all other modes of
+         operation will be disabled.
+
+         TX99 support enables Specific Absorption Rate (SAR) testing.
+         SAR is the unit of measurement for the amount of radio frequency(RF)
+         absorbed by the body when using a wireless device. The RF exposure
+         limits used are expressed in the terms of SAR, which is a measure
+         of the electric and magnetic field strength and power density for
+         transmitters operating at frequencies from 300 kHz to 100 GHz.
+         Regulatory bodies around the world require that wireless device
+         be evaluated to meet the RF exposure limits set forth in the
+         governmental SAR regulations.
+
 config ATH9K_LEGACY_RATE_CONTROL
        bool "Atheros ath9k rate control"
        depends on ATH9K
index 75ee9e7..6205ef5 100644 (file)
@@ -14,9 +14,7 @@ ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
 ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
 ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
 ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \
-               dfs.o \
-               dfs_pattern_detector.o \
-               dfs_pri_detector.o
+               dfs.o
 ath9k-$(CONFIG_PM_SLEEP) += wow.o
 
 obj-$(CONFIG_ATH9K) += ath9k.o
index 072e4b5..2dff276 100644 (file)
@@ -54,7 +54,7 @@ static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
        struct platform_device *pdev = to_platform_device(sc->dev);
        struct ath9k_platform_data *pdata;
 
-       pdata = (struct ath9k_platform_data *) pdev->dev.platform_data;
+       pdata = dev_get_platdata(&pdev->dev);
        if (off >= (ARRAY_SIZE(pdata->eeprom_data))) {
                ath_err(common,
                        "%s: flash read failed, offset %08x is out of range\n",
@@ -84,7 +84,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
        struct ath_hw *ah;
        char hw_name[64];
 
-       if (!pdev->dev.platform_data) {
+       if (!dev_get_platdata(&pdev->dev)) {
                dev_err(&pdev->dev, "no platform data specified\n");
                return -EINVAL;
        }
index be466b0..d28923b 100644 (file)
@@ -338,10 +338,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
                    aniState->cckNoiseImmunityLevel !=
                    ATH9K_ANI_CCK_DEF_LEVEL) {
                        ath_dbg(common, ANI,
-                               "Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
+                               "Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
                                ah->opmode,
                                chan->channel,
-                               chan->channelFlags,
                                is_scanning,
                                aniState->ofdmNoiseImmunityLevel,
                                aniState->cckNoiseImmunityLevel);
@@ -354,10 +353,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
                 * restore historical levels for this channel
                 */
                ath_dbg(common, ANI,
-                       "Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
+                       "Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
                        ah->opmode,
                        chan->channel,
-                       chan->channelFlags,
                        is_scanning,
                        aniState->ofdmNoiseImmunityLevel,
                        aniState->cckNoiseImmunityLevel);
index dd1cc73..bd048cc 100644 (file)
@@ -332,7 +332,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                }
 
                if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
-                   ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
+                   div_ant_conf->lna1_lna2_switch_delta)
                        div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
                else
                        div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
@@ -554,42 +554,22 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x10: /* LNA2 A-B */
-                       if ((antcomb->scan == 0) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
-                               ant_conf->fast_div_bias = 0x3f;
-                       } else {
-                               ant_conf->fast_div_bias = 0x1;
-                       }
+                       ant_conf->fast_div_bias = 0x2;
                        break;
                case 0x12: /* LNA2 LNA1 */
-                       ant_conf->fast_div_bias = 0x39;
+                       ant_conf->fast_div_bias = 0x3f;
                        break;
                case 0x13: /* LNA2 A+B */
-                       if ((antcomb->scan == 0) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
-                               ant_conf->fast_div_bias = 0x3f;
-                       } else {
-                               ant_conf->fast_div_bias = 0x1;
-                       }
+                       ant_conf->fast_div_bias = 0x2;
                        break;
                case 0x20: /* LNA1 A-B */
-                       if ((antcomb->scan == 0) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
-                               ant_conf->fast_div_bias = 0x3f;
-                       } else {
-                               ant_conf->fast_div_bias = 0x4;
-                       }
+                       ant_conf->fast_div_bias = 0x3;
                        break;
                case 0x21: /* LNA1 LNA2 */
-                       ant_conf->fast_div_bias = 0x6;
+                       ant_conf->fast_div_bias = 0x3;
                        break;
                case 0x23: /* LNA1 A+B */
-                       if ((antcomb->scan == 0) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
-                               ant_conf->fast_div_bias = 0x3f;
-                       } else {
-                               ant_conf->fast_div_bias = 0x6;
-                       }
+                       ant_conf->fast_div_bias = 0x3;
                        break;
                case 0x30: /* A+B A-B */
                        ant_conf->fast_div_bias = 0x1;
@@ -638,7 +618,7 @@ static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
                antcomb->rssi_sub = alt_rssi_avg;
                antcomb->scan = false;
                if (antcomb->rssi_lna2 >
-                   (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+                   (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) {
                        /* use LNA2 as main LNA */
                        if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
                            (antcomb->rssi_add > antcomb->rssi_sub)) {
index 0865647..ff415e8 100644 (file)
@@ -626,12 +626,11 @@ static void ar5008_hw_override_ini(struct ath_hw *ah,
                if (AR_SREV_9287_11_OR_LATER(ah))
                        val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
 
+               val |= AR_PCU_MISC_MODE2_CFP_IGNORE;
+
                REG_WRITE(ah, AR_PCU_MISC_MODE2, val);
        }
 
-       REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
-                   AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
-
        if (AR_SREV_9280_20_OR_LATER(ah))
                return;
        /*
@@ -667,14 +666,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah,
        if (IS_CHAN_HT40(chan)) {
                phymode |= AR_PHY_FC_DYN2040_EN;
 
-               if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
-                   (chan->chanmode == CHANNEL_G_HT40PLUS))
+               if (IS_CHAN_HT40PLUS(chan))
                        phymode |= AR_PHY_FC_DYN2040_PRI_CH;
 
        }
        REG_WRITE(ah, AR_PHY_TURBO, phymode);
 
-       ath9k_hw_set11nmac2040(ah);
+       ath9k_hw_set11nmac2040(ah, chan);
 
        ENABLE_REGWRITE_BUFFER(ah);
 
@@ -692,31 +690,12 @@ static int ar5008_hw_process_ini(struct ath_hw *ah,
        int i, regWrites = 0;
        u32 modesIndex, freqIndex;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               freqIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
+       if (IS_CHAN_5GHZ(chan)) {
                freqIndex = 1;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               freqIndex = 2;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       } else {
                freqIndex = 2;
-               break;
-
-       default:
-               return -EINVAL;
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
        }
 
        /*
@@ -815,8 +794,10 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan)
        if (chan == NULL)
                return;
 
-       rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
-               ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
+       if (IS_CHAN_2GHZ(chan))
+               rfMode |= AR_PHY_MODE_DYNAMIC;
+       else
+               rfMode |= AR_PHY_MODE_OFDM;
 
        if (!AR_SREV_9280_20_OR_LATER(ah))
                rfMode |= (IS_CHAN_5GHZ(chan)) ?
@@ -1219,12 +1200,11 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
 
        iniDef = &aniState->iniDef;
 
-       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
+       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
                ah->hw_version.macVersion,
                ah->hw_version.macRev,
                ah->opmode,
-               chan->channel,
-               chan->channelFlags);
+               chan->channel);
 
        val = REG_READ(ah, AR_PHY_SFCORR);
        iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
index 9f58974..cdc7400 100644 (file)
@@ -33,15 +33,12 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah,
        bool supported = false;
        switch (ah->supp_cals & cal_type) {
        case IQ_MISMATCH_CAL:
-               /* Run IQ Mismatch for non-CCK only */
-               if (!IS_CHAN_B(chan))
-                       supported = true;
+               supported = true;
                break;
        case ADC_GAIN_CAL:
        case ADC_DC_CAL:
                /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
-               if (!IS_CHAN_B(chan) &&
-                   !((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
+               if (!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
                      IS_CHAN_HT20(chan)))
                        supported = true;
                break;
@@ -671,7 +668,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
 
        nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF);
        if (ah->caldata)
-               nfcal_pending = ah->caldata->nfcal_pending;
+               nfcal_pending = test_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
 
        if (currCal && !nfcal &&
            (currCal->calState == CAL_RUNNING ||
@@ -861,7 +858,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
        ar9002_hw_pa_cal(ah, true);
 
        if (ah->caldata)
-               ah->caldata->nfcal_pending = true;
+               set_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
 
        ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
 
index fb61b08..5c95fd9 100644 (file)
@@ -419,28 +419,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
        u32 modesIndex;
        int i;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
-               break;
-
-       default:
-               return;
-       }
+       if (IS_CHAN_5GHZ(chan))
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       else
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
        ENABLE_REGWRITE_BUFFER(ah);
 
index 1fc1fa9..f087117 100644 (file)
@@ -485,7 +485,7 @@ static void ar9002_hw_do_getnf(struct ath_hw *ah,
        if (IS_CHAN_HT40(ah->curchan))
                nfarray[3] = sign_extend32(nf, 8);
 
-       if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+       if (!(ah->rxchainmask & BIT(1)))
                return;
 
        nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR);
@@ -532,6 +532,7 @@ static void ar9002_hw_antdiv_comb_conf_get(struct ath_hw *ah,
                                 AR_PHY_9285_ANT_DIV_ALT_LNACONF_S;
        antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >>
                                  AR_PHY_9285_FAST_DIV_BIAS_S;
+       antconf->lna1_lna2_switch_delta = -1;
        antconf->lna1_lna2_delta = -3;
        antconf->div_group = 0;
 }
@@ -679,6 +680,26 @@ static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah)
        }
 }
 
+static void ar9002_hw_tx99_start(struct ath_hw *ah, u32 qnum)
+{
+       REG_SET_BIT(ah, 0x9864, 0x7f000);
+       REG_SET_BIT(ah, 0x9924, 0x7f00fe);
+       REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+       REG_WRITE(ah, AR_CR, AR_CR_RXD);
+       REG_WRITE(ah, AR_DLCL_IFS(qnum), 0);
+       REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20);
+       REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20);
+       REG_WRITE(ah, AR_D_FPCTL, 0x10|qnum);
+       REG_WRITE(ah, AR_TIME_OUT, 0x00000400);
+       REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff);
+       REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ);
+}
+
+static void ar9002_hw_tx99_stop(struct ath_hw *ah)
+{
+       REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+}
+
 void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -700,6 +721,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity;
 #endif
+       ops->tx99_start = ar9002_hw_tx99_start;
+       ops->tx99_stop = ar9002_hw_tx99_stop;
 
        ar9002_hw_set_nf_limits(ah);
 }
index 6988e1d..22934d3 100644 (file)
@@ -727,8 +727,12 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
        REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
                      AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1);
 
-       if (caldata)
-               caldata->done_txiqcal_once = is_reusable;
+       if (caldata) {
+               if (is_reusable)
+                       set_bit(TXIQCAL_DONE, &caldata->cal_flags);
+               else
+                       clear_bit(TXIQCAL_DONE, &caldata->cal_flags);
+       }
 
        return;
 }
@@ -961,18 +965,44 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
 }
 
 static void ar9003_hw_do_manual_peak_cal(struct ath_hw *ah,
-                                        struct ath9k_channel *chan)
+                                        struct ath9k_channel *chan,
+                                        bool run_rtt_cal)
 {
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
        int i;
 
        if (!AR_SREV_9462(ah) && !AR_SREV_9565(ah) && !AR_SREV_9485(ah))
                return;
 
+       if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && !run_rtt_cal)
+               return;
+
        for (i = 0; i < AR9300_MAX_CHAINS; i++) {
                if (!(ah->rxchainmask & (1 << i)))
                        continue;
                ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan));
        }
+
+       if (caldata)
+               set_bit(SW_PKDET_DONE, &caldata->cal_flags);
+
+       if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && caldata) {
+               if (IS_CHAN_2GHZ(chan)){
+                       caldata->caldac[0] = REG_READ_FIELD(ah,
+                                                   AR_PHY_65NM_RXRF_AGC(0),
+                                                   AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR);
+                       caldata->caldac[1] = REG_READ_FIELD(ah,
+                                                   AR_PHY_65NM_RXRF_AGC(1),
+                                                   AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR);
+               } else {
+                       caldata->caldac[0] = REG_READ_FIELD(ah,
+                                                   AR_PHY_65NM_RXRF_AGC(0),
+                                                   AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR);
+                       caldata->caldac[1] = REG_READ_FIELD(ah,
+                                                   AR_PHY_65NM_RXRF_AGC(1),
+                                                   AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR);
+               }
+       }
 }
 
 static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable)
@@ -990,7 +1020,7 @@ static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable)
        txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) &
                          AR_PHY_AGC_CONTROL_CLC_SUCCESS);
 
-       if (caldata->done_txclcal_once) {
+       if (test_bit(TXCLCAL_DONE, &caldata->cal_flags)) {
                for (i = 0; i < AR9300_MAX_CHAINS; i++) {
                        if (!(ah->txchainmask & (1 << i)))
                                continue;
@@ -1006,7 +1036,7 @@ static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable)
                                caldata->tx_clcal[i][j] =
                                        REG_READ(ah, CL_TAB_ENTRY(cl_idx[i]));
                }
-               caldata->done_txclcal_once = true;
+               set_bit(TXCLCAL_DONE, &caldata->cal_flags);
        }
 }
 
@@ -1019,6 +1049,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
        bool is_reusable = true, status = true;
        bool run_rtt_cal = false, run_agc_cal, sep_iq_cal = false;
        bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT);
+       u32 rx_delay = 0;
        u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL |
                                          AR_PHY_AGC_CONTROL_FLTR_CAL   |
                                          AR_PHY_AGC_CONTROL_PKDET_CAL;
@@ -1042,17 +1073,22 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
                ar9003_hw_rtt_clear_hist(ah);
        }
 
-       if (rtt && !run_rtt_cal) {
-               agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL);
-               agc_supp_cals &= agc_ctrl;
-               agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL |
-                            AR_PHY_AGC_CONTROL_FLTR_CAL |
-                            AR_PHY_AGC_CONTROL_PKDET_CAL);
-               REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl);
+       if (rtt) {
+               if (!run_rtt_cal) {
+                       agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL);
+                       agc_supp_cals &= agc_ctrl;
+                       agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL |
+                                     AR_PHY_AGC_CONTROL_FLTR_CAL |
+                                     AR_PHY_AGC_CONTROL_PKDET_CAL);
+                       REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl);
+               } else {
+                       if (ah->ah_flags & AH_FASTCC)
+                               run_agc_cal = true;
+               }
        }
 
        if (ah->enabled_cals & TX_CL_CAL) {
-               if (caldata && caldata->done_txclcal_once)
+               if (caldata && test_bit(TXCLCAL_DONE, &caldata->cal_flags))
                        REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL,
                                    AR_PHY_CL_CAL_ENABLE);
                else {
@@ -1076,14 +1112,14 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
         * AGC calibration
         */
        if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) {
-               if (caldata && !caldata->done_txiqcal_once)
+               if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags))
                        REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
                                    AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
                else
                        REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
                                    AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
                txiqcal_done = run_agc_cal = true;
-       } else if (caldata && !caldata->done_txiqcal_once) {
+       } else if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags)) {
                run_agc_cal = true;
                sep_iq_cal = true;
        }
@@ -1099,6 +1135,15 @@ skip_tx_iqcal:
                REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
        }
 
+       if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) {
+               rx_delay = REG_READ(ah, AR_PHY_RX_DELAY);
+               /* Disable BB_active */
+               REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
+               udelay(5);
+               REG_WRITE(ah, AR_PHY_RX_DELAY, AR_PHY_RX_DELAY_DELAY);
+               REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+       }
+
        if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
                /* Calibrate the AGC */
                REG_WRITE(ah, AR_PHY_AGC_CONTROL,
@@ -1110,7 +1155,12 @@ skip_tx_iqcal:
                                       AR_PHY_AGC_CONTROL_CAL,
                                       0, AH_WAIT_TIMEOUT);
 
-               ar9003_hw_do_manual_peak_cal(ah, chan);
+               ar9003_hw_do_manual_peak_cal(ah, chan, run_rtt_cal);
+       }
+
+       if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) {
+               REG_WRITE(ah, AR_PHY_RX_DELAY, rx_delay);
+               udelay(5);
        }
 
        if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
@@ -1133,19 +1183,23 @@ skip_tx_iqcal:
 
        if (txiqcal_done)
                ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable);
-       else if (caldata && caldata->done_txiqcal_once)
+       else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags))
                ar9003_hw_tx_iq_cal_reload(ah);
 
        ar9003_hw_cl_cal_post_proc(ah, is_reusable);
 
        if (run_rtt_cal && caldata) {
                if (is_reusable) {
-                       if (!ath9k_hw_rfbus_req(ah))
+                       if (!ath9k_hw_rfbus_req(ah)) {
                                ath_err(ath9k_hw_common(ah),
                                        "Could not stop baseband\n");
-                       else
+                       } else {
                                ar9003_hw_rtt_fill_hist(ah);
 
+                               if (test_bit(SW_PKDET_DONE, &caldata->cal_flags))
+                                       ar9003_hw_rtt_load_hist(ah);
+                       }
+
                        ath9k_hw_rfbus_done(ah);
                }
 
index f486480..1ec5235 100644 (file)
@@ -2991,7 +2991,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
        case EEP_CHAIN_MASK_REDUCE:
                return (pBase->miscConfiguration >> 0x3) & 0x1;
        case EEP_ANT_DIV_CTL1:
-               return eep->base_ext1.ant_div_control;
+               if (AR_SREV_9565(ah))
+                       return AR9300_EEP_ANTDIV_CONTROL_DEFAULT_VALUE;
+               else
+                       return eep->base_ext1.ant_div_control;
        case EEP_ANTENNA_GAIN_5G:
                return eep->modalHeader5G.antennaGain;
        case EEP_ANTENNA_GAIN_2G:
@@ -3424,12 +3427,12 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        struct ar9300_base_eep_hdr *pBase;
 
        if (!dump_base_hdr) {
-               len += snprintf(buf + len, size - len,
-                               "%20s :\n", "2GHz modal Header");
+               len += scnprintf(buf + len, size - len,
+                                "%20s :\n", "2GHz modal Header");
                len = ar9003_dump_modal_eeprom(buf, len, size,
                                                &eep->modalHeader2G);
-               len += snprintf(buf + len, size - len,
-                               "%20s :\n", "5GHz modal Header");
+               len += scnprintf(buf + len, size - len,
+                                "%20s :\n", "5GHz modal Header");
                len = ar9003_dump_modal_eeprom(buf, len, size,
                                                &eep->modalHeader5G);
                goto out;
@@ -3479,8 +3482,8 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        PR_EEP("Rx Gain", pBase->txrxgain & 0xf);
        PR_EEP("SW Reg", le32_to_cpu(pBase->swreg));
 
-       len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                       ah->eeprom.ar9300_eep.macAddr);
+       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+                        ah->eeprom.ar9300_eep.macAddr);
 out:
        if (len > size)
                len = size;
@@ -3656,9 +3659,23 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                if (AR_SREV_9565(ah)) {
                        if (common->bt_ant_diversity) {
                                regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S);
+
+                               REG_SET_BIT(ah, AR_PHY_RESTART,
+                                           AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
+
+                               /* Force WLAN LNA diversity ON */
+                               REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
+                                           AR_BTCOEX_WL_LNADIV_FORCE_ON);
                        } else {
                                regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S);
                                regval &= ~(1 << AR_PHY_ANT_SW_RX_PROT_S);
+
+                               REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                           (1 << AR_PHY_ANT_SW_RX_PROT_S));
+
+                               /* Force WLAN LNA diversity OFF */
+                               REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
+                                           AR_BTCOEX_WL_LNADIV_FORCE_ON);
                        }
                }
 
@@ -3669,7 +3686,8 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                regval &= (~AR_FAST_DIV_ENABLE);
                regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
 
-               if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+               if ((AR_SREV_9485(ah) || AR_SREV_9565(ah))
+                   && common->bt_ant_diversity)
                        regval |= AR_FAST_DIV_ENABLE;
 
                REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
index 75d4fb4..0e5daa5 100644 (file)
@@ -52,6 +52,8 @@
 #define AR9300_PAPRD_SCALE_2           0x70000000
 #define AR9300_PAPRD_SCALE_2_S         28
 
+#define AR9300_EEP_ANTDIV_CONTROL_DEFAULT_VALUE 0xc9
+
 /* Delta from which to start power to pdadc table */
 /* This offset is used in both open loop and closed loop power control
  * schemes. In open loop power control, it is not really needed, but for
index 608bb48..20e4909 100644 (file)
@@ -187,17 +187,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
                INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
                               ar9485_1_1_baseband_core_txfir_coeff_japan_2484);
 
-               /* Load PCIE SERDES settings from INI */
-
-               /* Awake Setting */
-
-               INIT_INI_ARRAY(&ah->iniPcieSerdes,
-                               ar9485_1_1_pcie_phy_clkreq_disable_L1);
-
-               /* Sleep Setting */
-
-               INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
-                               ar9485_1_1_pcie_phy_clkreq_disable_L1);
+               if (ah->config.no_pll_pwrsave) {
+                       INIT_INI_ARRAY(&ah->iniPcieSerdes,
+                                      ar9485_1_1_pcie_phy_clkreq_disable_L1);
+                       INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+                                      ar9485_1_1_pcie_phy_clkreq_disable_L1);
+               } else {
+                       INIT_INI_ARRAY(&ah->iniPcieSerdes,
+                                      ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+                       INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+                                      ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+               }
        } else if (AR_SREV_9462_21(ah)) {
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
                               ar9462_2p1_mac_core);
@@ -364,6 +364,8 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
 
                INIT_INI_ARRAY(&ah->iniModesFastClock,
                                ar9565_1p0_modes_fast_clock);
+               INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
+                              ar9565_1p0_baseband_core_txfir_coeff_japan_2484);
        } else {
                /* mac */
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
@@ -628,6 +630,9 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
        else if (AR_SREV_9462_20(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                                ar9462_common_rx_gain_table_2p0);
+       else if (AR_SREV_9565(ah))
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9565_1p0_Common_rx_gain_table);
        else
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                                ar9300Common_rx_gain_table_2p2);
index 8dd0692..7b94a6c 100644 (file)
@@ -753,9 +753,9 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                    1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT);
 
        if (caldata) {
-               caldata->done_txiqcal_once = false;
-               caldata->done_txclcal_once = false;
-               caldata->rtt_done = false;
+               clear_bit(TXIQCAL_DONE, &caldata->cal_flags);
+               clear_bit(TXCLCAL_DONE, &caldata->cal_flags);
+               clear_bit(RTT_DONE, &caldata->cal_flags);
        }
 
        if (!ath9k_hw_init_cal(ah, chan))
index e897648..11f5358 100644 (file)
@@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
        if (IS_CHAN_HT40(chan)) {
                phymode |= AR_PHY_GC_DYN2040_EN;
                /* Configure control (primary) channel at +-10MHz */
-               if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
-                   (chan->chanmode == CHANNEL_G_HT40PLUS))
+               if (IS_CHAN_HT40PLUS(chan))
                        phymode |= AR_PHY_GC_DYN2040_PRI_CH;
 
        }
@@ -565,7 +564,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode);
 
        /* Configure MAC for 20/40 operation */
-       ath9k_hw_set11nmac2040(ah);
+       ath9k_hw_set11nmac2040(ah, chan);
 
        /* global transmit timeout (25 TUs default)*/
        REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
@@ -627,11 +626,10 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
         * MAC addr only will fail.
         */
        val = REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE);
-       REG_WRITE(ah, AR_PCU_MISC_MODE2,
-                 val | AR_AGG_WEP_ENABLE_FIX | AR_AGG_WEP_ENABLE);
-
-       REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
-                   AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+       val |= AR_AGG_WEP_ENABLE_FIX |
+              AR_AGG_WEP_ENABLE |
+              AR_PCU_MISC_MODE2_CFP_IGNORE;
+       REG_WRITE(ah, AR_PCU_MISC_MODE2, val);
 
        if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
                REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
@@ -683,41 +681,22 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah,
 {
        int ret;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               if (chan->channel <= 5350)
-                       ret = 1;
-               else if ((chan->channel > 5350) && (chan->channel <= 5600))
-                       ret = 3;
-               else
-                       ret = 5;
-               break;
-
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               if (chan->channel <= 5350)
-                       ret = 2;
-               else if ((chan->channel > 5350) && (chan->channel <= 5600))
-                       ret = 4;
+       if (IS_CHAN_2GHZ(chan)) {
+               if (IS_CHAN_HT40(chan))
+                       return 7;
                else
-                       ret = 6;
-               break;
-
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               ret = 8;
-               break;
+                       return 8;
+       }
 
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               ret = 7;
-               break;
+       if (chan->channel <= 5350)
+               ret = 1;
+       else if ((chan->channel > 5350) && (chan->channel <= 5600))
+               ret = 3;
+       else
+               ret = 5;
 
-       default:
-               ret = -EINVAL;
-       }
+       if (IS_CHAN_HT40(chan))
+               ret++;
 
        return ret;
 }
@@ -728,28 +707,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        unsigned int regWrites = 0, i;
        u32 modesIndex;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
-               break;
-
-       default:
-               return -EINVAL;
-       }
+       if (IS_CHAN_5GHZ(chan))
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       else
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
        /*
         * SOC, MAC, BB, RADIO initvals.
@@ -847,8 +808,10 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah,
        if (chan == NULL)
                return;
 
-       rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
-               ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
+       if (IS_CHAN_2GHZ(chan))
+               rfMode |= AR_PHY_MODE_DYNAMIC;
+       else
+               rfMode |= AR_PHY_MODE_OFDM;
 
        if (IS_CHAN_A_FAST_CLOCK(ah, chan))
                rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
@@ -1274,12 +1237,11 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
        aniState = &ah->ani;
        iniDef = &aniState->iniDef;
 
-       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
+       ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
                ah->hw_version.macVersion,
                ah->hw_version.macRev,
                ah->opmode,
-               chan->channel,
-               chan->channelFlags);
+               chan->channel);
 
        val = REG_READ(ah, AR_PHY_SFCORR);
        iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
@@ -1375,15 +1337,19 @@ static void ar9003_hw_antdiv_comb_conf_get(struct ath_hw *ah,
                                  AR_PHY_ANT_FAST_DIV_BIAS_S;
 
        if (AR_SREV_9330_11(ah)) {
+               antconf->lna1_lna2_switch_delta = -1;
                antconf->lna1_lna2_delta = -9;
                antconf->div_group = 1;
        } else if (AR_SREV_9485(ah)) {
+               antconf->lna1_lna2_switch_delta = -1;
                antconf->lna1_lna2_delta = -9;
                antconf->div_group = 2;
        } else if (AR_SREV_9565(ah)) {
-               antconf->lna1_lna2_delta = -3;
+               antconf->lna1_lna2_switch_delta = 3;
+               antconf->lna1_lna2_delta = -9;
                antconf->div_group = 3;
        } else {
+               antconf->lna1_lna2_switch_delta = -1;
                antconf->lna1_lna2_delta = -3;
                antconf->div_group = 0;
        }
@@ -1488,18 +1454,25 @@ static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
                }
        } else if (AR_SREV_9565(ah)) {
                if (enable) {
+                       REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                   AR_ANT_DIV_ENABLE);
                        REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
                                    (1 << AR_PHY_ANT_SW_RX_PROT_S));
-                       if (ah->curchan && IS_CHAN_2GHZ(ah->curchan))
-                               REG_SET_BIT(ah, AR_PHY_RESTART,
-                                           AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
+                       REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
+                                   AR_FAST_DIV_ENABLE);
+                       REG_SET_BIT(ah, AR_PHY_RESTART,
+                                   AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
                        REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
                                    AR_BTCOEX_WL_LNADIV_FORCE_ON);
                } else {
-                       REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                   AR_ANT_DIV_ENABLE);
                        REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
                                    (1 << AR_PHY_ANT_SW_RX_PROT_S));
-                       REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_PHY_CCK_DETECT,
+                                   AR_FAST_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_PHY_RESTART,
+                                   AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
                        REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
                                    AR_BTCOEX_WL_LNADIV_FORCE_ON);
 
@@ -1526,28 +1499,10 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
        unsigned int regWrites = 0;
        u32 modesIndex;
 
-       switch (chan->chanmode) {
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-               modesIndex = 1;
-               break;
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               modesIndex = 2;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_B:
-               modesIndex = 4;
-               break;
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               modesIndex = 3;
-               break;
-
-       default:
-               return -EINVAL;
-       }
+       if (IS_CHAN_5GHZ(chan))
+               modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
+       else
+               modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
        if (modesIndex == ah->modes_index) {
                *ini_reloaded = false;
@@ -1662,6 +1617,98 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah)
        }
 }
 
+static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum)
+{
+       REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR);
+       REG_SET_BIT(ah, 0x9864, 0x7f000);
+       REG_SET_BIT(ah, 0x9924, 0x7f00fe);
+       REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+       REG_WRITE(ah, AR_CR, AR_CR_RXD);
+       REG_WRITE(ah, AR_DLCL_IFS(qnum), 0);
+       REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */
+       REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20);
+       REG_WRITE(ah, AR_TIME_OUT, 0x00000400);
+       REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff);
+       REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ);
+}
+
+static void ar9003_hw_tx99_stop(struct ath_hw *ah)
+{
+       REG_CLR_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR);
+       REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+}
+
+static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower)
+{
+       static s16 p_pwr_array[ar9300RateSize] = { 0 };
+       unsigned int i;
+
+       if (txpower <= MAX_RATE_POWER) {
+               for (i = 0; i < ar9300RateSize; i++)
+                       p_pwr_array[i] = txpower;
+       } else {
+               for (i = 0; i < ar9300RateSize; i++)
+                       p_pwr_array[i] = MAX_RATE_POWER;
+       }
+
+       REG_WRITE(ah, 0xa458, 0);
+
+       REG_WRITE(ah, 0xa3c0,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24],  0));
+       REG_WRITE(ah, 0xa3c4,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54],  24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48],  16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36],   8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0));
+       REG_WRITE(ah, 0xa3c8,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L],  0));
+       REG_WRITE(ah, 0xa3cc,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S],   24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L],   16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S],     8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L],  0));
+       REG_WRITE(ah, 0xa3d0,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_5],  24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_4],  16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8)|
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0));
+       REG_WRITE(ah, 0xa3d4,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_7],   8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_6],   0));
+       REG_WRITE(ah, 0xa3e4,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_15],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_14],  0));
+       REG_WRITE(ah, 0xa3e8,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_23],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_22],  0));
+       REG_WRITE(ah, 0xa3d8,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0));
+       REG_WRITE(ah, 0xa3dc,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_7],   8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_6],   0));
+       REG_WRITE(ah, 0xa3ec,
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_15],  8) |
+                 ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14],  0));
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1701,6 +1748,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity;
 #endif
+       ops->tx99_start = ar9003_hw_tx99_start;
+       ops->tx99_stop = ar9003_hw_tx99_stop;
+       ops->tx99_set_txpower = ar9003_hw_tx99_set_txpower;
 
        ar9003_hw_set_nf_limits(ah);
        ar9003_hw_set_radar_conf(ah);
index 6fd7523..fca6243 100644 (file)
 
 #define AR_PHY_CCA_NOM_VAL_9462_2GHZ          -127
 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ     -127
+#define AR_PHY_CCA_MAX_GOOD_VAL_9462_2GHZ     -60
+#define AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_2GHZ -95
 #define AR_PHY_CCA_NOM_VAL_9462_5GHZ          -127
 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ     -127
+#define AR_PHY_CCA_MAX_GOOD_VAL_9462_5GHZ     -60
+#define AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_5GHZ -100
 
 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ          -118
 
index 74de353..9344188 100644 (file)
@@ -118,6 +118,27 @@ void ar9003_hw_rtt_load_hist(struct ath_hw *ah)
        }
 }
 
+static void ar9003_hw_patch_rtt(struct ath_hw *ah, int index, int chain)
+{
+       int agc, caldac;
+
+       if (!test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags))
+               return;
+
+       if ((index != 5) || (chain >= 2))
+               return;
+
+       agc = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
+                            AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE);
+       if (!agc)
+               return;
+
+       caldac = ah->caldata->caldac[chain];
+       ah->caldata->rtt_table[chain][index] &= 0xFFFF05FF;
+       caldac = (caldac & 0x20) | ((caldac & 0x1F) << 7);
+       ah->caldata->rtt_table[chain][index] |= (caldac << 4);
+}
+
 static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index)
 {
        u32 val;
@@ -155,13 +176,16 @@ void ar9003_hw_rtt_fill_hist(struct ath_hw *ah)
                for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
                        ah->caldata->rtt_table[chain][i] =
                                ar9003_hw_rtt_fill_hist_entry(ah, chain, i);
+
+                       ar9003_hw_patch_rtt(ah, i, chain);
+
                        ath_dbg(ath9k_hw_common(ah), CALIBRATE,
                                "RTT value at idx %d, chain %d is: 0x%x\n",
                                i, chain, ah->caldata->rtt_table[chain][i]);
                }
        }
 
-       ah->caldata->rtt_done = true;
+       set_bit(RTT_DONE, &ah->caldata->cal_flags);
 }
 
 void ar9003_hw_rtt_clear_hist(struct ath_hw *ah)
@@ -176,7 +200,7 @@ void ar9003_hw_rtt_clear_hist(struct ath_hw *ah)
        }
 
        if (ah->caldata)
-               ah->caldata->rtt_done = false;
+               clear_bit(RTT_DONE, &ah->caldata->cal_flags);
 }
 
 bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan)
@@ -186,11 +210,37 @@ bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan)
        if (!ah->caldata)
                return false;
 
-       if (!ah->caldata->rtt_done)
+       if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) {
+               if (IS_CHAN_2GHZ(chan)){
+                       REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0),
+                                     AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR,
+                                     ah->caldata->caldac[0]);
+                       REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1),
+                                     AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR,
+                                     ah->caldata->caldac[1]);
+               } else {
+                       REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0),
+                                     AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR,
+                                     ah->caldata->caldac[0]);
+                       REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1),
+                                     AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR,
+                                     ah->caldata->caldac[1]);
+               }
+               REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1),
+                             AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
+               REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0),
+                             AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1);
+       }
+
+       if (!test_bit(RTT_DONE, &ah->caldata->cal_flags))
                return false;
 
        ar9003_hw_rtt_enable(ah);
-       ar9003_hw_rtt_set_mask(ah, 0x10);
+
+       if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags))
+               ar9003_hw_rtt_set_mask(ah, 0x30);
+       else
+               ar9003_hw_rtt_set_mask(ah, 0x10);
 
        if (!ath9k_hw_rfbus_req(ah)) {
                ath_err(ath9k_hw_common(ah), "Could not stop baseband\n");
index 88ff1d7..7c18452 100644 (file)
 
 /* AR9485 1.1 */
 
-#define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble
-
-static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18012e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
+static const u32 ar9485_1_1_mac_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+       {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+       {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+       {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+       {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+       {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+       {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+       {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
 };
 
 static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = {
@@ -34,6 +37,7 @@ static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = {
        {0x00009e00, 0x037216a0},
        {0x00009e04, 0x00182020},
        {0x00009e18, 0x00000000},
+       {0x00009e20, 0x000003a8},
        {0x00009e2c, 0x00004121},
        {0x00009e44, 0x02282324},
        {0x0000a000, 0x00060005},
@@ -174,7 +178,7 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = {
        {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
        {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
        {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
-       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da},
        {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
        {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
@@ -200,14 +204,14 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = {
        {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9},
        {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb},
        {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
-       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62001eee, 0x62001eee},
+       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001ff6, 0x66001ff6},
+       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
        {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -263,6 +267,11 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = {
 static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003},
+       {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a},
+       {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
+       {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
+       {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
        {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8},
        {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000},
        {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006},
@@ -297,6 +306,22 @@ static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = {
        {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
        {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
        {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501},
+       {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02},
+       {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02},
+       {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803},
+       {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04},
+       {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
        {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
        {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
        {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
@@ -341,6 +366,100 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = {
        {0x0000a2e0, 0x00000000, 0x00000000, 0xffc63a84, 0xffc63a84},
        {0x0000a2e4, 0x00000000, 0x00000000, 0xfe0fc000, 0xfe0fc000},
        {0x0000a2e8, 0x00000000, 0x00000000, 0xfff00000, 0xfff00000},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da},
+       {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
+       {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200},
+       {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202},
+       {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400},
+       {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402},
+       {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404},
+       {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603},
+       {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605},
+       {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03},
+       {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04},
+       {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20},
+       {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21},
+       {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62},
+       {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63},
+       {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65},
+       {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66},
+       {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645},
+       {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865},
+       {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86},
+       {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9},
+       {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb},
+       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62001eee, 0x62001eee},
+       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001ff6, 0x66001ff6},
+       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6},
+       {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501},
+       {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02},
+       {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02},
+       {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803},
+       {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04},
+       {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305},
+       {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db},
+       {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260},
+};
+
+static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+       {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a},
+       {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
+       {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
+       {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552},
        {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8},
        {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000},
@@ -427,7 +546,7 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = {
        {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260},
 };
 
-static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = {
+static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
        {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a},
@@ -521,12 +640,15 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = {
        {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260},
 };
 
-#define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1
-
 static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003},
-       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8},
+       {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a},
+       {0x0000a2dc, 0x00000000, 0x00000000, 0xffad452a, 0xffad452a},
+       {0x0000a2e0, 0x00000000, 0x00000000, 0xffc98634, 0xffc98634},
+       {0x0000a2e4, 0x00000000, 0x00000000, 0xfff60780, 0xfff60780},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0xfffff800, 0xfffff800},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
        {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000},
        {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006},
        {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201},
@@ -543,23 +665,39 @@ static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = {
        {0x0000a530, 0x48023ec6, 0x48023ec6, 0x310006e0, 0x310006e0},
        {0x0000a534, 0x4d023f01, 0x4d023f01, 0x330006e0, 0x330006e0},
        {0x0000a538, 0x53023f4b, 0x53023f4b, 0x3e0008e3, 0x3e0008e3},
-       {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x410008e5, 0x410008e5},
-       {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x430008e6, 0x430008e6},
-       {0x0000a544, 0x6502feca, 0x6502feca, 0x4a0008ec, 0x4a0008ec},
-       {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4e0008f1, 0x4e0008f1},
-       {0x0000a54c, 0x7203feca, 0x7203feca, 0x520008f3, 0x520008f3},
-       {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x54000eed, 0x54000eed},
-       {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x58000ef1, 0x58000ef1},
-       {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5c000ef3, 0x5c000ef3},
-       {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x60000ef5, 0x60000ef5},
-       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62000ef6, 0x62000ef6},
-       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x62000ef6, 0x62000ef6},
-       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
-       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
-       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
-       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
-       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
-       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x430008e6, 0x430008e6},
+       {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4a0008ec, 0x4a0008ec},
+       {0x0000a544, 0x6502feca, 0x6502feca, 0x4e0008f1, 0x4e0008f1},
+       {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x520008f3, 0x520008f3},
+       {0x0000a54c, 0x7203feca, 0x7203feca, 0x54000eed, 0x54000eed},
+       {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x58000ef1, 0x58000ef1},
+       {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5c000ef3, 0x5c000ef3},
+       {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x62000ef6, 0x62000ef6},
+       {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001ff0, 0x66001ff0},
+       {0x0000a560, 0x900fff0b, 0x900fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a564, 0x960fffcb, 0x960fffcb, 0x68001ff6, 0x68001ff6},
+       {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6},
+       {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a58c, 0x00000000, 0x00000000, 0x01804000, 0x01804000},
+       {0x0000a590, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02},
+       {0x0000a594, 0x00000000, 0x00000000, 0x0340ca02, 0x0340ca02},
+       {0x0000a598, 0x00000000, 0x00000000, 0x0340cd03, 0x0340cd03},
+       {0x0000a59c, 0x00000000, 0x00000000, 0x0340cd03, 0x0340cd03},
+       {0x0000a5a0, 0x00000000, 0x00000000, 0x06415304, 0x06415304},
+       {0x0000a5a4, 0x00000000, 0x00000000, 0x04c11905, 0x04c11905},
+       {0x0000a5a8, 0x00000000, 0x00000000, 0x06415905, 0x06415905},
+       {0x0000a5ac, 0x00000000, 0x00000000, 0x06415905, 0x06415905},
+       {0x0000a5b0, 0x00000000, 0x00000000, 0x06415905, 0x06415905},
+       {0x0000a5b4, 0x00000000, 0x00000000, 0x06415905, 0x06415905},
+       {0x0000a5b8, 0x00000000, 0x00000000, 0x06415905, 0x06415905},
+       {0x0000a5bc, 0x00000000, 0x00000000, 0x06415905, 0x06415905},
        {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
        {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
        {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a},
@@ -823,6 +961,7 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = {
        {0x00009e00, 0x03721b20},
        {0x00009e04, 0x00082020},
        {0x00009e18, 0x0300501e},
+       {0x00009e20, 0x000003ba},
        {0x00009e2c, 0x00002e21},
        {0x00009e44, 0x02182324},
        {0x0000a000, 0x00060005},
@@ -955,20 +1094,6 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = {
        {0x0000a1fc, 0x00000296},
 };
 
-static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18052e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
-static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18053e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
 static const u32 ar9485_1_1_soc_preamble[][2] = {
        /* Addr      allmodes  */
        {0x00004014, 0xba280400},
@@ -1001,7 +1126,6 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = {
        {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e},
        {0x00009e14, 0x31395d53, 0x31396053, 0x312e6053, 0x312e5d53},
        {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
-       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
        {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
        {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010},
        {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@@ -1020,7 +1144,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = {
        {0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0},
        {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
        {0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982},
        {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -1028,13 +1152,6 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = {
        {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 };
 
-static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = {
-       /* Addr      allmodes  */
-       {0x00018c00, 0x18013e5e},
-       {0x00018c04, 0x000801d8},
-       {0x00018c08, 0x0000080c},
-};
-
 static const u32 ar9485_1_1_radio_postamble[][2] = {
        /* Addr      allmodes  */
        {0x0001609c, 0x0b283f31},
@@ -1206,6 +1323,25 @@ static const u32 ar9485_1_1_mac_core[][2] = {
        {0x000083d0, 0x000301ff},
 };
 
-#define ar9485_1_1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484
+static const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a398, 0x00000000},
+       {0x0000a39c, 0x6f7f0301},
+       {0x0000a3a0, 0xca9228ee},
+};
+
+static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = {
+       /* Addr      allmodes  */
+       {0x00018c00, 0x18013e5e},
+       {0x00018c04, 0x000801d8},
+       {0x00018c08, 0x0000080c},
+};
+
+static const u32 ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1[][2] = {
+       /* Addr      allmodes  */
+       {0x00018c00, 0x1801265e},
+       {0x00018c04, 0x000801d8},
+       {0x00018c08, 0x0000080c},
+};
 
 #endif /* INITVALS_9485_H */
index e85a8b0..a8c757b 100644 (file)
@@ -272,9 +272,9 @@ static const u32 ar9565_1p0_baseband_core[][2] = {
        {0x0000a398, 0x001f0e0f},
        {0x0000a39c, 0x0075393f},
        {0x0000a3a0, 0xb79f6427},
-       {0x0000a3a4, 0x00000000},
-       {0x0000a3a8, 0xaaaaaaaa},
-       {0x0000a3ac, 0x3c466478},
+       {0x0000a3a4, 0x00000011},
+       {0x0000a3a8, 0xaaaaaa6e},
+       {0x0000a3ac, 0x3c466455},
        {0x0000a3c0, 0x20202020},
        {0x0000a3c4, 0x22222220},
        {0x0000a3c8, 0x20200020},
@@ -295,11 +295,11 @@ static const u32 ar9565_1p0_baseband_core[][2] = {
        {0x0000a404, 0x00000000},
        {0x0000a408, 0x0e79e5c6},
        {0x0000a40c, 0x00820820},
-       {0x0000a414, 0x1ce739ce},
+       {0x0000a414, 0x1ce739c5},
        {0x0000a418, 0x2d001dce},
-       {0x0000a41c, 0x1ce739ce},
+       {0x0000a41c, 0x1ce739c5},
        {0x0000a420, 0x000001ce},
-       {0x0000a424, 0x1ce739ce},
+       {0x0000a424, 0x1ce739c5},
        {0x0000a428, 0x000001ce},
        {0x0000a42c, 0x1ce739ce},
        {0x0000a430, 0x1ce739ce},
@@ -351,9 +351,9 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = {
        {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
        {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
-       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003a4, 0x000003a4},
        {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
-       {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222},
+       {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946220, 0xcf946220},
        {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
        {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
        {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@@ -452,6 +452,7 @@ static const u32 ar9565_1p0_Common_rx_gain_table[][2] = {
        /* Addr      allmodes  */
        {0x00004050, 0x00300300},
        {0x0000406c, 0x00100000},
+       {0x00009e20, 0x000003b6},
        {0x0000a000, 0x00010000},
        {0x0000a004, 0x00030002},
        {0x0000a008, 0x00050004},
@@ -1230,4 +1231,11 @@ static const u32 ar9565_1p0_modes_high_power_tx_gain_table[][5] = {
        {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 };
 
+static const u32 ar9565_1p0_baseband_core_txfir_coeff_japan_2484[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a398, 0x00000000},
+       {0x0000a39c, 0x6f7f0301},
+       {0x0000a3a0, 0xca9228ee},
+};
+
 #endif /* INITVALS_9565_1P0_H */
index 2ee35f6..60a5da5 100644 (file)
@@ -64,7 +64,6 @@ struct ath_node;
 
 struct ath_config {
        u16 txpowlimit;
-       u8 cabqReadytime;
 };
 
 /*************************/
@@ -207,6 +206,14 @@ struct ath_frame_info {
        u8 baw_tracked : 1;
 };
 
+struct ath_rxbuf {
+       struct list_head list;
+       struct sk_buff *bf_mpdu;
+       void *bf_desc;
+       dma_addr_t bf_daddr;
+       dma_addr_t bf_buf_addr;
+};
+
 struct ath_buf_state {
        u8 bf_type;
        u8 bfs_paprd;
@@ -307,7 +314,7 @@ struct ath_rx {
        struct ath_descdma rxdma;
        struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
 
-       struct ath_buf *buf_hold;
+       struct ath_rxbuf *buf_hold;
        struct sk_buff *frag;
 
        u32 ampdu_ref;
@@ -459,8 +466,8 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
 
 #define ATH_DUMP_BTCOEX(_s, _val)                              \
        do {                                                    \
-               len += snprintf(buf + len, size - len,          \
-                               "%20s : %10d\n", _s, (_val));   \
+               len += scnprintf(buf + len, size - len,         \
+                                "%20s : %10d\n", _s, (_val));  \
        } while (0)
 
 enum bt_op_flags {
@@ -581,7 +588,6 @@ static inline void ath_fill_led_pin(struct ath_softc *sc)
 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI 50
 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI 50
 
-#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1
 #define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4
 #define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2
 #define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2
@@ -626,12 +632,16 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
 /* Main driver core */
 /********************/
 
-#define ATH9K_PCI_CUS198     0x0001
-#define ATH9K_PCI_CUS230     0x0002
-#define ATH9K_PCI_CUS217     0x0004
-#define ATH9K_PCI_WOW        0x0008
-#define ATH9K_PCI_BT_ANT_DIV 0x0010
-#define ATH9K_PCI_D3_L1_WAR  0x0020
+#define ATH9K_PCI_CUS198          0x0001
+#define ATH9K_PCI_CUS230          0x0002
+#define ATH9K_PCI_CUS217          0x0004
+#define ATH9K_PCI_CUS252          0x0008
+#define ATH9K_PCI_WOW             0x0010
+#define ATH9K_PCI_BT_ANT_DIV      0x0020
+#define ATH9K_PCI_D3_L1_WAR       0x0040
+#define ATH9K_PCI_AR9565_1ANT     0x0080
+#define ATH9K_PCI_AR9565_2ANT     0x0100
+#define ATH9K_PCI_NO_PLL_PWRSAVE  0x0200
 
 /*
  * Default cache line size, in bytes.
@@ -769,6 +779,11 @@ struct ath_softc {
        enum spectral_mode spectral_mode;
        struct ath_spec_scan spec_config;
 
+       struct ieee80211_vif *tx99_vif;
+       struct sk_buff *tx99_skb;
+       bool tx99_state;
+       s16 tx99_power;
+
 #ifdef CONFIG_PM_SLEEP
        atomic_t wow_got_bmiss_intr;
        atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */
@@ -877,6 +892,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins)
  */
 enum ath_fft_sample_type {
        ATH_FFT_SAMPLE_HT20 = 1,
+       ATH_FFT_SAMPLE_HT20_40,
 };
 
 struct fft_sample_tlv {
@@ -903,6 +919,39 @@ struct fft_sample_ht20 {
        u8 data[SPECTRAL_HT20_NUM_BINS];
 } __packed;
 
+struct fft_sample_ht20_40 {
+       struct fft_sample_tlv tlv;
+
+       u8 channel_type;
+       __be16 freq;
+
+       s8 lower_rssi;
+       s8 upper_rssi;
+
+       __be64 tsf;
+
+       s8 lower_noise;
+       s8 upper_noise;
+
+       __be16 lower_max_magnitude;
+       __be16 upper_max_magnitude;
+
+       u8 lower_max_index;
+       u8 upper_max_index;
+
+       u8 lower_bitmap_weight;
+       u8 upper_bitmap_weight;
+
+       u8 max_exp;
+
+       u8 data[SPECTRAL_HT20_40_NUM_BINS];
+} __packed;
+
+int ath9k_tx99_init(struct ath_softc *sc);
+void ath9k_tx99_deinit(struct ath_softc *sc);
+int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb,
+                   struct ath_tx_control *txctl);
+
 void ath9k_tasklet(unsigned long data);
 int ath_cabq_update(struct ath_softc *);
 
@@ -924,7 +973,6 @@ void ath9k_deinit_device(struct ath_softc *sc);
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
 void ath9k_reload_chainmask_settings(struct ath_softc *sc);
 
-bool ath9k_uses_beacons(int type);
 void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
 int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
                               enum spectral_mode spectral_mode);
@@ -952,7 +1000,7 @@ void ath9k_ps_restore(struct ath_softc *sc);
 u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate);
 
 void ath_start_rfkill_poll(struct ath_softc *sc);
-extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw);
+void ath9k_rfkill_poll_state(struct ieee80211_hw *hw);
 void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct ath9k_vif_iter_data *iter_data);
index b5c16b3..17be353 100644 (file)
@@ -334,6 +334,8 @@ void ath9k_beacon_tasklet(unsigned long data)
        if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
                sc->beacon.bmisscnt++;
 
+               ath9k_hw_check_nav(ah);
+
                if (!ath9k_hw_check_alive(ah))
                        ieee80211_queue_work(sc->hw, &sc->hw_check_work);
 
index 5e8219a..278365b 100644 (file)
@@ -63,13 +63,13 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
        return ath9k_hw_get_nf_limits(ah, chan)->nominal;
 }
 
-s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
+                          s16 nf)
 {
        s8 noise = ATH_DEFAULT_NOISE_FLOOR;
 
-       if (chan && chan->noisefloor) {
-               s8 delta = chan->noisefloor -
-                          ATH9K_NF_CAL_NOISE_THRESH -
+       if (nf) {
+               s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH -
                           ath9k_hw_get_default_nf(ah, chan);
                if (delta > 0)
                        noise += delta;
@@ -119,7 +119,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
                        ath_dbg(common, CALIBRATE,
                                "NFmid[%d] (%d) > MAX (%d), %s\n",
                                i, h[i].privNF, limit->max,
-                               (cal->nfcal_interference ?
+                               (test_bit(NFCAL_INTF, &cal->cal_flags) ?
                                 "not corrected (due to interference)" :
                                 "correcting to MAX"));
 
@@ -130,7 +130,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
                         * we bypass this limit here in order to better deal
                         * with our environment.
                         */
-                       if (!cal->nfcal_interference)
+                       if (!test_bit(NFCAL_INTF, &cal->cal_flags))
                                h[i].privNF = limit->max;
                }
        }
@@ -141,7 +141,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
         * Re-enable the enforcement of the NF maximum again.
         */
        if (!high_nf_mid)
-               cal->nfcal_interference = false;
+               clear_bit(NFCAL_INTF, &cal->cal_flags);
 }
 
 static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
@@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
 bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
        struct ath9k_cal_list *currCal = ah->cal_list_curr;
 
        if (!ah->caldata)
@@ -208,7 +207,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
                return true;
 
        ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
-               currCal->calData->calType, conf->chandef.chan->center_freq);
+               currCal->calData->calType, ah->curchan->chan->center_freq);
 
        ah->caldata->CalValid &= ~currCal->calData->calType;
        currCal->calState = CAL_WAITING;
@@ -220,7 +219,7 @@ EXPORT_SYMBOL(ath9k_hw_reset_calvalid);
 void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
 {
        if (ah->caldata)
-               ah->caldata->nfcal_pending = true;
+               set_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
 
        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
                    AR_PHY_AGC_CONTROL_ENABLE_NF);
@@ -242,7 +241,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
        int32_t val;
        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
        s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
 
        if (ah->caldata)
@@ -252,7 +250,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
                if (chainmask & (1 << i)) {
                        s16 nfval;
 
-                       if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))
+                       if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
                                continue;
 
                        if (h)
@@ -314,7 +312,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
        ENABLE_REGWRITE_BUFFER(ah);
        for (i = 0; i < NUM_NF_READINGS; i++) {
                if (chainmask & (1 << i)) {
-                       if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))
+                       if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
                                continue;
 
                        val = REG_READ(ah, ah->nf_regs[i]);
@@ -391,10 +389,10 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
        }
 
        h = caldata->nfCalHist;
-       caldata->nfcal_pending = false;
+       clear_bit(NFCAL_PENDING, &caldata->cal_flags);
        ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
        chan->noisefloor = h[0].privNF;
-       ah->noise = ath9k_hw_getchan_noise(ah, chan);
+       ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor);
        return true;
 }
 EXPORT_SYMBOL(ath9k_hw_getnf);
@@ -408,7 +406,6 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
 
        ah->caldata->channel = chan->channel;
        ah->caldata->channelFlags = chan->channelFlags;
-       ah->caldata->chanmode = chan->chanmode;
        h = ah->caldata->nfCalHist;
        default_nf = ath9k_hw_get_default_nf(ah, chan);
        for (i = 0; i < NUM_NF_READINGS; i++) {
@@ -437,12 +434,12 @@ void ath9k_hw_bstuck_nfcal(struct ath_hw *ah)
         * the baseband update the internal NF value itself, similar to
         * what is being done after a full reset.
         */
-       if (!caldata->nfcal_pending)
+       if (!test_bit(NFCAL_PENDING, &caldata->cal_flags))
                ath9k_hw_start_nfcal(ah, true);
        else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF))
                ath9k_hw_getnf(ah, ah->curchan);
 
-       caldata->nfcal_interference = true;
+       set_bit(NFCAL_INTF, &caldata->cal_flags);
 }
 EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal);
 
index 3d70b8c..b8ed95e 100644 (file)
@@ -116,7 +116,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
 void ath9k_hw_bstuck_nfcal(struct ath_hw *ah);
 void ath9k_hw_reset_calibration(struct ath_hw *ah,
                                struct ath9k_cal_list *currCal);
-s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
+s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
+                          s16 nf);
 
 
 #endif /* CALIB_H */
index d3063c2..a7e5a05 100644 (file)
@@ -49,103 +49,64 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
 
-static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
-{
-       u32 chanmode = 0;
-
-       switch (chandef->chan->band) {
-       case IEEE80211_BAND_2GHZ:
-               switch (chandef->width) {
-               case NL80211_CHAN_WIDTH_20_NOHT:
-               case NL80211_CHAN_WIDTH_20:
-                       chanmode = CHANNEL_G_HT20;
-                       break;
-               case NL80211_CHAN_WIDTH_40:
-                       if (chandef->center_freq1 > chandef->chan->center_freq)
-                               chanmode = CHANNEL_G_HT40PLUS;
-                       else
-                               chanmode = CHANNEL_G_HT40MINUS;
-                       break;
-               default:
-                       break;
-               }
-               break;
-       case IEEE80211_BAND_5GHZ:
-               switch (chandef->width) {
-               case NL80211_CHAN_WIDTH_20_NOHT:
-               case NL80211_CHAN_WIDTH_20:
-                       chanmode = CHANNEL_A_HT20;
-                       break;
-               case NL80211_CHAN_WIDTH_40:
-                       if (chandef->center_freq1 > chandef->chan->center_freq)
-                               chanmode = CHANNEL_A_HT40PLUS;
-                       else
-                               chanmode = CHANNEL_A_HT40MINUS;
-                       break;
-               default:
-                       break;
-               }
-               break;
-       default:
-               break;
-       }
-
-       return chanmode;
-}
-
 /*
  * Update internal channel flags.
  */
-void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-                              struct cfg80211_chan_def *chandef)
+static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+                                     struct cfg80211_chan_def *chandef)
 {
-       ichan->channel = chandef->chan->center_freq;
-       ichan->chan = chandef->chan;
-
-       if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
-               ichan->chanmode = CHANNEL_G;
-               ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
-       } else {
-               ichan->chanmode = CHANNEL_A;
-               ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
-       }
+       struct ieee80211_channel *chan = chandef->chan;
+       u16 flags = 0;
+
+       ichan->channel = chan->center_freq;
+       ichan->chan = chan;
+
+       if (chan->band == IEEE80211_BAND_5GHZ)
+               flags |= CHANNEL_5GHZ;
 
        switch (chandef->width) {
        case NL80211_CHAN_WIDTH_5:
-               ichan->channelFlags |= CHANNEL_QUARTER;
+               flags |= CHANNEL_QUARTER;
                break;
        case NL80211_CHAN_WIDTH_10:
-               ichan->channelFlags |= CHANNEL_HALF;
+               flags |= CHANNEL_HALF;
                break;
        case NL80211_CHAN_WIDTH_20_NOHT:
                break;
        case NL80211_CHAN_WIDTH_20:
+               flags |= CHANNEL_HT;
+               break;
        case NL80211_CHAN_WIDTH_40:
-               ichan->chanmode = ath9k_get_extchanmode(chandef);
+               if (chandef->center_freq1 > chandef->chan->center_freq)
+                       flags |= CHANNEL_HT40PLUS | CHANNEL_HT;
+               else
+                       flags |= CHANNEL_HT40MINUS | CHANNEL_HT;
                break;
        default:
                WARN_ON(1);
        }
+
+       ichan->channelFlags = flags;
 }
-EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
 
 /*
  * Get the internal channel reference.
  */
-struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
-                                              struct ath_hw *ah)
+struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
+                                           struct ath_hw *ah,
+                                           struct cfg80211_chan_def *chandef)
 {
-       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       struct ieee80211_channel *curchan = chandef->chan;
        struct ath9k_channel *channel;
        u8 chan_idx;
 
        chan_idx = curchan->hw_value;
        channel = &ah->channels[chan_idx];
-       ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
+       ath9k_cmn_update_ichannel(channel, chandef);
 
        return channel;
 }
-EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
+EXPORT_SYMBOL(ath9k_cmn_get_channel);
 
 int ath9k_cmn_count_streams(unsigned int chainmask, int max)
 {
index e039bcb..eb85e1b 100644 (file)
        (((x) + ((mul)/2)) / (mul))
 
 int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
-void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-                              struct cfg80211_chan_def *chandef);
-struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
-                                              struct ath_hw *ah);
+struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
+                                           struct ath_hw *ah,
+                                           struct cfg80211_chan_def *chandef);
 int ath9k_cmn_count_streams(unsigned int chainmask, int max);
 void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
                                  enum ath_stomp_type stomp_type);
index c088744..83a2c59 100644 (file)
@@ -104,37 +104,37 @@ static ssize_t read_file_ani(struct file *file, char __user *user_buf,
                return -ENOMEM;
 
        if (common->disable_ani) {
-               len += snprintf(buf + len, size - len, "%s: %s\n",
-                               "ANI", "DISABLED");
+               len += scnprintf(buf + len, size - len, "%s: %s\n",
+                                "ANI", "DISABLED");
                goto exit;
        }
 
-       len += snprintf(buf + len, size - len, "%15s: %s\n",
-                       "ANI", "ENABLED");
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "ANI RESET", ah->stats.ast_ani_reset);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "SPUR UP", ah->stats.ast_ani_spurup);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "SPUR DOWN", ah->stats.ast_ani_spurup);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "MRC-CCK ON", ah->stats.ast_ani_ccklow);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "FIR-STEP UP", ah->stats.ast_ani_stepup);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
-       len += snprintf(buf + len, size - len, "%15s: %u\n",
-                       "CCK ERRORS", ah->stats.ast_ani_cckerrs);
+       len += scnprintf(buf + len, size - len, "%15s: %s\n",
+                        "ANI", "ENABLED");
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "ANI RESET", ah->stats.ast_ani_reset);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "SPUR UP", ah->stats.ast_ani_spurup);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "SPUR DOWN", ah->stats.ast_ani_spurup);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "MRC-CCK ON", ah->stats.ast_ani_ccklow);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "FIR-STEP UP", ah->stats.ast_ani_stepup);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
+       len += scnprintf(buf + len, size - len, "%15s: %u\n",
+                        "CCK ERRORS", ah->stats.ast_ani_cckerrs);
 exit:
        if (len > size)
                len = size;
@@ -280,70 +280,70 @@ static ssize_t read_file_antenna_diversity(struct file *file,
                return -ENOMEM;
 
        if (!(pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) {
-               len += snprintf(buf + len, size - len, "%s\n",
-                               "Antenna Diversity Combining is disabled");
+               len += scnprintf(buf + len, size - len, "%s\n",
+                                "Antenna Diversity Combining is disabled");
                goto exit;
        }
 
        ath9k_ps_wakeup(sc);
        ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
-       len += snprintf(buf + len, size - len, "Current MAIN config : %s\n",
-                       lna_conf_str[div_ant_conf.main_lna_conf]);
-       len += snprintf(buf + len, size - len, "Current ALT config  : %s\n",
-                       lna_conf_str[div_ant_conf.alt_lna_conf]);
-       len += snprintf(buf + len, size - len, "Average MAIN RSSI   : %d\n",
-                       as_main->rssi_avg);
-       len += snprintf(buf + len, size - len, "Average ALT RSSI    : %d\n\n",
-                       as_alt->rssi_avg);
+       len += scnprintf(buf + len, size - len, "Current MAIN config : %s\n",
+                        lna_conf_str[div_ant_conf.main_lna_conf]);
+       len += scnprintf(buf + len, size - len, "Current ALT config  : %s\n",
+                        lna_conf_str[div_ant_conf.alt_lna_conf]);
+       len += scnprintf(buf + len, size - len, "Average MAIN RSSI   : %d\n",
+                        as_main->rssi_avg);
+       len += scnprintf(buf + len, size - len, "Average ALT RSSI    : %d\n\n",
+                        as_alt->rssi_avg);
        ath9k_ps_restore(sc);
 
-       len += snprintf(buf + len, size - len, "Packet Receive Cnt:\n");
-       len += snprintf(buf + len, size - len, "-------------------\n");
-
-       len += snprintf(buf + len, size - len, "%30s%15s\n",
-                       "MAIN", "ALT");
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "TOTAL COUNT",
-                       as_main->recv_cnt,
-                       as_alt->recv_cnt);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA1",
-                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1],
-                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA2",
-                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2],
-                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA1 + LNA2",
-                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
-                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA1 - LNA2",
-                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
-                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
-
-       len += snprintf(buf + len, size - len, "\nLNA Config Attempts:\n");
-       len += snprintf(buf + len, size - len, "--------------------\n");
-
-       len += snprintf(buf + len, size - len, "%30s%15s\n",
-                       "MAIN", "ALT");
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA1",
-                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1],
-                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA2",
-                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2],
-                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA1 + LNA2",
-                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
-                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
-       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
-                       "LNA1 - LNA2",
-                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
-                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
+       len += scnprintf(buf + len, size - len, "Packet Receive Cnt:\n");
+       len += scnprintf(buf + len, size - len, "-------------------\n");
+
+       len += scnprintf(buf + len, size - len, "%30s%15s\n",
+                        "MAIN", "ALT");
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "TOTAL COUNT",
+                        as_main->recv_cnt,
+                        as_alt->recv_cnt);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA1",
+                        as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1],
+                        as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA2",
+                        as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2],
+                        as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA1 + LNA2",
+                        as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
+                        as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA1 - LNA2",
+                        as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
+                        as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
+
+       len += scnprintf(buf + len, size - len, "\nLNA Config Attempts:\n");
+       len += scnprintf(buf + len, size - len, "--------------------\n");
+
+       len += scnprintf(buf + len, size - len, "%30s%15s\n",
+                        "MAIN", "ALT");
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA1",
+                        as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1],
+                        as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA2",
+                        as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2],
+                        as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA1 + LNA2",
+                        as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
+                        as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
+       len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                        "LNA1 - LNA2",
+                        as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
+                        as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
 
 exit:
        if (len > size)
@@ -385,21 +385,21 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf,
                   (AR_MACMISC_MISC_OBS_BUS_1 <<
                    AR_MACMISC_MISC_OBS_BUS_MSB_S)));
 
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
-                       "Raw DMA Debug values:\n");
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
+                        "Raw DMA Debug values:\n");
 
        for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
                if (i % 4 == 0)
-                       len += snprintf(buf + len, DMA_BUF_LEN - len, "\n");
+                       len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n");
 
                val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32)));
-               len += snprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ",
-                               i, val[i]);
+               len += scnprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ",
+                                i, val[i]);
        }
 
-       len += snprintf(buf + len, DMA_BUF_LEN - len, "\n\n");
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
-                       "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
+       len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n\n");
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
+                        "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
 
        for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) {
                if (i == 8) {
@@ -412,39 +412,39 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf,
                        dcuBase++;
                }
 
-               len += snprintf(buf + len, DMA_BUF_LEN - len,
-                       "%2d          %2x      %1x     %2x           %2x\n",
-                       i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
-                       (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
-                       val[2] & (0x7 << (i * 3)) >> (i * 3),
-                       (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
+               len += scnprintf(buf + len, DMA_BUF_LEN - len,
+                        "%2d          %2x      %1x     %2x           %2x\n",
+                        i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
+                        (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
+                        val[2] & (0x7 << (i * 3)) >> (i * 3),
+                        (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
        }
 
-       len += snprintf(buf + len, DMA_BUF_LEN - len, "\n");
+       len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n");
 
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
                "qcu_stitch state:   %2x    qcu_fetch state:        %2x\n",
                (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
                "qcu_complete state: %2x    dcu_complete state:     %2x\n",
                (val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
                "dcu_arb state:      %2x    dcu_fp state:           %2x\n",
                (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
                "chan_idle_dur:     %3d    chan_idle_dur_valid:     %1d\n",
                (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
                "txfifo_valid_0:      %1d    txfifo_valid_1:          %1d\n",
                (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
                "txfifo_dcu_num_0:   %2d    txfifo_dcu_num_1:       %2d\n",
                (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
 
-       len += snprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n",
-                       REG_READ_D(ah, AR_OBS_BUS_1));
-       len += snprintf(buf + len, DMA_BUF_LEN - len,
-                       "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR));
+       len += scnprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n",
+                        REG_READ_D(ah, AR_OBS_BUS_1));
+       len += scnprintf(buf + len, DMA_BUF_LEN - len,
+                        "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR));
 
        ath9k_ps_restore(sc);
 
@@ -530,9 +530,9 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
 
 #define PR_IS(a, s)                                            \
        do {                                                    \
-               len += snprintf(buf + len, mxlen - len,         \
-                               "%21s: %10u\n", a,              \
-                               sc->debug.stats.istats.s);      \
+               len += scnprintf(buf + len, mxlen - len,        \
+                                "%21s: %10u\n", a,             \
+                                sc->debug.stats.istats.s);     \
        } while (0)
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
@@ -563,8 +563,8 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
        PR_IS("GENTIMER", gen_timer);
        PR_IS("TOTAL", total);
 
-       len += snprintf(buf + len, mxlen - len,
-                       "SYNC_CAUSE stats:\n");
+       len += scnprintf(buf + len, mxlen - len,
+                        "SYNC_CAUSE stats:\n");
 
        PR_IS("Sync-All", sync_cause_all);
        PR_IS("RTC-IRQ", sync_rtc_irq);
@@ -655,16 +655,16 @@ static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq,
 
        ath_txq_lock(sc, txq);
 
-       len += snprintf(buf + len, size - len, "%s: %d ",
-                       "qnum", txq->axq_qnum);
-       len += snprintf(buf + len, size - len, "%s: %2d ",
-                       "qdepth", txq->axq_depth);
-       len += snprintf(buf + len, size - len, "%s: %2d ",
-                       "ampdu-depth", txq->axq_ampdu_depth);
-       len += snprintf(buf + len, size - len, "%s: %3d ",
-                       "pending", txq->pending_frames);
-       len += snprintf(buf + len, size - len, "%s: %d\n",
-                       "stopped", txq->stopped);
+       len += scnprintf(buf + len, size - len, "%s: %d ",
+                        "qnum", txq->axq_qnum);
+       len += scnprintf(buf + len, size - len, "%s: %2d ",
+                        "qdepth", txq->axq_depth);
+       len += scnprintf(buf + len, size - len, "%s: %2d ",
+                        "ampdu-depth", txq->axq_ampdu_depth);
+       len += scnprintf(buf + len, size - len, "%s: %3d ",
+                        "pending", txq->pending_frames);
+       len += scnprintf(buf + len, size - len, "%s: %d\n",
+                        "stopped", txq->stopped);
 
        ath_txq_unlock(sc, txq);
        return len;
@@ -687,11 +687,11 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf,
 
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                txq = sc->tx.txq_map[i];
-               len += snprintf(buf + len, size - len, "(%s):  ", qname[i]);
+               len += scnprintf(buf + len, size - len, "(%s):  ", qname[i]);
                len += print_queue(sc, txq, buf + len, size - len);
        }
 
-       len += snprintf(buf + len, size - len, "(CAB): ");
+       len += scnprintf(buf + len, size - len, "(CAB): ");
        len += print_queue(sc, sc->beacon.cabq, buf + len, size - len);
 
        if (len > size)
@@ -716,80 +716,82 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
        unsigned int reg;
        u32 rxfilter;
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "BSSID: %pM\n", common->curbssid);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "BSSID-MASK: %pM\n", common->bssidmask);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "OPMODE: %s\n", ath_opmode_to_string(sc->sc_ah->opmode));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "BSSID: %pM\n", common->curbssid);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "BSSID-MASK: %pM\n", common->bssidmask);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "OPMODE: %s\n",
+                        ath_opmode_to_string(sc->sc_ah->opmode));
 
        ath9k_ps_wakeup(sc);
        rxfilter = ath9k_hw_getrxfilter(sc->sc_ah);
        ath9k_ps_restore(sc);
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "RXFILTER: 0x%x", rxfilter);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "RXFILTER: 0x%x", rxfilter);
 
        if (rxfilter & ATH9K_RX_FILTER_UCAST)
-               len += snprintf(buf + len, sizeof(buf) - len, " UCAST");
+               len += scnprintf(buf + len, sizeof(buf) - len, " UCAST");
        if (rxfilter & ATH9K_RX_FILTER_MCAST)
-               len += snprintf(buf + len, sizeof(buf) - len, " MCAST");
+               len += scnprintf(buf + len, sizeof(buf) - len, " MCAST");
        if (rxfilter & ATH9K_RX_FILTER_BCAST)
-               len += snprintf(buf + len, sizeof(buf) - len, " BCAST");
+               len += scnprintf(buf + len, sizeof(buf) - len, " BCAST");
        if (rxfilter & ATH9K_RX_FILTER_CONTROL)
-               len += snprintf(buf + len, sizeof(buf) - len, " CONTROL");
+               len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL");
        if (rxfilter & ATH9K_RX_FILTER_BEACON)
-               len += snprintf(buf + len, sizeof(buf) - len, " BEACON");
+               len += scnprintf(buf + len, sizeof(buf) - len, " BEACON");
        if (rxfilter & ATH9K_RX_FILTER_PROM)
-               len += snprintf(buf + len, sizeof(buf) - len, " PROM");
+               len += scnprintf(buf + len, sizeof(buf) - len, " PROM");
        if (rxfilter & ATH9K_RX_FILTER_PROBEREQ)
-               len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
+               len += scnprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
        if (rxfilter & ATH9K_RX_FILTER_PHYERR)
-               len += snprintf(buf + len, sizeof(buf) - len, " PHYERR");
+               len += scnprintf(buf + len, sizeof(buf) - len, " PHYERR");
        if (rxfilter & ATH9K_RX_FILTER_MYBEACON)
-               len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON");
+               len += scnprintf(buf + len, sizeof(buf) - len, " MYBEACON");
        if (rxfilter & ATH9K_RX_FILTER_COMP_BAR)
-               len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR");
+               len += scnprintf(buf + len, sizeof(buf) - len, " COMP_BAR");
        if (rxfilter & ATH9K_RX_FILTER_PSPOLL)
-               len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL");
+               len += scnprintf(buf + len, sizeof(buf) - len, " PSPOLL");
        if (rxfilter & ATH9K_RX_FILTER_PHYRADAR)
-               len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR");
+               len += scnprintf(buf + len, sizeof(buf) - len, " PHYRADAR");
        if (rxfilter & ATH9K_RX_FILTER_MCAST_BCAST_ALL)
-               len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL");
+               len += scnprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL");
        if (rxfilter & ATH9K_RX_FILTER_CONTROL_WRAPPER)
-               len += snprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER");
+               len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER");
 
-       len += snprintf(buf + len, sizeof(buf) - len, "\n");
+       len += scnprintf(buf + len, sizeof(buf) - len, "\n");
 
        reg = sc->sc_ah->imask;
 
-       len += snprintf(buf + len, sizeof(buf) - len, "INTERRUPT-MASK: 0x%x", reg);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "INTERRUPT-MASK: 0x%x", reg);
 
        if (reg & ATH9K_INT_SWBA)
-               len += snprintf(buf + len, sizeof(buf) - len, " SWBA");
+               len += scnprintf(buf + len, sizeof(buf) - len, " SWBA");
        if (reg & ATH9K_INT_BMISS)
-               len += snprintf(buf + len, sizeof(buf) - len, " BMISS");
+               len += scnprintf(buf + len, sizeof(buf) - len, " BMISS");
        if (reg & ATH9K_INT_CST)
-               len += snprintf(buf + len, sizeof(buf) - len, " CST");
+               len += scnprintf(buf + len, sizeof(buf) - len, " CST");
        if (reg & ATH9K_INT_RX)
-               len += snprintf(buf + len, sizeof(buf) - len, " RX");
+               len += scnprintf(buf + len, sizeof(buf) - len, " RX");
        if (reg & ATH9K_INT_RXHP)
-               len += snprintf(buf + len, sizeof(buf) - len, " RXHP");
+               len += scnprintf(buf + len, sizeof(buf) - len, " RXHP");
        if (reg & ATH9K_INT_RXLP)
-               len += snprintf(buf + len, sizeof(buf) - len, " RXLP");
+               len += scnprintf(buf + len, sizeof(buf) - len, " RXLP");
        if (reg & ATH9K_INT_BB_WATCHDOG)
-               len += snprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG");
+               len += scnprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG");
 
-       len += snprintf(buf + len, sizeof(buf) - len, "\n");
+       len += scnprintf(buf + len, sizeof(buf) - len, "\n");
 
        ath9k_calculate_iter_data(hw, NULL, &iter_data);
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i"
-                       " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
-                       iter_data.naps, iter_data.nstations, iter_data.nmeshes,
-                       iter_data.nwds, iter_data.nadhocs,
-                       sc->nvifs, sc->nbcnvifs);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i"
+                        " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
+                        iter_data.naps, iter_data.nstations, iter_data.nmeshes,
+                        iter_data.nwds, iter_data.nadhocs,
+                        sc->nvifs, sc->nbcnvifs);
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -805,27 +807,27 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf,
        char buf[512];
        unsigned int len = 0;
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "Baseband Hang",
-                       sc->debug.stats.reset[RESET_TYPE_BB_HANG]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "Baseband Watchdog",
-                       sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "Fatal HW Error",
-                       sc->debug.stats.reset[RESET_TYPE_FATAL_INT]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "TX HW error",
-                       sc->debug.stats.reset[RESET_TYPE_TX_ERROR]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "TX Path Hang",
-                       sc->debug.stats.reset[RESET_TYPE_TX_HANG]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "PLL RX Hang",
-                       sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%17s: %2d\n", "MCI Reset",
-                       sc->debug.stats.reset[RESET_TYPE_MCI]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "Baseband Hang",
+                        sc->debug.stats.reset[RESET_TYPE_BB_HANG]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "Baseband Watchdog",
+                        sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "Fatal HW Error",
+                        sc->debug.stats.reset[RESET_TYPE_FATAL_INT]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "TX HW error",
+                        sc->debug.stats.reset[RESET_TYPE_TX_ERROR]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "TX Path Hang",
+                        sc->debug.stats.reset[RESET_TYPE_TX_HANG]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "PLL RX Hang",
+                        sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%17s: %2d\n", "MCI Reset",
+                        sc->debug.stats.reset[RESET_TYPE_MCI]);
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -902,14 +904,14 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
 #define PHY_ERR(s, p) \
-       len += snprintf(buf + len, size - len, "%22s : %10u\n", s, \
-                       sc->debug.stats.rxstats.phy_err_stats[p]);
+       len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \
+                        sc->debug.stats.rxstats.phy_err_stats[p]);
 
 #define RXS_ERR(s, e)                                      \
        do {                                                \
-               len += snprintf(buf + len, size - len,      \
-                               "%22s : %10u\n", s,         \
-                               sc->debug.stats.rxstats.e); \
+               len += scnprintf(buf + len, size - len,     \
+                                "%22s : %10u\n", s,        \
+                                sc->debug.stats.rxstats.e);\
        } while (0)
 
        struct ath_softc *sc = file->private_data;
@@ -1048,6 +1050,9 @@ static ssize_t write_file_spec_scan_ctl(struct file *file,
        char buf[32];
        ssize_t len;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return -EOPNOTSUPP;
+
        len = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
@@ -1439,22 +1444,22 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
        if (!buf)
                return -ENOMEM;
 
-       len += snprintf(buf + len, size - len,
-                       "Channel Noise Floor : %d\n", ah->noise);
-       len += snprintf(buf + len, size - len,
-                       "Chain | privNF | # Readings | NF Readings\n");
+       len += scnprintf(buf + len, size - len,
+                        "Channel Noise Floor : %d\n", ah->noise);
+       len += scnprintf(buf + len, size - len,
+                        "Chain | privNF | # Readings | NF Readings\n");
        for (i = 0; i < NUM_NF_READINGS; i++) {
                if (!(chainmask & (1 << i)) ||
                    ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
                        continue;
 
                nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount;
-               len += snprintf(buf + len, size - len, " %d\t %d\t %d\t\t",
-                               i, h[i].privNF, nread);
+               len += scnprintf(buf + len, size - len, " %d\t %d\t %d\t\t",
+                                i, h[i].privNF, nread);
                for (j = 0; j < nread; j++)
-                       len += snprintf(buf + len, size - len,
-                                       " %d", h[i].nfCalBuffer[j]);
-               len += snprintf(buf + len, size - len, "\n");
+                       len += scnprintf(buf + len, size - len,
+                                        " %d", h[i].nfCalBuffer[j]);
+               len += scnprintf(buf + len, size - len, "\n");
        }
 
        if (len > size)
@@ -1543,8 +1548,8 @@ static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
                return -ENOMEM;
 
        if (!sc->sc_ah->common.btcoex_enabled) {
-               len = snprintf(buf, size, "%s\n",
-                              "BTCOEX is disabled");
+               len = scnprintf(buf, size, "%s\n",
+                               "BTCOEX is disabled");
                goto exit;
        }
 
@@ -1582,43 +1587,43 @@ static ssize_t read_file_node_stat(struct file *file, char __user *user_buf,
                return -ENOMEM;
 
        if (!an->sta->ht_cap.ht_supported) {
-               len = snprintf(buf, size, "%s\n",
-                              "HT not supported");
+               len = scnprintf(buf, size, "%s\n",
+                               "HT not supported");
                goto exit;
        }
 
-       len = snprintf(buf, size, "Max-AMPDU: %d\n",
-                      an->maxampdu);
-       len += snprintf(buf + len, size - len, "MPDU Density: %d\n\n",
-                       an->mpdudensity);
+       len = scnprintf(buf, size, "Max-AMPDU: %d\n",
+                       an->maxampdu);
+       len += scnprintf(buf + len, size - len, "MPDU Density: %d\n\n",
+                        an->mpdudensity);
 
-       len += snprintf(buf + len, size - len,
-                       "%2s%7s\n", "AC", "SCHED");
+       len += scnprintf(buf + len, size - len,
+                        "%2s%7s\n", "AC", "SCHED");
 
        for (acno = 0, ac = &an->ac[acno];
             acno < IEEE80211_NUM_ACS; acno++, ac++) {
                txq = ac->txq;
                ath_txq_lock(sc, txq);
-               len += snprintf(buf + len, size - len,
-                               "%2d%7d\n",
-                               acno, ac->sched);
+               len += scnprintf(buf + len, size - len,
+                                "%2d%7d\n",
+                                acno, ac->sched);
                ath_txq_unlock(sc, txq);
        }
 
-       len += snprintf(buf + len, size - len,
-                       "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n",
-                       "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE",
-                       "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED");
+       len += scnprintf(buf + len, size - len,
+                        "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n",
+                        "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE",
+                        "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED");
 
        for (tidno = 0, tid = &an->tid[tidno];
             tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
                txq = tid->ac->txq;
                ath_txq_lock(sc, txq);
-               len += snprintf(buf + len, size - len,
-                               "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n",
-                               tid->tidno, tid->seq_start, tid->seq_next,
-                               tid->baw_size, tid->baw_head, tid->baw_tail,
-                               tid->bar_index, tid->sched, tid->paused);
+               len += scnprintf(buf + len, size - len,
+                                "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n",
+                                tid->tidno, tid->seq_start, tid->seq_next,
+                                tid->baw_size, tid->baw_head, tid->baw_tail,
+                                tid->bar_index, tid->sched, tid->paused);
                ath_txq_unlock(sc, txq);
        }
 exit:
@@ -1773,6 +1778,111 @@ void ath9k_deinit_debug(struct ath_softc *sc)
        }
 }
 
+static ssize_t read_file_tx99(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[3];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->tx99_state);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_tx99(struct file *file, const char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       char buf[32];
+       bool start;
+       ssize_t len;
+       int r;
+
+       if (sc->nvifs > 1)
+               return -EOPNOTSUPP;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       if (strtobool(buf, &start))
+               return -EINVAL;
+
+       if (start == sc->tx99_state) {
+               if (!start)
+                       return count;
+               ath_dbg(common, XMIT, "Resetting TX99\n");
+               ath9k_tx99_deinit(sc);
+       }
+
+       if (!start) {
+               ath9k_tx99_deinit(sc);
+               return count;
+       }
+
+       r = ath9k_tx99_init(sc);
+       if (r)
+               return r;
+
+       return count;
+}
+
+static const struct file_operations fops_tx99 = {
+       .read = read_file_tx99,
+       .write = write_file_tx99,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_tx99_power(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d (%d dBm)\n",
+                     sc->tx99_power,
+                     sc->tx99_power / 2);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_tx99_power(struct file *file,
+                                    const char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       int r;
+       u8 tx_power;
+
+       r = kstrtou8_from_user(user_buf, count, 0, &tx_power);
+       if (r)
+               return r;
+
+       if (tx_power > MAX_RATE_POWER)
+               return -EINVAL;
+
+       sc->tx99_power = tx_power;
+
+       ath9k_ps_wakeup(sc);
+       ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power);
+       ath9k_ps_restore(sc);
+
+       return count;
+}
+
+static const struct file_operations fops_tx99_power = {
+       .read = read_file_tx99_power,
+       .write = write_file_tx99_power,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath9k_init_debug(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -1864,5 +1974,15 @@ int ath9k_init_debug(struct ath_hw *ah)
        debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_btcoex);
 #endif
+       if (config_enabled(CONFIG_ATH9K_TX99) &&
+           AR_SREV_9300_20_OR_LATER(ah)) {
+               debugfs_create_file("tx99", S_IRUSR | S_IWUSR,
+                                   sc->debug.debugfs_phy, sc,
+                                   &fops_tx99);
+               debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR,
+                                   sc->debug.debugfs_phy, sc,
+                                   &fops_tx99_power);
+       }
+
        return 0;
 }
index 6e1556f..d6e3fa4 100644 (file)
@@ -193,12 +193,12 @@ struct ath_tx_stats {
 #define TXSTATS sc->debug.stats.txstats
 #define PR(str, elem)                                                  \
        do {                                                            \
-               len += snprintf(buf + len, size - len,                  \
-                               "%s%13u%11u%10u%10u\n", str,            \
-                               TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem, \
-                               TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem, \
-                               TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem, \
-                               TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \
+               len += scnprintf(buf + len, size - len,                 \
+                                "%s%13u%11u%10u%10u\n", str,           \
+                                TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem,\
+                                TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem,\
+                                TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem,\
+                                TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \
        } while(0)
 
 #define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++)
index 3c839f0..c6fa3d5 100644 (file)
@@ -17,7 +17,7 @@
 
 #ifndef ATH9K_DFS_H
 #define ATH9K_DFS_H
-#include "dfs_pattern_detector.h"
+#include "../dfs_pattern_detector.h"
 
 #if defined(CONFIG_ATH9K_DFS_CERTIFIED)
 /**
index 3c6e413..8824610 100644 (file)
 
 #include "ath9k.h"
 #include "dfs_debug.h"
+#include "../dfs_pattern_detector.h"
 
-
-struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 };
+static struct ath_dfs_pool_stats dfs_pool_stats = { 0 };
 
 #define ATH9K_DFS_STAT(s, p) \
-       len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \
-                       sc->debug.stats.dfs_stats.p);
+       len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
+                        sc->debug.stats.dfs_stats.p);
 #define ATH9K_DFS_POOL_STAT(s, p) \
-       len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \
-                       global_dfs_pool_stats.p);
+       len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
+                        dfs_pool_stats.p);
 
 static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
                             size_t count, loff_t *ppos)
@@ -44,12 +44,21 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
        if (buf == NULL)
                return -ENOMEM;
 
-       len += snprintf(buf + len, size - len, "DFS support for "
-                       "macVersion = 0x%x, macRev = 0x%x: %s\n",
-                       hw_ver->macVersion, hw_ver->macRev,
-                       (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ?
+       len += scnprintf(buf + len, size - len, "DFS support for "
+                        "macVersion = 0x%x, macRev = 0x%x: %s\n",
+                        hw_ver->macVersion, hw_ver->macRev,
+                        (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ?
                                        "enabled" : "disabled");
-       len += snprintf(buf + len, size - len, "Pulse detector statistics:\n");
+
+       if (!sc->dfs_detector) {
+               len += scnprintf(buf + len, size - len,
+                                "DFS detector not enabled\n");
+               goto exit;
+       }
+
+       dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector);
+
+       len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");
        ATH9K_DFS_STAT("pulse events reported   ", pulses_total);
        ATH9K_DFS_STAT("invalid pulse events    ", pulses_no_dfs);
        ATH9K_DFS_STAT("DFS pulses detected     ", pulses_detected);
@@ -59,11 +68,12 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
        ATH9K_DFS_STAT("Primary channel pulses  ", pri_phy_errors);
        ATH9K_DFS_STAT("Secondary channel pulses", ext_phy_errors);
        ATH9K_DFS_STAT("Dual channel pulses     ", dc_phy_errors);
-       len += snprintf(buf + len, size - len, "Radar detector statistics "
-                       "(current DFS region: %d)\n", sc->dfs_detector->region);
+       len += scnprintf(buf + len, size - len, "Radar detector statistics "
+                        "(current DFS region: %d)\n",
+                        sc->dfs_detector->region);
        ATH9K_DFS_STAT("Pulse events processed  ", pulses_processed);
        ATH9K_DFS_STAT("Radars detected         ", radar_detected);
-       len += snprintf(buf + len, size - len, "Global Pool statistics:\n");
+       len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");
        ATH9K_DFS_POOL_STAT("Pool references         ", pool_reference);
        ATH9K_DFS_POOL_STAT("Pulses allocated        ", pulse_allocated);
        ATH9K_DFS_POOL_STAT("Pulses alloc error      ", pulse_alloc_error);
@@ -72,6 +82,7 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf,
        ATH9K_DFS_POOL_STAT("Seqs. alloc error       ", pseq_alloc_error);
        ATH9K_DFS_POOL_STAT("Seqs. in use            ", pseq_used);
 
+exit:
        if (len > size)
                len = size;
 
index e36810a..0a7ddf4 100644 (file)
@@ -51,25 +51,11 @@ struct ath_dfs_stats {
        u32 radar_detected;
 };
 
-/**
- * struct ath_dfs_pool_stats - DFS Statistics for global pools
- */
-struct ath_dfs_pool_stats {
-       u32 pool_reference;
-       u32 pulse_allocated;
-       u32 pulse_alloc_error;
-       u32 pulse_used;
-       u32 pseq_allocated;
-       u32 pseq_alloc_error;
-       u32 pseq_used;
-};
 #if defined(CONFIG_ATH9K_DFS_DEBUGFS)
 
 #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++)
 void ath9k_dfs_init_debug(struct ath_softc *sc);
 
-#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
-#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
 extern struct ath_dfs_pool_stats global_dfs_pool_stats;
 
 #else
@@ -77,8 +63,6 @@ extern struct ath_dfs_pool_stats global_dfs_pool_stats;
 #define DFS_STAT_INC(sc, c) do { } while (0)
 static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { }
 
-#define DFS_POOL_STAT_INC(c) do { } while (0)
-#define DFS_POOL_STAT_DEC(c) do { } while (0)
 #endif /* CONFIG_ATH9K_DFS_DEBUGFS */
 
 #endif /* ATH9K_DFS_DEBUG_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c
deleted file mode 100644 (file)
index 491305c..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include "dfs_pattern_detector.h"
-#include "dfs_pri_detector.h"
-#include "ath9k.h"
-
-/*
- * tolerated deviation of radar time stamp in usecs on both sides
- * TODO: this might need to be HW-dependent
- */
-#define PRI_TOLERANCE  16
-
-/**
- * struct radar_types - contains array of patterns defined for one DFS domain
- * @domain: DFS regulatory domain
- * @num_radar_types: number of radar types to follow
- * @radar_types: radar types array
- */
-struct radar_types {
-       enum nl80211_dfs_regions region;
-       u32 num_radar_types;
-       const struct radar_detector_specs *radar_types;
-};
-
-/* percentage on ppb threshold to trigger detection */
-#define MIN_PPB_THRESH 50
-#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
-#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
-/* percentage of pulse width tolerance */
-#define WIDTH_TOLERANCE 5
-#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
-#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
-
-#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)     \
-{                                                              \
-       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
-       (PRF2PRI(PMAX) - PRI_TOLERANCE),                        \
-       (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,  \
-       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
-}
-
-/* radar types as defined by ETSI EN-301-893 v1.5.1 */
-static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
-       ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
-       ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
-       ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
-       ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
-       ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
-       ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
-       ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
-};
-
-static const struct radar_types etsi_radar_types_v15 = {
-       .region                 = NL80211_DFS_ETSI,
-       .num_radar_types        = ARRAY_SIZE(etsi_radar_ref_types_v15),
-       .radar_types            = etsi_radar_ref_types_v15,
-};
-
-/* for now, we support ETSI radar types, FCC and JP are TODO */
-static const struct radar_types *dfs_domains[] = {
-       &etsi_radar_types_v15,
-};
-
-/**
- * get_dfs_domain_radar_types() - get radar types for a given DFS domain
- * @param domain DFS domain
- * @return radar_types ptr on success, NULL if DFS domain is not supported
- */
-static const struct radar_types *
-get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
-{
-       u32 i;
-       for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
-               if (dfs_domains[i]->region == region)
-                       return dfs_domains[i];
-       }
-       return NULL;
-}
-
-/**
- * struct channel_detector - detector elements for a DFS channel
- * @head: list_head
- * @freq: frequency for this channel detector in MHz
- * @detectors: array of dynamically created detector elements for this freq
- *
- * Channel detectors are required to provide multi-channel DFS detection, e.g.
- * to support off-channel scanning. A pattern detector has a list of channels
- * radar pulses have been reported for in the past.
- */
-struct channel_detector {
-       struct list_head head;
-       u16 freq;
-       struct pri_detector **detectors;
-};
-
-/* channel_detector_reset() - reset detector lines for a given channel */
-static void channel_detector_reset(struct dfs_pattern_detector *dpd,
-                                  struct channel_detector *cd)
-{
-       u32 i;
-       if (cd == NULL)
-               return;
-       for (i = 0; i < dpd->num_radar_types; i++)
-               cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
-}
-
-/* channel_detector_exit() - destructor */
-static void channel_detector_exit(struct dfs_pattern_detector *dpd,
-                                 struct channel_detector *cd)
-{
-       u32 i;
-       if (cd == NULL)
-               return;
-       list_del(&cd->head);
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               struct pri_detector *de = cd->detectors[i];
-               if (de != NULL)
-                       de->exit(de);
-       }
-       kfree(cd->detectors);
-       kfree(cd);
-}
-
-static struct channel_detector *
-channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
-{
-       u32 sz, i;
-       struct channel_detector *cd;
-       struct ath_common *common = ath9k_hw_common(dpd->ah);
-
-       cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
-       if (cd == NULL)
-               goto fail;
-
-       INIT_LIST_HEAD(&cd->head);
-       cd->freq = freq;
-       sz = sizeof(cd->detectors) * dpd->num_radar_types;
-       cd->detectors = kzalloc(sz, GFP_ATOMIC);
-       if (cd->detectors == NULL)
-               goto fail;
-
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               const struct radar_detector_specs *rs = &dpd->radar_spec[i];
-               struct pri_detector *de = pri_detector_init(rs);
-               if (de == NULL)
-                       goto fail;
-               cd->detectors[i] = de;
-       }
-       list_add(&cd->head, &dpd->channel_detectors);
-       return cd;
-
-fail:
-       ath_dbg(common, DFS,
-               "failed to allocate channel_detector for freq=%d\n", freq);
-       channel_detector_exit(dpd, cd);
-       return NULL;
-}
-
-/**
- * channel_detector_get() - get channel detector for given frequency
- * @param dpd instance pointer
- * @param freq frequency in MHz
- * @return pointer to channel detector on success, NULL otherwise
- *
- * Return existing channel detector for the given frequency or return a
- * newly create one.
- */
-static struct channel_detector *
-channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
-{
-       struct channel_detector *cd;
-       list_for_each_entry(cd, &dpd->channel_detectors, head) {
-               if (cd->freq == freq)
-                       return cd;
-       }
-       return channel_detector_create(dpd, freq);
-}
-
-/*
- * DFS Pattern Detector
- */
-
-/* dpd_reset(): reset all channel detectors */
-static void dpd_reset(struct dfs_pattern_detector *dpd)
-{
-       struct channel_detector *cd;
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry(cd, &dpd->channel_detectors, head)
-                       channel_detector_reset(dpd, cd);
-
-}
-static void dpd_exit(struct dfs_pattern_detector *dpd)
-{
-       struct channel_detector *cd, *cd0;
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
-                       channel_detector_exit(dpd, cd);
-       kfree(dpd);
-}
-
-static bool
-dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
-{
-       u32 i;
-       struct channel_detector *cd;
-
-       /*
-        * pulses received for a non-supported or un-initialized
-        * domain are treated as detected radars for fail-safety
-        */
-       if (dpd->region == NL80211_DFS_UNSET)
-               return true;
-
-       cd = channel_detector_get(dpd, event->freq);
-       if (cd == NULL)
-               return false;
-
-       dpd->last_pulse_ts = event->ts;
-       /* reset detector on time stamp wraparound, caused by TSF reset */
-       if (event->ts < dpd->last_pulse_ts)
-               dpd_reset(dpd);
-
-       /* do type individual pattern matching */
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               struct pri_detector *pd = cd->detectors[i];
-               struct pri_sequence *ps = pd->add_pulse(pd, event);
-               if (ps != NULL) {
-                       ath_dbg(ath9k_hw_common(dpd->ah), DFS,
-                               "DFS: radar found on freq=%d: id=%d, pri=%d, "
-                               "count=%d, count_false=%d\n",
-                               event->freq, pd->rs->type_id,
-                               ps->pri, ps->count, ps->count_falses);
-                       channel_detector_reset(dpd, cd);
-                       return true;
-               }
-       }
-       return false;
-}
-
-static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
-                          enum nl80211_dfs_regions region)
-{
-       const struct radar_types *rt;
-       struct channel_detector *cd, *cd0;
-
-       if (dpd->region == region)
-               return true;
-
-       dpd->region = NL80211_DFS_UNSET;
-
-       rt = get_dfs_domain_radar_types(region);
-       if (rt == NULL)
-               return false;
-
-       /* delete all channel detectors for previous DFS domain */
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
-                       channel_detector_exit(dpd, cd);
-       dpd->radar_spec = rt->radar_types;
-       dpd->num_radar_types = rt->num_radar_types;
-
-       dpd->region = region;
-       return true;
-}
-
-static struct dfs_pattern_detector default_dpd = {
-       .exit           = dpd_exit,
-       .set_dfs_domain = dpd_set_domain,
-       .add_pulse      = dpd_add_pulse,
-       .region         = NL80211_DFS_UNSET,
-};
-
-struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region)
-{
-       struct dfs_pattern_detector *dpd;
-       struct ath_common *common = ath9k_hw_common(ah);
-
-       dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
-       if (dpd == NULL)
-               return NULL;
-
-       *dpd = default_dpd;
-       INIT_LIST_HEAD(&dpd->channel_detectors);
-
-       dpd->ah = ah;
-       if (dpd->set_dfs_domain(dpd, region))
-               return dpd;
-
-       ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
-       kfree(dpd);
-       return NULL;
-}
-EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h
deleted file mode 100644 (file)
index 90a5abc..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef DFS_PATTERN_DETECTOR_H
-#define DFS_PATTERN_DETECTOR_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/nl80211.h>
-
-/**
- * struct pulse_event - describing pulses reported by PHY
- * @ts: pulse time stamp in us
- * @freq: channel frequency in MHz
- * @width: pulse duration in us
- * @rssi: rssi of radar event
- */
-struct pulse_event {
-       u64 ts;
-       u16 freq;
-       u8 width;
-       u8 rssi;
-};
-
-/**
- * struct radar_detector_specs - detector specs for a radar pattern type
- * @type_id: pattern type, as defined by regulatory
- * @width_min: minimum radar pulse width in [us]
- * @width_max: maximum radar pulse width in [us]
- * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
- * @pri_max: minimum pri in [us] (including tolerance)
- * @num_pri: maximum number of different pri for this type
- * @ppb: pulses per bursts for this type
- * @ppb_thresh: number of pulses required to trigger detection
- * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
- */
-struct radar_detector_specs {
-       u8 type_id;
-       u8 width_min;
-       u8 width_max;
-       u16 pri_min;
-       u16 pri_max;
-       u8 num_pri;
-       u8 ppb;
-       u8 ppb_thresh;
-       u8 max_pri_tolerance;
-};
-
-/**
- * struct dfs_pattern_detector - DFS pattern detector
- * @exit(): destructor
- * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
- * @add_pulse(): add radar pulse to detector, returns true on detection
- * @region: active DFS region, NL80211_DFS_UNSET until set
- * @num_radar_types: number of different radar types
- * @last_pulse_ts: time stamp of last valid pulse in usecs
- * @radar_detector_specs: array of radar detection specs
- * @channel_detectors: list connecting channel_detector elements
- */
-struct dfs_pattern_detector {
-       void (*exit)(struct dfs_pattern_detector *dpd);
-       bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
-                          enum nl80211_dfs_regions region);
-       bool (*add_pulse)(struct dfs_pattern_detector *dpd,
-                         struct pulse_event *pe);
-
-       enum nl80211_dfs_regions region;
-       u8 num_radar_types;
-       u64 last_pulse_ts;
-       /* needed for ath_dbg() */
-       struct ath_hw *ah;
-
-       const struct radar_detector_specs *radar_spec;
-       struct list_head channel_detectors;
-};
-
-/**
- * dfs_pattern_detector_init() - constructor for pattern detector class
- * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
- * @return instance pointer on success, NULL otherwise
- */
-#if defined(CONFIG_ATH9K_DFS_CERTIFIED)
-extern struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region);
-#else
-static inline struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region)
-{
-       return NULL;
-}
-#endif /* CONFIG_ATH9K_DFS_CERTIFIED */
-
-#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c
deleted file mode 100644 (file)
index 5ba4b6f..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#include "ath9k.h"
-#include "dfs_pattern_detector.h"
-#include "dfs_pri_detector.h"
-#include "dfs_debug.h"
-
-/**
- * struct pulse_elem - elements in pulse queue
- * @ts: time stamp in usecs
- */
-struct pulse_elem {
-       struct list_head head;
-       u64 ts;
-};
-
-/**
- * pde_get_multiple() - get number of multiples considering a given tolerance
- * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
- */
-static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
-{
-       u32 remainder;
-       u32 factor;
-       u32 delta;
-
-       if (fraction == 0)
-               return 0;
-
-       delta = (val < fraction) ? (fraction - val) : (val - fraction);
-
-       if (delta <= tolerance)
-               /* val and fraction are within tolerance */
-               return 1;
-
-       factor = val / fraction;
-       remainder = val % fraction;
-       if (remainder > tolerance) {
-               /* no exact match */
-               if ((fraction - remainder) <= tolerance)
-                       /* remainder is within tolerance */
-                       factor++;
-               else
-                       factor = 0;
-       }
-       return factor;
-}
-
-/**
- * DOC: Singleton Pulse and Sequence Pools
- *
- * Instances of pri_sequence and pulse_elem are kept in singleton pools to
- * reduce the number of dynamic allocations. They are shared between all
- * instances and grow up to the peak number of simultaneously used objects.
- *
- * Memory is freed after all references to the pools are released.
- */
-static u32 singleton_pool_references;
-static LIST_HEAD(pulse_pool);
-static LIST_HEAD(pseq_pool);
-static DEFINE_SPINLOCK(pool_lock);
-
-static void pool_register_ref(void)
-{
-       spin_lock_bh(&pool_lock);
-       singleton_pool_references++;
-       DFS_POOL_STAT_INC(pool_reference);
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_deregister_ref(void)
-{
-       spin_lock_bh(&pool_lock);
-       singleton_pool_references--;
-       DFS_POOL_STAT_DEC(pool_reference);
-       if (singleton_pool_references == 0) {
-               /* free singleton pools with no references left */
-               struct pri_sequence *ps, *ps0;
-               struct pulse_elem *p, *p0;
-
-               list_for_each_entry_safe(p, p0, &pulse_pool, head) {
-                       list_del(&p->head);
-                       DFS_POOL_STAT_DEC(pulse_allocated);
-                       kfree(p);
-               }
-               list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
-                       list_del(&ps->head);
-                       DFS_POOL_STAT_DEC(pseq_allocated);
-                       kfree(ps);
-               }
-       }
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_put_pulse_elem(struct pulse_elem *pe)
-{
-       spin_lock_bh(&pool_lock);
-       list_add(&pe->head, &pulse_pool);
-       DFS_POOL_STAT_DEC(pulse_used);
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_put_pseq_elem(struct pri_sequence *pse)
-{
-       spin_lock_bh(&pool_lock);
-       list_add(&pse->head, &pseq_pool);
-       DFS_POOL_STAT_DEC(pseq_used);
-       spin_unlock_bh(&pool_lock);
-}
-
-static struct pri_sequence *pool_get_pseq_elem(void)
-{
-       struct pri_sequence *pse = NULL;
-       spin_lock_bh(&pool_lock);
-       if (!list_empty(&pseq_pool)) {
-               pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
-               list_del(&pse->head);
-               DFS_POOL_STAT_INC(pseq_used);
-       }
-       spin_unlock_bh(&pool_lock);
-       return pse;
-}
-
-static struct pulse_elem *pool_get_pulse_elem(void)
-{
-       struct pulse_elem *pe = NULL;
-       spin_lock_bh(&pool_lock);
-       if (!list_empty(&pulse_pool)) {
-               pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
-               list_del(&pe->head);
-               DFS_POOL_STAT_INC(pulse_used);
-       }
-       spin_unlock_bh(&pool_lock);
-       return pe;
-}
-
-static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
-{
-       struct list_head *l = &pde->pulses;
-       if (list_empty(l))
-               return NULL;
-       return list_entry(l->prev, struct pulse_elem, head);
-}
-
-static bool pulse_queue_dequeue(struct pri_detector *pde)
-{
-       struct pulse_elem *p = pulse_queue_get_tail(pde);
-       if (p != NULL) {
-               list_del_init(&p->head);
-               pde->count--;
-               /* give it back to pool */
-               pool_put_pulse_elem(p);
-       }
-       return (pde->count > 0);
-}
-
-/* remove pulses older than window */
-static void pulse_queue_check_window(struct pri_detector *pde)
-{
-       u64 min_valid_ts;
-       struct pulse_elem *p;
-
-       /* there is no delta time with less than 2 pulses */
-       if (pde->count < 2)
-               return;
-
-       if (pde->last_ts <= pde->window_size)
-               return;
-
-       min_valid_ts = pde->last_ts - pde->window_size;
-       while ((p = pulse_queue_get_tail(pde)) != NULL) {
-               if (p->ts >= min_valid_ts)
-                       return;
-               pulse_queue_dequeue(pde);
-       }
-}
-
-static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
-{
-       struct pulse_elem *p = pool_get_pulse_elem();
-       if (p == NULL) {
-               p = kmalloc(sizeof(*p), GFP_ATOMIC);
-               if (p == NULL) {
-                       DFS_POOL_STAT_INC(pulse_alloc_error);
-                       return false;
-               }
-               DFS_POOL_STAT_INC(pulse_allocated);
-               DFS_POOL_STAT_INC(pulse_used);
-       }
-       INIT_LIST_HEAD(&p->head);
-       p->ts = ts;
-       list_add(&p->head, &pde->pulses);
-       pde->count++;
-       pde->last_ts = ts;
-       pulse_queue_check_window(pde);
-       if (pde->count >= pde->max_count)
-               pulse_queue_dequeue(pde);
-       return true;
-}
-
-static bool pseq_handler_create_sequences(struct pri_detector *pde,
-                                         u64 ts, u32 min_count)
-{
-       struct pulse_elem *p;
-       list_for_each_entry(p, &pde->pulses, head) {
-               struct pri_sequence ps, *new_ps;
-               struct pulse_elem *p2;
-               u32 tmp_false_count;
-               u64 min_valid_ts;
-               u32 delta_ts = ts - p->ts;
-
-               if (delta_ts < pde->rs->pri_min)
-                       /* ignore too small pri */
-                       continue;
-
-               if (delta_ts > pde->rs->pri_max)
-                       /* stop on too large pri (sorted list) */
-                       break;
-
-               /* build a new sequence with new potential pri */
-               ps.count = 2;
-               ps.count_falses = 0;
-               ps.first_ts = p->ts;
-               ps.last_ts = ts;
-               ps.pri = ts - p->ts;
-               ps.dur = ps.pri * (pde->rs->ppb - 1)
-                               + 2 * pde->rs->max_pri_tolerance;
-
-               p2 = p;
-               tmp_false_count = 0;
-               min_valid_ts = ts - ps.dur;
-               /* check which past pulses are candidates for new sequence */
-               list_for_each_entry_continue(p2, &pde->pulses, head) {
-                       u32 factor;
-                       if (p2->ts < min_valid_ts)
-                               /* stop on crossing window border */
-                               break;
-                       /* check if pulse match (multi)PRI */
-                       factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
-                                                 pde->rs->max_pri_tolerance);
-                       if (factor > 0) {
-                               ps.count++;
-                               ps.first_ts = p2->ts;
-                               /*
-                                * on match, add the intermediate falses
-                                * and reset counter
-                                */
-                               ps.count_falses += tmp_false_count;
-                               tmp_false_count = 0;
-                       } else {
-                               /* this is a potential false one */
-                               tmp_false_count++;
-                       }
-               }
-               if (ps.count < min_count)
-                       /* did not reach minimum count, drop sequence */
-                       continue;
-
-               /* this is a valid one, add it */
-               ps.deadline_ts = ps.first_ts + ps.dur;
-               new_ps = pool_get_pseq_elem();
-               if (new_ps == NULL) {
-                       new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
-                       if (new_ps == NULL) {
-                               DFS_POOL_STAT_INC(pseq_alloc_error);
-                               return false;
-                       }
-                       DFS_POOL_STAT_INC(pseq_allocated);
-                       DFS_POOL_STAT_INC(pseq_used);
-               }
-               memcpy(new_ps, &ps, sizeof(ps));
-               INIT_LIST_HEAD(&new_ps->head);
-               list_add(&new_ps->head, &pde->sequences);
-       }
-       return true;
-}
-
-/* check new ts and add to all matching existing sequences */
-static u32
-pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
-{
-       u32 max_count = 0;
-       struct pri_sequence *ps, *ps2;
-       list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
-               u32 delta_ts;
-               u32 factor;
-
-               /* first ensure that sequence is within window */
-               if (ts > ps->deadline_ts) {
-                       list_del_init(&ps->head);
-                       pool_put_pseq_elem(ps);
-                       continue;
-               }
-
-               delta_ts = ts - ps->last_ts;
-               factor = pde_get_multiple(delta_ts, ps->pri,
-                                         pde->rs->max_pri_tolerance);
-               if (factor > 0) {
-                       ps->last_ts = ts;
-                       ps->count++;
-
-                       if (max_count < ps->count)
-                               max_count = ps->count;
-               } else {
-                       ps->count_falses++;
-               }
-       }
-       return max_count;
-}
-
-static struct pri_sequence *
-pseq_handler_check_detection(struct pri_detector *pde)
-{
-       struct pri_sequence *ps;
-
-       if (list_empty(&pde->sequences))
-               return NULL;
-
-       list_for_each_entry(ps, &pde->sequences, head) {
-               /*
-                * we assume to have enough matching confidence if we
-                * 1) have enough pulses
-                * 2) have more matching than false pulses
-                */
-               if ((ps->count >= pde->rs->ppb_thresh) &&
-                   (ps->count * pde->rs->num_pri >= ps->count_falses))
-                       return ps;
-       }
-       return NULL;
-}
-
-
-/* free pulse queue and sequences list and give objects back to pools */
-static void pri_detector_reset(struct pri_detector *pde, u64 ts)
-{
-       struct pri_sequence *ps, *ps0;
-       struct pulse_elem *p, *p0;
-       list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
-               list_del_init(&ps->head);
-               pool_put_pseq_elem(ps);
-       }
-       list_for_each_entry_safe(p, p0, &pde->pulses, head) {
-               list_del_init(&p->head);
-               pool_put_pulse_elem(p);
-       }
-       pde->count = 0;
-       pde->last_ts = ts;
-}
-
-static void pri_detector_exit(struct pri_detector *de)
-{
-       pri_detector_reset(de, 0);
-       pool_deregister_ref();
-       kfree(de);
-}
-
-static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
-                                                  struct pulse_event *event)
-{
-       u32 max_updated_seq;
-       struct pri_sequence *ps;
-       u64 ts = event->ts;
-       const struct radar_detector_specs *rs = de->rs;
-
-       /* ignore pulses not within width range */
-       if ((rs->width_min > event->width) || (rs->width_max < event->width))
-               return NULL;
-
-       if ((ts - de->last_ts) < rs->max_pri_tolerance)
-               /* if delta to last pulse is too short, don't use this pulse */
-               return NULL;
-       de->last_ts = ts;
-
-       max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
-
-       if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
-               pri_detector_reset(de, ts);
-               return false;
-       }
-
-       ps = pseq_handler_check_detection(de);
-
-       if (ps == NULL)
-               pulse_queue_enqueue(de, ts);
-
-       return ps;
-}
-
-struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
-{
-       struct pri_detector *de;
-
-       de = kzalloc(sizeof(*de), GFP_ATOMIC);
-       if (de == NULL)
-               return NULL;
-       de->exit = pri_detector_exit;
-       de->add_pulse = pri_detector_add_pulse;
-       de->reset = pri_detector_reset;
-
-       INIT_LIST_HEAD(&de->sequences);
-       INIT_LIST_HEAD(&de->pulses);
-       de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
-       de->max_count = rs->ppb * 2;
-       de->rs = rs;
-
-       pool_register_ref();
-       return de;
-}
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h
deleted file mode 100644 (file)
index 723962d..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef DFS_PRI_DETECTOR_H
-#define DFS_PRI_DETECTOR_H
-
-#include <linux/list.h>
-
-/**
- * struct pri_sequence - sequence of pulses matching one PRI
- * @head: list_head
- * @pri: pulse repetition interval (PRI) in usecs
- * @dur: duration of sequence in usecs
- * @count: number of pulses in this sequence
- * @count_falses: number of not matching pulses in this sequence
- * @first_ts: time stamp of first pulse in usecs
- * @last_ts: time stamp of last pulse in usecs
- * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
- */
-struct pri_sequence {
-       struct list_head head;
-       u32 pri;
-       u32 dur;
-       u32 count;
-       u32 count_falses;
-       u64 first_ts;
-       u64 last_ts;
-       u64 deadline_ts;
-};
-
-/**
- * struct pri_detector - PRI detector element for a dedicated radar type
- * @exit(): destructor
- * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
- * @reset(): clear states and reset to given time stamp
- * @rs: detector specs for this detector element
- * @last_ts: last pulse time stamp considered for this element in usecs
- * @sequences: list_head holding potential pulse sequences
- * @pulses: list connecting pulse_elem objects
- * @count: number of pulses in queue
- * @max_count: maximum number of pulses to be queued
- * @window_size: window size back from newest pulse time stamp in usecs
- */
-struct pri_detector {
-       void (*exit)     (struct pri_detector *de);
-       struct pri_sequence *
-            (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
-       void (*reset)    (struct pri_detector *de, u64 ts);
-
-/* private: internal use only */
-       const struct radar_detector_specs *rs;
-       u64 last_ts;
-       struct list_head sequences;
-       struct list_head pulses;
-       u32 count;
-       u32 max_count;
-       u32 window_size;
-};
-
-struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs);
-
-#endif /* DFS_PRI_DETECTOR_H */
index 9ea8e4b..b409171 100644 (file)
@@ -129,10 +129,10 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        struct base_eep_header_4k *pBase = &eep->baseEepHeader;
 
        if (!dump_base_hdr) {
-               len += snprintf(buf + len, size - len,
-                               "%20s :\n", "2GHz modal Header");
+               len += scnprintf(buf + len, size - len,
+                                "%20s :\n", "2GHz modal Header");
                len = ath9k_dump_4k_modal_eeprom(buf, len, size,
-                                                 &eep->modalHeader);
+                                                &eep->modalHeader);
                goto out;
        }
 
@@ -160,8 +160,8 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
        PR_EEP("TX Gain type", pBase->txGainType);
 
-       len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                       pBase->macAddr);
+       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+                        pBase->macAddr);
 
 out:
        if (len > size)
index 3ae1f3d..e1d0c21 100644 (file)
@@ -125,8 +125,8 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        struct base_eep_ar9287_header *pBase = &eep->baseEepHeader;
 
        if (!dump_base_hdr) {
-               len += snprintf(buf + len, size - len,
-                               "%20s :\n", "2GHz modal Header");
+               len += scnprintf(buf + len, size - len,
+                                "%20s :\n", "2GHz modal Header");
                len = ar9287_dump_modal_eeprom(buf, len, size,
                                                &eep->modalHeader);
                goto out;
@@ -157,8 +157,8 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        PR_EEP("Power Table Offset", pBase->pwrTableOffset);
        PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl);
 
-       len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                       pBase->macAddr);
+       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+                        pBase->macAddr);
 
 out:
        if (len > size)
index 1c25368..39107e3 100644 (file)
@@ -205,12 +205,12 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        struct base_eep_header *pBase = &eep->baseEepHeader;
 
        if (!dump_base_hdr) {
-               len += snprintf(buf + len, size - len,
-                               "%20s :\n", "2GHz modal Header");
+               len += scnprintf(buf + len, size - len,
+                                "%20s :\n", "2GHz modal Header");
                len = ath9k_def_dump_modal_eeprom(buf, len, size,
                                                   &eep->modalHeader[0]);
-               len += snprintf(buf + len, size - len,
-                               "%20s :\n", "5GHz modal Header");
+               len += scnprintf(buf + len, size - len,
+                                "%20s :\n", "5GHz modal Header");
                len = ath9k_def_dump_modal_eeprom(buf, len, size,
                                                   &eep->modalHeader[1]);
                goto out;
@@ -240,8 +240,8 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
        PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF);
        PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl);
 
-       len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                       pBase->macAddr);
+       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+                        pBase->macAddr);
 
 out:
        if (len > size)
index 4b412aa..c34f212 100644 (file)
@@ -522,22 +522,22 @@ static int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
        ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx);
        ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count);
 
-       len += snprintf(buf + len, size - len, "BT Weights: ");
+       len += scnprintf(buf + len, size - len, "BT Weights: ");
        for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
-               len += snprintf(buf + len, size - len, "%08x ",
-                               btcoex_hw->bt_weight[i]);
-       len += snprintf(buf + len, size - len, "\n");
-       len += snprintf(buf + len, size - len, "WLAN Weights: ");
+               len += scnprintf(buf + len, size - len, "%08x ",
+                                btcoex_hw->bt_weight[i]);
+       len += scnprintf(buf + len, size - len, "\n");
+       len += scnprintf(buf + len, size - len, "WLAN Weights: ");
        for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++)
-               len += snprintf(buf + len, size - len, "%08x ",
-                               btcoex_hw->wlan_weight[i]);
-       len += snprintf(buf + len, size - len, "\n");
-       len += snprintf(buf + len, size - len, "Tx Priorities: ");
+               len += scnprintf(buf + len, size - len, "%08x ",
+                                btcoex_hw->wlan_weight[i]);
+       len += scnprintf(buf + len, size - len, "\n");
+       len += scnprintf(buf + len, size - len, "Tx Priorities: ");
        for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++)
-               len += snprintf(buf + len, size - len, "%08x ",
+               len += scnprintf(buf + len, size - len, "%08x ",
                                btcoex_hw->tx_prio[i]);
 
-       len += snprintf(buf + len, size - len, "\n");
+       len += scnprintf(buf + len, size - len, "\n");
 
        return len;
 }
index c1b45e2..fb071ee 100644 (file)
@@ -37,29 +37,29 @@ static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf,
 
        ath9k_htc_ps_restore(priv);
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "RX",
-                       be32_to_cpu(cmd_rsp.rx));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "RX",
+                        be32_to_cpu(cmd_rsp.rx));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "RXORN",
-                       be32_to_cpu(cmd_rsp.rxorn));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "RXORN",
+                        be32_to_cpu(cmd_rsp.rxorn));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "RXEOL",
-                       be32_to_cpu(cmd_rsp.rxeol));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "RXEOL",
+                        be32_to_cpu(cmd_rsp.rxeol));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "TXURN",
-                       be32_to_cpu(cmd_rsp.txurn));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "TXURN",
+                        be32_to_cpu(cmd_rsp.txurn));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "TXTO",
-                       be32_to_cpu(cmd_rsp.txto));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "TXTO",
+                        be32_to_cpu(cmd_rsp.txto));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "CST",
-                       be32_to_cpu(cmd_rsp.cst));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "CST",
+                        be32_to_cpu(cmd_rsp.cst));
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -95,41 +95,41 @@ static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf,
 
        ath9k_htc_ps_restore(priv);
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "Xretries",
-                       be32_to_cpu(cmd_rsp.xretries));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "Xretries",
+                        be32_to_cpu(cmd_rsp.xretries));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "FifoErr",
-                       be32_to_cpu(cmd_rsp.fifoerr));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "FifoErr",
+                        be32_to_cpu(cmd_rsp.fifoerr));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "Filtered",
-                       be32_to_cpu(cmd_rsp.filtered));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "Filtered",
+                        be32_to_cpu(cmd_rsp.filtered));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "TimerExp",
-                       be32_to_cpu(cmd_rsp.timer_exp));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "TimerExp",
+                        be32_to_cpu(cmd_rsp.timer_exp));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "ShortRetries",
-                       be32_to_cpu(cmd_rsp.shortretries));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "ShortRetries",
+                        be32_to_cpu(cmd_rsp.shortretries));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "LongRetries",
-                       be32_to_cpu(cmd_rsp.longretries));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "LongRetries",
+                        be32_to_cpu(cmd_rsp.longretries));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "QueueNull",
-                       be32_to_cpu(cmd_rsp.qnull));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "QueueNull",
+                        be32_to_cpu(cmd_rsp.qnull));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "EncapFail",
-                       be32_to_cpu(cmd_rsp.encap_fail));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "EncapFail",
+                        be32_to_cpu(cmd_rsp.encap_fail));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "NoBuf",
-                       be32_to_cpu(cmd_rsp.nobuf));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "NoBuf",
+                        be32_to_cpu(cmd_rsp.nobuf));
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -165,17 +165,17 @@ static ssize_t read_file_tgt_rx_stats(struct file *file, char __user *user_buf,
 
        ath9k_htc_ps_restore(priv);
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "NoBuf",
-                       be32_to_cpu(cmd_rsp.nobuf));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "NoBuf",
+                        be32_to_cpu(cmd_rsp.nobuf));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "HostSend",
-                       be32_to_cpu(cmd_rsp.host_send));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "HostSend",
+                        be32_to_cpu(cmd_rsp.host_send));
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "HostDone",
-                       be32_to_cpu(cmd_rsp.host_done));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "HostDone",
+                        be32_to_cpu(cmd_rsp.host_done));
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -197,37 +197,37 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
        char buf[512];
        unsigned int len = 0;
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "Buffers queued",
-                       priv->debug.tx_stats.buf_queued);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "Buffers completed",
-                       priv->debug.tx_stats.buf_completed);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "SKBs queued",
-                       priv->debug.tx_stats.skb_queued);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "SKBs success",
-                       priv->debug.tx_stats.skb_success);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "SKBs failed",
-                       priv->debug.tx_stats.skb_failed);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "CAB queued",
-                       priv->debug.tx_stats.cab_queued);
-
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "BE queued",
-                       priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "BK queued",
-                       priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "VI queued",
-                       priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "%20s : %10u\n", "VO queued",
-                       priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "Buffers queued",
+                        priv->debug.tx_stats.buf_queued);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "Buffers completed",
+                        priv->debug.tx_stats.buf_completed);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "SKBs queued",
+                        priv->debug.tx_stats.skb_queued);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "SKBs success",
+                        priv->debug.tx_stats.skb_success);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "SKBs failed",
+                        priv->debug.tx_stats.skb_failed);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "CAB queued",
+                        priv->debug.tx_stats.cab_queued);
+
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "BE queued",
+                        priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "BK queued",
+                        priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "VI queued",
+                        priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]);
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "%20s : %10u\n", "VO queued",
+                        priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]);
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -273,8 +273,8 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
 #define PHY_ERR(s, p)                                                  \
-       len += snprintf(buf + len, size - len, "%20s : %10u\n", s,      \
-                       priv->debug.rx_stats.err_phy_stats[p]);
+       len += scnprintf(buf + len, size - len, "%20s : %10u\n", s,     \
+                        priv->debug.rx_stats.err_phy_stats[p]);
 
        struct ath9k_htc_priv *priv = file->private_data;
        char *buf;
@@ -285,37 +285,37 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
        if (buf == NULL)
                return -ENOMEM;
 
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "SKBs allocated",
-                       priv->debug.rx_stats.skb_allocated);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "SKBs completed",
-                       priv->debug.rx_stats.skb_completed);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "SKBs Dropped",
-                       priv->debug.rx_stats.skb_dropped);
-
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "CRC ERR",
-                       priv->debug.rx_stats.err_crc);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "DECRYPT CRC ERR",
-                       priv->debug.rx_stats.err_decrypt_crc);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "MIC ERR",
-                       priv->debug.rx_stats.err_mic);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "PRE-DELIM CRC ERR",
-                       priv->debug.rx_stats.err_pre_delim);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "POST-DELIM CRC ERR",
-                       priv->debug.rx_stats.err_post_delim);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "DECRYPT BUSY ERR",
-                       priv->debug.rx_stats.err_decrypt_busy);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10u\n", "TOTAL PHY ERR",
-                       priv->debug.rx_stats.err_phy);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "SKBs allocated",
+                        priv->debug.rx_stats.skb_allocated);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "SKBs completed",
+                        priv->debug.rx_stats.skb_completed);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "SKBs Dropped",
+                        priv->debug.rx_stats.skb_dropped);
+
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "CRC ERR",
+                        priv->debug.rx_stats.err_crc);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "DECRYPT CRC ERR",
+                        priv->debug.rx_stats.err_decrypt_crc);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "MIC ERR",
+                        priv->debug.rx_stats.err_mic);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "PRE-DELIM CRC ERR",
+                        priv->debug.rx_stats.err_pre_delim);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "POST-DELIM CRC ERR",
+                        priv->debug.rx_stats.err_post_delim);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "DECRYPT BUSY ERR",
+                        priv->debug.rx_stats.err_decrypt_busy);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10u\n", "TOTAL PHY ERR",
+                        priv->debug.rx_stats.err_phy);
 
 
        PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN);
@@ -372,16 +372,16 @@ static ssize_t read_file_slot(struct file *file, char __user *user_buf,
 
        spin_lock_bh(&priv->tx.tx_lock);
 
-       len += snprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : ");
+       len += scnprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : ");
 
        len += bitmap_scnprintf(buf + len, sizeof(buf) - len,
                               priv->tx.tx_slot, MAX_TX_BUF_NUM);
 
-       len += snprintf(buf + len, sizeof(buf) - len, "\n");
+       len += scnprintf(buf + len, sizeof(buf) - len, "\n");
 
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "Used slots     : %d\n",
-                       bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM));
+       len += scnprintf(buf + len, sizeof(buf) - len,
+                        "Used slots     : %d\n",
+                        bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM));
 
        spin_unlock_bh(&priv->tx.tx_lock);
 
@@ -405,30 +405,30 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf,
        char buf[512];
        unsigned int len = 0;
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue));
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue));
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue));
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue));
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue));
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue));
 
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Failed queue", skb_queue_len(&priv->tx.tx_failed));
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Failed queue", skb_queue_len(&priv->tx.tx_failed));
 
        spin_lock_bh(&priv->tx.tx_lock);
-       len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
-                       "Queued count", priv->tx.queued_cnt);
+       len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n",
+                        "Queued count", priv->tx.queued_cnt);
        spin_unlock_bh(&priv->tx.tx_lock);
 
        if (len > sizeof(buf))
@@ -507,70 +507,70 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
        if (buf == NULL)
                return -ENOMEM;
 
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n", "Major Version",
-                       pBase->version >> 12);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n", "Minor Version",
-                       pBase->version & 0xFFF);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n", "Checksum",
-                       pBase->checksum);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n", "Length",
-                       pBase->length);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n", "RegDomain1",
-                       pBase->regDmn[0]);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n", "RegDomain2",
-                       pBase->regDmn[1]);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "TX Mask", pBase->txMask);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "RX Mask", pBase->rxMask);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Allow 5GHz",
-                       !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Allow 2GHz",
-                       !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Disable 2GHz HT20",
-                       !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Disable 2GHz HT40",
-                       !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Disable 5Ghz HT20",
-                       !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Disable 5Ghz HT40",
-                       !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Big Endian",
-                       !!(pBase->eepMisc & 0x01));
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Cal Bin Major Ver",
-                       (pBase->binBuildNumber >> 24) & 0xFF);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Cal Bin Minor Ver",
-                       (pBase->binBuildNumber >> 16) & 0xFF);
-       len += snprintf(buf + len, size - len,
-                       "%20s : %10d\n",
-                       "Cal Bin Build",
-                       (pBase->binBuildNumber >> 8) & 0xFF);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n", "Major Version",
+                        pBase->version >> 12);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n", "Minor Version",
+                        pBase->version & 0xFFF);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n", "Checksum",
+                        pBase->checksum);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n", "Length",
+                        pBase->length);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n", "RegDomain1",
+                        pBase->regDmn[0]);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n", "RegDomain2",
+                        pBase->regDmn[1]);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "TX Mask", pBase->txMask);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "RX Mask", pBase->rxMask);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Allow 5GHz",
+                        !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Allow 2GHz",
+                        !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Disable 2GHz HT20",
+                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Disable 2GHz HT40",
+                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Disable 5Ghz HT20",
+                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Disable 5Ghz HT40",
+                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Big Endian",
+                        !!(pBase->eepMisc & 0x01));
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Cal Bin Major Ver",
+                        (pBase->binBuildNumber >> 24) & 0xFF);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Cal Bin Minor Ver",
+                        (pBase->binBuildNumber >> 16) & 0xFF);
+       len += scnprintf(buf + len, size - len,
+                        "%20s : %10d\n",
+                        "Cal Bin Build",
+                        (pBase->binBuildNumber >> 8) & 0xFF);
 
        /*
         * UB91 specific data.
@@ -579,10 +579,10 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
                struct base_eep_header_4k *pBase4k =
                        &priv->ah->eeprom.map4k.baseEepHeader;
 
-               len += snprintf(buf + len, size - len,
-                               "%20s : %10d\n",
-                               "TX Gain type",
-                               pBase4k->txGainType);
+               len += scnprintf(buf + len, size - len,
+                                "%20s : %10d\n",
+                                "TX Gain type",
+                                pBase4k->txGainType);
        }
 
        /*
@@ -592,19 +592,19 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
                struct base_eep_ar9287_header *pBase9287 =
                        &priv->ah->eeprom.map9287.baseEepHeader;
 
-               len += snprintf(buf + len, size - len,
-                               "%20s : %10ddB\n",
-                               "Power Table Offset",
-                               pBase9287->pwrTableOffset);
+               len += scnprintf(buf + len, size - len,
+                                "%20s : %10ddB\n",
+                                "Power Table Offset",
+                                pBase9287->pwrTableOffset);
 
-               len += snprintf(buf + len, size - len,
-                               "%20s : %10d\n",
-                               "OpenLoop Power Ctrl",
-                               pBase9287->openLoopPwrCntl);
+               len += scnprintf(buf + len, size - len,
+                                "%20s : %10d\n",
+                                "OpenLoop Power Ctrl",
+                                pBase9287->openLoopPwrCntl);
        }
 
-       len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                       pBase->macAddr);
+       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
+                        pBase->macAddr);
        if (len > size)
                len = size;
 
@@ -627,8 +627,8 @@ static ssize_t read_4k_modal_eeprom(struct file *file,
 {
 #define PR_EEP(_s, _val)                                               \
        do {                                                            \
-               len += snprintf(buf + len, size - len, "%20s : %10d\n", \
-                               _s, (_val));                            \
+               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
+                                _s, (_val));                           \
        } while (0)
 
        struct ath9k_htc_priv *priv = file->private_data;
@@ -708,12 +708,12 @@ static ssize_t read_def_modal_eeprom(struct file *file,
        do {                                                            \
                if (pBase->opCapFlags & AR5416_OPFLAGS_11G) {           \
                        pModal = &priv->ah->eeprom.def.modalHeader[1];  \
-                       len += snprintf(buf + len, size - len, "%20s : %8d%7s", \
-                                       _s, (_val), "|");               \
+                       len += scnprintf(buf + len, size - len, "%20s : %8d%7s", \
+                                        _s, (_val), "|");              \
                }                                                       \
                if (pBase->opCapFlags & AR5416_OPFLAGS_11A) {           \
                        pModal = &priv->ah->eeprom.def.modalHeader[0];  \
-                       len += snprintf(buf + len, size - len, "%9d\n", \
+                       len += scnprintf(buf + len, size - len, "%9d\n",\
                                        (_val));                        \
                }                                                       \
        } while (0)
@@ -729,10 +729,10 @@ static ssize_t read_def_modal_eeprom(struct file *file,
        if (buf == NULL)
                return -ENOMEM;
 
-       len += snprintf(buf + len, size - len,
-                       "%31s %15s\n", "2G", "5G");
-       len += snprintf(buf + len, size - len,
-                       "%32s %16s\n", "====", "====\n");
+       len += scnprintf(buf + len, size - len,
+                        "%31s %15s\n", "2G", "5G");
+       len += scnprintf(buf + len, size - len,
+                        "%32s %16s\n", "====", "====\n");
 
        PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]);
        PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]);
@@ -814,8 +814,8 @@ static ssize_t read_9287_modal_eeprom(struct file *file,
 {
 #define PR_EEP(_s, _val)                                               \
        do {                                                            \
-               len += snprintf(buf + len, size - len, "%20s : %10d\n", \
-                               _s, (_val));                            \
+               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
+                                _s, (_val));                           \
        } while (0)
 
        struct ath9k_htc_priv *priv = file->private_data;
index d442581..9a2657f 100644 (file)
 static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
                                              struct ath9k_channel *ichan)
 {
-       enum htc_phymode mode;
-
-       mode = -EINVAL;
-
-       switch (ichan->chanmode) {
-       case CHANNEL_G:
-       case CHANNEL_G_HT20:
-       case CHANNEL_G_HT40PLUS:
-       case CHANNEL_G_HT40MINUS:
-               mode = HTC_MODE_11NG;
-               break;
-       case CHANNEL_A:
-       case CHANNEL_A_HT20:
-       case CHANNEL_A_HT40PLUS:
-       case CHANNEL_A_HT40MINUS:
-               mode = HTC_MODE_11NA;
-               break;
-       default:
-               break;
-       }
+       if (IS_CHAN_5GHZ(ichan))
+               return HTC_MODE_11NA;
 
-       WARN_ON(mode < 0);
-
-       return mode;
+       return HTC_MODE_11NG;
 }
 
 bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
@@ -926,7 +906,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
        WMI_CMD(WMI_FLUSH_RECV_CMDID);
 
        /* setup initial channel */
-       init_channel = ath9k_cmn_get_curchannel(hw, ah);
+       init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
 
        ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
        if (ret) {
@@ -1208,9 +1188,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
                ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
                        curchan->center_freq);
 
-               ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
-                                         &hw->conf.chandef);
-
+               ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef);
                if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
                        ath_err(common, "Unable to set channel\n");
                        ret = -EINVAL;
index 83f4927..4f9378d 100644 (file)
@@ -78,6 +78,22 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
 }
 
+static inline void ath9k_hw_tx99_start(struct ath_hw *ah, u32 qnum)
+{
+       ath9k_hw_ops(ah)->tx99_start(ah, qnum);
+}
+
+static inline void ath9k_hw_tx99_stop(struct ath_hw *ah)
+{
+       ath9k_hw_ops(ah)->tx99_stop(ah);
+}
+
+static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power)
+{
+       if (ath9k_hw_ops(ah)->tx99_set_txpower)
+               ath9k_hw_ops(ah)->tx99_set_txpower(ah, power);
+}
+
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 
 static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
index ecc6ec4..54b0415 100644 (file)
@@ -130,29 +130,29 @@ void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause)
 
 static void ath9k_hw_set_clockrate(struct ath_hw *ah)
 {
-       struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_channel *chan = ah->curchan;
        unsigned int clockrate;
 
        /* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */
        if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah))
                clockrate = 117;
-       else if (!ah->curchan) /* should really check for CCK instead */
+       else if (!chan) /* should really check for CCK instead */
                clockrate = ATH9K_CLOCK_RATE_CCK;
-       else if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ)
+       else if (IS_CHAN_2GHZ(chan))
                clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
        else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
                clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
        else
                clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
 
-       if (conf_is_ht40(conf))
+       if (IS_CHAN_HT40(chan))
                clockrate *= 2;
 
        if (ah->curchan) {
-               if (IS_CHAN_HALF_RATE(ah->curchan))
+               if (IS_CHAN_HALF_RATE(chan))
                        clockrate /= 2;
-               if (IS_CHAN_QUARTER_RATE(ah->curchan))
+               if (IS_CHAN_QUARTER_RATE(chan))
                        clockrate /= 4;
        }
 
@@ -190,10 +190,7 @@ EXPORT_SYMBOL(ath9k_hw_wait);
 void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
                          int hw_delay)
 {
-       if (IS_CHAN_B(chan))
-               hw_delay = (4 * hw_delay) / 22;
-       else
-               hw_delay /= 10;
+       hw_delay /= 10;
 
        if (IS_CHAN_HALF_RATE(chan))
                hw_delay *= 2;
@@ -294,8 +291,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
                return;
        }
 
-       if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
-           (chan->chanmode == CHANNEL_G_HT40PLUS)) {
+       if (IS_CHAN_HT40PLUS(chan)) {
                centers->synth_center =
                        chan->channel + HT40_CHANNEL_CENTER_SHIFT;
                extoff = 1;
@@ -549,6 +545,18 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
 
        ath9k_hw_ani_init(ah);
 
+       /*
+        * EEPROM needs to be initialized before we do this.
+        * This is required for regulatory compliance.
+        */
+       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+               u16 regdmn = ah->eep_ops->get_eeprom(ah, EEP_REG_0);
+               if ((regdmn & 0xF0) == CTL_FCC) {
+                       ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_2GHZ;
+                       ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_5GHZ;
+               }
+       }
+
        return 0;
 }
 
@@ -1030,7 +1038,6 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
 void ath9k_hw_init_global_settings(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
        const struct ath9k_channel *chan = ah->curchan;
        int acktimeout, ctstimeout, ack_offset = 0;
        int slottime;
@@ -1105,8 +1112,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
         * BA frames in some implementations, but it has been found to fix ACK
         * timeout issues in other cases as well.
         */
-       if (conf->chandef.chan &&
-           conf->chandef.chan->band == IEEE80211_BAND_2GHZ &&
+       if (IS_CHAN_2GHZ(chan) &&
            !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) {
                acktimeout += 64 - sifstime - ah->slottime;
                ctstimeout += 48 - sifstime - ah->slottime;
@@ -1148,9 +1154,7 @@ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan)
 {
        u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band);
 
-       if (IS_CHAN_B(chan))
-               ctl |= CTL_11B;
-       else if (IS_CHAN_G(chan))
+       if (IS_CHAN_2GHZ(chan))
                ctl |= CTL_11G;
        else
                ctl |= CTL_11A;
@@ -1498,10 +1502,8 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        int r;
 
        if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
-               u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
-               u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
-               band_switch = (cur != new);
-               mode_diff = (chan->chanmode != ah->curchan->chanmode);
+               band_switch = IS_CHAN_5GHZ(ah->curchan) != IS_CHAN_5GHZ(chan);
+               mode_diff = (chan->channelFlags != ah->curchan->channelFlags);
        }
 
        for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
@@ -1540,9 +1542,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        ath9k_hw_set_clockrate(ah);
        ath9k_hw_apply_txpower(ah, chan, false);
 
-       if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
-               ath9k_hw_set_delta_slope(ah, chan);
-
+       ath9k_hw_set_delta_slope(ah, chan);
        ath9k_hw_spur_mitigate_freq(ah, chan);
 
        if (band_switch || ini_reloaded)
@@ -1644,6 +1644,19 @@ hang_check_iter:
        return true;
 }
 
+void ath9k_hw_check_nav(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       u32 val;
+
+       val = REG_READ(ah, AR_NAV);
+       if (val != 0xdeadbeef && val > 0x7fff) {
+               ath_dbg(common, BSTUCK, "Abnormal NAV: 0x%x\n", val);
+               REG_WRITE(ah, AR_NAV, 0);
+       }
+}
+EXPORT_SYMBOL(ath9k_hw_check_nav);
+
 bool ath9k_hw_check_alive(struct ath_hw *ah)
 {
        int count = 50;
@@ -1799,20 +1812,11 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
                goto fail;
 
        /*
-        * If cross-band fcc is not supoprted, bail out if
-        * either channelFlags or chanmode differ.
-        *
-        * chanmode will be different if the HT operating mode
-        * changes because of CSA.
+        * If cross-band fcc is not supoprted, bail out if channelFlags differ.
         */
-       if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) {
-               if ((chan->channelFlags & CHANNEL_ALL) !=
-                   (ah->curchan->channelFlags & CHANNEL_ALL))
-                       goto fail;
-
-               if (chan->chanmode != ah->curchan->chanmode)
-                       goto fail;
-       }
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) &&
+           chan->channelFlags != ah->curchan->channelFlags)
+               goto fail;
 
        if (!ath9k_hw_check_alive(ah))
                goto fail;
@@ -1822,9 +1826,9 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
         * re-using are present.
         */
        if (AR_SREV_9462(ah) && (ah->caldata &&
-                                (!ah->caldata->done_txiqcal_once ||
-                                 !ah->caldata->done_txclcal_once ||
-                                 !ah->caldata->rtt_done)))
+                                (!test_bit(TXIQCAL_DONE, &ah->caldata->cal_flags) ||
+                                 !test_bit(TXCLCAL_DONE, &ah->caldata->cal_flags) ||
+                                 !test_bit(RTT_DONE, &ah->caldata->cal_flags))))
                goto fail;
 
        ath_dbg(common, RESET, "FastChannelChange for %d -> %d\n",
@@ -1874,15 +1878,14 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ah->caldata = caldata;
        if (caldata && (chan->channel != caldata->channel ||
-                       chan->channelFlags != caldata->channelFlags ||
-                       chan->chanmode != caldata->chanmode)) {
+                       chan->channelFlags != caldata->channelFlags)) {
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
        } else if (caldata) {
-               caldata->paprd_packet_sent = false;
+               clear_bit(PAPRD_PACKET_SENT, &caldata->cal_flags);
        }
-       ah->noise = ath9k_hw_getchan_noise(ah, chan);
+       ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor);
 
        if (fastcc) {
                r = ath9k_hw_do_fastcc(ah, chan);
@@ -1964,9 +1967,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ath9k_hw_init_mfp(ah);
 
-       if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
-               ath9k_hw_set_delta_slope(ah, chan);
-
+       ath9k_hw_set_delta_slope(ah, chan);
        ath9k_hw_spur_mitigate_freq(ah, chan);
        ah->eep_ops->set_board_values(ah, chan);
 
@@ -2017,8 +2018,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        ath9k_hw_init_bb(ah, chan);
 
        if (caldata) {
-               caldata->done_txiqcal_once = false;
-               caldata->done_txclcal_once = false;
+               clear_bit(TXIQCAL_DONE, &caldata->cal_flags);
+               clear_bit(TXCLCAL_DONE, &caldata->cal_flags);
        }
        if (!ath9k_hw_init_cal(ah, chan))
                return -EIO;
@@ -2943,12 +2944,11 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set)
 }
 EXPORT_SYMBOL(ath9k_hw_set_tsfadjust);
 
-void ath9k_hw_set11nmac2040(struct ath_hw *ah)
+void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan)
 {
-       struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
        u32 macmode;
 
-       if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca)
+       if (IS_CHAN_HT40(chan) && !ah->config.cwm_ignore_extcca)
                macmode = AR_2040_JOINED_RX_CLEAR;
        else
                macmode = 0;
@@ -3240,19 +3240,19 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
 
        /* chipsets >= AR9280 are single-chip */
        if (AR_SREV_9280_20_OR_LATER(ah)) {
-               used = snprintf(hw_name, len,
-                              "Atheros AR%s Rev:%x",
-                              ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
-                              ah->hw_version.macRev);
+               used = scnprintf(hw_name, len,
+                                "Atheros AR%s Rev:%x",
+                                ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
+                                ah->hw_version.macRev);
        }
        else {
-               used = snprintf(hw_name, len,
-                              "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x",
-                              ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
-                              ah->hw_version.macRev,
-                              ath9k_hw_rf_name((ah->hw_version.analog5GhzRev &
-                                               AR_RADIO_SREV_MAJOR)),
-                              ah->hw_version.phyRev);
+               used = scnprintf(hw_name, len,
+                                "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x",
+                                ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
+                                ah->hw_version.macRev,
+                                ath9k_hw_rf_name((ah->hw_version.analog5GhzRev
+                                                 & AR_RADIO_SREV_MAJOR)),
+                                ah->hw_version.phyRev);
        }
 
        hw_name[used] = '\0';
index 69a907b..a2c9a5d 100644 (file)
@@ -98,8 +98,8 @@
 
 #define PR_EEP(_s, _val)                                               \
        do {                                                            \
-               len += snprintf(buf + len, size - len, "%20s : %10d\n", \
-                               _s, (_val));                            \
+               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
+                                _s, (_val));                           \
        } while (0)
 
 #define SM(_v, _f)  (((_v) << _f##_S) & _f)
@@ -316,6 +316,7 @@ struct ath9k_ops_config {
        u32 ant_ctrl_comm2g_switch_enable;
        bool xatten_margin_cfg;
        bool alt_mingainidx;
+       bool no_pll_pwrsave;
 };
 
 enum ath9k_int {
@@ -369,55 +370,30 @@ enum ath9k_int {
        ATH9K_INT_NOCARD = 0xffffffff
 };
 
-#define CHANNEL_CCK       0x00020
-#define CHANNEL_OFDM      0x00040
-#define CHANNEL_2GHZ      0x00080
-#define CHANNEL_5GHZ      0x00100
-#define CHANNEL_PASSIVE   0x00200
-#define CHANNEL_DYN       0x00400
-#define CHANNEL_HALF      0x04000
-#define CHANNEL_QUARTER   0x08000
-#define CHANNEL_HT20      0x10000
-#define CHANNEL_HT40PLUS  0x20000
-#define CHANNEL_HT40MINUS 0x40000
-
-#define CHANNEL_A           (CHANNEL_5GHZ|CHANNEL_OFDM)
-#define CHANNEL_B           (CHANNEL_2GHZ|CHANNEL_CCK)
-#define CHANNEL_G           (CHANNEL_2GHZ|CHANNEL_OFDM)
-#define CHANNEL_G_HT20      (CHANNEL_2GHZ|CHANNEL_HT20)
-#define CHANNEL_A_HT20      (CHANNEL_5GHZ|CHANNEL_HT20)
-#define CHANNEL_G_HT40PLUS  (CHANNEL_2GHZ|CHANNEL_HT40PLUS)
-#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS)
-#define CHANNEL_A_HT40PLUS  (CHANNEL_5GHZ|CHANNEL_HT40PLUS)
-#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS)
-#define CHANNEL_ALL                            \
-       (CHANNEL_OFDM|                          \
-        CHANNEL_CCK|                           \
-        CHANNEL_2GHZ |                         \
-        CHANNEL_5GHZ |                         \
-        CHANNEL_HT20 |                         \
-        CHANNEL_HT40PLUS |                     \
-        CHANNEL_HT40MINUS)
-
 #define MAX_RTT_TABLE_ENTRY     6
 #define MAX_IQCAL_MEASUREMENT  8
 #define MAX_CL_TAB_ENTRY       16
 #define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j))
 
+enum ath9k_cal_flags {
+       RTT_DONE,
+       PAPRD_PACKET_SENT,
+       PAPRD_DONE,
+       NFCAL_PENDING,
+       NFCAL_INTF,
+       TXIQCAL_DONE,
+       TXCLCAL_DONE,
+       SW_PKDET_DONE,
+};
+
 struct ath9k_hw_cal_data {
        u16 channel;
-       u32 channelFlags;
-       u32 chanmode;
+       u16 channelFlags;
+       unsigned long cal_flags;
        int32_t CalValid;
        int8_t iCoff;
        int8_t qCoff;
-       bool rtt_done;
-       bool paprd_packet_sent;
-       bool paprd_done;
-       bool nfcal_pending;
-       bool nfcal_interference;
-       bool done_txiqcal_once;
-       bool done_txclcal_once;
+       u8 caldac[2];
        u16 small_signal_gain[AR9300_MAX_CHAINS];
        u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
        u32 num_measures[AR9300_MAX_CHAINS];
@@ -430,33 +406,34 @@ struct ath9k_hw_cal_data {
 struct ath9k_channel {
        struct ieee80211_channel *chan;
        u16 channel;
-       u32 channelFlags;
-       u32 chanmode;
+       u16 channelFlags;
        s16 noisefloor;
 };
 
-#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
-       (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \
-       (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \
-       (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS))
-#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0)
-#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0)
-#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
-#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0)
-#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
+#define CHANNEL_5GHZ           BIT(0)
+#define CHANNEL_HALF           BIT(1)
+#define CHANNEL_QUARTER                BIT(2)
+#define CHANNEL_HT             BIT(3)
+#define CHANNEL_HT40PLUS       BIT(4)
+#define CHANNEL_HT40MINUS      BIT(5)
+
+#define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ))
+#define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c))
+
+#define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF))
+#define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER))
 #define IS_CHAN_A_FAST_CLOCK(_ah, _c)                  \
-       ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) &&  \
-        ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
-
-/* These macros check chanmode and not channelFlags */
-#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
-#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) ||        \
-                         ((_c)->chanmode == CHANNEL_G_HT20))
-#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) ||    \
-                         ((_c)->chanmode == CHANNEL_A_HT40MINUS) ||    \
-                         ((_c)->chanmode == CHANNEL_G_HT40PLUS) ||     \
-                         ((_c)->chanmode == CHANNEL_G_HT40MINUS))
-#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c)))
+       (IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
+
+#define IS_CHAN_HT(_c) ((_c)->channelFlags & CHANNEL_HT)
+
+#define IS_CHAN_HT20(_c) (IS_CHAN_HT(_c) && !IS_CHAN_HT40(_c))
+
+#define IS_CHAN_HT40(_c) \
+       (!!((_c)->channelFlags & (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)))
+
+#define IS_CHAN_HT40PLUS(_c) ((_c)->channelFlags & CHANNEL_HT40PLUS)
+#define IS_CHAN_HT40MINUS(_c) ((_c)->channelFlags & CHANNEL_HT40MINUS)
 
 enum ath9k_power_mode {
        ATH9K_PM_AWAKE = 0,
@@ -558,6 +535,7 @@ struct ath_hw_antcomb_conf {
        u8 main_gaintb;
        u8 alt_gaintb;
        int lna1_lna2_delta;
+       int lna1_lna2_switch_delta;
        u8 div_group;
 };
 
@@ -726,6 +704,10 @@ struct ath_hw_ops {
        void (*spectral_scan_trigger)(struct ath_hw *ah);
        void (*spectral_scan_wait)(struct ath_hw *ah);
 
+       void (*tx99_start)(struct ath_hw *ah, u32 qnum);
+       void (*tx99_stop)(struct ath_hw *ah);
+       void (*tx99_set_txpower)(struct ath_hw *ah, u8 power);
+
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable);
 #endif
@@ -1026,10 +1008,11 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah);
 void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
 void ath9k_hw_init_global_settings(struct ath_hw *ah);
 u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
-void ath9k_hw_set11nmac2040(struct ath_hw *ah);
+void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan);
 void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
 void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
                                    const struct ath9k_beacon_state *bs);
+void ath9k_hw_check_nav(struct ath_hw *ah);
 bool ath9k_hw_check_alive(struct ath_hw *ah);
 
 bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
index 9a1f349..710192e 100644 (file)
@@ -347,7 +347,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        u8 *ds;
-       struct ath_buf *bf;
        int i, bsize, desc_len;
 
        ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
@@ -399,33 +398,68 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
                ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
 
        /* allocate buffers */
-       bsize = sizeof(struct ath_buf) * nbuf;
-       bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
-       if (!bf)
-               return -ENOMEM;
+       if (is_tx) {
+               struct ath_buf *bf;
+
+               bsize = sizeof(struct ath_buf) * nbuf;
+               bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+               if (!bf)
+                       return -ENOMEM;
 
-       for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
-               bf->bf_desc = ds;
-               bf->bf_daddr = DS2PHYS(dd, ds);
-
-               if (!(sc->sc_ah->caps.hw_caps &
-                     ATH9K_HW_CAP_4KB_SPLITTRANS)) {
-                       /*
-                        * Skip descriptor addresses which can cause 4KB
-                        * boundary crossing (addr + length) with a 32 dword
-                        * descriptor fetch.
-                        */
-                       while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
-                               BUG_ON((caddr_t) bf->bf_desc >=
-                                      ((caddr_t) dd->dd_desc +
-                                       dd->dd_desc_len));
-
-                               ds += (desc_len * ndesc);
-                               bf->bf_desc = ds;
-                               bf->bf_daddr = DS2PHYS(dd, ds);
+               for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
+                       bf->bf_desc = ds;
+                       bf->bf_daddr = DS2PHYS(dd, ds);
+
+                       if (!(sc->sc_ah->caps.hw_caps &
+                                 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
+                               /*
+                                * Skip descriptor addresses which can cause 4KB
+                                * boundary crossing (addr + length) with a 32 dword
+                                * descriptor fetch.
+                                */
+                               while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
+                                       BUG_ON((caddr_t) bf->bf_desc >=
+                                                  ((caddr_t) dd->dd_desc +
+                                               dd->dd_desc_len));
+
+                                       ds += (desc_len * ndesc);
+                                       bf->bf_desc = ds;
+                                       bf->bf_daddr = DS2PHYS(dd, ds);
+                               }
                        }
+                       list_add_tail(&bf->list, head);
+               }
+       } else {
+               struct ath_rxbuf *bf;
+
+               bsize = sizeof(struct ath_rxbuf) * nbuf;
+               bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+               if (!bf)
+                       return -ENOMEM;
+
+               for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
+                       bf->bf_desc = ds;
+                       bf->bf_daddr = DS2PHYS(dd, ds);
+
+                       if (!(sc->sc_ah->caps.hw_caps &
+                                 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
+                               /*
+                                * Skip descriptor addresses which can cause 4KB
+                                * boundary crossing (addr + length) with a 32 dword
+                                * descriptor fetch.
+                                */
+                               while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
+                                       BUG_ON((caddr_t) bf->bf_desc >=
+                                                  ((caddr_t) dd->dd_desc +
+                                               dd->dd_desc_len));
+
+                                       ds += (desc_len * ndesc);
+                                       bf->bf_desc = ds;
+                                       bf->bf_daddr = DS2PHYS(dd, ds);
+                               }
+                       }
+                       list_add_tail(&bf->list, head);
                }
-               list_add_tail(&bf->list, head);
        }
        return 0;
 }
@@ -437,7 +471,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
        sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
        sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
 
-       sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
        ath_cabq_update(sc);
 
        sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
@@ -547,6 +580,26 @@ static void ath9k_init_platform(struct ath_softc *sc)
        if (sc->driver_data & ATH9K_PCI_CUS217)
                ath_info(common, "CUS217 card detected\n");
 
+       if (sc->driver_data & ATH9K_PCI_CUS252)
+               ath_info(common, "CUS252 card detected\n");
+
+       if (sc->driver_data & ATH9K_PCI_AR9565_1ANT)
+               ath_info(common, "WB335 1-ANT card detected\n");
+
+       if (sc->driver_data & ATH9K_PCI_AR9565_2ANT)
+               ath_info(common, "WB335 2-ANT card detected\n");
+
+       /*
+        * Some WB335 cards do not support antenna diversity. Since
+        * we use a hardcoded value for AR9565 instead of using the
+        * EEPROM/OTP data, remove the combining feature from
+        * the HW capabilities bitmap.
+        */
+       if (sc->driver_data & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) {
+               if (!(sc->driver_data & ATH9K_PCI_BT_ANT_DIV))
+                       pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB;
+       }
+
        if (sc->driver_data & ATH9K_PCI_BT_ANT_DIV) {
                pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV;
                ath_info(common, "Set BT/WLAN RX diversity capability\n");
@@ -556,6 +609,11 @@ static void ath9k_init_platform(struct ath_softc *sc)
                ah->config.pcie_waen = 0x0040473b;
                ath_info(common, "Enable WAR for ASPM D3/L1\n");
        }
+
+       if (sc->driver_data & ATH9K_PCI_NO_PLL_PWRSAVE) {
+               ah->config.no_pll_pwrsave = true;
+               ath_info(common, "Disable PLL PowerSave\n");
+       }
 }
 
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@ -627,7 +685,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        sc->sc_ah = ah;
        pCap = &ah->caps;
 
-       sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET);
+       common = ath9k_hw_common(ah);
+       sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
+       sc->tx99_power = MAX_RATE_POWER + 1;
 
        if (!pdata) {
                ah->ah_flags |= AH_USE_EEPROM;
@@ -641,7 +701,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
                ah->external_reset = pdata->external_reset;
        }
 
-       common = ath9k_hw_common(ah);
        common->ops = &ah->reg_ops;
        common->bus_ops = bus_ops;
        common->ah = ah;
@@ -732,6 +791,7 @@ err_queues:
        ath9k_hw_deinit(ah);
 err_hw:
        ath9k_eeprom_release(sc);
+       dev_kfree_skb_any(sc->tx99_skb);
        return ret;
 }
 
@@ -748,7 +808,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
                chan = &sband->channels[i];
                ah->curchan = &ah->channels[chan->hw_value];
                cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
-               ath9k_cmn_update_ichannel(ah->curchan, &chandef);
+               ath9k_cmn_get_channel(sc->hw, ah, &chandef);
                ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
        }
 }
@@ -789,9 +849,9 @@ static const struct ieee80211_iface_limit if_limits[] = {
                                 BIT(NL80211_IFTYPE_P2P_GO) },
 };
 
-
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
-       { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_ADHOC) },
 };
 
 static const struct ieee80211_iface_combination if_comb[] = {
@@ -808,8 +868,8 @@ static const struct ieee80211_iface_combination if_comb[] = {
                .max_interfaces = 1,
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
-               .radar_detect_widths =  BIT(NL80211_CHAN_NO_HT) |
-                                       BIT(NL80211_CHAN_HT20),
+               .radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                       BIT(NL80211_CHAN_WIDTH_20),
        }
 };
 
@@ -850,17 +910,18 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 
        hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
-       hw->wiphy->interface_modes =
-               BIT(NL80211_IFTYPE_P2P_GO) |
-               BIT(NL80211_IFTYPE_P2P_CLIENT) |
-               BIT(NL80211_IFTYPE_AP) |
-               BIT(NL80211_IFTYPE_WDS) |
-               BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_ADHOC) |
-               BIT(NL80211_IFTYPE_MESH_POINT);
-
-       hw->wiphy->iface_combinations = if_comb;
-       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+       if (!config_enabled(CONFIG_ATH9K_TX99)) {
+               hw->wiphy->interface_modes =
+                       BIT(NL80211_IFTYPE_P2P_GO) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_WDS) |
+                       BIT(NL80211_IFTYPE_STATION) |
+                       BIT(NL80211_IFTYPE_ADHOC) |
+                       BIT(NL80211_IFTYPE_MESH_POINT);
+               hw->wiphy->iface_combinations = if_comb;
+               hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+       }
 
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
index 2f831db..aed7e29 100644 (file)
@@ -28,6 +28,13 @@ void ath_tx_complete_poll_work(struct work_struct *work)
        int i;
        bool needreset = false;
 
+
+       if (sc->tx99_state) {
+               ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
+                       "skip tx hung detection on tx99\n");
+               return;
+       }
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                txq = sc->tx.txq_map[i];
 
@@ -70,7 +77,7 @@ void ath_hw_check(struct work_struct *work)
        ath9k_ps_wakeup(sc);
        is_alive = ath9k_hw_check_alive(sc->sc_ah);
 
-       if (is_alive && !AR_SREV_9300(sc->sc_ah))
+       if ((is_alive && !AR_SREV_9300(sc->sc_ah)) || sc->tx99_state)
                goto out;
        else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
                ath_dbg(common, RESET,
@@ -141,6 +148,9 @@ void ath_hw_pll_work(struct work_struct *work)
        if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
                return;
 
+       if (sc->tx99_state)
+               return;
+
        ath9k_ps_wakeup(sc);
        pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
        ath9k_ps_restore(sc);
@@ -184,7 +194,7 @@ static void ath_paprd_activate(struct ath_softc *sc)
        struct ath9k_hw_cal_data *caldata = ah->caldata;
        int chain;
 
-       if (!caldata || !caldata->paprd_done) {
+       if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) {
                ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n");
                return;
        }
@@ -256,7 +266,9 @@ void ath_paprd_calibrate(struct work_struct *work)
        int len = 1800;
        int ret;
 
-       if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) {
+       if (!caldata ||
+           !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) ||
+           test_bit(PAPRD_DONE, &caldata->cal_flags)) {
                ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n");
                return;
        }
@@ -316,7 +328,7 @@ void ath_paprd_calibrate(struct work_struct *work)
        kfree_skb(skb);
 
        if (chain_ok) {
-               caldata->paprd_done = true;
+               set_bit(PAPRD_DONE, &caldata->cal_flags);
                ath_paprd_activate(sc);
        }
 
@@ -343,7 +355,7 @@ void ath_ani_calibrate(unsigned long data)
        u32 cal_interval, short_cal_interval, long_cal_interval;
        unsigned long flags;
 
-       if (ah->caldata && ah->caldata->nfcal_interference)
+       if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags))
                long_cal_interval = ATH_LONG_CALINTERVAL_INT;
        else
                long_cal_interval = ATH_LONG_CALINTERVAL;
@@ -432,7 +444,7 @@ set_timer:
        mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
 
        if (ar9003_is_paprd_enabled(ah) && ah->caldata) {
-               if (!ah->caldata->paprd_done) {
+               if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) {
                        ieee80211_queue_work(sc->hw, &sc->paprd_work);
                } else if (!ah->paprd_table_write_done) {
                        ath9k_ps_wakeup(sc);
@@ -516,7 +528,8 @@ void ath_update_survey_nf(struct ath_softc *sc, int channel)
 
        if (chan->noisefloor) {
                survey->filled |= SURVEY_INFO_NOISE_DBM;
-               survey->noise = ath9k_hw_getchan_noise(ah, chan);
+               survey->noise = ath9k_hw_getchan_noise(ah, chan,
+                                                      chan->noisefloor);
        }
 }
 
index a3eff09..6a18f9d 100644 (file)
@@ -374,7 +374,6 @@ EXPORT_SYMBOL(ath9k_hw_releasetxqueue);
 bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ath9k_channel *chan = ah->curchan;
        struct ath9k_tx_queue_info *qi;
        u32 cwMin, chanCwMin, value;
 
@@ -387,10 +386,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
        ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q);
 
        if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
-               if (chan && IS_CHAN_B(chan))
-                       chanCwMin = INIT_CWMIN_11B;
-               else
-                       chanCwMin = INIT_CWMIN;
+               chanCwMin = INIT_CWMIN;
 
                for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);
        } else
index bfccace..e3eed81 100644 (file)
@@ -603,8 +603,6 @@ enum ath9k_tx_queue_flags {
 #define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001
 
 #define ATH9K_DECOMP_MASK_SIZE     128
-#define ATH9K_READY_TIME_LO_BOUND  50
-#define ATH9K_READY_TIME_HI_BOUND  96
 
 enum ath9k_pkt_type {
        ATH9K_PKT_TYPE_NORMAL = 0,
index 709301f..74f452c 100644 (file)
@@ -312,17 +312,91 @@ out:
  * by reseting the chip.  To accomplish this we must first cleanup any pending
  * DMA, then restart stuff.
 */
-static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
-                   struct ath9k_channel *hchan)
+static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
 {
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath9k_channel *hchan;
+       struct ieee80211_channel *chan = chandef->chan;
+       unsigned long flags;
+       bool offchannel;
+       int pos = chan->hw_value;
+       int old_pos = -1;
        int r;
 
        if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return -EIO;
 
+       offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
+
+       if (ah->curchan)
+               old_pos = ah->curchan - &ah->channels[0];
+
+       ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+               chan->center_freq, chandef->width);
+
+       /* update survey stats for the old channel before switching */
+       spin_lock_irqsave(&common->cc_lock, flags);
+       ath_update_survey_stats(sc);
+       spin_unlock_irqrestore(&common->cc_lock, flags);
+
+       ath9k_cmn_get_channel(hw, ah, chandef);
+
+       /*
+        * If the operating channel changes, change the survey in-use flags
+        * along with it.
+        * Reset the survey data for the new channel, unless we're switching
+        * back to the operating channel from an off-channel operation.
+        */
+       if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
+               if (sc->cur_survey)
+                       sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
+
+               sc->cur_survey = &sc->survey[pos];
+
+               memset(sc->cur_survey, 0, sizeof(struct survey_info));
+               sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
+       } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
+               memset(&sc->survey[pos], 0, sizeof(struct survey_info));
+       }
+
+       hchan = &sc->sc_ah->channels[pos];
        r = ath_reset_internal(sc, hchan);
+       if (r)
+               return r;
 
-       return r;
+       /*
+        * The most recent snapshot of channel->noisefloor for the old
+        * channel is only available after the hardware reset. Copy it to
+        * the survey stats now.
+        */
+       if (old_pos >= 0)
+               ath_update_survey_nf(sc, old_pos);
+
+       /*
+        * Enable radar pulse detection if on a DFS channel. Spectral
+        * scanning and radar detection can not be used concurrently.
+        */
+       if (hw->conf.radar_enabled) {
+               u32 rxfilter;
+
+               /* set HW specific DFS configuration */
+               ath9k_hw_set_radar_params(ah);
+               rxfilter = ath9k_hw_getrxfilter(ah);
+               rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
+                               ATH9K_RX_FILTER_PHYERR;
+               ath9k_hw_setrxfilter(ah, rxfilter);
+               ath_dbg(common, DFS, "DFS enabled at freq %d\n",
+                       chan->center_freq);
+       } else {
+               /* perform spectral scan if requested. */
+               if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
+                       sc->spectral_mode == SPECTRAL_CHANSCAN)
+                       ath9k_spectral_scan_trigger(hw);
+       }
+
+       return 0;
 }
 
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -372,6 +446,13 @@ void ath9k_tasklet(unsigned long data)
                        type = RESET_TYPE_BB_WATCHDOG;
 
                ath9k_queue_reset(sc, type);
+
+               /*
+                * Increment the ref. counter here so that
+                * interrupts are enabled in the reset routine.
+                */
+               atomic_inc(&ah->intr_ref_cnt);
+               ath_dbg(common, ANY, "FATAL: Skipping interrupts\n");
                goto out;
        }
 
@@ -410,10 +491,9 @@ void ath9k_tasklet(unsigned long data)
 
        ath9k_btcoex_handle_interrupt(sc, status);
 
-out:
        /* re-enable hardware interrupt */
        ath9k_hw_enable_interrupts(ah);
-
+out:
        spin_unlock(&sc->sc_pcu_lock);
        ath9k_ps_restore(sc);
 }
@@ -594,7 +674,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
 
-       init_channel = ath9k_cmn_get_curchannel(hw, ah);
+       init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
 
        /* Reset SERDES registers */
        ath9k_hw_configpcipowersave(ah, false);
@@ -797,7 +877,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        }
 
        if (!ah->curchan)
-               ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
+               ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
 
        ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        ath9k_hw_phy_disable(ah);
@@ -816,7 +896,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        ath_dbg(common, CONFIG, "Driver halt\n");
 }
 
-bool ath9k_uses_beacons(int type)
+static bool ath9k_uses_beacons(int type)
 {
        switch (type) {
        case NL80211_IFTYPE_AP:
@@ -966,6 +1046,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
 
+       if (config_enabled(CONFIG_ATH9K_TX99)) {
+               if (sc->nvifs >= 1) {
+                       mutex_unlock(&sc->mutex);
+                       return -EOPNOTSUPP;
+               }
+               sc->tx99_vif = vif;
+       }
+
        ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
        sc->nvifs++;
 
@@ -994,9 +1082,15 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 
-       ath_dbg(common, CONFIG, "Change Interface\n");
        mutex_lock(&sc->mutex);
 
+       if (config_enabled(CONFIG_ATH9K_TX99)) {
+               mutex_unlock(&sc->mutex);
+               return -EOPNOTSUPP;
+       }
+
+       ath_dbg(common, CONFIG, "Change Interface\n");
+
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
@@ -1026,6 +1120,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&sc->mutex);
 
        sc->nvifs--;
+       sc->tx99_vif = NULL;
 
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
@@ -1047,6 +1142,9 @@ static void ath9k_enable_ps(struct ath_softc *sc)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        sc->ps_enabled = true;
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
                if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) {
@@ -1063,6 +1161,9 @@ static void ath9k_disable_ps(struct ath_softc *sc)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        sc->ps_enabled = false;
        ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
@@ -1086,6 +1187,9 @@ void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
        struct ath_common *common = ath9k_hw_common(ah);
        u32 rxfilter;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
                ath_err(common, "spectrum analyzer not implemented on this hardware\n");
                return;
@@ -1201,81 +1305,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        }
 
        if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
-               struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-               int pos = curchan->hw_value;
-               int old_pos = -1;
-               unsigned long flags;
-
-               if (ah->curchan)
-                       old_pos = ah->curchan - &ah->channels[0];
-
-               ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
-                       curchan->center_freq, hw->conf.chandef.width);
-
-               /* update survey stats for the old channel before switching */
-               spin_lock_irqsave(&common->cc_lock, flags);
-               ath_update_survey_stats(sc);
-               spin_unlock_irqrestore(&common->cc_lock, flags);
-
-               ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
-                                         &conf->chandef);
-
-               /*
-                * If the operating channel changes, change the survey in-use flags
-                * along with it.
-                * Reset the survey data for the new channel, unless we're switching
-                * back to the operating channel from an off-channel operation.
-                */
-               if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) &&
-                   sc->cur_survey != &sc->survey[pos]) {
-
-                       if (sc->cur_survey)
-                               sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
-
-                       sc->cur_survey = &sc->survey[pos];
-
-                       memset(sc->cur_survey, 0, sizeof(struct survey_info));
-                       sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
-               } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
-                       memset(&sc->survey[pos], 0, sizeof(struct survey_info));
-               }
-
-               if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
+               if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
                        ath_err(common, "Unable to set channel\n");
                        mutex_unlock(&sc->mutex);
                        ath9k_ps_restore(sc);
                        return -EINVAL;
                }
-
-               /*
-                * The most recent snapshot of channel->noisefloor for the old
-                * channel is only available after the hardware reset. Copy it to
-                * the survey stats now.
-                */
-               if (old_pos >= 0)
-                       ath_update_survey_nf(sc, old_pos);
-
-               /*
-                * Enable radar pulse detection if on a DFS channel. Spectral
-                * scanning and radar detection can not be used concurrently.
-                */
-               if (hw->conf.radar_enabled) {
-                       u32 rxfilter;
-
-                       /* set HW specific DFS configuration */
-                       ath9k_hw_set_radar_params(ah);
-                       rxfilter = ath9k_hw_getrxfilter(ah);
-                       rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
-                                   ATH9K_RX_FILTER_PHYERR;
-                       ath9k_hw_setrxfilter(ah, rxfilter);
-                       ath_dbg(common, DFS, "DFS enabled at freq %d\n",
-                               curchan->center_freq);
-               } else {
-                       /* perform spectral scan if requested. */
-                       if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
-                           sc->spectral_mode == SPECTRAL_CHANSCAN)
-                               ath9k_spectral_scan_trigger(hw);
-               }
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -1734,6 +1769,9 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
        unsigned long flags;
        int pos;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return -EOPNOTSUPP;
+
        spin_lock_irqsave(&common->cc_lock, flags);
        if (idx == 0)
                ath_update_survey_stats(sc);
@@ -1766,6 +1804,9 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return;
+
        mutex_lock(&sc->mutex);
        ah->coverage_class = coverage_class;
 
@@ -2332,6 +2373,134 @@ static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
        sc->csa_vif = vif;
 }
 
+static void ath9k_tx99_stop(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       ath_drain_all_txq(sc);
+       ath_startrecv(sc);
+
+       ath9k_hw_set_interrupts(ah);
+       ath9k_hw_enable_interrupts(ah);
+
+       ieee80211_wake_queues(sc->hw);
+
+       kfree_skb(sc->tx99_skb);
+       sc->tx99_skb = NULL;
+       sc->tx99_state = false;
+
+       ath9k_hw_tx99_stop(sc->sc_ah);
+       ath_dbg(common, XMIT, "TX99 stopped\n");
+}
+
+static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
+{
+       static u8 PN9Data[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24,
+                              0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50,
+                              0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1,
+                              0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18,
+                              0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8,
+                              0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84,
+                              0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3,
+                              0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0};
+       u32 len = 1200;
+       struct ieee80211_hw *hw = sc->hw;
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_tx_info *tx_info;
+       struct sk_buff *skb;
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       skb_put(skb, len);
+
+       memset(skb->data, 0, len);
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
+       hdr->duration_id = 0;
+
+       memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
+       memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
+       memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
+
+       hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
+
+       tx_info = IEEE80211_SKB_CB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
+       tx_info->band = hw->conf.chandef.chan->band;
+       tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
+       tx_info->control.vif = sc->tx99_vif;
+
+       memcpy(skb->data + sizeof(*hdr), PN9Data, sizeof(PN9Data));
+
+       return skb;
+}
+
+void ath9k_tx99_deinit(struct ath_softc *sc)
+{
+       ath_reset(sc);
+
+       ath9k_ps_wakeup(sc);
+       ath9k_tx99_stop(sc);
+       ath9k_ps_restore(sc);
+}
+
+int ath9k_tx99_init(struct ath_softc *sc)
+{
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_tx_control txctl;
+       int r;
+
+       if (sc->sc_flags & SC_OP_INVALID) {
+               ath_err(common,
+                       "driver is in invalid state unable to use TX99");
+               return -EINVAL;
+       }
+
+       sc->tx99_skb = ath9k_build_tx99_skb(sc);
+       if (!sc->tx99_skb)
+               return -ENOMEM;
+
+       memset(&txctl, 0, sizeof(txctl));
+       txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+
+       ath_reset(sc);
+
+       ath9k_ps_wakeup(sc);
+
+       ath9k_hw_disable_interrupts(ah);
+       atomic_set(&ah->intr_ref_cnt, -1);
+       ath_drain_all_txq(sc);
+       ath_stoprecv(sc);
+
+       sc->tx99_state = true;
+
+       ieee80211_stop_queues(hw);
+
+       if (sc->tx99_power == MAX_RATE_POWER + 1)
+               sc->tx99_power = MAX_RATE_POWER;
+
+       ath9k_hw_tx99_set_txpower(ah, sc->tx99_power);
+       r = ath9k_tx99_send(sc, sc->tx99_skb, &txctl);
+       if (r) {
+               ath_dbg(common, XMIT, "Failed to xmit TX99 skb\n");
+               return r;
+       }
+
+       ath_dbg(common, XMIT, "TX99 xmit started using %d ( %ddBm)\n",
+               sc->tx99_power,
+               sc->tx99_power / 2);
+
+       /* We leave the harware awake as it will be chugging on */
+
+       return 0;
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
index 815bee2..0ac1b5f 100644 (file)
@@ -661,9 +661,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all)
        chan_start = wlan_chan - 10;
        chan_end = wlan_chan + 10;
 
-       if (chan->chanmode == CHANNEL_G_HT40PLUS)
+       if (IS_CHAN_HT40PLUS(chan))
                chan_end += 20;
-       else if (chan->chanmode == CHANNEL_G_HT40MINUS)
+       else if (IS_CHAN_HT40MINUS(chan))
                chan_start -= 20;
 
        /* adjust side band */
@@ -707,11 +707,11 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
 
        if (setchannel) {
                struct ath9k_hw_cal_data *caldata = &sc->caldata;
-               if ((caldata->chanmode == CHANNEL_G_HT40PLUS) &&
+               if (IS_CHAN_HT40PLUS(ah->curchan) &&
                    (ah->curchan->channel > caldata->channel) &&
                    (ah->curchan->channel <= caldata->channel + 20))
                        return;
-               if ((caldata->chanmode == CHANNEL_G_HT40MINUS) &&
+               if (IS_CHAN_HT40MINUS(ah->curchan) &&
                    (ah->curchan->channel < caldata->channel) &&
                    (ah->curchan->channel >= caldata->channel - 20))
                        return;
index d089a7c..b5656fc 100644 (file)
@@ -195,6 +195,93 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
                         0x3219),
          .driver_data = ATH9K_PCI_BT_ANT_DIV },
 
+       /* AR9485 cards with PLL power-save disabled by default. */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2C97),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2100),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1C56, /* ASKEY */
+                        0x4001),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x11AD, /* LITEON */
+                        0x6627),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x11AD, /* LITEON */
+                        0x6628),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE04E),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE04F),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x144F, /* ASKEY */
+                        0x7197),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x2000),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x2001),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1186),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1F86),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1195),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1F95),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x1C00),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x1B9A, /* XAVI */
+                        0x1C01),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_ASUSTEK,
+                        0x850D),
+         .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
 
@@ -269,7 +356,200 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
 
        { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E  AR9462 */
        { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E  AR1111/AR9485 */
-       { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E  AR9565 */
+
+       /* CUS252 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3028),
+         .driver_data = ATH9K_PCI_CUS252 |
+                        ATH9K_PCI_AR9565_2ANT |
+                        ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2176),
+         .driver_data = ATH9K_PCI_CUS252 |
+                        ATH9K_PCI_AR9565_2ANT |
+                        ATH9K_PCI_BT_ANT_DIV },
+
+       /* WB335 1-ANT */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE068),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x185F, /* WNC */
+                        0xA119),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0632),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x6671),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x1B9A, /* XAVI */
+                        0x2811),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x1B9A, /* XAVI */
+                        0x2812),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+
+       /* WB335 1-ANT / Antenna Diversity */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3025),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3026),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x302B),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE069),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x185F, /* WNC */
+                        0x3028),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0622),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0672),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0662),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x213A),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3026),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_HP,
+                        0x18E3),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_HP,
+                        0x217F),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_DELL,
+                        0x020E),
+         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
+
+       /* WB335 2-ANT */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x411A),
+         .driver_data = ATH9K_PCI_AR9565_2ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x411B),
+         .driver_data = ATH9K_PCI_AR9565_2ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x411C),
+         .driver_data = ATH9K_PCI_AR9565_2ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x411D),
+         .driver_data = ATH9K_PCI_AR9565_2ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x411E),
+         .driver_data = ATH9K_PCI_AR9565_2ANT },
+
+       /* WB335 2-ANT / Antenna-Diversity */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3027),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x302C),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0642),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0652),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0612),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2130),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x144F, /* ASKEY */
+                        0x7202),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x1B9A, /* XAVI */
+                        0x2810),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x185F, /* WNC */
+                        0x3027),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+
+       /* PCI-E AR9565 (WB335) */
+       { PCI_VDEVICE(ATHEROS, 0x0036),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+
        { 0 }
 };
 
index d3d7c51..d829bb6 100644 (file)
@@ -1387,31 +1387,31 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
                int used_mcs = 0, used_htmode = 0;
 
                if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) {
-                       used_mcs = snprintf(mcs, 5, "%d",
-                               rc->rate_table->info[i].ratecode);
+                       used_mcs = scnprintf(mcs, 5, "%d",
+                                            rc->rate_table->info[i].ratecode);
 
                        if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy))
-                               used_htmode = snprintf(htmode, 5, "HT40");
+                               used_htmode = scnprintf(htmode, 5, "HT40");
                        else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy))
-                               used_htmode = snprintf(htmode, 5, "HT20");
+                               used_htmode = scnprintf(htmode, 5, "HT20");
                        else
-                               used_htmode = snprintf(htmode, 5, "????");
+                               used_htmode = scnprintf(htmode, 5, "????");
                }
 
                mcs[used_mcs] = '\0';
                htmode[used_htmode] = '\0';
 
-               len += snprintf(buf + len, max - len,
-                       "%6s %6s %3u.%d: "
-                       "%10u %10u %10u %10u\n",
-                       htmode,
-                       mcs,
-                       ratekbps / 1000,
-                       (ratekbps % 1000) / 100,
-                       stats->success,
-                       stats->retries,
-                       stats->xretries,
-                       stats->per);
+               len += scnprintf(buf + len, max - len,
+                                "%6s %6s %3u.%d: "
+                                "%10u %10u %10u %10u\n",
+                                htmode,
+                                mcs,
+                                ratekbps / 1000,
+                                (ratekbps % 1000) / 100,
+                                stats->success,
+                                stats->retries,
+                                stats->xretries,
+                                stats->per);
        }
 
        if (len > max)
index ab9e3a8..95ddca5 100644 (file)
@@ -19,7 +19,7 @@
 #include "ath9k.h"
 #include "ar9003_mac.h"
 
-#define SKB_CB_ATHBUF(__skb)   (*((struct ath_buf **)__skb->cb))
+#define SKB_CB_ATHBUF(__skb)   (*((struct ath_rxbuf **)__skb->cb))
 
 static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
 {
@@ -35,7 +35,7 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
  * buffer (or rx fifo). This can incorrectly acknowledge packets
  * to a sender if last desc is self-linked.
  */
-static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
+static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -68,7 +68,7 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
        sc->rx.rxlink = &ds->ds_link;
 }
 
-static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
 {
        if (sc->rx.buf_hold)
                ath_rx_buf_link(sc, sc->rx.buf_hold);
@@ -112,13 +112,13 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_rx_edma *rx_edma;
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
 
        rx_edma = &sc->rx.rx_edma[qtype];
        if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize)
                return false;
 
-       bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
        list_del_init(&bf->list);
 
        skb = bf->bf_mpdu;
@@ -138,7 +138,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
                                  enum ath9k_rx_qtype qtype)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_buf *bf, *tbf;
+       struct ath_rxbuf *bf, *tbf;
 
        if (list_empty(&sc->rx.rxbuf)) {
                ath_dbg(common, QUEUE, "No free rx buf available\n");
@@ -154,7 +154,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
 static void ath_rx_remove_buffer(struct ath_softc *sc,
                                 enum ath9k_rx_qtype qtype)
 {
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        struct ath_rx_edma *rx_edma;
        struct sk_buff *skb;
 
@@ -171,7 +171,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
 
        ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
        ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
@@ -199,7 +199,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_hw *ah = sc->sc_ah;
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int error = 0, i;
        u32 size;
 
@@ -211,7 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
        ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
                               ah->caps.rx_hp_qdepth);
 
-       size = sizeof(struct ath_buf) * nbufs;
+       size = sizeof(struct ath_rxbuf) * nbufs;
        bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
        if (!bf)
                return -ENOMEM;
@@ -271,7 +271,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int error = 0;
 
        spin_lock_init(&sc->sc_pcu_lock);
@@ -332,7 +332,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
                ath_rx_edma_cleanup(sc);
@@ -375,6 +375,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
 {
        u32 rfilt;
 
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return 0;
+
        rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
                | ATH9K_RX_FILTER_MCAST;
 
@@ -427,7 +430,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
 int ath_startrecv(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
-       struct ath_buf *bf, *tbf;
+       struct ath_rxbuf *bf, *tbf;
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
                ath_edma_start_recv(sc);
@@ -447,7 +450,7 @@ int ath_startrecv(struct ath_softc *sc)
        if (list_empty(&sc->rx.rxbuf))
                goto start_recv;
 
-       bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
        ath9k_hw_putrxbuf(ah, bf->bf_daddr);
        ath9k_hw_rxena(ah);
 
@@ -603,13 +606,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
 static bool ath_edma_get_buffers(struct ath_softc *sc,
                                 enum ath9k_rx_qtype qtype,
                                 struct ath_rx_status *rs,
-                                struct ath_buf **dest)
+                                struct ath_rxbuf **dest)
 {
        struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct sk_buff *skb;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int ret;
 
        skb = skb_peek(&rx_edma->rx_fifo);
@@ -653,11 +656,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc,
        return true;
 }
 
-static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
+static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
                                                struct ath_rx_status *rs,
                                                enum ath9k_rx_qtype qtype)
 {
-       struct ath_buf *bf = NULL;
+       struct ath_rxbuf *bf = NULL;
 
        while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
                if (!bf)
@@ -668,13 +671,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
        return NULL;
 }
 
-static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
+static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
                                           struct ath_rx_status *rs)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_desc *ds;
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        int ret;
 
        if (list_empty(&sc->rx.rxbuf)) {
@@ -682,7 +685,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
                return NULL;
        }
 
-       bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
        if (bf == sc->rx.buf_hold)
                return NULL;
 
@@ -702,7 +705,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
        ret = ath9k_hw_rxprocdesc(ah, ds, rs);
        if (ret == -EINPROGRESS) {
                struct ath_rx_status trs;
-               struct ath_buf *tbf;
+               struct ath_rxbuf *tbf;
                struct ath_desc *tds;
 
                memset(&trs, 0, sizeof(trs));
@@ -711,7 +714,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
                        return NULL;
                }
 
-               tbf = list_entry(bf->list.next, struct ath_buf, list);
+               tbf = list_entry(bf->list.next, struct ath_rxbuf, list);
 
                /*
                 * On some hardware the descriptor status words could
@@ -972,14 +975,15 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
 {
 #ifdef CONFIG_ATH9K_DEBUGFS
        struct ath_hw *ah = sc->sc_ah;
-       u8 bins[SPECTRAL_HT20_NUM_BINS];
-       u8 *vdata = (u8 *)hdr;
-       struct fft_sample_ht20 fft_sample;
+       u8 num_bins, *bins, *vdata = (u8 *)hdr;
+       struct fft_sample_ht20 fft_sample_20;
+       struct fft_sample_ht20_40 fft_sample_40;
+       struct fft_sample_tlv *tlv;
        struct ath_radar_info *radar_info;
-       struct ath_ht20_mag_info *mag_info;
        int len = rs->rs_datalen;
        int dc_pos;
-       u16 length, max_magnitude;
+       u16 fft_len, length, freq = ah->curchan->chan->center_freq;
+       enum nl80211_channel_type chan_type;
 
        /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
         * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
@@ -997,45 +1001,44 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
                return 0;
 
-       /* Variation in the data length is possible and will be fixed later.
-        * Note that we only support HT20 for now.
-        *
-        * TODO: add HT20_40 support as well.
-        */
-       if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) ||
-           (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1))
-               return 1;
-
-       fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20;
-       length = sizeof(fft_sample) - sizeof(fft_sample.tlv);
-       fft_sample.tlv.length = __cpu_to_be16(length);
+       chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
+       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
+           (chan_type == NL80211_CHAN_HT40PLUS)) {
+               fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
+               num_bins = SPECTRAL_HT20_40_NUM_BINS;
+               bins = (u8 *)fft_sample_40.data;
+       } else {
+               fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
+               num_bins = SPECTRAL_HT20_NUM_BINS;
+               bins = (u8 *)fft_sample_20.data;
+       }
 
-       fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq);
-       fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
-       fft_sample.noise = ah->noise;
+       /* Variation in the data length is possible and will be fixed later */
+       if ((len > fft_len + 2) || (len < fft_len - 1))
+               return 1;
 
-       switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) {
+       switch (len - fft_len) {
        case 0:
                /* length correct, nothing to do. */
-               memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS);
+               memcpy(bins, vdata, num_bins);
                break;
        case -1:
                /* first byte missing, duplicate it. */
-               memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1);
+               memcpy(&bins[1], vdata, num_bins - 1);
                bins[0] = vdata[0];
                break;
        case 2:
                /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
                memcpy(bins, vdata, 30);
                bins[30] = vdata[31];
-               memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31);
+               memcpy(&bins[31], &vdata[33], num_bins - 31);
                break;
        case 1:
                /* MAC added 2 extra bytes AND first byte is missing. */
                bins[0] = vdata[0];
-               memcpy(&bins[0], vdata, 30);
+               memcpy(&bins[1], vdata, 30);
                bins[31] = vdata[31];
-               memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32);
+               memcpy(&bins[32], &vdata[33], num_bins - 32);
                break;
        default:
                return 1;
@@ -1044,23 +1047,93 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        /* DC value (value in the middle) is the blind spot of the spectral
         * sample and invalid, interpolate it.
         */
-       dc_pos = SPECTRAL_HT20_NUM_BINS / 2;
+       dc_pos = num_bins / 2;
        bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
 
-       /* mag data is at the end of the frame, in front of radar_info */
-       mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
+       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
+           (chan_type == NL80211_CHAN_HT40PLUS)) {
+               s8 lower_rssi, upper_rssi;
+               s16 ext_nf;
+               u8 lower_max_index, upper_max_index;
+               u8 lower_bitmap_w, upper_bitmap_w;
+               u16 lower_mag, upper_mag;
+               struct ath9k_hw_cal_data *caldata = ah->caldata;
+               struct ath_ht20_40_mag_info *mag_info;
+
+               if (caldata)
+                       ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
+                                       caldata->nfCalHist[3].privNF);
+               else
+                       ext_nf = ATH_DEFAULT_NOISE_FLOOR;
+
+               length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
+               fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
+               fft_sample_40.tlv.length = __cpu_to_be16(length);
+               fft_sample_40.freq = __cpu_to_be16(freq);
+               fft_sample_40.channel_type = chan_type;
+
+               if (chan_type == NL80211_CHAN_HT40PLUS) {
+                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
+                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0);
 
-       /* copy raw bins without scaling them */
-       memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS);
-       fft_sample.max_exp = mag_info->max_exp & 0xf;
+                       fft_sample_40.lower_noise = ah->noise;
+                       fft_sample_40.upper_noise = ext_nf;
+               } else {
+                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0);
+                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
 
-       max_magnitude = spectral_max_magnitude(mag_info->all_bins);
-       fft_sample.max_magnitude = __cpu_to_be16(max_magnitude);
-       fft_sample.max_index = spectral_max_index(mag_info->all_bins);
-       fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins);
-       fft_sample.tsf = __cpu_to_be64(tsf);
+                       fft_sample_40.lower_noise = ext_nf;
+                       fft_sample_40.upper_noise = ah->noise;
+               }
+               fft_sample_40.lower_rssi = lower_rssi;
+               fft_sample_40.upper_rssi = upper_rssi;
+
+               mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
+               lower_mag = spectral_max_magnitude(mag_info->lower_bins);
+               upper_mag = spectral_max_magnitude(mag_info->upper_bins);
+               fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
+               fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
+               lower_max_index = spectral_max_index(mag_info->lower_bins);
+               upper_max_index = spectral_max_index(mag_info->upper_bins);
+               fft_sample_40.lower_max_index = lower_max_index;
+               fft_sample_40.upper_max_index = upper_max_index;
+               lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
+               upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
+               fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
+               fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
+               fft_sample_40.max_exp = mag_info->max_exp & 0xf;
+
+               fft_sample_40.tsf = __cpu_to_be64(tsf);
+
+               tlv = (struct fft_sample_tlv *)&fft_sample_40;
+       } else {
+               u8 max_index, bitmap_w;
+               u16 magnitude;
+               struct ath_ht20_mag_info *mag_info;
+
+               length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
+               fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
+               fft_sample_20.tlv.length = __cpu_to_be16(length);
+               fft_sample_20.freq = __cpu_to_be16(freq);
+
+               fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
+               fft_sample_20.noise = ah->noise;
+
+               mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
+               magnitude = spectral_max_magnitude(mag_info->all_bins);
+               fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
+               max_index = spectral_max_index(mag_info->all_bins);
+               fft_sample_20.max_index = max_index;
+               bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
+               fft_sample_20.bitmap_weight = bitmap_w;
+               fft_sample_20.max_exp = mag_info->max_exp & 0xf;
+
+               fft_sample_20.tsf = __cpu_to_be64(tsf);
+
+               tlv = (struct fft_sample_tlv *)&fft_sample_20;
+       }
 
-       ath_debug_send_fft_sample(sc, &fft_sample.tlv);
+       ath_debug_send_fft_sample(sc, tlv);
        return 1;
 #else
        return 0;
@@ -1308,7 +1381,7 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc,
 
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
-       struct ath_buf *bf;
+       struct ath_rxbuf *bf;
        struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
        struct ieee80211_rx_status *rxs;
        struct ath_hw *ah = sc->sc_ah;
index fde6da6..0db37f2 100644 (file)
@@ -39,7 +39,7 @@ struct wmi_fw_version {
 struct wmi_event_swba {
        __be64 tsf;
        u8 beacon_pending;
-};
+} __packed;
 
 /*
  * 64 - HTC header - WMI header - 1 / txstatus
index dd30452..09cdbcd 100644 (file)
@@ -1241,12 +1241,13 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                if (bf->bf_next)
                        info.link = bf->bf_next->bf_daddr;
                else
-                       info.link = 0;
+                       info.link = (sc->tx99_state) ? bf->bf_daddr : 0;
 
                if (!bf_first) {
                        bf_first = bf;
 
-                       info.flags = ATH9K_TXDESC_INTREQ;
+                       if (!sc->tx99_state)
+                               info.flags = ATH9K_TXDESC_INTREQ;
                        if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) ||
                            txq == sc->tx.uapsdq)
                                info.flags |= ATH9K_TXDESC_CLRDMASK;
@@ -1704,16 +1705,9 @@ int ath_cabq_update(struct ath_softc *sc)
        int qnum = sc->beacon.cabq->axq_qnum;
 
        ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
-       /*
-        * Ensure the readytime % is within the bounds.
-        */
-       if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
-               sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
-       else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
-               sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
 
        qi.tqi_readyTime = (cur_conf->beacon_interval *
-                           sc->config.cabqReadytime) / 100;
+                           ATH_CABQ_READY_TIME) / 100;
        ath_txq_update(sc, qnum, &qi);
 
        return 0;
@@ -1948,7 +1942,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
                        txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
        }
 
-       if (!edma) {
+       if (!edma || sc->tx99_state) {
                TX_STAT_INC(txq->axq_qnum, txstart);
                ath9k_hw_txstart(ah, txq->axq_qnum);
        }
@@ -2027,6 +2021,9 @@ static void setup_frame_info(struct ieee80211_hw *hw,
                fi->keyix = ATH9K_TXKEYIX_INVALID;
        fi->keytype = keytype;
        fi->framelen = framelen;
+
+       if (!rate)
+               return;
        fi->rtscts_rate = rate->hw_value;
        if (short_preamble)
                fi->rtscts_rate |= rate->hw_value_short;
@@ -2037,8 +2034,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
        struct ath_hw *ah = sc->sc_ah;
        struct ath9k_channel *curchan = ah->curchan;
 
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) &&
-           (curchan->channelFlags & CHANNEL_5GHZ) &&
+       if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) &&
            (chainmask == 0x7) && (rate < 0x90))
                return 0x3;
        else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) &&
@@ -2329,7 +2325,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
        ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb);
 
        if (sc->sc_ah->caldata)
-               sc->sc_ah->caldata->paprd_packet_sent = true;
+               set_bit(PAPRD_PACKET_SENT, &sc->sc_ah->caldata->cal_flags);
 
        if (!(tx_flags & ATH_TX_ERROR))
                /* Frame was ACKed */
@@ -2379,6 +2375,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
 
        dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE);
        bf->bf_buf_addr = 0;
+       if (sc->tx99_state)
+               goto skip_tx_complete;
 
        if (bf->bf_state.bfs_paprd) {
                if (time_after(jiffies,
@@ -2391,6 +2389,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
                ath_debug_stat_tx(sc, bf, ts, txq, tx_flags);
                ath_tx_complete(sc, skb, tx_flags, txq);
        }
+skip_tx_complete:
        /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't
         * accidentally reference it later.
         */
@@ -2749,3 +2748,46 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
                ath_txq_unlock(sc, txq);
        }
 }
+
+int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb,
+                   struct ath_tx_control *txctl)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ath_frame_info *fi = get_frame_info(skb);
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_buf *bf;
+       int padpos, padsize;
+
+       padpos = ieee80211_hdrlen(hdr->frame_control);
+       padsize = padpos & 3;
+
+       if (padsize && skb->len > padpos) {
+               if (skb_headroom(skb) < padsize) {
+                       ath_dbg(common, XMIT,
+                               "tx99 padding failed\n");
+               return -EINVAL;
+               }
+
+               skb_push(skb, padsize);
+               memmove(skb->data, skb->data + padsize, padpos);
+       }
+
+       fi->keyix = ATH9K_TXKEYIX_INVALID;
+       fi->framelen = skb->len + FCS_LEN;
+       fi->keytype = ATH9K_KEY_TYPE_CLEAR;
+
+       bf = ath_tx_setup_buffer(sc, txctl->txq, NULL, skb);
+       if (!bf) {
+               ath_dbg(common, XMIT, "tx99 buffer setup failed\n");
+               return -EINVAL;
+       }
+
+       ath_set_rates(sc->tx99_vif, NULL, bf);
+
+       ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr);
+       ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum);
+
+       ath_tx_send_normal(sc, txctl->txq, NULL, skb);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
new file mode 100644 (file)
index 0000000..a1a69c5
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+#include "ath.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE  16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+       enum nl80211_dfs_regions region;
+       u32 num_radar_types;
+       const struct radar_detector_specs *radar_types;
+};
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+/* percentage of pulse width tolerance */
+#define WIDTH_TOLERANCE 5
+#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
+#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
+
+#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)     \
+{                                                              \
+       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+       (PRF2PRI(PMAX) - PRI_TOLERANCE),                        \
+       (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,  \
+       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
+}
+
+/* radar types as defined by ETSI EN-301-893 v1.5.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
+       ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
+       ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
+       ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
+       ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
+       ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
+       ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
+       ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v15 = {
+       .region                 = NL80211_DFS_ETSI,
+       .num_radar_types        = ARRAY_SIZE(etsi_radar_ref_types_v15),
+       .radar_types            = etsi_radar_ref_types_v15,
+};
+
+/* for now, we support ETSI radar types, FCC and JP are TODO */
+static const struct radar_types *dfs_domains[] = {
+       &etsi_radar_types_v15,
+};
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+       u32 i;
+       for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+               if (dfs_domains[i]->region == region)
+                       return dfs_domains[i];
+       }
+       return NULL;
+}
+
+/**
+ * struct channel_detector - detector elements for a DFS channel
+ * @head: list_head
+ * @freq: frequency for this channel detector in MHz
+ * @detectors: array of dynamically created detector elements for this freq
+ *
+ * Channel detectors are required to provide multi-channel DFS detection, e.g.
+ * to support off-channel scanning. A pattern detector has a list of channels
+ * radar pulses have been reported for in the past.
+ */
+struct channel_detector {
+       struct list_head head;
+       u16 freq;
+       struct pri_detector **detectors;
+};
+
+/* channel_detector_reset() - reset detector lines for a given channel */
+static void channel_detector_reset(struct dfs_pattern_detector *dpd,
+                                  struct channel_detector *cd)
+{
+       u32 i;
+       if (cd == NULL)
+               return;
+       for (i = 0; i < dpd->num_radar_types; i++)
+               cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
+}
+
+/* channel_detector_exit() - destructor */
+static void channel_detector_exit(struct dfs_pattern_detector *dpd,
+                                 struct channel_detector *cd)
+{
+       u32 i;
+       if (cd == NULL)
+               return;
+       list_del(&cd->head);
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               struct pri_detector *de = cd->detectors[i];
+               if (de != NULL)
+                       de->exit(de);
+       }
+       kfree(cd->detectors);
+       kfree(cd);
+}
+
+static struct channel_detector *
+channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
+{
+       u32 sz, i;
+       struct channel_detector *cd;
+
+       cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
+       if (cd == NULL)
+               goto fail;
+
+       INIT_LIST_HEAD(&cd->head);
+       cd->freq = freq;
+       sz = sizeof(cd->detectors) * dpd->num_radar_types;
+       cd->detectors = kzalloc(sz, GFP_ATOMIC);
+       if (cd->detectors == NULL)
+               goto fail;
+
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+               struct pri_detector *de = pri_detector_init(rs);
+               if (de == NULL)
+                       goto fail;
+               cd->detectors[i] = de;
+       }
+       list_add(&cd->head, &dpd->channel_detectors);
+       return cd;
+
+fail:
+       ath_dbg(dpd->common, DFS,
+               "failed to allocate channel_detector for freq=%d\n", freq);
+       channel_detector_exit(dpd, cd);
+       return NULL;
+}
+
+/**
+ * channel_detector_get() - get channel detector for given frequency
+ * @param dpd instance pointer
+ * @param freq frequency in MHz
+ * @return pointer to channel detector on success, NULL otherwise
+ *
+ * Return existing channel detector for the given frequency or return a
+ * newly create one.
+ */
+static struct channel_detector *
+channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
+{
+       struct channel_detector *cd;
+       list_for_each_entry(cd, &dpd->channel_detectors, head) {
+               if (cd->freq == freq)
+                       return cd;
+       }
+       return channel_detector_create(dpd, freq);
+}
+
+/*
+ * DFS Pattern Detector
+ */
+
+/* dpd_reset(): reset all channel detectors */
+static void dpd_reset(struct dfs_pattern_detector *dpd)
+{
+       struct channel_detector *cd;
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry(cd, &dpd->channel_detectors, head)
+                       channel_detector_reset(dpd, cd);
+
+}
+static void dpd_exit(struct dfs_pattern_detector *dpd)
+{
+       struct channel_detector *cd, *cd0;
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+                       channel_detector_exit(dpd, cd);
+       kfree(dpd);
+}
+
+static bool
+dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
+{
+       u32 i;
+       struct channel_detector *cd;
+
+       /*
+        * pulses received for a non-supported or un-initialized
+        * domain are treated as detected radars for fail-safety
+        */
+       if (dpd->region == NL80211_DFS_UNSET)
+               return true;
+
+       cd = channel_detector_get(dpd, event->freq);
+       if (cd == NULL)
+               return false;
+
+       dpd->last_pulse_ts = event->ts;
+       /* reset detector on time stamp wraparound, caused by TSF reset */
+       if (event->ts < dpd->last_pulse_ts)
+               dpd_reset(dpd);
+
+       /* do type individual pattern matching */
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               struct pri_detector *pd = cd->detectors[i];
+               struct pri_sequence *ps = pd->add_pulse(pd, event);
+               if (ps != NULL) {
+                       ath_dbg(dpd->common, DFS,
+                               "DFS: radar found on freq=%d: id=%d, pri=%d, "
+                               "count=%d, count_false=%d\n",
+                               event->freq, pd->rs->type_id,
+                               ps->pri, ps->count, ps->count_falses);
+                       channel_detector_reset(dpd, cd);
+                       return true;
+               }
+       }
+       return false;
+}
+
+static struct ath_dfs_pool_stats
+dpd_get_stats(struct dfs_pattern_detector *dpd)
+{
+       return global_dfs_pool_stats;
+}
+
+static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
+                          enum nl80211_dfs_regions region)
+{
+       const struct radar_types *rt;
+       struct channel_detector *cd, *cd0;
+
+       if (dpd->region == region)
+               return true;
+
+       dpd->region = NL80211_DFS_UNSET;
+
+       rt = get_dfs_domain_radar_types(region);
+       if (rt == NULL)
+               return false;
+
+       /* delete all channel detectors for previous DFS domain */
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+                       channel_detector_exit(dpd, cd);
+       dpd->radar_spec = rt->radar_types;
+       dpd->num_radar_types = rt->num_radar_types;
+
+       dpd->region = region;
+       return true;
+}
+
+static struct dfs_pattern_detector default_dpd = {
+       .exit           = dpd_exit,
+       .set_dfs_domain = dpd_set_domain,
+       .add_pulse      = dpd_add_pulse,
+       .get_stats      = dpd_get_stats,
+       .region         = NL80211_DFS_UNSET,
+};
+
+struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+                         enum nl80211_dfs_regions region)
+{
+       struct dfs_pattern_detector *dpd;
+
+       if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS))
+               return NULL;
+
+       dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
+       if (dpd == NULL)
+               return NULL;
+
+       *dpd = default_dpd;
+       INIT_LIST_HEAD(&dpd->channel_detectors);
+
+       dpd->common = common;
+       if (dpd->set_dfs_domain(dpd, region))
+               return dpd;
+
+       ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
+       kfree(dpd);
+       return NULL;
+}
+EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h
new file mode 100644 (file)
index 0000000..dde2652
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DFS_PATTERN_DETECTOR_H
+#define DFS_PATTERN_DETECTOR_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/nl80211.h>
+
+/**
+ * struct ath_dfs_pool_stats - DFS Statistics for global pools
+ */
+struct ath_dfs_pool_stats {
+       u32 pool_reference;
+       u32 pulse_allocated;
+       u32 pulse_alloc_error;
+       u32 pulse_used;
+       u32 pseq_allocated;
+       u32 pseq_alloc_error;
+       u32 pseq_used;
+};
+
+/**
+ * struct pulse_event - describing pulses reported by PHY
+ * @ts: pulse time stamp in us
+ * @freq: channel frequency in MHz
+ * @width: pulse duration in us
+ * @rssi: rssi of radar event
+ */
+struct pulse_event {
+       u64 ts;
+       u16 freq;
+       u8 width;
+       u8 rssi;
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ */
+struct radar_detector_specs {
+       u8 type_id;
+       u8 width_min;
+       u8 width_max;
+       u16 pri_min;
+       u16 pri_max;
+       u8 num_pri;
+       u8 ppb;
+       u8 ppb_thresh;
+       u8 max_pri_tolerance;
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @exit(): destructor
+ * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
+ * @add_pulse(): add radar pulse to detector, returns true on detection
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+       void (*exit)(struct dfs_pattern_detector *dpd);
+       bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
+                          enum nl80211_dfs_regions region);
+       bool (*add_pulse)(struct dfs_pattern_detector *dpd,
+                         struct pulse_event *pe);
+
+       struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd);
+       enum nl80211_dfs_regions region;
+       u8 num_radar_types;
+       u64 last_pulse_ts;
+       /* needed for ath_dbg() */
+       struct ath_common *common;
+
+       const struct radar_detector_specs *radar_spec;
+       struct list_head channel_detectors;
+};
+
+/**
+ * dfs_pattern_detector_init() - constructor for pattern detector class
+ * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
+ * @return instance pointer on success, NULL otherwise
+ */
+extern struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+                         enum nl80211_dfs_regions region);
+#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c
new file mode 100644 (file)
index 0000000..43b6081
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "ath.h"
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+
+struct ath_dfs_pool_stats global_dfs_pool_stats = {};
+
+#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
+#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+       struct list_head head;
+       u64 ts;
+};
+
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+       u32 remainder;
+       u32 factor;
+       u32 delta;
+
+       if (fraction == 0)
+               return 0;
+
+       delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+       if (delta <= tolerance)
+               /* val and fraction are within tolerance */
+               return 1;
+
+       factor = val / fraction;
+       remainder = val % fraction;
+       if (remainder > tolerance) {
+               /* no exact match */
+               if ((fraction - remainder) <= tolerance)
+                       /* remainder is within tolerance */
+                       factor++;
+               else
+                       factor = 0;
+       }
+       return factor;
+}
+
+/**
+ * DOC: Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+       spin_lock_bh(&pool_lock);
+       singleton_pool_references++;
+       DFS_POOL_STAT_INC(pool_reference);
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+       spin_lock_bh(&pool_lock);
+       singleton_pool_references--;
+       DFS_POOL_STAT_DEC(pool_reference);
+       if (singleton_pool_references == 0) {
+               /* free singleton pools with no references left */
+               struct pri_sequence *ps, *ps0;
+               struct pulse_elem *p, *p0;
+
+               list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+                       list_del(&p->head);
+                       DFS_POOL_STAT_DEC(pulse_allocated);
+                       kfree(p);
+               }
+               list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+                       list_del(&ps->head);
+                       DFS_POOL_STAT_DEC(pseq_allocated);
+                       kfree(ps);
+               }
+       }
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+       spin_lock_bh(&pool_lock);
+       list_add(&pe->head, &pulse_pool);
+       DFS_POOL_STAT_DEC(pulse_used);
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+       spin_lock_bh(&pool_lock);
+       list_add(&pse->head, &pseq_pool);
+       DFS_POOL_STAT_DEC(pseq_used);
+       spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+       struct pri_sequence *pse = NULL;
+       spin_lock_bh(&pool_lock);
+       if (!list_empty(&pseq_pool)) {
+               pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+               list_del(&pse->head);
+               DFS_POOL_STAT_INC(pseq_used);
+       }
+       spin_unlock_bh(&pool_lock);
+       return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+       struct pulse_elem *pe = NULL;
+       spin_lock_bh(&pool_lock);
+       if (!list_empty(&pulse_pool)) {
+               pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+               list_del(&pe->head);
+               DFS_POOL_STAT_INC(pulse_used);
+       }
+       spin_unlock_bh(&pool_lock);
+       return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+       struct list_head *l = &pde->pulses;
+       if (list_empty(l))
+               return NULL;
+       return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+       struct pulse_elem *p = pulse_queue_get_tail(pde);
+       if (p != NULL) {
+               list_del_init(&p->head);
+               pde->count--;
+               /* give it back to pool */
+               pool_put_pulse_elem(p);
+       }
+       return (pde->count > 0);
+}
+
+/* remove pulses older than window */
+static void pulse_queue_check_window(struct pri_detector *pde)
+{
+       u64 min_valid_ts;
+       struct pulse_elem *p;
+
+       /* there is no delta time with less than 2 pulses */
+       if (pde->count < 2)
+               return;
+
+       if (pde->last_ts <= pde->window_size)
+               return;
+
+       min_valid_ts = pde->last_ts - pde->window_size;
+       while ((p = pulse_queue_get_tail(pde)) != NULL) {
+               if (p->ts >= min_valid_ts)
+                       return;
+               pulse_queue_dequeue(pde);
+       }
+}
+
+static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+       struct pulse_elem *p = pool_get_pulse_elem();
+       if (p == NULL) {
+               p = kmalloc(sizeof(*p), GFP_ATOMIC);
+               if (p == NULL) {
+                       DFS_POOL_STAT_INC(pulse_alloc_error);
+                       return false;
+               }
+               DFS_POOL_STAT_INC(pulse_allocated);
+               DFS_POOL_STAT_INC(pulse_used);
+       }
+       INIT_LIST_HEAD(&p->head);
+       p->ts = ts;
+       list_add(&p->head, &pde->pulses);
+       pde->count++;
+       pde->last_ts = ts;
+       pulse_queue_check_window(pde);
+       if (pde->count >= pde->max_count)
+               pulse_queue_dequeue(pde);
+       return true;
+}
+
+static bool pseq_handler_create_sequences(struct pri_detector *pde,
+                                         u64 ts, u32 min_count)
+{
+       struct pulse_elem *p;
+       list_for_each_entry(p, &pde->pulses, head) {
+               struct pri_sequence ps, *new_ps;
+               struct pulse_elem *p2;
+               u32 tmp_false_count;
+               u64 min_valid_ts;
+               u32 delta_ts = ts - p->ts;
+
+               if (delta_ts < pde->rs->pri_min)
+                       /* ignore too small pri */
+                       continue;
+
+               if (delta_ts > pde->rs->pri_max)
+                       /* stop on too large pri (sorted list) */
+                       break;
+
+               /* build a new sequence with new potential pri */
+               ps.count = 2;
+               ps.count_falses = 0;
+               ps.first_ts = p->ts;
+               ps.last_ts = ts;
+               ps.pri = ts - p->ts;
+               ps.dur = ps.pri * (pde->rs->ppb - 1)
+                               + 2 * pde->rs->max_pri_tolerance;
+
+               p2 = p;
+               tmp_false_count = 0;
+               min_valid_ts = ts - ps.dur;
+               /* check which past pulses are candidates for new sequence */
+               list_for_each_entry_continue(p2, &pde->pulses, head) {
+                       u32 factor;
+                       if (p2->ts < min_valid_ts)
+                               /* stop on crossing window border */
+                               break;
+                       /* check if pulse match (multi)PRI */
+                       factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+                                                 pde->rs->max_pri_tolerance);
+                       if (factor > 0) {
+                               ps.count++;
+                               ps.first_ts = p2->ts;
+                               /*
+                                * on match, add the intermediate falses
+                                * and reset counter
+                                */
+                               ps.count_falses += tmp_false_count;
+                               tmp_false_count = 0;
+                       } else {
+                               /* this is a potential false one */
+                               tmp_false_count++;
+                       }
+               }
+               if (ps.count < min_count)
+                       /* did not reach minimum count, drop sequence */
+                       continue;
+
+               /* this is a valid one, add it */
+               ps.deadline_ts = ps.first_ts + ps.dur;
+               new_ps = pool_get_pseq_elem();
+               if (new_ps == NULL) {
+                       new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
+                       if (new_ps == NULL) {
+                               DFS_POOL_STAT_INC(pseq_alloc_error);
+                               return false;
+                       }
+                       DFS_POOL_STAT_INC(pseq_allocated);
+                       DFS_POOL_STAT_INC(pseq_used);
+               }
+               memcpy(new_ps, &ps, sizeof(ps));
+               INIT_LIST_HEAD(&new_ps->head);
+               list_add(&new_ps->head, &pde->sequences);
+       }
+       return true;
+}
+
+/* check new ts and add to all matching existing sequences */
+static u32
+pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+       u32 max_count = 0;
+       struct pri_sequence *ps, *ps2;
+       list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+               u32 delta_ts;
+               u32 factor;
+
+               /* first ensure that sequence is within window */
+               if (ts > ps->deadline_ts) {
+                       list_del_init(&ps->head);
+                       pool_put_pseq_elem(ps);
+                       continue;
+               }
+
+               delta_ts = ts - ps->last_ts;
+               factor = pde_get_multiple(delta_ts, ps->pri,
+                                         pde->rs->max_pri_tolerance);
+               if (factor > 0) {
+                       ps->last_ts = ts;
+                       ps->count++;
+
+                       if (max_count < ps->count)
+                               max_count = ps->count;
+               } else {
+                       ps->count_falses++;
+               }
+       }
+       return max_count;
+}
+
+static struct pri_sequence *
+pseq_handler_check_detection(struct pri_detector *pde)
+{
+       struct pri_sequence *ps;
+
+       if (list_empty(&pde->sequences))
+               return NULL;
+
+       list_for_each_entry(ps, &pde->sequences, head) {
+               /*
+                * we assume to have enough matching confidence if we
+                * 1) have enough pulses
+                * 2) have more matching than false pulses
+                */
+               if ((ps->count >= pde->rs->ppb_thresh) &&
+                   (ps->count * pde->rs->num_pri >= ps->count_falses))
+                       return ps;
+       }
+       return NULL;
+}
+
+
+/* free pulse queue and sequences list and give objects back to pools */
+static void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+       struct pri_sequence *ps, *ps0;
+       struct pulse_elem *p, *p0;
+       list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+               list_del_init(&ps->head);
+               pool_put_pseq_elem(ps);
+       }
+       list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+               list_del_init(&p->head);
+               pool_put_pulse_elem(p);
+       }
+       pde->count = 0;
+       pde->last_ts = ts;
+}
+
+static void pri_detector_exit(struct pri_detector *de)
+{
+       pri_detector_reset(de, 0);
+       pool_deregister_ref();
+       kfree(de);
+}
+
+static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
+                                                  struct pulse_event *event)
+{
+       u32 max_updated_seq;
+       struct pri_sequence *ps;
+       u64 ts = event->ts;
+       const struct radar_detector_specs *rs = de->rs;
+
+       /* ignore pulses not within width range */
+       if ((rs->width_min > event->width) || (rs->width_max < event->width))
+               return NULL;
+
+       if ((ts - de->last_ts) < rs->max_pri_tolerance)
+               /* if delta to last pulse is too short, don't use this pulse */
+               return NULL;
+       de->last_ts = ts;
+
+       max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
+
+       if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
+               pri_detector_reset(de, ts);
+               return NULL;
+       }
+
+       ps = pseq_handler_check_detection(de);
+
+       if (ps == NULL)
+               pulse_queue_enqueue(de, ts);
+
+       return ps;
+}
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
+{
+       struct pri_detector *de;
+
+       de = kzalloc(sizeof(*de), GFP_ATOMIC);
+       if (de == NULL)
+               return NULL;
+       de->exit = pri_detector_exit;
+       de->add_pulse = pri_detector_add_pulse;
+       de->reset = pri_detector_reset;
+
+       INIT_LIST_HEAD(&de->sequences);
+       INIT_LIST_HEAD(&de->pulses);
+       de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
+       de->max_count = rs->ppb * 2;
+       de->rs = rs;
+
+       pool_register_ref();
+       return de;
+}
diff --git a/drivers/net/wireless/ath/dfs_pri_detector.h b/drivers/net/wireless/ath/dfs_pri_detector.h
new file mode 100644 (file)
index 0000000..79f0fff
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DFS_PRI_DETECTOR_H
+#define DFS_PRI_DETECTOR_H
+
+#include <linux/list.h>
+
+extern struct ath_dfs_pool_stats global_dfs_pool_stats;
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ */
+struct pri_sequence {
+       struct list_head head;
+       u32 pri;
+       u32 dur;
+       u32 count;
+       u32 count_falses;
+       u64 first_ts;
+       u64 last_ts;
+       u64 deadline_ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @exit(): destructor
+ * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
+ * @reset(): clear states and reset to given time stamp
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ */
+struct pri_detector {
+       void (*exit)     (struct pri_detector *de);
+       struct pri_sequence *
+            (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
+       void (*reset)    (struct pri_detector *de, u64 ts);
+
+/* private: internal use only */
+       const struct radar_detector_specs *rs;
+       u64 last_ts;
+       struct list_head sequences;
+       struct list_head pulses;
+       u32 count;
+       u32 max_count;
+       u32 window_size;
+};
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs);
+
+#endif /* DFS_PRI_DETECTOR_H */
index 7d077c7..c00687e 100644 (file)
@@ -356,14 +356,131 @@ static u16 ath_regd_find_country_by_name(char *alpha2)
        return -1;
 }
 
+static int __ath_reg_dyn_country(struct wiphy *wiphy,
+                                struct ath_regulatory *reg,
+                                struct regulatory_request *request)
+{
+       u16 country_code;
+
+       if (!ath_is_world_regd(reg))
+               return -EINVAL;
+
+       country_code = ath_regd_find_country_by_name(request->alpha2);
+       if (country_code == (u16) -1)
+               return -EINVAL;
+
+       reg->current_rd = COUNTRY_ERD_FLAG;
+       reg->current_rd |= country_code;
+
+       __ath_regd_init(reg);
+
+       ath_reg_apply_world_flags(wiphy, request->initiator, reg);
+
+       return 0;
+}
+
+static void ath_reg_dyn_country(struct wiphy *wiphy,
+                               struct ath_regulatory *reg,
+                               struct regulatory_request *request)
+{
+       if (__ath_reg_dyn_country(wiphy, reg, request))
+               return;
+
+       printk(KERN_DEBUG "ath: regdomain 0x%0x "
+                         "dynamically updated by %s\n",
+              reg->current_rd,
+              reg_initiator_name(request->initiator));
+}
+
+static bool dynamic_country_user_possible(struct ath_regulatory *reg)
+{
+       if (config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
+               return true;
+
+       switch (reg->country_code) {
+       case CTRY_UNITED_STATES:
+       case CTRY_JAPAN1:
+       case CTRY_JAPAN2:
+       case CTRY_JAPAN3:
+       case CTRY_JAPAN4:
+       case CTRY_JAPAN5:
+       case CTRY_JAPAN6:
+       case CTRY_JAPAN7:
+       case CTRY_JAPAN8:
+       case CTRY_JAPAN9:
+       case CTRY_JAPAN10:
+       case CTRY_JAPAN11:
+       case CTRY_JAPAN12:
+       case CTRY_JAPAN13:
+       case CTRY_JAPAN14:
+       case CTRY_JAPAN15:
+       case CTRY_JAPAN16:
+       case CTRY_JAPAN17:
+       case CTRY_JAPAN18:
+       case CTRY_JAPAN19:
+       case CTRY_JAPAN20:
+       case CTRY_JAPAN21:
+       case CTRY_JAPAN22:
+       case CTRY_JAPAN23:
+       case CTRY_JAPAN24:
+       case CTRY_JAPAN25:
+       case CTRY_JAPAN26:
+       case CTRY_JAPAN27:
+       case CTRY_JAPAN28:
+       case CTRY_JAPAN29:
+       case CTRY_JAPAN30:
+       case CTRY_JAPAN31:
+       case CTRY_JAPAN32:
+       case CTRY_JAPAN33:
+       case CTRY_JAPAN34:
+       case CTRY_JAPAN35:
+       case CTRY_JAPAN36:
+       case CTRY_JAPAN37:
+       case CTRY_JAPAN38:
+       case CTRY_JAPAN39:
+       case CTRY_JAPAN40:
+       case CTRY_JAPAN41:
+       case CTRY_JAPAN42:
+       case CTRY_JAPAN43:
+       case CTRY_JAPAN44:
+       case CTRY_JAPAN45:
+       case CTRY_JAPAN46:
+       case CTRY_JAPAN47:
+       case CTRY_JAPAN48:
+       case CTRY_JAPAN49:
+       case CTRY_JAPAN50:
+       case CTRY_JAPAN51:
+       case CTRY_JAPAN52:
+       case CTRY_JAPAN53:
+       case CTRY_JAPAN54:
+       case CTRY_JAPAN55:
+       case CTRY_JAPAN56:
+       case CTRY_JAPAN57:
+       case CTRY_JAPAN58:
+       case CTRY_JAPAN59:
+               return false;
+       }
+
+       return true;
+}
+
+static void ath_reg_dyn_country_user(struct wiphy *wiphy,
+                                    struct ath_regulatory *reg,
+                                    struct regulatory_request *request)
+{
+       if (!config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS))
+               return;
+       if (!dynamic_country_user_possible(reg))
+               return;
+       ath_reg_dyn_country(wiphy, reg, request);
+}
+
 void ath_reg_notifier_apply(struct wiphy *wiphy,
                            struct regulatory_request *request,
                            struct ath_regulatory *reg)
 {
        struct ath_common *common = container_of(reg, struct ath_common,
                                                 regulatory);
-       u16 country_code;
-
        /* We always apply this */
        ath_reg_apply_radar_flags(wiphy);
 
@@ -388,25 +505,12 @@ void ath_reg_notifier_apply(struct wiphy *wiphy,
                       sizeof(struct ath_regulatory));
                break;
        case NL80211_REGDOM_SET_BY_DRIVER:
+               break;
        case NL80211_REGDOM_SET_BY_USER:
+               ath_reg_dyn_country_user(wiphy, reg, request);
                break;
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-               if (!ath_is_world_regd(reg))
-                       break;
-
-               country_code = ath_regd_find_country_by_name(request->alpha2);
-               if (country_code == (u16) -1)
-                       break;
-
-               reg->current_rd = COUNTRY_ERD_FLAG;
-               reg->current_rd |= country_code;
-
-               printk(KERN_DEBUG "ath: regdomain 0x%0x updated by CountryIE\n",
-                       reg->current_rd);
-               __ath_regd_init(reg);
-
-               ath_reg_apply_world_flags(wiphy, request->initiator, reg);
-
+               ath_reg_dyn_country(wiphy, reg, request);
                break;
        }
 }
diff --git a/drivers/net/wireless/ath/wcn36xx/Kconfig b/drivers/net/wireless/ath/wcn36xx/Kconfig
new file mode 100644 (file)
index 0000000..591ebae
--- /dev/null
@@ -0,0 +1,16 @@
+config WCN36XX
+       tristate "Qualcomm Atheros WCN3660/3680 support"
+       depends on MAC80211 && HAS_DMA
+       ---help---
+         This module adds support for wireless adapters based on
+         Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets.
+
+         If you choose to build a module, it'll be called wcn36xx.
+
+config WCN36XX_DEBUGFS
+       bool "WCN36XX debugfs support"
+       depends on WCN36XX
+       ---help---
+         Enabled debugfs support
+
+         If unsure, say Y to make it easier to debug problems.
diff --git a/drivers/net/wireless/ath/wcn36xx/Makefile b/drivers/net/wireless/ath/wcn36xx/Makefile
new file mode 100644 (file)
index 0000000..50c43b4
--- /dev/null
@@ -0,0 +1,7 @@
+obj-$(CONFIG_WCN36XX) := wcn36xx.o
+wcn36xx-y +=   main.o \
+               dxe.o \
+               txrx.o \
+               smd.o \
+               pmc.o \
+               debug.o
diff --git a/drivers/net/wireless/ath/wcn36xx/debug.c b/drivers/net/wireless/ath/wcn36xx/debug.c
new file mode 100644 (file)
index 0000000..ef44a2d
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include "wcn36xx.h"
+#include "debug.h"
+#include "pmc.h"
+
+#ifdef CONFIG_WCN36XX_DEBUGFS
+
+static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct wcn36xx *wcn = file->private_data;
+       struct wcn36xx_vif *vif_priv = NULL;
+       struct ieee80211_vif *vif = NULL;
+       char buf[3];
+
+       list_for_each_entry(vif_priv, &wcn->vif_list, list) {
+                       vif = container_of((void *)vif_priv,
+                                  struct ieee80211_vif,
+                                  drv_priv);
+                       if (NL80211_IFTYPE_STATION == vif->type) {
+                               if (vif_priv->pw_state == WCN36XX_BMPS)
+                                       buf[0] = '1';
+                               else
+                                       buf[0] = '0';
+                               break;
+                       }
+       }
+       buf[1] = '\n';
+       buf[2] = 0x00;
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t write_file_bool_bmps(struct file *file,
+                                   const char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct wcn36xx *wcn = file->private_data;
+       struct wcn36xx_vif *vif_priv = NULL;
+       struct ieee80211_vif *vif = NULL;
+
+       char buf[32];
+       int buf_size;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       switch (buf[0]) {
+       case 'y':
+       case 'Y':
+       case '1':
+               list_for_each_entry(vif_priv, &wcn->vif_list, list) {
+                       vif = container_of((void *)vif_priv,
+                                  struct ieee80211_vif,
+                                  drv_priv);
+                       if (NL80211_IFTYPE_STATION == vif->type) {
+                               wcn36xx_enable_keep_alive_null_packet(wcn, vif);
+                               wcn36xx_pmc_enter_bmps_state(wcn, vif);
+                       }
+               }
+               break;
+       case 'n':
+       case 'N':
+       case '0':
+               list_for_each_entry(vif_priv, &wcn->vif_list, list) {
+                       vif = container_of((void *)vif_priv,
+                                  struct ieee80211_vif,
+                                  drv_priv);
+                       if (NL80211_IFTYPE_STATION == vif->type)
+                               wcn36xx_pmc_exit_bmps_state(wcn, vif);
+               }
+               break;
+       }
+
+       return count;
+}
+
+static const struct file_operations fops_wcn36xx_bmps = {
+       .open = simple_open,
+       .read  =       read_file_bool_bmps,
+       .write =       write_file_bool_bmps,
+};
+
+static ssize_t write_file_dump(struct file *file,
+                                   const char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct wcn36xx *wcn = file->private_data;
+       char buf[255], *tmp;
+       int buf_size;
+       u32 arg[WCN36xx_MAX_DUMP_ARGS];
+       int i;
+
+       memset(buf, 0, sizeof(buf));
+       memset(arg, 0, sizeof(arg));
+
+       buf_size = min(count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       tmp = buf;
+
+       for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) {
+               char *begin;
+               begin = strsep(&tmp, " ");
+               if (begin == NULL)
+                       break;
+
+               if (kstrtou32(begin, 0, &arg[i]) != 0)
+                       break;
+       }
+
+       wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2],
+                    arg[3], arg[4]);
+       wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]);
+
+       return count;
+}
+
+static const struct file_operations fops_wcn36xx_dump = {
+       .open = simple_open,
+       .write =       write_file_dump,
+};
+
+#define ADD_FILE(name, mode, fop, priv_data)           \
+       do {                                                    \
+               struct dentry *d;                               \
+               d = debugfs_create_file(__stringify(name),      \
+                                       mode, dfs->rootdir,     \
+                                       priv_data, fop);        \
+               dfs->file_##name.dentry = d;                    \
+               if (IS_ERR(d)) {                                \
+                       wcn36xx_warn("Create the debugfs entry failed");\
+                       dfs->file_##name.dentry = NULL;         \
+               }                                               \
+       } while (0)
+
+
+void wcn36xx_debugfs_init(struct wcn36xx *wcn)
+{
+       struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
+
+       dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME,
+                                         wcn->hw->wiphy->debugfsdir);
+       if (IS_ERR(dfs->rootdir)) {
+               wcn36xx_warn("Create the debugfs failed\n");
+               dfs->rootdir = NULL;
+       }
+
+       ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR,
+                &fops_wcn36xx_bmps, wcn);
+       ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn);
+}
+
+void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
+{
+       struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
+       debugfs_remove_recursive(dfs->rootdir);
+}
+
+#endif /* CONFIG_WCN36XX_DEBUGFS */
diff --git a/drivers/net/wireless/ath/wcn36xx/debug.h b/drivers/net/wireless/ath/wcn36xx/debug.h
new file mode 100644 (file)
index 0000000..46307aa
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WCN36XX_DEBUG_H_
+#define _WCN36XX_DEBUG_H_
+
+#include <linux/kernel.h>
+
+#define WCN36xx_MAX_DUMP_ARGS  5
+
+#ifdef CONFIG_WCN36XX_DEBUGFS
+struct wcn36xx_dfs_file {
+       struct dentry *dentry;
+       u32 value;
+};
+
+struct wcn36xx_dfs_entry {
+       struct dentry *rootdir;
+       struct wcn36xx_dfs_file file_bmps_switcher;
+       struct wcn36xx_dfs_file file_dump;
+};
+
+void wcn36xx_debugfs_init(struct wcn36xx *wcn);
+void wcn36xx_debugfs_exit(struct wcn36xx *wcn);
+
+#else
+static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn)
+{
+}
+static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
+{
+}
+
+#endif /* CONFIG_WCN36XX_DEBUGFS */
+
+#endif /* _WCN36XX_DEBUG_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
new file mode 100644 (file)
index 0000000..ee25786
--- /dev/null
@@ -0,0 +1,805 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* DXE - DMA transfer engine
+ * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX.
+ * through low channels data packets are transfered
+ * through high channels managment packets are transfered
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/interrupt.h>
+#include "wcn36xx.h"
+#include "txrx.h"
+
+void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low)
+{
+       struct wcn36xx_dxe_ch *ch = is_low ?
+               &wcn->dxe_tx_l_ch :
+               &wcn->dxe_tx_h_ch;
+
+       return ch->head_blk_ctl->bd_cpu_addr;
+}
+
+static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
+{
+       wcn36xx_dbg(WCN36XX_DBG_DXE,
+                   "wcn36xx_dxe_write_register: addr=%x, data=%x\n",
+                   addr, data);
+
+       writel(data, wcn->mmio + addr);
+}
+
+static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
+{
+       *data = readl(wcn->mmio + addr);
+
+       wcn36xx_dbg(WCN36XX_DBG_DXE,
+                   "wcn36xx_dxe_read_register: addr=%x, data=%x\n",
+                   addr, *data);
+}
+
+static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch)
+{
+       struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next;
+       int i;
+
+       for (i = 0; i < ch->desc_num && ctl; i++) {
+               next = ctl->next;
+               kfree(ctl);
+               ctl = next;
+       }
+}
+
+static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch)
+{
+       struct wcn36xx_dxe_ctl *prev_ctl = NULL;
+       struct wcn36xx_dxe_ctl *cur_ctl = NULL;
+       int i;
+
+       for (i = 0; i < ch->desc_num; i++) {
+               cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL);
+               if (!cur_ctl)
+                       goto out_fail;
+
+               cur_ctl->ctl_blk_order = i;
+               if (i == 0) {
+                       ch->head_blk_ctl = cur_ctl;
+                       ch->tail_blk_ctl = cur_ctl;
+               } else if (ch->desc_num - 1 == i) {
+                       prev_ctl->next = cur_ctl;
+                       cur_ctl->next = ch->head_blk_ctl;
+               } else {
+                       prev_ctl->next = cur_ctl;
+               }
+               prev_ctl = cur_ctl;
+       }
+
+       return 0;
+
+out_fail:
+       wcn36xx_dxe_free_ctl_block(ch);
+       return -ENOMEM;
+}
+
+int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn)
+{
+       int ret;
+
+       wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L;
+       wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H;
+       wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L;
+       wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H;
+
+       wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L;
+       wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H;
+       wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L;
+       wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H;
+
+       wcn->dxe_tx_l_ch.dxe_wq =  WCN36XX_DXE_WQ_TX_L;
+       wcn->dxe_tx_h_ch.dxe_wq =  WCN36XX_DXE_WQ_TX_H;
+
+       wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD;
+       wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD;
+
+       wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB;
+       wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB;
+
+       wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L;
+       wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H;
+
+       wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L;
+       wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H;
+
+       /* DXE control block allocation */
+       ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch);
+       if (ret)
+               goto out_err;
+       ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch);
+       if (ret)
+               goto out_err;
+       ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch);
+       if (ret)
+               goto out_err;
+       ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch);
+       if (ret)
+               goto out_err;
+
+       /* Initialize SMSM state  Clear TX Enable RING EMPTY STATE */
+       ret = wcn->ctrl_ops->smsm_change_state(
+               WCN36XX_SMSM_WLAN_TX_ENABLE,
+               WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY);
+
+       return 0;
+
+out_err:
+       wcn36xx_err("Failed to allocate DXE control blocks\n");
+       wcn36xx_dxe_free_ctl_blks(wcn);
+       return -ENOMEM;
+}
+
+void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn)
+{
+       wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch);
+       wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch);
+       wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch);
+       wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch);
+}
+
+static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch)
+{
+       struct wcn36xx_dxe_desc *cur_dxe = NULL;
+       struct wcn36xx_dxe_desc *prev_dxe = NULL;
+       struct wcn36xx_dxe_ctl *cur_ctl = NULL;
+       size_t size;
+       int i;
+
+       size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
+       wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr,
+                                             GFP_KERNEL);
+       if (!wcn_ch->cpu_addr)
+               return -ENOMEM;
+
+       memset(wcn_ch->cpu_addr, 0, size);
+
+       cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr;
+       cur_ctl = wcn_ch->head_blk_ctl;
+
+       for (i = 0; i < wcn_ch->desc_num; i++) {
+               cur_ctl->desc = cur_dxe;
+               cur_ctl->desc_phy_addr = wcn_ch->dma_addr +
+                       i * sizeof(struct wcn36xx_dxe_desc);
+
+               switch (wcn_ch->ch_type) {
+               case WCN36XX_DXE_CH_TX_L:
+                       cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L;
+                       cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L;
+                       break;
+               case WCN36XX_DXE_CH_TX_H:
+                       cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H;
+                       cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H;
+                       break;
+               case WCN36XX_DXE_CH_RX_L:
+                       cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
+                       cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L;
+                       break;
+               case WCN36XX_DXE_CH_RX_H:
+                       cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
+                       cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H;
+                       break;
+               }
+               if (0 == i) {
+                       cur_dxe->phy_next_l = 0;
+               } else if ((0 < i) && (i < wcn_ch->desc_num - 1)) {
+                       prev_dxe->phy_next_l =
+                               cur_ctl->desc_phy_addr;
+               } else if (i == (wcn_ch->desc_num - 1)) {
+                       prev_dxe->phy_next_l =
+                               cur_ctl->desc_phy_addr;
+                       cur_dxe->phy_next_l =
+                               wcn_ch->head_blk_ctl->desc_phy_addr;
+               }
+               cur_ctl = cur_ctl->next;
+               prev_dxe = cur_dxe;
+               cur_dxe++;
+       }
+
+       return 0;
+}
+
+static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch,
+                                  struct wcn36xx_dxe_mem_pool *pool)
+{
+       int i, chunk_size = pool->chunk_size;
+       dma_addr_t bd_phy_addr = pool->phy_addr;
+       void *bd_cpu_addr = pool->virt_addr;
+       struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl;
+
+       for (i = 0; i < ch->desc_num; i++) {
+               /* Only every second dxe needs a bd pointer,
+                  the other will point to the skb data */
+               if (!(i & 1)) {
+                       cur->bd_phy_addr = bd_phy_addr;
+                       cur->bd_cpu_addr = bd_cpu_addr;
+                       bd_phy_addr += chunk_size;
+                       bd_cpu_addr += chunk_size;
+               } else {
+                       cur->bd_phy_addr = 0;
+                       cur->bd_cpu_addr = NULL;
+               }
+               cur = cur->next;
+       }
+}
+
+static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
+{
+       int reg_data = 0;
+
+       wcn36xx_dxe_read_register(wcn,
+                                 WCN36XX_DXE_INT_MASK_REG,
+                                 &reg_data);
+
+       reg_data |= wcn_ch;
+
+       wcn36xx_dxe_write_register(wcn,
+                                  WCN36XX_DXE_INT_MASK_REG,
+                                  (int)reg_data);
+       return 0;
+}
+
+static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl)
+{
+       struct wcn36xx_dxe_desc *dxe = ctl->desc;
+       struct sk_buff *skb;
+
+       skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       dxe->dst_addr_l = dma_map_single(NULL,
+                                        skb_tail_pointer(skb),
+                                        WCN36XX_PKT_SIZE,
+                                        DMA_FROM_DEVICE);
+       ctl->skb = skb;
+
+       return 0;
+}
+
+static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn,
+                                   struct wcn36xx_dxe_ch *wcn_ch)
+{
+       int i;
+       struct wcn36xx_dxe_ctl *cur_ctl = NULL;
+
+       cur_ctl = wcn_ch->head_blk_ctl;
+
+       for (i = 0; i < wcn_ch->desc_num; i++) {
+               wcn36xx_dxe_fill_skb(cur_ctl);
+               cur_ctl = cur_ctl->next;
+       }
+
+       return 0;
+}
+
+static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn,
+                                    struct wcn36xx_dxe_ch *wcn_ch)
+{
+       struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl;
+       int i;
+
+       for (i = 0; i < wcn_ch->desc_num; i++) {
+               kfree_skb(cur->skb);
+               cur = cur->next;
+       }
+}
+
+void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
+{
+       struct ieee80211_tx_info *info;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wcn->dxe_lock, flags);
+       skb = wcn->tx_ack_skb;
+       wcn->tx_ack_skb = NULL;
+       spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+
+       if (!skb) {
+               wcn36xx_warn("Spurious TX complete indication\n");
+               return;
+       }
+
+       info = IEEE80211_SKB_CB(skb);
+
+       if (status == 1)
+               info->flags |= IEEE80211_TX_STAT_ACK;
+
+       wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status);
+
+       ieee80211_tx_status_irqsafe(wcn->hw, skb);
+       ieee80211_wake_queues(wcn->hw);
+}
+
+static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
+{
+       struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl;
+       struct ieee80211_tx_info *info;
+       unsigned long flags;
+
+       /*
+        * Make at least one loop of do-while because in case ring is
+        * completely full head and tail are pointing to the same element
+        * and while-do will not make any cycles.
+        */
+       do {
+               if (ctl->skb) {
+                       dma_unmap_single(NULL, ctl->desc->src_addr_l,
+                                        ctl->skb->len, DMA_TO_DEVICE);
+                       info = IEEE80211_SKB_CB(ctl->skb);
+                       if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
+                               /* Keep frame until TX status comes */
+                               ieee80211_free_txskb(wcn->hw, ctl->skb);
+                       }
+                       spin_lock_irqsave(&ctl->skb_lock, flags);
+                       if (wcn->queues_stopped) {
+                               wcn->queues_stopped = false;
+                               ieee80211_wake_queues(wcn->hw);
+                       }
+                       spin_unlock_irqrestore(&ctl->skb_lock, flags);
+
+                       ctl->skb = NULL;
+               }
+               ctl = ctl->next;
+       } while (ctl != ch->head_blk_ctl &&
+              !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK));
+
+       ch->tail_blk_ctl = ctl;
+}
+
+static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
+{
+       struct wcn36xx *wcn = (struct wcn36xx *)dev;
+       int int_src, int_reason;
+
+       wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
+
+       if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) {
+               wcn36xx_dxe_read_register(wcn,
+                                         WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
+                                         &int_reason);
+
+               /* TODO: Check int_reason */
+
+               wcn36xx_dxe_write_register(wcn,
+                                          WCN36XX_DXE_0_INT_CLR,
+                                          WCN36XX_INT_MASK_CHAN_TX_H);
+
+               wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
+                                          WCN36XX_INT_MASK_CHAN_TX_H);
+               wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n");
+               reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
+       }
+
+       if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
+               wcn36xx_dxe_read_register(wcn,
+                                         WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
+                                         &int_reason);
+               /* TODO: Check int_reason */
+
+               wcn36xx_dxe_write_register(wcn,
+                                          WCN36XX_DXE_0_INT_CLR,
+                                          WCN36XX_INT_MASK_CHAN_TX_L);
+
+               wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
+                                          WCN36XX_INT_MASK_CHAN_TX_L);
+               wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n");
+               reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev)
+{
+       struct wcn36xx *wcn = (struct wcn36xx *)dev;
+
+       disable_irq_nosync(wcn->rx_irq);
+       wcn36xx_dxe_rx_frame(wcn);
+       enable_irq(wcn->rx_irq);
+       return IRQ_HANDLED;
+}
+
+static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn)
+{
+       int ret;
+
+       ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete,
+                         IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn);
+       if (ret) {
+               wcn36xx_err("failed to alloc tx irq\n");
+               goto out_err;
+       }
+
+       ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH,
+                         "wcn36xx_rx", wcn);
+       if (ret) {
+               wcn36xx_err("failed to alloc rx irq\n");
+               goto out_txirq;
+       }
+
+       enable_irq_wake(wcn->rx_irq);
+
+       return 0;
+
+out_txirq:
+       free_irq(wcn->tx_irq, wcn);
+out_err:
+       return ret;
+
+}
+
+static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
+                                    struct wcn36xx_dxe_ch *ch)
+{
+       struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl;
+       struct wcn36xx_dxe_desc *dxe = ctl->desc;
+       dma_addr_t  dma_addr;
+       struct sk_buff *skb;
+
+       while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
+               skb = ctl->skb;
+               dma_addr = dxe->dst_addr_l;
+               wcn36xx_dxe_fill_skb(ctl);
+
+               switch (ch->ch_type) {
+               case WCN36XX_DXE_CH_RX_L:
+                       dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
+                       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
+                                                  WCN36XX_DXE_INT_CH1_MASK);
+                       break;
+               case WCN36XX_DXE_CH_RX_H:
+                       dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
+                       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
+                                                  WCN36XX_DXE_INT_CH3_MASK);
+                       break;
+               default:
+                       wcn36xx_warn("Unknown channel\n");
+               }
+
+               dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE,
+                                DMA_FROM_DEVICE);
+               wcn36xx_rx_skb(wcn, skb);
+               ctl = ctl->next;
+               dxe = ctl->desc;
+       }
+
+       ch->head_blk_ctl = ctl;
+
+       return 0;
+}
+
+void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
+{
+       int int_src;
+
+       wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
+
+       /* RX_LOW_PRI */
+       if (int_src & WCN36XX_DXE_INT_CH1_MASK) {
+               wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
+                                          WCN36XX_DXE_INT_CH1_MASK);
+               wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch));
+       }
+
+       /* RX_HIGH_PRI */
+       if (int_src & WCN36XX_DXE_INT_CH3_MASK) {
+               /* Clean up all the INT within this channel */
+               wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
+                                          WCN36XX_DXE_INT_CH3_MASK);
+               wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch));
+       }
+
+       if (!int_src)
+               wcn36xx_warn("No DXE interrupt pending\n");
+}
+
+int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn)
+{
+       size_t s;
+       void *cpu_addr;
+
+       /* Allocate BD headers for MGMT frames */
+
+       /* Where this come from ask QC */
+       wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
+               16 - (WCN36XX_BD_CHUNK_SIZE % 8);
+
+       s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H;
+       cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr,
+                                     GFP_KERNEL);
+       if (!cpu_addr)
+               goto out_err;
+
+       wcn->mgmt_mem_pool.virt_addr = cpu_addr;
+       memset(cpu_addr, 0, s);
+
+       /* Allocate BD headers for DATA frames */
+
+       /* Where this come from ask QC */
+       wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
+               16 - (WCN36XX_BD_CHUNK_SIZE % 8);
+
+       s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L;
+       cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr,
+                                     GFP_KERNEL);
+       if (!cpu_addr)
+               goto out_err;
+
+       wcn->data_mem_pool.virt_addr = cpu_addr;
+       memset(cpu_addr, 0, s);
+
+       return 0;
+
+out_err:
+       wcn36xx_dxe_free_mem_pools(wcn);
+       wcn36xx_err("Failed to allocate BD mempool\n");
+       return -ENOMEM;
+}
+
+void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
+{
+       if (wcn->mgmt_mem_pool.virt_addr)
+               dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size *
+                                 WCN36XX_DXE_CH_DESC_NUMB_TX_H,
+                                 wcn->mgmt_mem_pool.virt_addr,
+                                 wcn->mgmt_mem_pool.phy_addr);
+
+       if (wcn->data_mem_pool.virt_addr) {
+               dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size *
+                                 WCN36XX_DXE_CH_DESC_NUMB_TX_L,
+                                 wcn->data_mem_pool.virt_addr,
+                                 wcn->data_mem_pool.phy_addr);
+       }
+}
+
+int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
+                        struct wcn36xx_vif *vif_priv,
+                        struct sk_buff *skb,
+                        bool is_low)
+{
+       struct wcn36xx_dxe_ctl *ctl = NULL;
+       struct wcn36xx_dxe_desc *desc = NULL;
+       struct wcn36xx_dxe_ch *ch = NULL;
+       unsigned long flags;
+
+       ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
+
+       ctl = ch->head_blk_ctl;
+
+       spin_lock_irqsave(&ctl->next->skb_lock, flags);
+
+       /*
+        * If skb is not null that means that we reached the tail of the ring
+        * hence ring is full. Stop queues to let mac80211 back off until ring
+        * has an empty slot again.
+        */
+       if (NULL != ctl->next->skb) {
+               ieee80211_stop_queues(wcn->hw);
+               wcn->queues_stopped = true;
+               spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
+
+       ctl->skb = NULL;
+       desc = ctl->desc;
+
+       /* Set source address of the BD we send */
+       desc->src_addr_l = ctl->bd_phy_addr;
+
+       desc->dst_addr_l = ch->dxe_wq;
+       desc->fr_len = sizeof(struct wcn36xx_tx_bd);
+       desc->ctrl = ch->ctrl_bd;
+
+       wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
+
+       wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
+                        (char *)desc, sizeof(*desc));
+       wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
+                        "BD   >>> ", (char *)ctl->bd_cpu_addr,
+                        sizeof(struct wcn36xx_tx_bd));
+
+       /* Set source address of the SKB we send */
+       ctl = ctl->next;
+       ctl->skb = skb;
+       desc = ctl->desc;
+       if (ctl->bd_cpu_addr) {
+               wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
+               return -EINVAL;
+       }
+
+       desc->src_addr_l = dma_map_single(NULL,
+                                         ctl->skb->data,
+                                         ctl->skb->len,
+                                         DMA_TO_DEVICE);
+
+       desc->dst_addr_l = ch->dxe_wq;
+       desc->fr_len = ctl->skb->len;
+
+       /* set dxe descriptor to VALID */
+       desc->ctrl = ch->ctrl_skb;
+
+       wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
+                        (char *)desc, sizeof(*desc));
+       wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB   >>> ",
+                        (char *)ctl->skb->data, ctl->skb->len);
+
+       /* Move the head of the ring to the next empty descriptor */
+        ch->head_blk_ctl = ctl->next;
+
+       /*
+        * When connected and trying to send data frame chip can be in sleep
+        * mode and writing to the register will not wake up the chip. Instead
+        * notify chip about new frame through SMSM bus.
+        */
+       if (is_low &&  vif_priv->pw_state == WCN36XX_BMPS) {
+               wcn->ctrl_ops->smsm_change_state(
+                                 0,
+                                 WCN36XX_SMSM_WLAN_TX_ENABLE);
+       } else {
+               /* indicate End Of Packet and generate interrupt on descriptor
+                * done.
+                */
+               wcn36xx_dxe_write_register(wcn,
+                       ch->reg_ctrl, ch->def_ctrl);
+       }
+
+       return 0;
+}
+
+int wcn36xx_dxe_init(struct wcn36xx *wcn)
+{
+       int reg_data = 0, ret;
+
+       reg_data = WCN36XX_DXE_REG_RESET;
+       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);
+
+       /* Setting interrupt path */
+       reg_data = WCN36XX_DXE_CCU_INT;
+       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
+
+       /***************************************/
+       /* Init descriptors for TX LOW channel */
+       /***************************************/
+       wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch);
+       wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);
+
+       /* Write channel head to a NEXT register */
+       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L,
+               wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr);
+
+       /* Program DMA destination addr for TX LOW */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_CH_DEST_ADDR_TX_L,
+               WCN36XX_DXE_WQ_TX_L);
+
+       wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);
+       wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);
+
+       /***************************************/
+       /* Init descriptors for TX HIGH channel */
+       /***************************************/
+       wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch);
+       wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);
+
+       /* Write channel head to a NEXT register */
+       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H,
+               wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr);
+
+       /* Program DMA destination addr for TX HIGH */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_CH_DEST_ADDR_TX_H,
+               WCN36XX_DXE_WQ_TX_H);
+
+       wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, &reg_data);
+
+       /* Enable channel interrupts */
+       wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);
+
+       /***************************************/
+       /* Init descriptors for RX LOW channel */
+       /***************************************/
+       wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch);
+
+       /* For RX we need to preallocated buffers */
+       wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
+
+       /* Write channel head to a NEXT register */
+       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L,
+               wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr);
+
+       /* Write DMA source address */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_CH_SRC_ADDR_RX_L,
+               WCN36XX_DXE_WQ_RX_L);
+
+       /* Program preallocated destination address */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_CH_DEST_ADDR_RX_L,
+               wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l);
+
+       /* Enable default control registers */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_REG_CTL_RX_L,
+               WCN36XX_DXE_CH_DEFAULT_CTL_RX_L);
+
+       /* Enable channel interrupts */
+       wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);
+
+       /***************************************/
+       /* Init descriptors for RX HIGH channel */
+       /***************************************/
+       wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch);
+
+       /* For RX we need to prealocat buffers */
+       wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);
+
+       /* Write chanel head to a NEXT register */
+       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H,
+               wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr);
+
+       /* Write DMA source address */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_CH_SRC_ADDR_RX_H,
+               WCN36XX_DXE_WQ_RX_H);
+
+       /* Program preallocated destination address */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_CH_DEST_ADDR_RX_H,
+                wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l);
+
+       /* Enable default control registers */
+       wcn36xx_dxe_write_register(wcn,
+               WCN36XX_DXE_REG_CTL_RX_H,
+               WCN36XX_DXE_CH_DEFAULT_CTL_RX_H);
+
+       /* Enable channel interrupts */
+       wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);
+
+       ret = wcn36xx_dxe_request_irqs(wcn);
+       if (ret < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       return ret;
+}
+
+void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
+{
+       free_irq(wcn->tx_irq, wcn);
+       free_irq(wcn->rx_irq, wcn);
+
+       if (wcn->tx_ack_skb) {
+               ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
+               wcn->tx_ack_skb = NULL;
+       }
+
+       wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch);
+       wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch);
+}
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h
new file mode 100644 (file)
index 0000000..c88562f
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DXE_H_
+#define _DXE_H_
+
+#include "wcn36xx.h"
+
+/*
+TX_LOW = DMA0
+TX_HIGH        = DMA4
+RX_LOW = DMA1
+RX_HIGH        = DMA3
+H2H_TEST_RX_TX = DMA2
+*/
+
+/* DXE registers */
+#define WCN36XX_DXE_MEM_BASE                   0x03000000
+#define WCN36XX_DXE_MEM_REG                    0x202000
+
+#define WCN36XX_DXE_CCU_INT                    0xA0011
+#define WCN36XX_DXE_REG_CCU_INT                        0x200b10
+
+/* TODO This must calculated properly but not hardcoded */
+#define WCN36XX_DXE_CTRL_TX_L                  0x328a44
+#define WCN36XX_DXE_CTRL_TX_H                  0x32ce44
+#define WCN36XX_DXE_CTRL_RX_L                  0x12ad2f
+#define WCN36XX_DXE_CTRL_RX_H                  0x12d12f
+#define WCN36XX_DXE_CTRL_TX_H_BD               0x30ce45
+#define WCN36XX_DXE_CTRL_TX_H_SKB              0x32ce4d
+#define WCN36XX_DXE_CTRL_TX_L_BD               0x308a45
+#define WCN36XX_DXE_CTRL_TX_L_SKB              0x328a4d
+
+/* TODO This must calculated properly but not hardcoded */
+#define WCN36XX_DXE_WQ_TX_L                    0x17
+#define WCN36XX_DXE_WQ_TX_H                    0x17
+#define WCN36XX_DXE_WQ_RX_L                    0xB
+#define WCN36XX_DXE_WQ_RX_H                    0x4
+
+/* DXE descriptor control filed */
+#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001)
+
+/* TODO This must calculated properly but not hardcoded */
+/* DXE default control register values */
+#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L                0x847EAD2F
+#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H                0x84FED12F
+#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H                0x853ECF4D
+#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L                0x843e8b4d
+
+/* Common DXE registers */
+#define WCN36XX_DXE_MEM_CSR                    (WCN36XX_DXE_MEM_REG + 0x00)
+#define WCN36XX_DXE_REG_CSR_RESET              (WCN36XX_DXE_MEM_REG + 0x00)
+#define WCN36XX_DXE_ENCH_ADDR                  (WCN36XX_DXE_MEM_REG + 0x04)
+#define WCN36XX_DXE_REG_CH_EN                  (WCN36XX_DXE_MEM_REG + 0x08)
+#define WCN36XX_DXE_REG_CH_DONE                        (WCN36XX_DXE_MEM_REG + 0x0C)
+#define WCN36XX_DXE_REG_CH_ERR                 (WCN36XX_DXE_MEM_REG + 0x10)
+#define WCN36XX_DXE_INT_MASK_REG               (WCN36XX_DXE_MEM_REG + 0x18)
+#define WCN36XX_DXE_INT_SRC_RAW_REG            (WCN36XX_DXE_MEM_REG + 0x20)
+       /* #define WCN36XX_DXE_INT_CH6_MASK     0x00000040 */
+       /* #define WCN36XX_DXE_INT_CH5_MASK     0x00000020 */
+       #define WCN36XX_DXE_INT_CH4_MASK        0x00000010
+       #define WCN36XX_DXE_INT_CH3_MASK        0x00000008
+       /* #define WCN36XX_DXE_INT_CH2_MASK     0x00000004 */
+       #define WCN36XX_DXE_INT_CH1_MASK        0x00000002
+       #define WCN36XX_DXE_INT_CH0_MASK        0x00000001
+#define WCN36XX_DXE_0_INT_CLR                  (WCN36XX_DXE_MEM_REG + 0x30)
+#define WCN36XX_DXE_0_INT_ED_CLR               (WCN36XX_DXE_MEM_REG + 0x34)
+#define WCN36XX_DXE_0_INT_DONE_CLR             (WCN36XX_DXE_MEM_REG + 0x38)
+#define WCN36XX_DXE_0_INT_ERR_CLR              (WCN36XX_DXE_MEM_REG + 0x3C)
+
+#define WCN36XX_DXE_0_CH0_STATUS               (WCN36XX_DXE_MEM_REG + 0x404)
+#define WCN36XX_DXE_0_CH1_STATUS               (WCN36XX_DXE_MEM_REG + 0x444)
+#define WCN36XX_DXE_0_CH2_STATUS               (WCN36XX_DXE_MEM_REG + 0x484)
+#define WCN36XX_DXE_0_CH3_STATUS               (WCN36XX_DXE_MEM_REG + 0x4C4)
+#define WCN36XX_DXE_0_CH4_STATUS               (WCN36XX_DXE_MEM_REG + 0x504)
+
+#define WCN36XX_DXE_REG_RESET                  0x5c89
+
+/* Temporary BMU Workqueue 4 */
+#define WCN36XX_DXE_BMU_WQ_RX_LOW              0xB
+#define WCN36XX_DXE_BMU_WQ_RX_HIGH             0x4
+/* DMA channel offset */
+#define WCN36XX_DXE_TX_LOW_OFFSET              0x400
+#define WCN36XX_DXE_TX_HIGH_OFFSET             0x500
+#define WCN36XX_DXE_RX_LOW_OFFSET              0x440
+#define WCN36XX_DXE_RX_HIGH_OFFSET             0x4C0
+
+/* Address of the next DXE descriptor */
+#define WCN36XX_DXE_CH_NEXT_DESC_ADDR          0x001C
+#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L     (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_NEXT_DESC_ADDR)
+#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H     (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_NEXT_DESC_ADDR)
+#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L     (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_NEXT_DESC_ADDR)
+#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H     (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_NEXT_DESC_ADDR)
+
+/* DXE Descriptor source address */
+#define WCN36XX_DXE_CH_SRC_ADDR                        0x000C
+#define WCN36XX_DXE_CH_SRC_ADDR_RX_L           (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_SRC_ADDR)
+#define WCN36XX_DXE_CH_SRC_ADDR_RX_H           (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_SRC_ADDR)
+
+/* DXE Descriptor address destination address */
+#define WCN36XX_DXE_CH_DEST_ADDR               0x0014
+#define WCN36XX_DXE_CH_DEST_ADDR_TX_L          (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_DEST_ADDR)
+#define WCN36XX_DXE_CH_DEST_ADDR_TX_H          (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_DEST_ADDR)
+#define WCN36XX_DXE_CH_DEST_ADDR_RX_L          (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_DEST_ADDR)
+#define WCN36XX_DXE_CH_DEST_ADDR_RX_H          (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_DEST_ADDR)
+
+/* Interrupt status */
+#define WCN36XX_DXE_CH_STATUS_REG_ADDR         0x0004
+#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L    (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_STATUS_REG_ADDR)
+#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H    (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_STATUS_REG_ADDR)
+#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L    (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_LOW_OFFSET + \
+                                                WCN36XX_DXE_CH_STATUS_REG_ADDR)
+#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H    (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_HIGH_OFFSET + \
+                                                WCN36XX_DXE_CH_STATUS_REG_ADDR)
+
+
+/* DXE default control register */
+#define WCN36XX_DXE_REG_CTL_RX_L               (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_LOW_OFFSET)
+#define WCN36XX_DXE_REG_CTL_RX_H               (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_RX_HIGH_OFFSET)
+#define WCN36XX_DXE_REG_CTL_TX_H               (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_HIGH_OFFSET)
+#define WCN36XX_DXE_REG_CTL_TX_L               (WCN36XX_DXE_MEM_REG + \
+                                                WCN36XX_DXE_TX_LOW_OFFSET)
+
+#define WCN36XX_SMSM_WLAN_TX_ENABLE            0x00000400
+#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY       0x00000200
+
+
+/* Interrupt control channel mask */
+#define WCN36XX_INT_MASK_CHAN_TX_L             0x00000001
+#define WCN36XX_INT_MASK_CHAN_RX_L             0x00000002
+#define WCN36XX_INT_MASK_CHAN_RX_H             0x00000008
+#define WCN36XX_INT_MASK_CHAN_TX_H             0x00000010
+
+#define WCN36XX_BD_CHUNK_SIZE                  128
+
+#define WCN36XX_PKT_SIZE                       0xF20
+enum wcn36xx_dxe_ch_type {
+       WCN36XX_DXE_CH_TX_L,
+       WCN36XX_DXE_CH_TX_H,
+       WCN36XX_DXE_CH_RX_L,
+       WCN36XX_DXE_CH_RX_H
+};
+
+/* amount of descriptors per channel */
+enum wcn36xx_dxe_ch_desc_num {
+       WCN36XX_DXE_CH_DESC_NUMB_TX_L           = 128,
+       WCN36XX_DXE_CH_DESC_NUMB_TX_H           = 10,
+       WCN36XX_DXE_CH_DESC_NUMB_RX_L           = 512,
+       WCN36XX_DXE_CH_DESC_NUMB_RX_H           = 40
+};
+
+/**
+ * struct wcn36xx_dxe_desc - describes descriptor of one DXE buffer
+ *
+ * @ctrl: is a union that consists of following bits:
+ * union {
+ *     u32     valid           :1; //0 = DMA stop, 1 = DMA continue with this
+ *                                 //descriptor
+ *     u32     transfer_type   :2; //0 = Host to Host space
+ *     u32     eop             :1; //End of Packet
+ *     u32     bd_handling     :1; //if transferType = Host to BMU, then 0
+ *                                 // means first 128 bytes contain BD, and 1
+ *                                 // means create new empty BD
+ *     u32     siq             :1; // SIQ
+ *     u32     diq             :1; // DIQ
+ *     u32     pdu_rel         :1; //0 = don't release BD and PDUs when done,
+ *                                 // 1 = release them
+ *     u32     bthld_sel       :4; //BMU Threshold Select
+ *     u32     prio            :3; //Specifies the priority level to use for
+ *                                 // the transfer
+ *     u32     stop_channel    :1; //1 = DMA stops processing further, channel
+ *                                 //requires re-enabling after this
+ *     u32     intr            :1; //Interrupt on Descriptor Done
+ *     u32     rsvd            :1; //reserved
+ *     u32     size            :14;//14 bits used - ignored for BMU transfers,
+ *                                 //only used for host to host transfers?
+ * } ctrl;
+ */
+struct wcn36xx_dxe_desc {
+       u32     ctrl;
+       u32     fr_len;
+
+       u32     src_addr_l;
+       u32     dst_addr_l;
+       u32     phy_next_l;
+       u32     src_addr_h;
+       u32     dst_addr_h;
+       u32     phy_next_h;
+} __packed;
+
+/* DXE Control block */
+struct wcn36xx_dxe_ctl {
+       struct wcn36xx_dxe_ctl  *next;
+       struct wcn36xx_dxe_desc *desc;
+       unsigned int            desc_phy_addr;
+       int                     ctl_blk_order;
+       struct sk_buff          *skb;
+       spinlock_t              skb_lock;
+       void                    *bd_cpu_addr;
+       dma_addr_t              bd_phy_addr;
+};
+
+struct wcn36xx_dxe_ch {
+       enum wcn36xx_dxe_ch_type        ch_type;
+       void                            *cpu_addr;
+       dma_addr_t                      dma_addr;
+       enum wcn36xx_dxe_ch_desc_num    desc_num;
+       /* DXE control block ring */
+       struct wcn36xx_dxe_ctl          *head_blk_ctl;
+       struct wcn36xx_dxe_ctl          *tail_blk_ctl;
+
+       /* DXE channel specific configs */
+       u32                             dxe_wq;
+       u32                             ctrl_bd;
+       u32                             ctrl_skb;
+       u32                             reg_ctrl;
+       u32                             def_ctrl;
+};
+
+/* Memory Pool for BD headers */
+struct wcn36xx_dxe_mem_pool {
+       int             chunk_size;
+       void            *virt_addr;
+       dma_addr_t      phy_addr;
+};
+
+struct wcn36xx_vif;
+int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn);
+void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn);
+void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn);
+int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn);
+void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn);
+int wcn36xx_dxe_init(struct wcn36xx *wcn);
+void wcn36xx_dxe_deinit(struct wcn36xx *wcn);
+int wcn36xx_dxe_init_channels(struct wcn36xx *wcn);
+int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
+                        struct wcn36xx_vif *vif_priv,
+                        struct sk_buff *skb,
+                        bool is_low);
+void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
+void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low);
+#endif /* _DXE_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
new file mode 100644 (file)
index 0000000..c02dbc6
--- /dev/null
@@ -0,0 +1,4657 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HAL_H_
+#define _HAL_H_
+
+/*---------------------------------------------------------------------------
+  API VERSIONING INFORMATION
+
+  The RIVA API is versioned as MAJOR.MINOR.VERSION.REVISION
+  The MAJOR is incremented for major product/architecture changes
+      (and then MINOR/VERSION/REVISION are zeroed)
+  The MINOR is incremented for minor product/architecture changes
+      (and then VERSION/REVISION are zeroed)
+  The VERSION is incremented if a significant API change occurs
+      (and then REVISION is zeroed)
+  The REVISION is incremented if an insignificant API change occurs
+      or if a new API is added
+  All values are in the range 0..255 (ie they are 8-bit values)
+ ---------------------------------------------------------------------------*/
+#define WCN36XX_HAL_VER_MAJOR 1
+#define WCN36XX_HAL_VER_MINOR 4
+#define WCN36XX_HAL_VER_VERSION 1
+#define WCN36XX_HAL_VER_REVISION 2
+
+/* This is to force compiler to use the maximum of an int ( 4 bytes ) */
+#define WCN36XX_HAL_MAX_ENUM_SIZE    0x7FFFFFFF
+#define WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE    0x7FFF
+
+/* Max no. of transmit categories */
+#define STACFG_MAX_TC    8
+
+/* The maximum value of access category */
+#define WCN36XX_HAL_MAX_AC  4
+
+#define WCN36XX_HAL_IPV4_ADDR_LEN       4
+
+#define WALN_HAL_STA_INVALID_IDX 0xFF
+#define WCN36XX_HAL_BSS_INVALID_IDX 0xFF
+
+/* Default Beacon template size */
+#define BEACON_TEMPLATE_SIZE 0x180
+
+/* Param Change Bitmap sent to HAL */
+#define PARAM_BCN_INTERVAL_CHANGED                      (1 << 0)
+#define PARAM_SHORT_PREAMBLE_CHANGED                 (1 << 1)
+#define PARAM_SHORT_SLOT_TIME_CHANGED                 (1 << 2)
+#define PARAM_llACOEXIST_CHANGED                            (1 << 3)
+#define PARAM_llBCOEXIST_CHANGED                            (1 << 4)
+#define PARAM_llGCOEXIST_CHANGED                            (1 << 5)
+#define PARAM_HT20MHZCOEXIST_CHANGED                  (1<<6)
+#define PARAM_NON_GF_DEVICES_PRESENT_CHANGED (1<<7)
+#define PARAM_RIFS_MODE_CHANGED                            (1<<8)
+#define PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED   (1<<9)
+#define PARAM_OBSS_MODE_CHANGED                               (1<<10)
+#define PARAM_BEACON_UPDATE_MASK \
+       (PARAM_BCN_INTERVAL_CHANGED |                                   \
+        PARAM_SHORT_PREAMBLE_CHANGED |                                 \
+        PARAM_SHORT_SLOT_TIME_CHANGED |                                \
+        PARAM_llACOEXIST_CHANGED |                                     \
+        PARAM_llBCOEXIST_CHANGED |                                     \
+        PARAM_llGCOEXIST_CHANGED |                                     \
+        PARAM_HT20MHZCOEXIST_CHANGED |                                 \
+        PARAM_NON_GF_DEVICES_PRESENT_CHANGED |                         \
+        PARAM_RIFS_MODE_CHANGED |                                      \
+        PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED |                         \
+        PARAM_OBSS_MODE_CHANGED)
+
+/* dump command response Buffer size */
+#define DUMPCMD_RSP_BUFFER 100
+
+/* version string max length (including NULL) */
+#define WCN36XX_HAL_VERSION_LENGTH  64
+
+/* message types for messages exchanged between WDI and HAL */
+enum wcn36xx_hal_host_msg_type {
+       /* Init/De-Init */
+       WCN36XX_HAL_START_REQ = 0,
+       WCN36XX_HAL_START_RSP = 1,
+       WCN36XX_HAL_STOP_REQ = 2,
+       WCN36XX_HAL_STOP_RSP = 3,
+
+       /* Scan */
+       WCN36XX_HAL_INIT_SCAN_REQ = 4,
+       WCN36XX_HAL_INIT_SCAN_RSP = 5,
+       WCN36XX_HAL_START_SCAN_REQ = 6,
+       WCN36XX_HAL_START_SCAN_RSP = 7,
+       WCN36XX_HAL_END_SCAN_REQ = 8,
+       WCN36XX_HAL_END_SCAN_RSP = 9,
+       WCN36XX_HAL_FINISH_SCAN_REQ = 10,
+       WCN36XX_HAL_FINISH_SCAN_RSP = 11,
+
+       /* HW STA configuration/deconfiguration */
+       WCN36XX_HAL_CONFIG_STA_REQ = 12,
+       WCN36XX_HAL_CONFIG_STA_RSP = 13,
+       WCN36XX_HAL_DELETE_STA_REQ = 14,
+       WCN36XX_HAL_DELETE_STA_RSP = 15,
+       WCN36XX_HAL_CONFIG_BSS_REQ = 16,
+       WCN36XX_HAL_CONFIG_BSS_RSP = 17,
+       WCN36XX_HAL_DELETE_BSS_REQ = 18,
+       WCN36XX_HAL_DELETE_BSS_RSP = 19,
+
+       /* Infra STA asscoiation */
+       WCN36XX_HAL_JOIN_REQ = 20,
+       WCN36XX_HAL_JOIN_RSP = 21,
+       WCN36XX_HAL_POST_ASSOC_REQ = 22,
+       WCN36XX_HAL_POST_ASSOC_RSP = 23,
+
+       /* Security */
+       WCN36XX_HAL_SET_BSSKEY_REQ = 24,
+       WCN36XX_HAL_SET_BSSKEY_RSP = 25,
+       WCN36XX_HAL_SET_STAKEY_REQ = 26,
+       WCN36XX_HAL_SET_STAKEY_RSP = 27,
+       WCN36XX_HAL_RMV_BSSKEY_REQ = 28,
+       WCN36XX_HAL_RMV_BSSKEY_RSP = 29,
+       WCN36XX_HAL_RMV_STAKEY_REQ = 30,
+       WCN36XX_HAL_RMV_STAKEY_RSP = 31,
+
+       /* Qos Related */
+       WCN36XX_HAL_ADD_TS_REQ = 32,
+       WCN36XX_HAL_ADD_TS_RSP = 33,
+       WCN36XX_HAL_DEL_TS_REQ = 34,
+       WCN36XX_HAL_DEL_TS_RSP = 35,
+       WCN36XX_HAL_UPD_EDCA_PARAMS_REQ = 36,
+       WCN36XX_HAL_UPD_EDCA_PARAMS_RSP = 37,
+       WCN36XX_HAL_ADD_BA_REQ = 38,
+       WCN36XX_HAL_ADD_BA_RSP = 39,
+       WCN36XX_HAL_DEL_BA_REQ = 40,
+       WCN36XX_HAL_DEL_BA_RSP = 41,
+
+       WCN36XX_HAL_CH_SWITCH_REQ = 42,
+       WCN36XX_HAL_CH_SWITCH_RSP = 43,
+       WCN36XX_HAL_SET_LINK_ST_REQ = 44,
+       WCN36XX_HAL_SET_LINK_ST_RSP = 45,
+       WCN36XX_HAL_GET_STATS_REQ = 46,
+       WCN36XX_HAL_GET_STATS_RSP = 47,
+       WCN36XX_HAL_UPDATE_CFG_REQ = 48,
+       WCN36XX_HAL_UPDATE_CFG_RSP = 49,
+
+       WCN36XX_HAL_MISSED_BEACON_IND = 50,
+       WCN36XX_HAL_UNKNOWN_ADDR2_FRAME_RX_IND = 51,
+       WCN36XX_HAL_MIC_FAILURE_IND = 52,
+       WCN36XX_HAL_FATAL_ERROR_IND = 53,
+       WCN36XX_HAL_SET_KEYDONE_MSG = 54,
+
+       /* NV Interface */
+       WCN36XX_HAL_DOWNLOAD_NV_REQ = 55,
+       WCN36XX_HAL_DOWNLOAD_NV_RSP = 56,
+
+       WCN36XX_HAL_ADD_BA_SESSION_REQ = 57,
+       WCN36XX_HAL_ADD_BA_SESSION_RSP = 58,
+       WCN36XX_HAL_TRIGGER_BA_REQ = 59,
+       WCN36XX_HAL_TRIGGER_BA_RSP = 60,
+       WCN36XX_HAL_UPDATE_BEACON_REQ = 61,
+       WCN36XX_HAL_UPDATE_BEACON_RSP = 62,
+       WCN36XX_HAL_SEND_BEACON_REQ = 63,
+       WCN36XX_HAL_SEND_BEACON_RSP = 64,
+
+       WCN36XX_HAL_SET_BCASTKEY_REQ = 65,
+       WCN36XX_HAL_SET_BCASTKEY_RSP = 66,
+       WCN36XX_HAL_DELETE_STA_CONTEXT_IND = 67,
+       WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_REQ = 68,
+       WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP = 69,
+
+       /* PTT interface support */
+       WCN36XX_HAL_PROCESS_PTT_REQ = 70,
+       WCN36XX_HAL_PROCESS_PTT_RSP = 71,
+
+       /* BTAMP related events */
+       WCN36XX_HAL_SIGNAL_BTAMP_EVENT_REQ = 72,
+       WCN36XX_HAL_SIGNAL_BTAMP_EVENT_RSP = 73,
+       WCN36XX_HAL_TL_HAL_FLUSH_AC_REQ = 74,
+       WCN36XX_HAL_TL_HAL_FLUSH_AC_RSP = 75,
+
+       WCN36XX_HAL_ENTER_IMPS_REQ = 76,
+       WCN36XX_HAL_EXIT_IMPS_REQ = 77,
+       WCN36XX_HAL_ENTER_BMPS_REQ = 78,
+       WCN36XX_HAL_EXIT_BMPS_REQ = 79,
+       WCN36XX_HAL_ENTER_UAPSD_REQ = 80,
+       WCN36XX_HAL_EXIT_UAPSD_REQ = 81,
+       WCN36XX_HAL_UPDATE_UAPSD_PARAM_REQ = 82,
+       WCN36XX_HAL_CONFIGURE_RXP_FILTER_REQ = 83,
+       WCN36XX_HAL_ADD_BCN_FILTER_REQ = 84,
+       WCN36XX_HAL_REM_BCN_FILTER_REQ = 85,
+       WCN36XX_HAL_ADD_WOWL_BCAST_PTRN = 86,
+       WCN36XX_HAL_DEL_WOWL_BCAST_PTRN = 87,
+       WCN36XX_HAL_ENTER_WOWL_REQ = 88,
+       WCN36XX_HAL_EXIT_WOWL_REQ = 89,
+       WCN36XX_HAL_HOST_OFFLOAD_REQ = 90,
+       WCN36XX_HAL_SET_RSSI_THRESH_REQ = 91,
+       WCN36XX_HAL_GET_RSSI_REQ = 92,
+       WCN36XX_HAL_SET_UAPSD_AC_PARAMS_REQ = 93,
+       WCN36XX_HAL_CONFIGURE_APPS_CPU_WAKEUP_STATE_REQ = 94,
+
+       WCN36XX_HAL_ENTER_IMPS_RSP = 95,
+       WCN36XX_HAL_EXIT_IMPS_RSP = 96,
+       WCN36XX_HAL_ENTER_BMPS_RSP = 97,
+       WCN36XX_HAL_EXIT_BMPS_RSP = 98,
+       WCN36XX_HAL_ENTER_UAPSD_RSP = 99,
+       WCN36XX_HAL_EXIT_UAPSD_RSP = 100,
+       WCN36XX_HAL_SET_UAPSD_AC_PARAMS_RSP = 101,
+       WCN36XX_HAL_UPDATE_UAPSD_PARAM_RSP = 102,
+       WCN36XX_HAL_CONFIGURE_RXP_FILTER_RSP = 103,
+       WCN36XX_HAL_ADD_BCN_FILTER_RSP = 104,
+       WCN36XX_HAL_REM_BCN_FILTER_RSP = 105,
+       WCN36XX_HAL_SET_RSSI_THRESH_RSP = 106,
+       WCN36XX_HAL_HOST_OFFLOAD_RSP = 107,
+       WCN36XX_HAL_ADD_WOWL_BCAST_PTRN_RSP = 108,
+       WCN36XX_HAL_DEL_WOWL_BCAST_PTRN_RSP = 109,
+       WCN36XX_HAL_ENTER_WOWL_RSP = 110,
+       WCN36XX_HAL_EXIT_WOWL_RSP = 111,
+       WCN36XX_HAL_RSSI_NOTIFICATION_IND = 112,
+       WCN36XX_HAL_GET_RSSI_RSP = 113,
+       WCN36XX_HAL_CONFIGURE_APPS_CPU_WAKEUP_STATE_RSP = 114,
+
+       /* 11k related events */
+       WCN36XX_HAL_SET_MAX_TX_POWER_REQ = 115,
+       WCN36XX_HAL_SET_MAX_TX_POWER_RSP = 116,
+
+       /* 11R related msgs */
+       WCN36XX_HAL_AGGR_ADD_TS_REQ = 117,
+       WCN36XX_HAL_AGGR_ADD_TS_RSP = 118,
+
+       /* P2P  WLAN_FEATURE_P2P */
+       WCN36XX_HAL_SET_P2P_GONOA_REQ = 119,
+       WCN36XX_HAL_SET_P2P_GONOA_RSP = 120,
+
+       /* WLAN Dump commands */
+       WCN36XX_HAL_DUMP_COMMAND_REQ = 121,
+       WCN36XX_HAL_DUMP_COMMAND_RSP = 122,
+
+       /* OEM_DATA FEATURE SUPPORT */
+       WCN36XX_HAL_START_OEM_DATA_REQ = 123,
+       WCN36XX_HAL_START_OEM_DATA_RSP = 124,
+
+       /* ADD SELF STA REQ and RSP */
+       WCN36XX_HAL_ADD_STA_SELF_REQ = 125,
+       WCN36XX_HAL_ADD_STA_SELF_RSP = 126,
+
+       /* DEL SELF STA SUPPORT */
+       WCN36XX_HAL_DEL_STA_SELF_REQ = 127,
+       WCN36XX_HAL_DEL_STA_SELF_RSP = 128,
+
+       /* Coex Indication */
+       WCN36XX_HAL_COEX_IND = 129,
+
+       /* Tx Complete Indication */
+       WCN36XX_HAL_OTA_TX_COMPL_IND = 130,
+
+       /* Host Suspend/resume messages */
+       WCN36XX_HAL_HOST_SUSPEND_IND = 131,
+       WCN36XX_HAL_HOST_RESUME_REQ = 132,
+       WCN36XX_HAL_HOST_RESUME_RSP = 133,
+
+       WCN36XX_HAL_SET_TX_POWER_REQ = 134,
+       WCN36XX_HAL_SET_TX_POWER_RSP = 135,
+       WCN36XX_HAL_GET_TX_POWER_REQ = 136,
+       WCN36XX_HAL_GET_TX_POWER_RSP = 137,
+
+       WCN36XX_HAL_P2P_NOA_ATTR_IND = 138,
+
+       WCN36XX_HAL_ENABLE_RADAR_DETECT_REQ = 139,
+       WCN36XX_HAL_ENABLE_RADAR_DETECT_RSP = 140,
+       WCN36XX_HAL_GET_TPC_REPORT_REQ = 141,
+       WCN36XX_HAL_GET_TPC_REPORT_RSP = 142,
+       WCN36XX_HAL_RADAR_DETECT_IND = 143,
+       WCN36XX_HAL_RADAR_DETECT_INTR_IND = 144,
+       WCN36XX_HAL_KEEP_ALIVE_REQ = 145,
+       WCN36XX_HAL_KEEP_ALIVE_RSP = 146,
+
+       /* PNO messages */
+       WCN36XX_HAL_SET_PREF_NETWORK_REQ = 147,
+       WCN36XX_HAL_SET_PREF_NETWORK_RSP = 148,
+       WCN36XX_HAL_SET_RSSI_FILTER_REQ = 149,
+       WCN36XX_HAL_SET_RSSI_FILTER_RSP = 150,
+       WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ = 151,
+       WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP = 152,
+       WCN36XX_HAL_PREF_NETW_FOUND_IND = 153,
+
+       WCN36XX_HAL_SET_TX_PER_TRACKING_REQ = 154,
+       WCN36XX_HAL_SET_TX_PER_TRACKING_RSP = 155,
+       WCN36XX_HAL_TX_PER_HIT_IND = 156,
+
+       WCN36XX_HAL_8023_MULTICAST_LIST_REQ = 157,
+       WCN36XX_HAL_8023_MULTICAST_LIST_RSP = 158,
+
+       WCN36XX_HAL_SET_PACKET_FILTER_REQ = 159,
+       WCN36XX_HAL_SET_PACKET_FILTER_RSP = 160,
+       WCN36XX_HAL_PACKET_FILTER_MATCH_COUNT_REQ = 161,
+       WCN36XX_HAL_PACKET_FILTER_MATCH_COUNT_RSP = 162,
+       WCN36XX_HAL_CLEAR_PACKET_FILTER_REQ = 163,
+       WCN36XX_HAL_CLEAR_PACKET_FILTER_RSP = 164,
+
+       /*
+        * This is temp fix. Should be removed once Host and Riva code is
+        * in sync.
+        */
+       WCN36XX_HAL_INIT_SCAN_CON_REQ = 165,
+
+       WCN36XX_HAL_SET_POWER_PARAMS_REQ = 166,
+       WCN36XX_HAL_SET_POWER_PARAMS_RSP = 167,
+
+       WCN36XX_HAL_TSM_STATS_REQ = 168,
+       WCN36XX_HAL_TSM_STATS_RSP = 169,
+
+       /* wake reason indication (WOW) */
+       WCN36XX_HAL_WAKE_REASON_IND = 170,
+
+       /* GTK offload support */
+       WCN36XX_HAL_GTK_OFFLOAD_REQ = 171,
+       WCN36XX_HAL_GTK_OFFLOAD_RSP = 172,
+       WCN36XX_HAL_GTK_OFFLOAD_GETINFO_REQ = 173,
+       WCN36XX_HAL_GTK_OFFLOAD_GETINFO_RSP = 174,
+
+       WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ = 175,
+       WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP = 176,
+       WCN36XX_HAL_EXCLUDE_UNENCRYPTED_IND = 177,
+
+       WCN36XX_HAL_SET_THERMAL_MITIGATION_REQ = 178,
+       WCN36XX_HAL_SET_THERMAL_MITIGATION_RSP = 179,
+
+       WCN36XX_HAL_UPDATE_VHT_OP_MODE_REQ = 182,
+       WCN36XX_HAL_UPDATE_VHT_OP_MODE_RSP = 183,
+
+       WCN36XX_HAL_P2P_NOA_START_IND = 184,
+
+       WCN36XX_HAL_GET_ROAM_RSSI_REQ = 185,
+       WCN36XX_HAL_GET_ROAM_RSSI_RSP = 186,
+
+       WCN36XX_HAL_CLASS_B_STATS_IND = 187,
+       WCN36XX_HAL_DEL_BA_IND = 188,
+       WCN36XX_HAL_DHCP_START_IND = 189,
+       WCN36XX_HAL_DHCP_STOP_IND = 190,
+
+       WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE
+};
+
+/* Enumeration for Version */
+enum wcn36xx_hal_host_msg_version {
+       WCN36XX_HAL_MSG_VERSION0 = 0,
+       WCN36XX_HAL_MSG_VERSION1 = 1,
+       /* define as 2 bytes data */
+       WCN36XX_HAL_MSG_WCNSS_CTRL_VERSION = 0x7FFF,
+       WCN36XX_HAL_MSG_VERSION_MAX_FIELD = WCN36XX_HAL_MSG_WCNSS_CTRL_VERSION
+};
+
+enum driver_type {
+       DRIVER_TYPE_PRODUCTION = 0,
+       DRIVER_TYPE_MFG = 1,
+       DRIVER_TYPE_DVT = 2,
+       DRIVER_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum wcn36xx_hal_stop_type {
+       HAL_STOP_TYPE_SYS_RESET,
+       HAL_STOP_TYPE_SYS_DEEP_SLEEP,
+       HAL_STOP_TYPE_RF_KILL,
+       HAL_STOP_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum wcn36xx_hal_sys_mode {
+       HAL_SYS_MODE_NORMAL,
+       HAL_SYS_MODE_LEARN,
+       HAL_SYS_MODE_SCAN,
+       HAL_SYS_MODE_PROMISC,
+       HAL_SYS_MODE_SUSPEND_LINK,
+       HAL_SYS_MODE_ROAM_SCAN,
+       HAL_SYS_MODE_ROAM_SUSPEND_LINK,
+       HAL_SYS_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum phy_chan_bond_state {
+       /* 20MHz IF bandwidth centered on IF carrier */
+       PHY_SINGLE_CHANNEL_CENTERED = 0,
+
+       /* 40MHz IF bandwidth with lower 20MHz supporting the primary channel */
+       PHY_DOUBLE_CHANNEL_LOW_PRIMARY = 1,
+
+       /* 40MHz IF bandwidth centered on IF carrier */
+       PHY_DOUBLE_CHANNEL_CENTERED = 2,
+
+       /* 40MHz IF bandwidth with higher 20MHz supporting the primary ch */
+       PHY_DOUBLE_CHANNEL_HIGH_PRIMARY = 3,
+
+       /* 20/40MHZ offset LOW 40/80MHZ offset CENTERED */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED = 4,
+
+       /* 20/40MHZ offset CENTERED 40/80MHZ offset CENTERED */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED = 5,
+
+       /* 20/40MHZ offset HIGH 40/80MHZ offset CENTERED */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED = 6,
+
+       /* 20/40MHZ offset LOW 40/80MHZ offset LOW */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW = 7,
+
+       /* 20/40MHZ offset HIGH 40/80MHZ offset LOW */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW = 8,
+
+       /* 20/40MHZ offset LOW 40/80MHZ offset HIGH */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH = 9,
+
+       /* 20/40MHZ offset-HIGH 40/80MHZ offset HIGH */
+       PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH = 10,
+
+       PHY_CHANNEL_BONDING_STATE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* Spatial Multiplexing(SM) Power Save mode */
+enum wcn36xx_hal_ht_mimo_state {
+       /* Static SM Power Save mode */
+       WCN36XX_HAL_HT_MIMO_PS_STATIC = 0,
+
+       /* Dynamic SM Power Save mode */
+       WCN36XX_HAL_HT_MIMO_PS_DYNAMIC = 1,
+
+       /* reserved */
+       WCN36XX_HAL_HT_MIMO_PS_NA = 2,
+
+       /* SM Power Save disabled */
+       WCN36XX_HAL_HT_MIMO_PS_NO_LIMIT = 3,
+
+       WCN36XX_HAL_HT_MIMO_PS_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* each station added has a rate mode which specifies the sta attributes */
+enum sta_rate_mode {
+       STA_TAURUS = 0,
+       STA_TITAN,
+       STA_POLARIS,
+       STA_11b,
+       STA_11bg,
+       STA_11a,
+       STA_11n,
+       STA_11ac,
+       STA_INVALID_RATE_MODE = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* 1,2,5.5,11 */
+#define WCN36XX_HAL_NUM_DSSS_RATES           4
+
+/* 6,9,12,18,24,36,48,54 */
+#define WCN36XX_HAL_NUM_OFDM_RATES           8
+
+/* 72,96,108 */
+#define WCN36XX_HAL_NUM_POLARIS_RATES       3
+
+#define WCN36XX_HAL_MAC_MAX_SUPPORTED_MCS_SET    16
+
+enum wcn36xx_hal_bss_type {
+       WCN36XX_HAL_INFRASTRUCTURE_MODE,
+
+       /* Added for softAP support */
+       WCN36XX_HAL_INFRA_AP_MODE,
+
+       WCN36XX_HAL_IBSS_MODE,
+
+       /* Added for BT-AMP support */
+       WCN36XX_HAL_BTAMP_STA_MODE,
+
+       /* Added for BT-AMP support */
+       WCN36XX_HAL_BTAMP_AP_MODE,
+
+       WCN36XX_HAL_AUTO_MODE,
+
+       WCN36XX_HAL_DONOT_USE_BSS_TYPE = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum wcn36xx_hal_nw_type {
+       WCN36XX_HAL_11A_NW_TYPE,
+       WCN36XX_HAL_11B_NW_TYPE,
+       WCN36XX_HAL_11G_NW_TYPE,
+       WCN36XX_HAL_11N_NW_TYPE,
+       WCN36XX_HAL_DONOT_USE_NW_TYPE = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+#define WCN36XX_HAL_MAC_RATESET_EID_MAX            12
+
+enum wcn36xx_hal_ht_operating_mode {
+       /* No Protection */
+       WCN36XX_HAL_HT_OP_MODE_PURE,
+
+       /* Overlap Legacy device present, protection is optional */
+       WCN36XX_HAL_HT_OP_MODE_OVERLAP_LEGACY,
+
+       /* No legacy device, but 20 MHz HT present */
+       WCN36XX_HAL_HT_OP_MODE_NO_LEGACY_20MHZ_HT,
+
+       /* Protection is required */
+       WCN36XX_HAL_HT_OP_MODE_MIXED,
+
+       WCN36XX_HAL_HT_OP_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* Encryption type enum used with peer */
+enum ani_ed_type {
+       WCN36XX_HAL_ED_NONE,
+       WCN36XX_HAL_ED_WEP40,
+       WCN36XX_HAL_ED_WEP104,
+       WCN36XX_HAL_ED_TKIP,
+       WCN36XX_HAL_ED_CCMP,
+       WCN36XX_HAL_ED_WPI,
+       WCN36XX_HAL_ED_AES_128_CMAC,
+       WCN36XX_HAL_ED_NOT_IMPLEMENTED = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+#define WLAN_MAX_KEY_RSC_LEN                16
+#define WLAN_WAPI_KEY_RSC_LEN               16
+
+/* MAX key length when ULA is used */
+#define WCN36XX_HAL_MAC_MAX_KEY_LENGTH              32
+#define WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS     4
+
+/*
+ * Enum to specify whether key is used for TX only, RX only or both.
+ */
+enum ani_key_direction {
+       WCN36XX_HAL_TX_ONLY,
+       WCN36XX_HAL_RX_ONLY,
+       WCN36XX_HAL_TX_RX,
+       WCN36XX_HAL_TX_DEFAULT,
+       WCN36XX_HAL_DONOT_USE_KEY_DIRECTION = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum ani_wep_type {
+       WCN36XX_HAL_WEP_STATIC,
+       WCN36XX_HAL_WEP_DYNAMIC,
+       WCN36XX_HAL_WEP_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum wcn36xx_hal_link_state {
+
+       WCN36XX_HAL_LINK_IDLE_STATE = 0,
+       WCN36XX_HAL_LINK_PREASSOC_STATE = 1,
+       WCN36XX_HAL_LINK_POSTASSOC_STATE = 2,
+       WCN36XX_HAL_LINK_AP_STATE = 3,
+       WCN36XX_HAL_LINK_IBSS_STATE = 4,
+
+       /* BT-AMP Case */
+       WCN36XX_HAL_LINK_BTAMP_PREASSOC_STATE = 5,
+       WCN36XX_HAL_LINK_BTAMP_POSTASSOC_STATE = 6,
+       WCN36XX_HAL_LINK_BTAMP_AP_STATE = 7,
+       WCN36XX_HAL_LINK_BTAMP_STA_STATE = 8,
+
+       /* Reserved for HAL Internal Use */
+       WCN36XX_HAL_LINK_LEARN_STATE = 9,
+       WCN36XX_HAL_LINK_SCAN_STATE = 10,
+       WCN36XX_HAL_LINK_FINISH_SCAN_STATE = 11,
+       WCN36XX_HAL_LINK_INIT_CAL_STATE = 12,
+       WCN36XX_HAL_LINK_FINISH_CAL_STATE = 13,
+       WCN36XX_HAL_LINK_LISTEN_STATE = 14,
+
+       WCN36XX_HAL_LINK_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+enum wcn36xx_hal_stats_mask {
+       HAL_SUMMARY_STATS_INFO = 0x00000001,
+       HAL_GLOBAL_CLASS_A_STATS_INFO = 0x00000002,
+       HAL_GLOBAL_CLASS_B_STATS_INFO = 0x00000004,
+       HAL_GLOBAL_CLASS_C_STATS_INFO = 0x00000008,
+       HAL_GLOBAL_CLASS_D_STATS_INFO = 0x00000010,
+       HAL_PER_STA_STATS_INFO = 0x00000020
+};
+
+/* BT-AMP events type */
+enum bt_amp_event_type {
+       BTAMP_EVENT_CONNECTION_START,
+       BTAMP_EVENT_CONNECTION_STOP,
+       BTAMP_EVENT_CONNECTION_TERMINATED,
+
+       /* This and beyond are invalid values */
+       BTAMP_EVENT_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE,
+};
+
+/* PE Statistics */
+enum pe_stats_mask {
+       PE_SUMMARY_STATS_INFO = 0x00000001,
+       PE_GLOBAL_CLASS_A_STATS_INFO = 0x00000002,
+       PE_GLOBAL_CLASS_B_STATS_INFO = 0x00000004,
+       PE_GLOBAL_CLASS_C_STATS_INFO = 0x00000008,
+       PE_GLOBAL_CLASS_D_STATS_INFO = 0x00000010,
+       PE_PER_STA_STATS_INFO = 0x00000020,
+
+       /* This and beyond are invalid values */
+       PE_STATS_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/*
+ * Configuration Parameter IDs
+ */
+#define WCN36XX_HAL_CFG_STA_ID                         0
+#define WCN36XX_HAL_CFG_CURRENT_TX_ANTENNA             1
+#define WCN36XX_HAL_CFG_CURRENT_RX_ANTENNA             2
+#define WCN36XX_HAL_CFG_LOW_GAIN_OVERRIDE              3
+#define WCN36XX_HAL_CFG_POWER_STATE_PER_CHAIN          4
+#define WCN36XX_HAL_CFG_CAL_PERIOD                     5
+#define WCN36XX_HAL_CFG_CAL_CONTROL                    6
+#define WCN36XX_HAL_CFG_PROXIMITY                      7
+#define WCN36XX_HAL_CFG_NETWORK_DENSITY                        8
+#define WCN36XX_HAL_CFG_MAX_MEDIUM_TIME                        9
+#define WCN36XX_HAL_CFG_MAX_MPDUS_IN_AMPDU             10
+#define WCN36XX_HAL_CFG_RTS_THRESHOLD                  11
+#define WCN36XX_HAL_CFG_SHORT_RETRY_LIMIT              12
+#define WCN36XX_HAL_CFG_LONG_RETRY_LIMIT               13
+#define WCN36XX_HAL_CFG_FRAGMENTATION_THRESHOLD                14
+#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_ZERO         15
+#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_ONE          16
+#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_TWO          17
+#define WCN36XX_HAL_CFG_FIXED_RATE                     18
+#define WCN36XX_HAL_CFG_RETRYRATE_POLICY               19
+#define WCN36XX_HAL_CFG_RETRYRATE_SECONDARY            20
+#define WCN36XX_HAL_CFG_RETRYRATE_TERTIARY             21
+#define WCN36XX_HAL_CFG_FORCE_POLICY_PROTECTION                22
+#define WCN36XX_HAL_CFG_FIXED_RATE_MULTICAST_24GHZ     23
+#define WCN36XX_HAL_CFG_FIXED_RATE_MULTICAST_5GHZ      24
+#define WCN36XX_HAL_CFG_DEFAULT_RATE_INDEX_24GHZ       25
+#define WCN36XX_HAL_CFG_DEFAULT_RATE_INDEX_5GHZ                26
+#define WCN36XX_HAL_CFG_MAX_BA_SESSIONS                        27
+#define WCN36XX_HAL_CFG_PS_DATA_INACTIVITY_TIMEOUT     28
+#define WCN36XX_HAL_CFG_PS_ENABLE_BCN_FILTER           29
+#define WCN36XX_HAL_CFG_PS_ENABLE_RSSI_MONITOR         30
+#define WCN36XX_HAL_CFG_NUM_BEACON_PER_RSSI_AVERAGE    31
+#define WCN36XX_HAL_CFG_STATS_PERIOD                   32
+#define WCN36XX_HAL_CFG_CFP_MAX_DURATION               33
+#define WCN36XX_HAL_CFG_FRAME_TRANS_ENABLED            34
+#define WCN36XX_HAL_CFG_DTIM_PERIOD                    35
+#define WCN36XX_HAL_CFG_EDCA_WMM_ACBK                  36
+#define WCN36XX_HAL_CFG_EDCA_WMM_ACBE                  37
+#define WCN36XX_HAL_CFG_EDCA_WMM_ACVO                  38
+#define WCN36XX_HAL_CFG_EDCA_WMM_ACVI                  39
+#define WCN36XX_HAL_CFG_BA_THRESHOLD_HIGH              40
+#define WCN36XX_HAL_CFG_MAX_BA_BUFFERS                 41
+#define WCN36XX_HAL_CFG_RPE_POLLING_THRESHOLD          42
+#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC0_REG        43
+#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC1_REG        44
+#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC2_REG        45
+#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC3_REG        46
+#define WCN36XX_HAL_CFG_NO_OF_ONCHIP_REORDER_SESSIONS  47
+#define WCN36XX_HAL_CFG_PS_LISTEN_INTERVAL             48
+#define WCN36XX_HAL_CFG_PS_HEART_BEAT_THRESHOLD                49
+#define WCN36XX_HAL_CFG_PS_NTH_BEACON_FILTER           50
+#define WCN36XX_HAL_CFG_PS_MAX_PS_POLL                 51
+#define WCN36XX_HAL_CFG_PS_MIN_RSSI_THRESHOLD          52
+#define WCN36XX_HAL_CFG_PS_RSSI_FILTER_PERIOD          53
+#define WCN36XX_HAL_CFG_PS_BROADCAST_FRAME_FILTER_ENABLE 54
+#define WCN36XX_HAL_CFG_PS_IGNORE_DTIM                 55
+#define WCN36XX_HAL_CFG_PS_ENABLE_BCN_EARLY_TERM       56
+#define WCN36XX_HAL_CFG_DYNAMIC_PS_POLL_VALUE          57
+#define WCN36XX_HAL_CFG_PS_NULLDATA_AP_RESP_TIMEOUT    58
+#define WCN36XX_HAL_CFG_TELE_BCN_WAKEUP_EN             59
+#define WCN36XX_HAL_CFG_TELE_BCN_TRANS_LI              60
+#define WCN36XX_HAL_CFG_TELE_BCN_TRANS_LI_IDLE_BCNS    61
+#define WCN36XX_HAL_CFG_TELE_BCN_MAX_LI                        62
+#define WCN36XX_HAL_CFG_TELE_BCN_MAX_LI_IDLE_BCNS      63
+#define WCN36XX_HAL_CFG_TX_PWR_CTRL_ENABLE             64
+#define WCN36XX_HAL_CFG_VALID_RADAR_CHANNEL_LIST       65
+#define WCN36XX_HAL_CFG_TX_POWER_24_20                 66
+#define WCN36XX_HAL_CFG_TX_POWER_24_40                 67
+#define WCN36XX_HAL_CFG_TX_POWER_50_20                 68
+#define WCN36XX_HAL_CFG_TX_POWER_50_40                 69
+#define WCN36XX_HAL_CFG_MCAST_BCAST_FILTER_SETTING     70
+#define WCN36XX_HAL_CFG_BCN_EARLY_TERM_WAKEUP_INTERVAL 71
+#define WCN36XX_HAL_CFG_MAX_TX_POWER_2_4               72
+#define WCN36XX_HAL_CFG_MAX_TX_POWER_5                 73
+#define WCN36XX_HAL_CFG_INFRA_STA_KEEP_ALIVE_PERIOD    74
+#define WCN36XX_HAL_CFG_ENABLE_CLOSE_LOOP              75
+#define WCN36XX_HAL_CFG_BTC_EXECUTION_MODE             76
+#define WCN36XX_HAL_CFG_BTC_DHCP_BT_SLOTS_TO_BLOCK     77
+#define WCN36XX_HAL_CFG_BTC_A2DP_DHCP_BT_SUB_INTERVALS 78
+#define WCN36XX_HAL_CFG_PS_TX_INACTIVITY_TIMEOUT       79
+#define WCN36XX_HAL_CFG_WCNSS_API_VERSION              80
+#define WCN36XX_HAL_CFG_AP_KEEPALIVE_TIMEOUT           81
+#define WCN36XX_HAL_CFG_GO_KEEPALIVE_TIMEOUT           82
+#define WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST            83
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_INQ_BT          84
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_PAGE_BT         85
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_CONN_BT         86
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_LE_BT           87
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_INQ_WLAN                88
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_PAGE_WLAN       89
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_CONN_WLAN       90
+#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_LE_WLAN         91
+#define WCN36XX_HAL_CFG_BTC_DYN_MAX_LEN_BT             92
+#define WCN36XX_HAL_CFG_BTC_DYN_MAX_LEN_WLAN           93
+#define WCN36XX_HAL_CFG_BTC_MAX_SCO_BLOCK_PERC         94
+#define WCN36XX_HAL_CFG_BTC_DHCP_PROT_ON_A2DP          95
+#define WCN36XX_HAL_CFG_BTC_DHCP_PROT_ON_SCO           96
+#define WCN36XX_HAL_CFG_ENABLE_UNICAST_FILTER          97
+#define WCN36XX_HAL_CFG_MAX_ASSOC_LIMIT                        98
+#define WCN36XX_HAL_CFG_ENABLE_LPWR_IMG_TRANSITION     99
+#define WCN36XX_HAL_CFG_ENABLE_MCC_ADAPTIVE_SCHEDULER  100
+#define WCN36XX_HAL_CFG_ENABLE_DETECT_PS_SUPPORT       101
+#define WCN36XX_HAL_CFG_AP_LINK_MONITOR_TIMEOUT                102
+#define WCN36XX_HAL_CFG_BTC_DWELL_TIME_MULTIPLIER      103
+#define WCN36XX_HAL_CFG_ENABLE_TDLS_OXYGEN_MODE                104
+#define WCN36XX_HAL_CFG_MAX_PARAMS                     105
+
+/* Message definitons - All the messages below need to be packed */
+
+/* Definition for HAL API Version. */
+struct wcnss_wlan_version {
+       u8 revision;
+       u8 version;
+       u8 minor;
+       u8 major;
+} __packed;
+
+/* Definition for Encryption Keys */
+struct wcn36xx_hal_keys {
+       u8 id;
+
+       /* 0 for multicast */
+       u8 unicast;
+
+       enum ani_key_direction direction;
+
+       /* Usage is unknown */
+       u8 rsc[WLAN_MAX_KEY_RSC_LEN];
+
+       /* =1 for authenticator,=0 for supplicant */
+       u8 pae_role;
+
+       u16 length;
+       u8 key[WCN36XX_HAL_MAC_MAX_KEY_LENGTH];
+} __packed;
+
+/*
+ * set_sta_key_params Moving here since it is shared by
+ * configbss/setstakey msgs
+ */
+struct wcn36xx_hal_set_sta_key_params {
+       /* STA Index */
+       u16 sta_index;
+
+       /* Encryption Type used with peer */
+       enum ani_ed_type enc_type;
+
+       /* STATIC/DYNAMIC - valid only for WEP */
+       enum ani_wep_type wep_type;
+
+       /* Default WEP key, valid only for static WEP, must between 0 and 3. */
+       u8 def_wep_idx;
+
+       /* valid only for non-static WEP encyrptions */
+       struct wcn36xx_hal_keys key[WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS];
+
+       /*
+        * Control for Replay Count, 1= Single TID based replay count on Tx
+        * 0 = Per TID based replay count on TX
+        */
+       u8 single_tid_rc;
+
+} __packed;
+
+/* 4-byte control message header used by HAL*/
+struct wcn36xx_hal_msg_header {
+       enum wcn36xx_hal_host_msg_type msg_type:16;
+       enum wcn36xx_hal_host_msg_version msg_version:16;
+       u32 len;
+} __packed;
+
+/* Config format required by HAL for each CFG item*/
+struct wcn36xx_hal_cfg {
+       /* Cfg Id. The Id required by HAL is exported by HAL
+        * in shared header file between UMAC and HAL.*/
+       u16 id;
+
+       /* Length of the Cfg. This parameter is used to go to next cfg
+        * in the TLV format.*/
+       u16 len;
+
+       /* Padding bytes for unaligned address's */
+       u16 pad_bytes;
+
+       /* Reserve bytes for making cfgVal to align address */
+       u16 reserve;
+
+       /* Following the uCfgLen field there should be a 'uCfgLen' bytes
+        * containing the uCfgValue ; u8 uCfgValue[uCfgLen] */
+} __packed;
+
+struct wcn36xx_hal_mac_start_parameters {
+       /* Drive Type - Production or FTM etc */
+       enum driver_type type;
+
+       /* Length of the config buffer */
+       u32 len;
+
+       /* Following this there is a TLV formatted buffer of length
+        * "len" bytes containing all config values.
+        * The TLV is expected to be formatted like this:
+        * 0           15            31           31+CFG_LEN-1        length-1
+        * |   CFG_ID   |   CFG_LEN   |   CFG_BODY    |  CFG_ID  |......|
+        */
+} __packed;
+
+struct wcn36xx_hal_mac_start_req_msg {
+       /* config buffer must start in TLV format just here */
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_mac_start_parameters params;
+} __packed;
+
+struct wcn36xx_hal_mac_start_rsp_params {
+       /* success or failure */
+       u16 status;
+
+       /* Max number of STA supported by the device */
+       u8 stations;
+
+       /* Max number of BSS supported by the device */
+       u8 bssids;
+
+       /* API Version */
+       struct wcnss_wlan_version version;
+
+       /* CRM build information */
+       u8 crm_version[WCN36XX_HAL_VERSION_LENGTH];
+
+       /* hardware/chipset/misc version information */
+       u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH];
+
+} __packed;
+
+struct wcn36xx_hal_mac_start_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_mac_start_rsp_params start_rsp_params;
+} __packed;
+
+struct wcn36xx_hal_mac_stop_req_params {
+       /* The reason for which the device is being stopped */
+       enum wcn36xx_hal_stop_type reason;
+
+} __packed;
+
+struct wcn36xx_hal_mac_stop_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_mac_stop_req_params stop_req_params;
+} __packed;
+
+struct wcn36xx_hal_mac_stop_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+} __packed;
+
+struct wcn36xx_hal_update_cfg_req_msg {
+       /*
+        * Note: The length specified in tHalUpdateCfgReqMsg messages should be
+        * header.msgLen = sizeof(tHalUpdateCfgReqMsg) + uConfigBufferLen
+        */
+       struct wcn36xx_hal_msg_header header;
+
+       /* Length of the config buffer. Allows UMAC to update multiple CFGs */
+       u32 len;
+
+       /*
+        * Following this there is a TLV formatted buffer of length
+        * "uConfigBufferLen" bytes containing all config values.
+        * The TLV is expected to be formatted like this:
+        * 0           15            31           31+CFG_LEN-1        length-1
+        * |   CFG_ID   |   CFG_LEN   |   CFG_BODY    |  CFG_ID  |......|
+        */
+
+} __packed;
+
+struct wcn36xx_hal_update_cfg_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+} __packed;
+
+/* Frame control field format (2 bytes) */
+struct wcn36xx_hal_mac_frame_ctl {
+
+#ifndef ANI_LITTLE_BIT_ENDIAN
+
+       u8 subType:4;
+       u8 type:2;
+       u8 protVer:2;
+
+       u8 order:1;
+       u8 wep:1;
+       u8 moreData:1;
+       u8 powerMgmt:1;
+       u8 retry:1;
+       u8 moreFrag:1;
+       u8 fromDS:1;
+       u8 toDS:1;
+
+#else
+
+       u8 protVer:2;
+       u8 type:2;
+       u8 subType:4;
+
+       u8 toDS:1;
+       u8 fromDS:1;
+       u8 moreFrag:1;
+       u8 retry:1;
+       u8 powerMgmt:1;
+       u8 moreData:1;
+       u8 wep:1;
+       u8 order:1;
+
+#endif
+
+};
+
+/* Sequence control field */
+struct wcn36xx_hal_mac_seq_ctl {
+       u8 fragNum:4;
+       u8 seqNumLo:4;
+       u8 seqNumHi:8;
+};
+
+/* Management header format */
+struct wcn36xx_hal_mac_mgmt_hdr {
+       struct wcn36xx_hal_mac_frame_ctl fc;
+       u8 durationLo;
+       u8 durationHi;
+       u8 da[6];
+       u8 sa[6];
+       u8 bssId[6];
+       struct wcn36xx_hal_mac_seq_ctl seqControl;
+};
+
+/* FIXME: pronto v1 apparently has 4 */
+#define WCN36XX_HAL_NUM_BSSID               2
+
+/* Scan Entry to hold active BSS idx's */
+struct wcn36xx_hal_scan_entry {
+       u8 bss_index[WCN36XX_HAL_NUM_BSSID];
+       u8 active_bss_count;
+};
+
+struct wcn36xx_hal_init_scan_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* LEARN - AP Role
+          SCAN - STA Role */
+       enum wcn36xx_hal_sys_mode mode;
+
+       /* BSSID of the BSS */
+       u8 bssid[ETH_ALEN];
+
+       /* Whether BSS needs to be notified */
+       u8 notify;
+
+       /* Kind of frame to be used for notifying the BSS (Data Null, QoS
+        * Null, or CTS to Self). Must always be a valid frame type. */
+       u8 frame_type;
+
+       /* UMAC has the option of passing the MAC frame to be used for
+        * notifying the BSS. If non-zero, HAL will use the MAC frame
+        * buffer pointed to by macMgmtHdr. If zero, HAL will generate the
+        * appropriate MAC frame based on frameType. */
+       u8 frame_len;
+
+       /* Following the framelength there is a MAC frame buffer if
+        * frameLength is non-zero. */
+       struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr;
+
+       /* Entry to hold number of active BSS idx's */
+       struct wcn36xx_hal_scan_entry scan_entry;
+};
+
+struct wcn36xx_hal_init_scan_con_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* LEARN - AP Role
+          SCAN - STA Role */
+       enum wcn36xx_hal_sys_mode mode;
+
+       /* BSSID of the BSS */
+       u8 bssid[ETH_ALEN];
+
+       /* Whether BSS needs to be notified */
+       u8 notify;
+
+       /* Kind of frame to be used for notifying the BSS (Data Null, QoS
+        * Null, or CTS to Self). Must always be a valid frame type. */
+       u8 frame_type;
+
+       /* UMAC has the option of passing the MAC frame to be used for
+        * notifying the BSS. If non-zero, HAL will use the MAC frame
+        * buffer pointed to by macMgmtHdr. If zero, HAL will generate the
+        * appropriate MAC frame based on frameType. */
+       u8 frame_length;
+
+       /* Following the framelength there is a MAC frame buffer if
+        * frameLength is non-zero. */
+       struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr;
+
+       /* Entry to hold number of active BSS idx's */
+       struct wcn36xx_hal_scan_entry scan_entry;
+
+       /* Single NoA usage in Scanning */
+       u8 use_noa;
+
+       /* Indicates the scan duration (in ms) */
+       u16 scan_duration;
+
+};
+
+struct wcn36xx_hal_init_scan_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+} __packed;
+
+struct wcn36xx_hal_start_scan_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Indicates the channel to scan */
+       u8 scan_channel;
+} __packed;
+
+struct wcn36xx_hal_start_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u32 start_tsf[2];
+       u8 tx_mgmt_power;
+
+} __packed;
+
+struct wcn36xx_hal_end_scan_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Indicates the channel to stop scanning. Not used really. But
+        * retained for symmetry with "start Scan" message. It can also
+        * help in error check if needed. */
+       u8 scan_channel;
+} __packed;
+
+struct wcn36xx_hal_end_scan_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+} __packed;
+
+struct wcn36xx_hal_finish_scan_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Identifies the operational state of the AP/STA
+        * LEARN - AP Role SCAN - STA Role */
+       enum wcn36xx_hal_sys_mode mode;
+
+       /* Operating channel to tune to. */
+       u8 oper_channel;
+
+       /* Channel Bonding state If 20/40 MHz is operational, this will
+        * indicate the 40 MHz extension channel in combination with the
+        * control channel */
+       enum phy_chan_bond_state cb_state;
+
+       /* BSSID of the BSS */
+       u8 bssid[ETH_ALEN];
+
+       /* Whether BSS needs to be notified */
+       u8 notify;
+
+       /* Kind of frame to be used for notifying the BSS (Data Null, QoS
+        * Null, or CTS to Self). Must always be a valid frame type. */
+       u8 frame_type;
+
+       /* UMAC has the option of passing the MAC frame to be used for
+        * notifying the BSS. If non-zero, HAL will use the MAC frame
+        * buffer pointed to by macMgmtHdr. If zero, HAL will generate the
+        * appropriate MAC frame based on frameType. */
+       u8 frame_length;
+
+       /* Following the framelength there is a MAC frame buffer if
+        * frameLength is non-zero. */
+       struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr;
+
+       /* Entry to hold number of active BSS idx's */
+       struct wcn36xx_hal_scan_entry scan_entry;
+
+} __packed;
+
+struct wcn36xx_hal_finish_scan_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+} __packed;
+
+enum wcn36xx_hal_rate_index {
+       HW_RATE_INDEX_1MBPS     = 0x82,
+       HW_RATE_INDEX_2MBPS     = 0x84,
+       HW_RATE_INDEX_5_5MBPS   = 0x8B,
+       HW_RATE_INDEX_6MBPS     = 0x0C,
+       HW_RATE_INDEX_9MBPS     = 0x12,
+       HW_RATE_INDEX_11MBPS    = 0x96,
+       HW_RATE_INDEX_12MBPS    = 0x18,
+       HW_RATE_INDEX_18MBPS    = 0x24,
+       HW_RATE_INDEX_24MBPS    = 0x30,
+       HW_RATE_INDEX_36MBPS    = 0x48,
+       HW_RATE_INDEX_48MBPS    = 0x60,
+       HW_RATE_INDEX_54MBPS    = 0x6C
+};
+
+struct wcn36xx_hal_supported_rates {
+       /*
+        * For Self STA Entry: this represents Self Mode.
+        * For Peer Stations, this represents the mode of the peer.
+        * On Station:
+        *
+        * --this mode is updated when PE adds the Self Entry.
+        *
+        * -- OR when PE sends 'ADD_BSS' message and station context in BSS
+        *    is used to indicate the mode of the AP.
+        *
+        * ON AP:
+        *
+        * -- this mode is updated when PE sends 'ADD_BSS' and Sta entry
+        *     for that BSS is used to indicate the self mode of the AP.
+        *
+        * -- OR when a station is associated, PE sends 'ADD_STA' message
+        *    with this mode updated.
+        */
+
+       enum sta_rate_mode op_rate_mode;
+
+       /* 11b, 11a and aniLegacyRates are IE rates which gives rate in
+        * unit of 500Kbps */
+       u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES];
+       u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES];
+       u16 legacy_rates[WCN36XX_HAL_NUM_POLARIS_RATES];
+       u16 reserved;
+
+       /* Taurus only supports 26 Titan Rates(no ESF/concat Rates will be
+        * supported) First 26 bits are reserved for those Titan rates and
+        * the last 4 bits(bit28-31) for Taurus, 2(bit26-27) bits are
+        * reserved. */
+       /* Titan and Taurus Rates */
+       u32 enhanced_rate_bitmap;
+
+       /*
+        * 0-76 bits used, remaining reserved
+        * bits 0-15 and 32 should be set.
+        */
+       u8 supported_mcs_set[WCN36XX_HAL_MAC_MAX_SUPPORTED_MCS_SET];
+
+       /*
+        * RX Highest Supported Data Rate defines the highest data
+        * rate that the STA is able to receive, in unites of 1Mbps.
+        * This value is derived from "Supported MCS Set field" inside
+        * the HT capability element.
+        */
+       u16 rx_highest_data_rate;
+
+} __packed;
+
+struct wcn36xx_hal_config_sta_params {
+       /* BSSID of STA */
+       u8 bssid[ETH_ALEN];
+
+       /* ASSOC ID, as assigned by UMAC */
+       u16 aid;
+
+       /* STA entry Type: 0 - Self, 1 - Other/Peer, 2 - BSSID, 3 - BCAST */
+       u8 type;
+
+       /* Short Preamble Supported. */
+       u8 short_preamble_supported;
+
+       /* MAC Address of STA */
+       u8 mac[ETH_ALEN];
+
+       /* Listen interval of the STA */
+       u16 listen_interval;
+
+       /* Support for 11e/WMM */
+       u8 wmm_enabled;
+
+       /* 11n HT capable STA */
+       u8 ht_capable;
+
+       /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */
+       u8 tx_channel_width_set;
+
+       /* RIFS mode 0 - NA, 1 - Allowed */
+       u8 rifs_mode;
+
+       /* L-SIG TXOP Protection mechanism
+          0 - No Support, 1 - Supported
+          SG - there is global field */
+       u8 lsig_txop_protection;
+
+       /* Max Ampdu Size supported by STA. TPE programming.
+          0 : 8k , 1 : 16k, 2 : 32k, 3 : 64k */
+       u8 max_ampdu_size;
+
+       /* Max Ampdu density. Used by RA.  3 : 0~7 : 2^(11nAMPDUdensity -4) */
+       u8 max_ampdu_density;
+
+       /* Max AMSDU size 1 : 3839 bytes, 0 : 7935 bytes */
+       u8 max_amsdu_size;
+
+       /* Short GI support for 40Mhz packets */
+       u8 sgi_40mhz;
+
+       /* Short GI support for 20Mhz packets */
+       u8 sgi_20Mhz;
+
+       /* TODO move this parameter to the end for 3680 */
+       /* These rates are the intersection of peer and self capabilities. */
+       struct wcn36xx_hal_supported_rates supported_rates;
+
+       /* Robust Management Frame (RMF) enabled/disabled */
+       u8 rmf;
+
+       /* The unicast encryption type in the association */
+       u32 encrypt_type;
+
+       /* HAL should update the existing STA entry, if this flag is set. UMAC
+          will set this flag in case of RE-ASSOC, where we want to reuse the
+          old STA ID. 0 = Add, 1 = Update */
+       u8 action;
+
+       /* U-APSD Flags: 1b per AC.  Encoded as follows:
+          b7 b6 b5 b4 b3 b2 b1 b0 =
+          X  X  X  X  BE BK VI VO */
+       u8 uapsd;
+
+       /* Max SP Length */
+       u8 max_sp_len;
+
+       /* 11n Green Field preamble support
+          0 - Not supported, 1 - Supported */
+       u8 green_field_capable;
+
+       /* MIMO Power Save mode */
+       enum wcn36xx_hal_ht_mimo_state mimo_ps;
+
+       /* Delayed BA Support */
+       u8 delayed_ba_support;
+
+       /* Max AMPDU duration in 32us */
+       u8 max_ampdu_duration;
+
+       /* HT STA should set it to 1 if it is enabled in BSS. HT STA should
+        * set it to 0 if AP does not support it. This indication is sent
+        * to HAL and HAL uses this flag to pickup up appropriate 40Mhz
+        * rates. */
+       u8 dsss_cck_mode_40mhz;
+
+       /* Valid STA Idx when action=Update. Set to 0xFF when invalid!
+        * Retained for backward compalibity with existing HAL code */
+       u8 sta_index;
+
+       /* BSSID of BSS to which station is associated. Set to 0xFF when
+        * invalid. Retained for backward compalibity with existing HAL
+        * code */
+       u8 bssid_index;
+
+       u8 p2p;
+
+       /* TODO add this parameter for 3680. */
+       /* Reserved to align next field on a dword boundary */
+       /* u8 reserved; */
+} __packed;
+
+struct wcn36xx_hal_config_sta_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_config_sta_params sta_params;
+} __packed;
+
+struct wcn36xx_hal_config_sta_params_v1 {
+       /* BSSID of STA */
+       u8 bssid[ETH_ALEN];
+
+       /* ASSOC ID, as assigned by UMAC */
+       u16 aid;
+
+       /* STA entry Type: 0 - Self, 1 - Other/Peer, 2 - BSSID, 3 - BCAST */
+       u8 type;
+
+       /* Short Preamble Supported. */
+       u8 short_preamble_supported;
+
+       /* MAC Address of STA */
+       u8 mac[ETH_ALEN];
+
+       /* Listen interval of the STA */
+       u16 listen_interval;
+
+       /* Support for 11e/WMM */
+       u8 wmm_enabled;
+
+       /* 11n HT capable STA */
+       u8 ht_capable;
+
+       /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */
+       u8 tx_channel_width_set;
+
+       /* RIFS mode 0 - NA, 1 - Allowed */
+       u8 rifs_mode;
+
+       /* L-SIG TXOP Protection mechanism
+          0 - No Support, 1 - Supported
+          SG - there is global field */
+       u8 lsig_txop_protection;
+
+       /* Max Ampdu Size supported by STA. TPE programming.
+          0 : 8k , 1 : 16k, 2 : 32k, 3 : 64k */
+       u8 max_ampdu_size;
+
+       /* Max Ampdu density. Used by RA.  3 : 0~7 : 2^(11nAMPDUdensity -4) */
+       u8 max_ampdu_density;
+
+       /* Max AMSDU size 1 : 3839 bytes, 0 : 7935 bytes */
+       u8 max_amsdu_size;
+
+       /* Short GI support for 40Mhz packets */
+       u8 sgi_40mhz;
+
+       /* Short GI support for 20Mhz packets */
+       u8 sgi_20Mhz;
+
+       /* Robust Management Frame (RMF) enabled/disabled */
+       u8 rmf;
+
+       /* The unicast encryption type in the association */
+       u32 encrypt_type;
+
+       /* HAL should update the existing STA entry, if this flag is set. UMAC
+          will set this flag in case of RE-ASSOC, where we want to reuse the
+          old STA ID. 0 = Add, 1 = Update */
+       u8 action;
+
+       /* U-APSD Flags: 1b per AC.  Encoded as follows:
+          b7 b6 b5 b4 b3 b2 b1 b0 =
+          X  X  X  X  BE BK VI VO */
+       u8 uapsd;
+
+       /* Max SP Length */
+       u8 max_sp_len;
+
+       /* 11n Green Field preamble support
+          0 - Not supported, 1 - Supported */
+       u8 green_field_capable;
+
+       /* MIMO Power Save mode */
+       enum wcn36xx_hal_ht_mimo_state mimo_ps;
+
+       /* Delayed BA Support */
+       u8 delayed_ba_support;
+
+       /* Max AMPDU duration in 32us */
+       u8 max_ampdu_duration;
+
+       /* HT STA should set it to 1 if it is enabled in BSS. HT STA should
+        * set it to 0 if AP does not support it. This indication is sent
+        * to HAL and HAL uses this flag to pickup up appropriate 40Mhz
+        * rates. */
+       u8 dsss_cck_mode_40mhz;
+
+       /* Valid STA Idx when action=Update. Set to 0xFF when invalid!
+        * Retained for backward compalibity with existing HAL code */
+       u8 sta_index;
+
+       /* BSSID of BSS to which station is associated. Set to 0xFF when
+        * invalid. Retained for backward compalibity with existing HAL
+        * code */
+       u8 bssid_index;
+
+       u8 p2p;
+
+       /* Reserved to align next field on a dword boundary */
+       u8 reserved;
+
+       /* These rates are the intersection of peer and self capabilities. */
+       struct wcn36xx_hal_supported_rates supported_rates;
+} __packed;
+
+struct wcn36xx_hal_config_sta_req_msg_v1 {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_config_sta_params_v1 sta_params;
+} __packed;
+
+struct config_sta_rsp_params {
+       /* success or failure */
+       u32 status;
+
+       /* Station index; valid only when 'status' field value SUCCESS */
+       u8 sta_index;
+
+       /* BSSID Index of BSS to which the station is associated */
+       u8 bssid_index;
+
+       /* DPU Index for PTK */
+       u8 dpu_index;
+
+       /* DPU Index for GTK */
+       u8 bcast_dpu_index;
+
+       /* DPU Index for IGTK  */
+       u8 bcast_mgmt_dpu_idx;
+
+       /* PTK DPU signature */
+       u8 uc_ucast_sig;
+
+       /* GTK DPU isignature */
+       u8 uc_bcast_sig;
+
+       /* IGTK DPU signature */
+       u8 uc_mgmt_sig;
+
+       u8 p2p;
+
+} __packed;
+
+struct wcn36xx_hal_config_sta_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       struct config_sta_rsp_params params;
+} __packed;
+
+/* Delete STA Request message */
+struct wcn36xx_hal_delete_sta_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Index of STA to delete */
+       u8 sta_index;
+
+} __packed;
+
+/* Delete STA Response message */
+struct wcn36xx_hal_delete_sta_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* Index of STA deleted */
+       u8 sta_id;
+} __packed;
+
+/* 12 Bytes long because this structure can be used to represent rate and
+ * extended rate set IEs. The parser assume this to be at least 12 */
+struct wcn36xx_hal_rate_set {
+       u8 num_rates;
+       u8 rate[WCN36XX_HAL_MAC_RATESET_EID_MAX];
+} __packed;
+
+/* access category record */
+struct wcn36xx_hal_aci_aifsn {
+#ifndef ANI_LITTLE_BIT_ENDIAN
+       u8 rsvd:1;
+       u8 aci:2;
+       u8 acm:1;
+       u8 aifsn:4;
+#else
+       u8 aifsn:4;
+       u8 acm:1;
+       u8 aci:2;
+       u8 rsvd:1;
+#endif
+} __packed;
+
+/* contention window size */
+struct wcn36xx_hal_mac_cw {
+#ifndef ANI_LITTLE_BIT_ENDIAN
+       u8 max:4;
+       u8 min:4;
+#else
+       u8 min:4;
+       u8 max:4;
+#endif
+} __packed;
+
+struct wcn36xx_hal_edca_param_record {
+       struct wcn36xx_hal_aci_aifsn aci;
+       struct wcn36xx_hal_mac_cw cw;
+       u16 txop_limit;
+} __packed;
+
+struct wcn36xx_hal_mac_ssid {
+       u8 length;
+       u8 ssid[32];
+} __packed;
+
+/* Concurrency role. These are generic IDs that identify the various roles
+ *  in the software system. */
+enum wcn36xx_hal_con_mode {
+       WCN36XX_HAL_STA_MODE = 0,
+
+       /* to support softAp mode . This is misleading.
+          It means AP MODE only. */
+       WCN36XX_HAL_STA_SAP_MODE = 1,
+
+       WCN36XX_HAL_P2P_CLIENT_MODE,
+       WCN36XX_HAL_P2P_GO_MODE,
+       WCN36XX_HAL_MONITOR_MODE,
+};
+
+/* This is a bit pattern to be set for each mode
+ * bit 0 - sta mode
+ * bit 1 - ap mode
+ * bit 2 - p2p client mode
+ * bit 3 - p2p go mode */
+enum wcn36xx_hal_concurrency_mode {
+       HAL_STA = 1,
+       HAL_SAP = 2,
+
+       /* to support sta, softAp  mode . This means STA+AP mode */
+       HAL_STA_SAP = 3,
+
+       HAL_P2P_CLIENT = 4,
+       HAL_P2P_GO = 8,
+       HAL_MAX_CONCURRENCY_PERSONA = 4
+};
+
+struct wcn36xx_hal_config_bss_params {
+       /* BSSID */
+       u8 bssid[ETH_ALEN];
+
+       /* Self Mac Address */
+       u8 self_mac_addr[ETH_ALEN];
+
+       /* BSS type */
+       enum wcn36xx_hal_bss_type bss_type;
+
+       /* Operational Mode: AP =0, STA = 1 */
+       u8 oper_mode;
+
+       /* Network Type */
+       enum wcn36xx_hal_nw_type nw_type;
+
+       /* Used to classify PURE_11G/11G_MIXED to program MTU */
+       u8 short_slot_time_supported;
+
+       /* Co-exist with 11a STA */
+       u8 lla_coexist;
+
+       /* Co-exist with 11b STA */
+       u8 llb_coexist;
+
+       /* Co-exist with 11g STA */
+       u8 llg_coexist;
+
+       /* Coexistence with 11n STA */
+       u8 ht20_coexist;
+
+       /* Non GF coexist flag */
+       u8 lln_non_gf_coexist;
+
+       /* TXOP protection support */
+       u8 lsig_tx_op_protection_full_support;
+
+       /* RIFS mode */
+       u8 rifs_mode;
+
+       /* Beacon Interval in TU */
+       u16 beacon_interval;
+
+       /* DTIM period */
+       u8 dtim_period;
+
+       /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */
+       u8 tx_channel_width_set;
+
+       /* Operating channel */
+       u8 oper_channel;
+
+       /* Extension channel for channel bonding */
+       u8 ext_channel;
+
+       /* Reserved to align next field on a dword boundary */
+       u8 reserved;
+
+       /* TODO move sta to the end for 3680 */
+       /* Context of the station being added in HW
+        *  Add a STA entry for "itself" -
+        *
+        *  On AP  - Add the AP itself in an "STA context"
+        *
+        *  On STA - Add the AP to which this STA is joining in an
+        *  "STA context"
+        */
+       struct wcn36xx_hal_config_sta_params sta;
+       /* SSID of the BSS */
+       struct wcn36xx_hal_mac_ssid ssid;
+
+       /* HAL should update the existing BSS entry, if this flag is set.
+        * UMAC will set this flag in case of reassoc, where we want to
+        * resue the the old BSSID and still return success 0 = Add, 1 =
+        * Update */
+       u8 action;
+
+       /* MAC Rate Set */
+       struct wcn36xx_hal_rate_set rateset;
+
+       /* Enable/Disable HT capabilities of the BSS */
+       u8 ht;
+
+       /* Enable/Disable OBSS protection */
+       u8 obss_prot_enabled;
+
+       /* RMF enabled/disabled */
+       u8 rmf;
+
+       /* HT Operating Mode operating mode of the 802.11n STA */
+       enum wcn36xx_hal_ht_operating_mode ht_oper_mode;
+
+       /* Dual CTS Protection: 0 - Unused, 1 - Used */
+       u8 dual_cts_protection;
+
+       /* Probe Response Max retries */
+       u8 max_probe_resp_retry_limit;
+
+       /* To Enable Hidden ssid */
+       u8 hidden_ssid;
+
+       /* To Enable Disable FW Proxy Probe Resp */
+       u8 proxy_probe_resp;
+
+       /* Boolean to indicate if EDCA params are valid. UMAC might not
+        * have valid EDCA params or might not desire to apply EDCA params
+        * during config BSS. 0 implies Not Valid ; Non-Zero implies
+        * valid */
+       u8 edca_params_valid;
+
+       /* EDCA Parameters for Best Effort Access Category */
+       struct wcn36xx_hal_edca_param_record acbe;
+
+       /* EDCA Parameters forBackground Access Category */
+       struct wcn36xx_hal_edca_param_record acbk;
+
+       /* EDCA Parameters for Video Access Category */
+       struct wcn36xx_hal_edca_param_record acvi;
+
+       /* EDCA Parameters for Voice Access Category */
+       struct wcn36xx_hal_edca_param_record acvo;
+
+       /* Ext Bss Config Msg if set */
+       u8 ext_set_sta_key_param_valid;
+
+       /* SetStaKeyParams for ext bss msg */
+       struct wcn36xx_hal_set_sta_key_params ext_set_sta_key_param;
+
+       /* Persona for the BSS can be STA,AP,GO,CLIENT value same as enum
+        * wcn36xx_hal_con_mode */
+       u8 wcn36xx_hal_persona;
+
+       u8 spectrum_mgt_enable;
+
+       /* HAL fills in the tx power used for mgmt frames in txMgmtPower */
+       s8 tx_mgmt_power;
+
+       /* maxTxPower has max power to be used after applying the power
+        * constraint if any */
+       s8 max_tx_power;
+} __packed;
+
+struct wcn36xx_hal_config_bss_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_config_bss_params bss_params;
+} __packed;
+
+struct wcn36xx_hal_config_bss_params_v1 {
+       /* BSSID */
+       u8 bssid[ETH_ALEN];
+
+       /* Self Mac Address */
+       u8 self_mac_addr[ETH_ALEN];
+
+       /* BSS type */
+       enum wcn36xx_hal_bss_type bss_type;
+
+       /* Operational Mode: AP =0, STA = 1 */
+       u8 oper_mode;
+
+       /* Network Type */
+       enum wcn36xx_hal_nw_type nw_type;
+
+       /* Used to classify PURE_11G/11G_MIXED to program MTU */
+       u8 short_slot_time_supported;
+
+       /* Co-exist with 11a STA */
+       u8 lla_coexist;
+
+       /* Co-exist with 11b STA */
+       u8 llb_coexist;
+
+       /* Co-exist with 11g STA */
+       u8 llg_coexist;
+
+       /* Coexistence with 11n STA */
+       u8 ht20_coexist;
+
+       /* Non GF coexist flag */
+       u8 lln_non_gf_coexist;
+
+       /* TXOP protection support */
+       u8 lsig_tx_op_protection_full_support;
+
+       /* RIFS mode */
+       u8 rifs_mode;
+
+       /* Beacon Interval in TU */
+       u16 beacon_interval;
+
+       /* DTIM period */
+       u8 dtim_period;
+
+       /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */
+       u8 tx_channel_width_set;
+
+       /* Operating channel */
+       u8 oper_channel;
+
+       /* Extension channel for channel bonding */
+       u8 ext_channel;
+
+       /* Reserved to align next field on a dword boundary */
+       u8 reserved;
+
+       /* SSID of the BSS */
+       struct wcn36xx_hal_mac_ssid ssid;
+
+       /* HAL should update the existing BSS entry, if this flag is set.
+        * UMAC will set this flag in case of reassoc, where we want to
+        * resue the the old BSSID and still return success 0 = Add, 1 =
+        * Update */
+       u8 action;
+
+       /* MAC Rate Set */
+       struct wcn36xx_hal_rate_set rateset;
+
+       /* Enable/Disable HT capabilities of the BSS */
+       u8 ht;
+
+       /* Enable/Disable OBSS protection */
+       u8 obss_prot_enabled;
+
+       /* RMF enabled/disabled */
+       u8 rmf;
+
+       /* HT Operating Mode operating mode of the 802.11n STA */
+       enum wcn36xx_hal_ht_operating_mode ht_oper_mode;
+
+       /* Dual CTS Protection: 0 - Unused, 1 - Used */
+       u8 dual_cts_protection;
+
+       /* Probe Response Max retries */
+       u8 max_probe_resp_retry_limit;
+
+       /* To Enable Hidden ssid */
+       u8 hidden_ssid;
+
+       /* To Enable Disable FW Proxy Probe Resp */
+       u8 proxy_probe_resp;
+
+       /* Boolean to indicate if EDCA params are valid. UMAC might not
+        * have valid EDCA params or might not desire to apply EDCA params
+        * during config BSS. 0 implies Not Valid ; Non-Zero implies
+        * valid */
+       u8 edca_params_valid;
+
+       /* EDCA Parameters for Best Effort Access Category */
+       struct wcn36xx_hal_edca_param_record acbe;
+
+       /* EDCA Parameters forBackground Access Category */
+       struct wcn36xx_hal_edca_param_record acbk;
+
+       /* EDCA Parameters for Video Access Category */
+       struct wcn36xx_hal_edca_param_record acvi;
+
+       /* EDCA Parameters for Voice Access Category */
+       struct wcn36xx_hal_edca_param_record acvo;
+
+       /* Ext Bss Config Msg if set */
+       u8 ext_set_sta_key_param_valid;
+
+       /* SetStaKeyParams for ext bss msg */
+       struct wcn36xx_hal_set_sta_key_params ext_set_sta_key_param;
+
+       /* Persona for the BSS can be STA,AP,GO,CLIENT value same as enum
+        * wcn36xx_hal_con_mode */
+       u8 wcn36xx_hal_persona;
+
+       u8 spectrum_mgt_enable;
+
+       /* HAL fills in the tx power used for mgmt frames in txMgmtPower */
+       s8 tx_mgmt_power;
+
+       /* maxTxPower has max power to be used after applying the power
+        * constraint if any */
+       s8 max_tx_power;
+
+       /* Context of the station being added in HW
+        *  Add a STA entry for "itself" -
+        *
+        *  On AP  - Add the AP itself in an "STA context"
+        *
+        *  On STA - Add the AP to which this STA is joining in an
+        *  "STA context"
+        */
+       struct wcn36xx_hal_config_sta_params_v1 sta;
+} __packed;
+
+struct wcn36xx_hal_config_bss_req_msg_v1 {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_config_bss_params_v1 bss_params;
+} __packed;
+
+struct wcn36xx_hal_config_bss_rsp_params {
+       /* Success or Failure */
+       u32 status;
+
+       /* BSS index allocated by HAL */
+       u8 bss_index;
+
+       /* DPU descriptor index for PTK */
+       u8 dpu_desc_index;
+
+       /* PTK DPU signature */
+       u8 ucast_dpu_signature;
+
+       /* DPU descriptor index for GTK */
+       u8 bcast_dpu_desc_indx;
+
+       /* GTK DPU signature */
+       u8 bcast_dpu_signature;
+
+       /* DPU descriptor for IGTK */
+       u8 mgmt_dpu_desc_index;
+
+       /* IGTK DPU signature */
+       u8 mgmt_dpu_signature;
+
+       /* Station Index for BSS entry */
+       u8 bss_sta_index;
+
+       /* Self station index for this BSS */
+       u8 bss_self_sta_index;
+
+       /* Bcast station for buffering bcast frames in AP role */
+       u8 bss_bcast_sta_idx;
+
+       /* MAC Address of STA(PEER/SELF) in staContext of configBSSReq */
+       u8 mac[ETH_ALEN];
+
+       /* HAL fills in the tx power used for mgmt frames in this field. */
+       s8 tx_mgmt_power;
+
+} __packed;
+
+struct wcn36xx_hal_config_bss_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_config_bss_rsp_params bss_rsp_params;
+} __packed;
+
+struct wcn36xx_hal_delete_bss_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* BSS index to be deleted */
+       u8 bss_index;
+
+} __packed;
+
+struct wcn36xx_hal_delete_bss_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Success or Failure */
+       u32 status;
+
+       /* BSS index that has been deleted */
+       u8 bss_index;
+
+} __packed;
+
+struct wcn36xx_hal_join_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Indicates the BSSID to which STA is going to associate */
+       u8 bssid[ETH_ALEN];
+
+       /* Indicates the channel to switch to. */
+       u8 channel;
+
+       /* Self STA MAC */
+       u8 self_sta_mac_addr[ETH_ALEN];
+
+       /* Local power constraint */
+       u8 local_power_constraint;
+
+       /* Secondary channel offset */
+       enum phy_chan_bond_state secondary_channel_offset;
+
+       /* link State */
+       enum wcn36xx_hal_link_state link_state;
+
+       /* Max TX power */
+       s8 max_tx_power;
+} __packed;
+
+struct wcn36xx_hal_join_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* HAL fills in the tx power used for mgmt frames in this field */
+       u8 tx_mgmt_power;
+} __packed;
+
+struct post_assoc_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       struct wcn36xx_hal_config_sta_params sta_params;
+       struct wcn36xx_hal_config_bss_params bss_params;
+};
+
+struct post_assoc_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct config_sta_rsp_params sta_rsp_params;
+       struct wcn36xx_hal_config_bss_rsp_params bss_rsp_params;
+};
+
+/* This is used to create a set of WEP keys for a given BSS. */
+struct wcn36xx_hal_set_bss_key_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* BSS Index of the BSS */
+       u8 bss_idx;
+
+       /* Encryption Type used with peer */
+       enum ani_ed_type enc_type;
+
+       /* Number of keys */
+       u8 num_keys;
+
+       /* Array of keys. */
+       struct wcn36xx_hal_keys keys[WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS];
+
+       /* Control for Replay Count, 1= Single TID based replay count on Tx
+        * 0 = Per TID based replay count on TX */
+       u8 single_tid_rc;
+} __packed;
+
+/* tagged version of set bss key */
+struct wcn36xx_hal_set_bss_key_req_msg_tagged {
+       struct wcn36xx_hal_set_bss_key_req_msg Msg;
+       u32 tag;
+} __packed;
+
+struct wcn36xx_hal_set_bss_key_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+} __packed;
+
+/*
+ * This is used  configure the key information on a given station.
+ * When the sec_type is WEP40 or WEP104, the def_wep_idx is used to locate
+ * a preconfigured key from a BSS the station assoicated with; otherwise
+ * a new key descriptor is created based on the key field.
+ */
+struct wcn36xx_hal_set_sta_key_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_set_sta_key_params set_sta_key_params;
+} __packed;
+
+struct wcn36xx_hal_set_sta_key_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+} __packed;
+
+struct wcn36xx_hal_remove_bss_key_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* BSS Index of the BSS */
+       u8 bss_idx;
+
+       /* Encryption Type used with peer */
+       enum ani_ed_type enc_type;
+
+       /* Key Id */
+       u8 key_id;
+
+       /* STATIC/DYNAMIC. Used in Nullifying in Key Descriptors for
+        * Static/Dynamic keys */
+       enum ani_wep_type wep_type;
+} __packed;
+
+struct wcn36xx_hal_remove_bss_key_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+} __packed;
+
+/*
+ * This is used by PE to Remove the key information on a given station.
+ */
+struct wcn36xx_hal_remove_sta_key_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* STA Index */
+       u16 sta_idx;
+
+       /* Encryption Type used with peer */
+       enum ani_ed_type enc_type;
+
+       /* Key Id */
+       u8 key_id;
+
+       /* Whether to invalidate the Broadcast key or Unicast key. In case
+        * of WEP, the same key is used for both broadcast and unicast. */
+       u8 unicast;
+
+} __packed;
+
+struct wcn36xx_hal_remove_sta_key_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /*success or failure */
+       u32 status;
+
+} __packed;
+
+#ifdef FEATURE_OEM_DATA_SUPPORT
+
+#ifndef OEM_DATA_REQ_SIZE
+#define OEM_DATA_REQ_SIZE 134
+#endif
+
+#ifndef OEM_DATA_RSP_SIZE
+#define OEM_DATA_RSP_SIZE 1968
+#endif
+
+struct start_oem_data_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u32 status;
+       tSirMacAddr self_mac_addr;
+       u8 oem_data_req[OEM_DATA_REQ_SIZE];
+
+};
+
+struct start_oem_data_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 oem_data_rsp[OEM_DATA_RSP_SIZE];
+};
+
+#endif
+
+struct wcn36xx_hal_switch_channel_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Channel number */
+       u8 channel_number;
+
+       /* Local power constraint */
+       u8 local_power_constraint;
+
+       /* Secondary channel offset */
+       enum phy_chan_bond_state secondary_channel_offset;
+
+       /* HAL fills in the tx power used for mgmt frames in this field. */
+       u8 tx_mgmt_power;
+
+       /* Max TX power */
+       u8 max_tx_power;
+
+       /* Self STA MAC */
+       u8 self_sta_mac_addr[ETH_ALEN];
+
+       /* VO WIFI comment: BSSID needed to identify session. As the
+        * request has power constraints, this should be applied only to
+        * that session Since MTU timing and EDCA are sessionized, this
+        * struct needs to be sessionized and bssid needs to be out of the
+        * VOWifi feature flag V IMP: Keep bssId field at the end of this
+        * msg. It is used to mantain backward compatbility by way of
+        * ignoring if using new host/old FW or old host/new FW since it is
+        * at the end of this struct
+        */
+       u8 bssid[ETH_ALEN];
+} __packed;
+
+struct wcn36xx_hal_switch_channel_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Status */
+       u32 status;
+
+       /* Channel number - same as in request */
+       u8 channel_number;
+
+       /* HAL fills in the tx power used for mgmt frames in this field */
+       u8 tx_mgmt_power;
+
+       /* BSSID needed to identify session - same as in request */
+       u8 bssid[ETH_ALEN];
+
+} __packed;
+
+struct update_edca_params_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /*BSS Index */
+       u16 bss_index;
+
+       /* Best Effort */
+       struct wcn36xx_hal_edca_param_record acbe;
+
+       /* Background */
+       struct wcn36xx_hal_edca_param_record acbk;
+
+       /* Video */
+       struct wcn36xx_hal_edca_param_record acvi;
+
+       /* Voice */
+       struct wcn36xx_hal_edca_param_record acvo;
+};
+
+struct update_edca_params_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct dpu_stats_params {
+       /* Index of STA to which the statistics */
+       u16 sta_index;
+
+       /* Encryption mode */
+       u8 enc_mode;
+
+       /* status */
+       u32 status;
+
+       /* Statistics */
+       u32 send_blocks;
+       u32 recv_blocks;
+       u32 replays;
+       u8 mic_error_cnt;
+       u32 prot_excl_cnt;
+       u16 format_err_cnt;
+       u16 un_decryptable_cnt;
+       u32 decrypt_err_cnt;
+       u32 decrypt_ok_cnt;
+};
+
+struct wcn36xx_hal_stats_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Valid STA Idx for per STA stats request */
+       u32 sta_id;
+
+       /* Categories of stats requested as specified in eHalStatsMask */
+       u32 stats_mask;
+};
+
+struct ani_summary_stats_info {
+       /* Total number of packets(per AC) that were successfully
+        * transmitted with retries */
+       u32 retry_cnt[4];
+
+       /* The number of MSDU packets and MMPDU frames per AC that the
+        * 802.11 station successfully transmitted after more than one
+        * retransmission attempt */
+       u32 multiple_retry_cnt[4];
+
+       /* Total number of packets(per AC) that were successfully
+        * transmitted (with and without retries, including multi-cast,
+        * broadcast) */
+       u32 tx_frm_cnt[4];
+
+       /* Total number of packets that were successfully received (after
+        * appropriate filter rules including multi-cast, broadcast) */
+       u32 rx_frm_cnt;
+
+       /* Total number of duplicate frames received successfully */
+       u32 frm_dup_cnt;
+
+       /* Total number packets(per AC) failed to transmit */
+       u32 fail_cnt[4];
+
+       /* Total number of RTS/CTS sequence failures for transmission of a
+        * packet */
+       u32 rts_fail_cnt;
+
+       /* Total number packets failed transmit because of no ACK from the
+        * remote entity */
+       u32 ack_fail_cnt;
+
+       /* Total number of RTS/CTS sequence success for transmission of a
+        * packet */
+       u32 rts_succ_cnt;
+
+       /* The sum of the receive error count and dropped-receive-buffer
+        * error count. HAL will provide this as a sum of (FCS error) +
+        * (Fail get BD/PDU in HW) */
+       u32 rx_discard_cnt;
+
+       /*
+        * The receive error count. HAL will provide the RxP FCS error
+        * global counter. */
+       u32 rx_error_cnt;
+
+       /* The sum of the transmit-directed byte count, transmit-multicast
+        * byte count and transmit-broadcast byte count. HAL will sum TPE
+        * UC/MC/BCAST global counters to provide this. */
+       u32 tx_byte_cnt;
+};
+
+/* defines tx_rate_flags */
+enum tx_rate_info {
+       /* Legacy rates */
+       HAL_TX_RATE_LEGACY = 0x1,
+
+       /* HT20 rates */
+       HAL_TX_RATE_HT20 = 0x2,
+
+       /* HT40 rates */
+       HAL_TX_RATE_HT40 = 0x4,
+
+       /* Rate with Short guard interval */
+       HAL_TX_RATE_SGI = 0x8,
+
+       /* Rate with Long guard interval */
+       HAL_TX_RATE_LGI = 0x10
+};
+
+struct ani_global_class_a_stats_info {
+       /* The number of MPDU frames received by the 802.11 station for
+        * MSDU packets or MMPDU frames */
+       u32 rx_frag_cnt;
+
+       /* The number of MPDU frames received by the 802.11 station for
+        * MSDU packets or MMPDU frames when a promiscuous packet filter
+        * was enabled */
+       u32 promiscuous_rx_frag_cnt;
+
+       /* The receiver input sensitivity referenced to a FER of 8% at an
+        * MPDU length of 1024 bytes at the antenna connector. Each element
+        * of the array shall correspond to a supported rate and the order
+        * shall be the same as the supporteRates parameter. */
+       u32 rx_input_sensitivity;
+
+       /* The maximum transmit power in dBm upto one decimal. for eg: if
+        * it is 10.5dBm, the value would be 105 */
+       u32 max_pwr;
+
+       /* Number of times the receiver failed to synchronize with the
+        * incoming signal after detecting the sync in the preamble of the
+        * transmitted PLCP protocol data unit. */
+       u32 sync_fail_cnt;
+
+       /* Legacy transmit rate, in units of 500 kbit/sec, for the most
+        * recently transmitted frame */
+       u32 tx_rate;
+
+       /* mcs index for HT20 and HT40 rates */
+       u32 mcs_index;
+
+       /* to differentiate between HT20 and HT40 rates; short and long
+        * guard interval */
+       u32 tx_rate_flags;
+};
+
+struct ani_global_security_stats {
+       /* The number of unencrypted received MPDU frames that the MAC
+        * layer discarded when the IEEE 802.11 dot11ExcludeUnencrypted
+        * management information base (MIB) object is enabled */
+       u32 rx_wep_unencrypted_frm_cnt;
+
+       /* The number of received MSDU packets that that the 802.11 station
+        * discarded because of MIC failures */
+       u32 rx_mic_fail_cnt;
+
+       /* The number of encrypted MPDU frames that the 802.11 station
+        * failed to decrypt because of a TKIP ICV error */
+       u32 tkip_icv_err;
+
+       /* The number of received MPDU frames that the 802.11 discarded
+        * because of an invalid AES-CCMP format */
+       u32 aes_ccmp_format_err;
+
+       /* The number of received MPDU frames that the 802.11 station
+        * discarded because of the AES-CCMP replay protection procedure */
+       u32 aes_ccmp_replay_cnt;
+
+       /* The number of received MPDU frames that the 802.11 station
+        * discarded because of errors detected by the AES-CCMP decryption
+        * algorithm */
+       u32 aes_ccmp_decrpt_err;
+
+       /* The number of encrypted MPDU frames received for which a WEP
+        * decryption key was not available on the 802.11 station */
+       u32 wep_undecryptable_cnt;
+
+       /* The number of encrypted MPDU frames that the 802.11 station
+        * failed to decrypt because of a WEP ICV error */
+       u32 wep_icv_err;
+
+       /* The number of received encrypted packets that the 802.11 station
+        * successfully decrypted */
+       u32 rx_decrypt_succ_cnt;
+
+       /* The number of encrypted packets that the 802.11 station failed
+        * to decrypt */
+       u32 rx_decrypt_fail_cnt;
+};
+
+struct ani_global_class_b_stats_info {
+       struct ani_global_security_stats uc_stats;
+       struct ani_global_security_stats mc_bc_stats;
+};
+
+struct ani_global_class_c_stats_info {
+       /* This counter shall be incremented for a received A-MSDU frame
+        * with the stations MAC address in the address 1 field or an
+        * A-MSDU frame with a group address in the address 1 field */
+       u32 rx_amsdu_cnt;
+
+       /* This counter shall be incremented when the MAC receives an AMPDU
+        * from the PHY */
+       u32 rx_ampdu_cnt;
+
+       /* This counter shall be incremented when a Frame is transmitted
+        * only on the primary channel */
+       u32 tx_20_frm_cnt;
+
+       /* This counter shall be incremented when a Frame is received only
+        * on the primary channel */
+       u32 rx_20_frm_cnt;
+
+       /* This counter shall be incremented by the number of MPDUs
+        * received in the A-MPDU when an A-MPDU is received */
+       u32 rx_mpdu_in_ampdu_cnt;
+
+       /* This counter shall be incremented when an MPDU delimiter has a
+        * CRC error when this is the first CRC error in the received AMPDU
+        * or when the previous delimiter has been decoded correctly */
+       u32 ampdu_delimiter_crc_err;
+};
+
+struct ani_per_sta_stats_info {
+       /* The number of MPDU frames that the 802.11 station transmitted
+        * and acknowledged through a received 802.11 ACK frame */
+       u32 tx_frag_cnt[4];
+
+       /* This counter shall be incremented when an A-MPDU is transmitted */
+       u32 tx_ampdu_cnt;
+
+       /* This counter shall increment by the number of MPDUs in the AMPDU
+        * when an A-MPDU is transmitted */
+       u32 tx_mpdu_in_ampdu_cnt;
+};
+
+struct wcn36xx_hal_stats_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Success or Failure */
+       u32 status;
+
+       /* STA Idx */
+       u32 sta_index;
+
+       /* Categories of STATS being returned as per eHalStatsMask */
+       u32 stats_mask;
+
+       /* message type is same as the request type */
+       u16 msg_type;
+
+       /* length of the entire request, includes the pStatsBuf length too */
+       u16 msg_len;
+};
+
+struct wcn36xx_hal_set_link_state_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bssid[ETH_ALEN];
+       enum wcn36xx_hal_link_state state;
+       u8 self_mac_addr[ETH_ALEN];
+
+} __packed;
+
+struct set_link_state_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+/* TSPEC Params */
+struct wcn36xx_hal_ts_info_tfc {
+#ifndef ANI_LITTLE_BIT_ENDIAN
+       u16 ackPolicy:2;
+       u16 userPrio:3;
+       u16 psb:1;
+       u16 aggregation:1;
+       u16 accessPolicy:2;
+       u16 direction:2;
+       u16 tsid:4;
+       u16 trafficType:1;
+#else
+       u16 trafficType:1;
+       u16 tsid:4;
+       u16 direction:2;
+       u16 accessPolicy:2;
+       u16 aggregation:1;
+       u16 psb:1;
+       u16 userPrio:3;
+       u16 ackPolicy:2;
+#endif
+};
+
+/* Flag to schedule the traffic type */
+struct wcn36xx_hal_ts_info_sch {
+#ifndef ANI_LITTLE_BIT_ENDIAN
+       u8 rsvd:7;
+       u8 schedule:1;
+#else
+       u8 schedule:1;
+       u8 rsvd:7;
+#endif
+};
+
+/* Traffic and scheduling info */
+struct wcn36xx_hal_ts_info {
+       struct wcn36xx_hal_ts_info_tfc traffic;
+       struct wcn36xx_hal_ts_info_sch schedule;
+};
+
+/* Information elements */
+struct wcn36xx_hal_tspec_ie {
+       u8 type;
+       u8 length;
+       struct wcn36xx_hal_ts_info ts_info;
+       u16 nom_msdu_size;
+       u16 max_msdu_size;
+       u32 min_svc_interval;
+       u32 max_svc_interval;
+       u32 inact_interval;
+       u32 suspend_interval;
+       u32 svc_start_time;
+       u32 min_data_rate;
+       u32 mean_data_rate;
+       u32 peak_data_rate;
+       u32 max_burst_sz;
+       u32 delay_bound;
+       u32 min_phy_rate;
+       u16 surplus_bw;
+       u16 medium_time;
+};
+
+struct add_ts_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index */
+       u16 sta_index;
+
+       /* TSPEC handler uniquely identifying a TSPEC for a STA in a BSS */
+       u16 tspec_index;
+
+       /* To program TPE with required parameters */
+       struct wcn36xx_hal_tspec_ie tspec;
+
+       /* U-APSD Flags: 1b per AC.  Encoded as follows:
+          b7 b6 b5 b4 b3 b2 b1 b0 =
+          X  X  X  X  BE BK VI VO */
+       u8 uapsd;
+
+       /* These parameters are for all the access categories */
+
+       /* Service Interval */
+       u32 service_interval[WCN36XX_HAL_MAX_AC];
+
+       /* Suspend Interval */
+       u32 suspend_interval[WCN36XX_HAL_MAX_AC];
+
+       /* Delay Interval */
+       u32 delay_interval[WCN36XX_HAL_MAX_AC];
+};
+
+struct add_rs_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct del_ts_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index */
+       u16 sta_index;
+
+       /* TSPEC identifier uniquely identifying a TSPEC for a STA in a BSS */
+       u16 tspec_index;
+
+       /* To lookup station id using the mac address */
+       u8 bssid[ETH_ALEN];
+};
+
+struct del_ts_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+/* End of TSpec Parameters */
+
+/* Start of BLOCK ACK related Parameters */
+
+struct wcn36xx_hal_add_ba_session_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index */
+       u16 sta_index;
+
+       /* Peer MAC Address */
+       u8 mac_addr[ETH_ALEN];
+
+       /* ADDBA Action Frame dialog token
+          HAL will not interpret this object */
+       u8 dialog_token;
+
+       /* TID for which the BA is being setup
+          This identifies the TC or TS of interest */
+       u8 tid;
+
+       /* 0 - Delayed BA (Not supported)
+          1 - Immediate BA */
+       u8 policy;
+
+       /* Indicates the number of buffers for this TID (baTID)
+          NOTE - This is the requested buffer size. When this
+          is processed by HAL and subsequently by HDD, it is
+          possible that HDD may change this buffer size. Any
+          change in the buffer size should be noted by PE and
+          advertized appropriately in the ADDBA response */
+       u16 buffer_size;
+
+       /* BA timeout in TU's 0 means no timeout will occur */
+       u16 timeout;
+
+       /* b0..b3 - Fragment Number - Always set to 0
+          b4..b15 - Starting Sequence Number of first MSDU
+          for which this BA is setup */
+       u16 ssn;
+
+       /* ADDBA direction
+          1 - Originator
+          0 - Recipient */
+       u8 direction;
+} __packed;
+
+struct wcn36xx_hal_add_ba_session_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* Dialog token */
+       u8 dialog_token;
+
+       /* TID for which the BA session has been setup */
+       u8 ba_tid;
+
+       /* BA Buffer Size allocated for the current BA session */
+       u8 ba_buffer_size;
+
+       u8 ba_session_id;
+
+       /* Reordering Window buffer */
+       u8 win_size;
+
+       /* Station Index to id the sta */
+       u8 sta_index;
+
+       /* Starting Sequence Number */
+       u16 ssn;
+} __packed;
+
+struct wcn36xx_hal_add_ba_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Session Id */
+       u8 session_id;
+
+       /* Reorder Window Size */
+       u8 win_size;
+/* Old FW 1.2.2.4 does not support this*/
+#ifdef FEATURE_ON_CHIP_REORDERING
+       u8 reordering_done_on_chip;
+#endif
+} __packed;
+
+struct wcn36xx_hal_add_ba_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* Dialog token */
+       u8 dialog_token;
+} __packed;
+
+struct add_ba_info {
+       u16 ba_enable:1;
+       u16 starting_seq_num:12;
+       u16 reserved:3;
+};
+
+struct wcn36xx_hal_trigger_ba_rsp_candidate {
+       u8 sta_addr[ETH_ALEN];
+       struct add_ba_info ba_info[STACFG_MAX_TC];
+} __packed;
+
+struct wcn36xx_hal_trigget_ba_req_candidate {
+       u8 sta_index;
+       u8 tid_bitmap;
+} __packed;
+
+struct wcn36xx_hal_trigger_ba_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Session Id */
+       u8 session_id;
+
+       /* baCandidateCnt is followed by trigger BA
+        * Candidate List(tTriggerBaCandidate)
+        */
+       u16 candidate_cnt;
+
+} __packed;
+
+struct wcn36xx_hal_trigger_ba_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* TO SUPPORT BT-AMP */
+       u8 bssid[ETH_ALEN];
+
+       /* success or failure */
+       u32 status;
+
+       /* baCandidateCnt is followed by trigger BA
+        * Rsp Candidate List(tTriggerRspBaCandidate)
+        */
+       u16 candidate_cnt;
+} __packed;
+
+struct wcn36xx_hal_del_ba_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index */
+       u16 sta_index;
+
+       /* TID for which the BA session is being deleted */
+       u8 tid;
+
+       /* DELBA direction
+          1 - Originator
+          0 - Recipient */
+       u8 direction;
+} __packed;
+
+struct wcn36xx_hal_del_ba_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+} __packed;
+
+struct tsm_stats_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Traffic Id */
+       u8 tid;
+
+       u8 bssid[ETH_ALEN];
+};
+
+struct tsm_stats_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /*success or failure */
+       u32 status;
+
+       /* Uplink Packet Queue delay */
+       u16 uplink_pkt_queue_delay;
+
+       /* Uplink Packet Queue delay histogram */
+       u16 uplink_pkt_queue_delay_hist[4];
+
+       /* Uplink Packet Transmit delay */
+       u32 uplink_pkt_tx_delay;
+
+       /* Uplink Packet loss */
+       u16 uplink_pkt_loss;
+
+       /* Uplink Packet count */
+       u16 uplink_pkt_count;
+
+       /* Roaming count */
+       u8 roaming_count;
+
+       /* Roaming Delay */
+       u16 roaming_delay;
+};
+
+struct set_key_done_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /*bssid of the keys */
+       u8 bssidx;
+       u8 enc_type;
+};
+
+struct wcn36xx_hal_nv_img_download_req_msg {
+       /* Note: The length specified in wcn36xx_hal_nv_img_download_req_msg
+        * messages should be
+        * header.len = sizeof(wcn36xx_hal_nv_img_download_req_msg) +
+        * nv_img_buffer_size */
+       struct wcn36xx_hal_msg_header header;
+
+       /* Fragment sequence number of the NV Image. Note that NV Image
+        * might not fit into one message due to size limitation of the SMD
+        * channel FIFO. UMAC can hence choose to chop the NV blob into
+        * multiple fragments starting with seqeunce number 0, 1, 2 etc.
+        * The last fragment MUST be indicated by marking the
+        * isLastFragment field to 1. Note that all the NV blobs would be
+        * concatenated together by HAL without any padding bytes in
+        * between.*/
+       u16 frag_number;
+
+       /* Is this the last fragment? When set to 1 it indicates that no
+        * more fragments will be sent by UMAC and HAL can concatenate all
+        * the NV blobs rcvd & proceed with the parsing. HAL would generate
+        * a WCN36XX_HAL_DOWNLOAD_NV_RSP to the WCN36XX_HAL_DOWNLOAD_NV_REQ
+        * after it receives each fragment */
+       u16 last_fragment;
+
+       /* NV Image size (number of bytes) */
+       u32 nv_img_buffer_size;
+
+       /* Following the 'nv_img_buffer_size', there should be
+        * nv_img_buffer_size bytes of NV Image i.e.
+        * u8[nv_img_buffer_size] */
+} __packed;
+
+struct wcn36xx_hal_nv_img_download_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Success or Failure. HAL would generate a
+        * WCN36XX_HAL_DOWNLOAD_NV_RSP after each fragment */
+       u32 status;
+} __packed;
+
+struct wcn36xx_hal_nv_store_ind {
+       /* Note: The length specified in tHalNvStoreInd messages should be
+        * header.msgLen = sizeof(tHalNvStoreInd) + nvBlobSize */
+       struct wcn36xx_hal_msg_header header;
+
+       /* NV Item */
+       u32 table_id;
+
+       /* Size of NV Blob */
+       u32 nv_blob_size;
+
+       /* Following the 'nvBlobSize', there should be nvBlobSize bytes of
+        * NV blob i.e. u8[nvBlobSize] */
+};
+
+/* End of Block Ack Related Parameters */
+
+#define WCN36XX_HAL_CIPHER_SEQ_CTR_SIZE 6
+
+/* Definition for MIC failure indication MAC reports this each time a MIC
+ * failure occures on Rx TKIP packet
+ */
+struct mic_failure_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bssid[ETH_ALEN];
+
+       /* address used to compute MIC */
+       u8 src_addr[ETH_ALEN];
+
+       /* transmitter address */
+       u8 ta_addr[ETH_ALEN];
+
+       u8 dst_addr[ETH_ALEN];
+
+       u8 multicast;
+
+       /* first byte of IV */
+       u8 iv1;
+
+       /* second byte of IV */
+       u8 key_id;
+
+       /* sequence number */
+       u8 tsc[WCN36XX_HAL_CIPHER_SEQ_CTR_SIZE];
+
+       /* receive address */
+       u8 rx_addr[ETH_ALEN];
+};
+
+struct update_vht_op_mode_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u16 op_mode;
+       u16 sta_id;
+};
+
+struct update_vht_op_mode_params_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u32 status;
+};
+
+struct update_beacon_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bss_index;
+
+       /* shortPreamble mode. HAL should update all the STA rates when it
+        * receives this message */
+       u8 short_preamble;
+
+       /* short Slot time. */
+       u8 short_slot_time;
+
+       /* Beacon Interval */
+       u16 beacon_interval;
+
+       /* Protection related */
+       u8 lla_coexist;
+       u8 llb_coexist;
+       u8 llg_coexist;
+       u8 ht20_coexist;
+       u8 lln_non_gf_coexist;
+       u8 lsig_tx_op_protection_full_support;
+       u8 rifs_mode;
+
+       u16 param_change_bitmap;
+};
+
+struct update_beacon_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       u32 status;
+};
+
+struct wcn36xx_hal_send_beacon_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* length of the template. */
+       u32 beacon_length;
+
+       /* Beacon data. */
+       u8 beacon[BEACON_TEMPLATE_SIZE];
+
+       u8 bssid[ETH_ALEN];
+
+       /* TIM IE offset from the beginning of the template. */
+       u32 tim_ie_offset;
+
+       /* P2P IE offset from the begining of the template */
+       u16 p2p_ie_offset;
+} __packed;
+
+struct send_beacon_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       u32 status;
+} __packed;
+
+struct enable_radar_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bssid[ETH_ALEN];
+       u8 channel;
+};
+
+struct enable_radar_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Link Parameters */
+       u8 bssid[ETH_ALEN];
+
+       /* success or failure */
+       u32 status;
+};
+
+struct radar_detect_intr_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 radar_det_channel;
+};
+
+struct radar_detect_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* channel number in which the RADAR detected */
+       u8 channel_number;
+
+       /* RADAR pulse width in usecond */
+       u16 radar_pulse_width;
+
+       /* Number of RADAR pulses */
+       u16 num_radar_pulse;
+};
+
+struct wcn36xx_hal_get_tpc_report_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 sta[ETH_ALEN];
+       u8 dialog_token;
+       u8 txpower;
+};
+
+struct wcn36xx_hal_get_tpc_report_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_send_probe_resp_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 probe_resp_template[BEACON_TEMPLATE_SIZE];
+       u32 probe_resp_template_len;
+       u32 proxy_probe_req_valid_ie_bmap[8];
+       u8 bssid[ETH_ALEN];
+};
+
+struct send_probe_resp_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct send_unknown_frame_rx_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_delete_sta_context_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u16 aid;
+       u16 sta_id;
+
+       /* TO SUPPORT BT-AMP */
+       u8 bssid[ETH_ALEN];
+
+       /* HAL copies bssid from the sta table. */
+       u8 addr2[ETH_ALEN];
+
+       /* To unify the keepalive / unknown A2 / tim-based disa */
+       u16 reason_code;
+} __packed;
+
+struct indicate_del_sta {
+       struct wcn36xx_hal_msg_header header;
+       u8 aid;
+       u8 sta_index;
+       u8 bss_index;
+       u8 reason_code;
+       u32 status;
+};
+
+struct bt_amp_event_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       enum bt_amp_event_type btAmpEventType;
+};
+
+struct bt_amp_event_rsp {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct tl_hal_flush_ac_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index. originates from HAL */
+       u8 sta_id;
+
+       /* TID for which the transmit queue is being flushed */
+       u8 tid;
+};
+
+struct tl_hal_flush_ac_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index. originates from HAL */
+       u8 sta_id;
+
+       /* TID for which the transmit queue is being flushed */
+       u8 tid;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_enter_imps_req_msg {
+       struct wcn36xx_hal_msg_header header;
+};
+
+struct wcn36xx_hal_exit_imps_req {
+       struct wcn36xx_hal_msg_header header;
+};
+
+struct wcn36xx_hal_enter_bmps_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bss_index;
+
+       /* TBTT value derived from the last beacon */
+#ifndef BUILD_QWPTTSTATIC
+       u64 tbtt;
+#endif
+       u8 dtim_count;
+
+       /* DTIM period given to HAL during association may not be valid, if
+        * association is based on ProbeRsp instead of beacon. */
+       u8 dtim_period;
+
+       /* For CCX and 11R Roaming */
+       u32 rssi_filter_period;
+
+       u32 num_beacon_per_rssi_average;
+       u8 rssi_filter_enable;
+} __packed;
+
+struct wcn36xx_hal_exit_bmps_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 send_data_null;
+       u8 bss_index;
+} __packed;
+
+struct wcn36xx_hal_missed_beacon_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bss_index;
+} __packed;
+
+/* Beacon Filtering data structures */
+
+/* The above structure would be followed by multiple of below mentioned
+ * structure
+ */
+struct beacon_filter_ie {
+       u8 element_id;
+       u8 check_ie_presence;
+       u8 offset;
+       u8 value;
+       u8 bitmask;
+       u8 ref;
+};
+
+struct wcn36xx_hal_add_bcn_filter_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u16 capability_info;
+       u16 capability_mask;
+       u16 beacon_interval;
+       u16 ie_num;
+       u8 bss_index;
+       u8 reserved;
+};
+
+struct wcn36xx_hal_rem_bcn_filter_req {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 ie_Count;
+       u8 rem_ie_id[1];
+};
+
+#define WCN36XX_HAL_IPV4_ARP_REPLY_OFFLOAD                  0
+#define WCN36XX_HAL_IPV6_NEIGHBOR_DISCOVERY_OFFLOAD         1
+#define WCN36XX_HAL_IPV6_NS_OFFLOAD                         2
+#define WCN36XX_HAL_IPV6_ADDR_LEN                           16
+#define WCN36XX_HAL_OFFLOAD_DISABLE                         0
+#define WCN36XX_HAL_OFFLOAD_ENABLE                          1
+#define WCN36XX_HAL_OFFLOAD_BCAST_FILTER_ENABLE             0x2
+#define WCN36XX_HAL_OFFLOAD_ARP_AND_BCAST_FILTER_ENABLE        \
+       (HAL_OFFLOAD_ENABLE|HAL_OFFLOAD_BCAST_FILTER_ENABLE)
+
+struct wcn36xx_hal_ns_offload_params {
+       u8 src_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN];
+       u8 self_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN];
+
+       /* Only support 2 possible Network Advertisement IPv6 address */
+       u8 target_ipv6_addr1[WCN36XX_HAL_IPV6_ADDR_LEN];
+       u8 target_ipv6_addr2[WCN36XX_HAL_IPV6_ADDR_LEN];
+
+       u8 self_addr[ETH_ALEN];
+       u8 src_ipv6_addr_valid:1;
+       u8 target_ipv6_addr1_valid:1;
+       u8 target_ipv6_addr2_valid:1;
+       u8 reserved1:5;
+
+       /* make it DWORD aligned */
+       u8 reserved2;
+
+       /* slot index for this offload */
+       u32 slot_index;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_host_offload_req {
+       u8 offload_Type;
+
+       /* enable or disable */
+       u8 enable;
+
+       union {
+               u8 host_ipv4_addr[4];
+               u8 host_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN];
+       } u;
+};
+
+struct wcn36xx_hal_host_offload_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_host_offload_req host_offload_params;
+       struct wcn36xx_hal_ns_offload_params ns_offload_params;
+};
+
+/* Packet Types. */
+#define WCN36XX_HAL_KEEP_ALIVE_NULL_PKT              1
+#define WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP     2
+
+/* Enable or disable keep alive */
+#define WCN36XX_HAL_KEEP_ALIVE_DISABLE   0
+#define WCN36XX_HAL_KEEP_ALIVE_ENABLE    1
+#define WCN36XX_KEEP_ALIVE_TIME_PERIOD  30 /* unit: s */
+
+/* Keep Alive request. */
+struct wcn36xx_hal_keep_alive_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 packet_type;
+       u32 time_period;
+       u8 host_ipv4_addr[WCN36XX_HAL_IPV4_ADDR_LEN];
+       u8 dest_ipv4_addr[WCN36XX_HAL_IPV4_ADDR_LEN];
+       u8 dest_addr[ETH_ALEN];
+       u8 bss_index;
+} __packed;
+
+struct wcn36xx_hal_rssi_threshold_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       s8 threshold1:8;
+       s8 threshold2:8;
+       s8 threshold3:8;
+       u8 thres1_pos_notify:1;
+       u8 thres1_neg_notify:1;
+       u8 thres2_pos_notify:1;
+       u8 thres2_neg_notify:1;
+       u8 thres3_pos_notify:1;
+       u8 thres3_neg_notify:1;
+       u8 reserved10:2;
+};
+
+struct wcn36xx_hal_enter_uapsd_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bk_delivery:1;
+       u8 be_delivery:1;
+       u8 vi_delivery:1;
+       u8 vo_delivery:1;
+       u8 bk_trigger:1;
+       u8 be_trigger:1;
+       u8 vi_trigger:1;
+       u8 vo_trigger:1;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_exit_uapsd_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       u8 bss_index;
+};
+
+#define WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE 128
+#define WCN36XX_HAL_WOWL_BCAST_MAX_NUM_PATTERNS 16
+
+struct wcn36xx_hal_wowl_add_bcast_ptrn_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Pattern ID */
+       u8 id;
+
+       /* Pattern byte offset from beginning of the 802.11 packet to start
+        * of the wake-up pattern */
+       u8 byte_Offset;
+
+       /* Non-Zero Pattern size */
+       u8 size;
+
+       /* Pattern */
+       u8 pattern[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE];
+
+       /* Non-zero pattern mask size */
+       u8 mask_size;
+
+       /* Pattern mask */
+       u8 mask[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE];
+
+       /* Extra pattern */
+       u8 extra[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE];
+
+       /* Extra pattern mask */
+       u8 mask_extra[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE];
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_wow_del_bcast_ptrn_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Pattern ID of the wakeup pattern to be deleted */
+       u8 id;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_wowl_enter_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Enables/disables magic packet filtering */
+       u8 magic_packet_enable;
+
+       /* Magic pattern */
+       u8 magic_pattern[ETH_ALEN];
+
+       /* Enables/disables packet pattern filtering in firmware. Enabling
+        * this flag enables broadcast pattern matching in Firmware. If
+        * unicast pattern matching is also desired,
+        * ucUcastPatternFilteringEnable flag must be set tot true as well
+        */
+       u8 pattern_filtering_enable;
+
+       /* Enables/disables unicast packet pattern filtering. This flag
+        * specifies whether we want to do pattern match on unicast packets
+        * as well and not just broadcast packets. This flag has no effect
+        * if the ucPatternFilteringEnable (main controlling flag) is set
+        * to false
+        */
+       u8 ucast_pattern_filtering_enable;
+
+       /* This configuration is valid only when magicPktEnable=1. It
+        * requests hardware to wake up when it receives the Channel Switch
+        * Action Frame.
+        */
+       u8 wow_channel_switch_receive;
+
+       /* This configuration is valid only when magicPktEnable=1. It
+        * requests hardware to wake up when it receives the
+        * Deauthentication Frame.
+        */
+       u8 wow_deauth_receive;
+
+       /* This configuration is valid only when magicPktEnable=1. It
+        * requests hardware to wake up when it receives the Disassociation
+        * Frame.
+        */
+       u8 wow_disassoc_receive;
+
+       /* This configuration is valid only when magicPktEnable=1. It
+        * requests hardware to wake up when it has missed consecutive
+        * beacons. This is a hardware register configuration (NOT a
+        * firmware configuration).
+        */
+       u8 wow_max_missed_beacons;
+
+       /* This configuration is valid only when magicPktEnable=1. This is
+        * a timeout value in units of microsec. It requests hardware to
+        * unconditionally wake up after it has stayed in WoWLAN mode for
+        * some time. Set 0 to disable this feature.
+        */
+       u8 wow_max_sleep;
+
+       /* This configuration directs the WoW packet filtering to look for
+        * EAP-ID requests embedded in EAPOL frames and use this as a wake
+        * source.
+        */
+       u8 wow_eap_id_request_enable;
+
+       /* This configuration directs the WoW packet filtering to look for
+        * EAPOL-4WAY requests and use this as a wake source.
+        */
+       u8 wow_eapol_4way_enable;
+
+       /* This configuration allows a host wakeup on an network scan
+        * offload match.
+        */
+       u8 wow_net_scan_offload_match;
+
+       /* This configuration allows a host wakeup on any GTK rekeying
+        * error.
+        */
+       u8 wow_gtk_rekey_error;
+
+       /* This configuration allows a host wakeup on BSS connection loss.
+        */
+       u8 wow_bss_connection_loss;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_wowl_exit_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_get_rssi_req_msg {
+       struct wcn36xx_hal_msg_header header;
+};
+
+struct wcn36xx_hal_get_roam_rssi_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Valid STA Idx for per STA stats request */
+       u32 sta_id;
+};
+
+struct wcn36xx_hal_set_uapsd_ac_params_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* STA index */
+       u8 sta_idx;
+
+       /* Access Category */
+       u8 ac;
+
+       /* User Priority */
+       u8 up;
+
+       /* Service Interval */
+       u32 service_interval;
+
+       /* Suspend Interval */
+       u32 suspend_interval;
+
+       /* Delay Interval */
+       u32 delay_interval;
+};
+
+struct wcn36xx_hal_configure_rxp_filter_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 set_mcst_bcst_filter_setting;
+       u8 set_mcst_bcst_filter;
+};
+
+struct wcn36xx_hal_enter_imps_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_exit_imps_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_enter_bmps_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 bss_index;
+} __packed;
+
+struct wcn36xx_hal_exit_bmps_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 bss_index;
+} __packed;
+
+struct wcn36xx_hal_enter_uapsd_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_exit_uapsd_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_rssi_notification_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u32 rssi_thres1_pos_cross:1;
+       u32 rssi_thres1_neg_cross:1;
+       u32 rssi_thres2_pos_cross:1;
+       u32 rssi_thres2_neg_cross:1;
+       u32 rssi_thres3_pos_cross:1;
+       u32 rssi_thres3_neg_cross:1;
+       u32 avg_rssi:8;
+       u32 reserved:18;
+
+};
+
+struct wcn36xx_hal_get_rssio_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+       s8 rssi;
+
+};
+
+struct wcn36xx_hal_get_roam_rssi_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 sta_id;
+       s8 rssi;
+};
+
+struct wcn36xx_hal_wowl_enter_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_wowl_exit_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_add_bcn_filter_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_rem_bcn_filter_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_add_wowl_bcast_ptrn_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_del_wowl_bcast_ptrn_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_host_offload_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_keep_alive_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_set_rssi_thresh_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_set_uapsd_ac_params_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_configure_rxp_filter_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct set_max_tx_pwr_req {
+       struct wcn36xx_hal_msg_header header;
+
+       /* BSSID is needed to identify which session issued this request.
+        * As the request has power constraints, this should be applied
+        * only to that session */
+       u8 bssid[ETH_ALEN];
+
+       u8 self_addr[ETH_ALEN];
+
+       /* In request, power == MaxTx power to be used. */
+       u8 power;
+};
+
+struct set_max_tx_pwr_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* power == tx power used for management frames */
+       u8 power;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct set_tx_pwr_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* TX Power in milli watts */
+       u32 tx_power;
+
+       u8 bss_index;
+};
+
+struct set_tx_pwr_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct get_tx_pwr_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 sta_id;
+};
+
+struct get_tx_pwr_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* TX Power in milli watts */
+       u32 tx_power;
+};
+
+struct set_p2p_gonoa_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 opp_ps;
+       u32 ct_window;
+       u8 count;
+       u32 duration;
+       u32 interval;
+       u32 single_noa_duration;
+       u8 ps_selection;
+};
+
+struct set_p2p_gonoa_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_add_sta_self_req {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 self_addr[ETH_ALEN];
+       u32 status;
+} __packed;
+
+struct wcn36xx_hal_add_sta_self_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* Self STA Index */
+       u8 self_sta_index;
+
+       /* DPU Index (IGTK, PTK, GTK all same) */
+       u8 dpu_index;
+
+       /* DPU Signature */
+       u8 dpu_signature;
+} __packed;
+
+struct wcn36xx_hal_del_sta_self_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 self_addr[ETH_ALEN];
+} __packed;
+
+struct wcn36xx_hal_del_sta_self_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /*success or failure */
+       u32 status;
+
+       u8 self_addr[ETH_ALEN];
+} __packed;
+
+struct aggr_add_ts_req {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Station Index */
+       u16 sta_idx;
+
+       /* TSPEC handler uniquely identifying a TSPEC for a STA in a BSS.
+        * This will carry the bitmap with the bit positions representing
+        * different AC.s */
+       u16 tspec_index;
+
+       /* Tspec info per AC To program TPE with required parameters */
+       struct wcn36xx_hal_tspec_ie tspec[WCN36XX_HAL_MAX_AC];
+
+       /* U-APSD Flags: 1b per AC.  Encoded as follows:
+          b7 b6 b5 b4 b3 b2 b1 b0 =
+          X  X  X  X  BE BK VI VO */
+       u8 uapsd;
+
+       /* These parameters are for all the access categories */
+
+       /* Service Interval */
+       u32 service_interval[WCN36XX_HAL_MAX_AC];
+
+       /* Suspend Interval */
+       u32 suspend_interval[WCN36XX_HAL_MAX_AC];
+
+       /* Delay Interval */
+       u32 delay_interval[WCN36XX_HAL_MAX_AC];
+};
+
+struct aggr_add_ts_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status0;
+
+       /* FIXME PRIMA for future use for 11R */
+       u32 status1;
+};
+
+struct wcn36xx_hal_configure_apps_cpu_wakeup_state_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 is_apps_cpu_awake;
+};
+
+struct wcn36xx_hal_configure_apps_cpu_wakeup_state_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_dump_cmd_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u32 arg1;
+       u32 arg2;
+       u32 arg3;
+       u32 arg4;
+       u32 arg5;
+} __packed;
+
+struct wcn36xx_hal_dump_cmd_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* Length of the responce message */
+       u32 rsp_length;
+
+       /* FIXME: Currently considering the the responce will be less than
+        * 100bytes */
+       u8 rsp_buffer[DUMPCMD_RSP_BUFFER];
+} __packed;
+
+#define WLAN_COEX_IND_DATA_SIZE (4)
+#define WLAN_COEX_IND_TYPE_DISABLE_HB_MONITOR (0)
+#define WLAN_COEX_IND_TYPE_ENABLE_HB_MONITOR (1)
+
+struct coex_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Coex Indication Type */
+       u32 type;
+
+       /* Coex Indication Data */
+       u32 data[WLAN_COEX_IND_DATA_SIZE];
+};
+
+struct wcn36xx_hal_tx_compl_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Tx Complete Indication Success or Failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_wlan_host_suspend_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u32 configured_mcst_bcst_filter_setting;
+       u32 active_session_count;
+};
+
+struct wcn36xx_hal_wlan_exclude_unencrpted_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 dot11_exclude_unencrypted;
+       u8 bssid[ETH_ALEN];
+};
+
+struct noa_attr_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 index;
+       u8 opp_ps_flag;
+       u16 ctwin;
+
+       u16 noa1_interval_count;
+       u16 bss_index;
+       u32 noa1_duration;
+       u32 noa1_interval;
+       u32 noa1_starttime;
+
+       u16 noa2_interval_count;
+       u16 reserved2;
+       u32 noa2_duration;
+       u32 noa2_interval;
+       u32 noa2_start_time;
+
+       u32 status;
+};
+
+struct noa_start_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u32 status;
+       u32 bss_index;
+};
+
+struct wcn36xx_hal_wlan_host_resume_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 configured_mcst_bcst_filter_setting;
+};
+
+struct wcn36xx_hal_host_resume_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+struct wcn36xx_hal_del_ba_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u16 sta_idx;
+
+       /* Peer MAC Address, whose BA session has timed out */
+       u8 peer_addr[ETH_ALEN];
+
+       /* TID for which a BA session timeout is being triggered */
+       u8 ba_tid;
+
+       /* DELBA direction
+        * 1 - Originator
+        * 0 - Recipient
+        */
+       u8 direction;
+
+       u32 reason_code;
+
+       /* TO SUPPORT BT-AMP */
+       u8 bssid[ETH_ALEN];
+};
+
+/* PNO Messages */
+
+/* Max number of channels that a network can be found on */
+#define WCN36XX_HAL_PNO_MAX_NETW_CHANNELS  26
+
+/* Max number of channels that a network can be found on */
+#define WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX  60
+
+/* Maximum numbers of networks supported by PNO */
+#define WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS  16
+
+/* The number of scan time intervals that can be programmed into PNO */
+#define WCN36XX_HAL_PNO_MAX_SCAN_TIMERS    10
+
+/* Maximum size of the probe template */
+#define WCN36XX_HAL_PNO_MAX_PROBE_SIZE     450
+
+/* Type of PNO enabling:
+ *
+ * Immediate - scanning will start immediately and PNO procedure will be
+ * repeated based on timer
+ *
+ * Suspend - scanning will start at suspend
+ *
+ * Resume - scanning will start on system resume
+ */
+enum pno_mode {
+       PNO_MODE_IMMEDIATE,
+       PNO_MODE_ON_SUSPEND,
+       PNO_MODE_ON_RESUME,
+       PNO_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* Authentication type */
+enum auth_type {
+       AUTH_TYPE_ANY = 0,
+       AUTH_TYPE_OPEN_SYSTEM = 1,
+
+       /* Upper layer authentication types */
+       AUTH_TYPE_WPA = 2,
+       AUTH_TYPE_WPA_PSK = 3,
+
+       AUTH_TYPE_RSN = 4,
+       AUTH_TYPE_RSN_PSK = 5,
+       AUTH_TYPE_FT_RSN = 6,
+       AUTH_TYPE_FT_RSN_PSK = 7,
+       AUTH_TYPE_WAPI_WAI_CERTIFICATE = 8,
+       AUTH_TYPE_WAPI_WAI_PSK = 9,
+
+       AUTH_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* Encryption type */
+enum ed_type {
+       ED_ANY = 0,
+       ED_NONE = 1,
+       ED_WEP = 2,
+       ED_TKIP = 3,
+       ED_CCMP = 4,
+       ED_WPI = 5,
+
+       ED_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* SSID broadcast  type */
+enum ssid_bcast_type {
+       BCAST_UNKNOWN = 0,
+       BCAST_NORMAL = 1,
+       BCAST_HIDDEN = 2,
+
+       BCAST_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
+};
+
+/* The network description for which PNO will have to look for */
+struct network_type {
+       /* SSID of the BSS */
+       struct wcn36xx_hal_mac_ssid ssid;
+
+       /* Authentication type for the network */
+       enum auth_type authentication;
+
+       /* Encryption type for the network */
+       enum ed_type encryption;
+
+       /* Indicate the channel on which the Network can be found 0 - if
+        * all channels */
+       u8 channel_count;
+       u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS];
+
+       /* Indicates the RSSI threshold for the network to be considered */
+       u8 rssi_threshold;
+};
+
+struct scan_timer {
+       /* How much it should wait */
+       u32 value;
+
+       /* How many times it should repeat that wait value 0 - keep using
+        * this timer until PNO is disabled */
+       u32 repeat;
+
+       /* e.g: 2 3 4 0 - it will wait 2s between consecutive scans for 3
+        * times - after that it will wait 4s between consecutive scans
+        * until disabled */
+};
+
+/* The network parameters to be sent to the PNO algorithm */
+struct scan_timers_type {
+       /* set to 0 if you wish for PNO to use its default telescopic timer */
+       u8 count;
+
+       /* A set value represents the amount of time that PNO will wait
+        * between two consecutive scan procedures If the desired is for a
+        * uniform timer that fires always at the exact same interval - one
+        * single value is to be set If there is a desire for a more
+        * complex - telescopic like timer multiple values can be set -
+        * once PNO reaches the end of the array it will continue scanning
+        * at intervals presented by the last value */
+       struct scan_timer values[WCN36XX_HAL_PNO_MAX_SCAN_TIMERS];
+};
+
+/* Preferred network list request */
+struct set_pref_netw_list_req {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Enable PNO */
+       u32 enable;
+
+       /* Immediate,  On Suspend,   On Resume */
+       enum pno_mode mode;
+
+       /* Number of networks sent for PNO */
+       u32 networks_count;
+
+       /* The networks that PNO needs to look for */
+       struct network_type networks[WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS];
+
+       /* The scan timers required for PNO */
+       struct scan_timers_type scan_timers;
+
+       /* Probe template for 2.4GHz band */
+       u16 band_24g_probe_size;
+       u8 band_24g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE];
+
+       /* Probe template for 5GHz band */
+       u16 band_5g_probe_size;
+       u8 band_5g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE];
+};
+
+/* The network description for which PNO will have to look for */
+struct network_type_new {
+       /* SSID of the BSS */
+       struct wcn36xx_hal_mac_ssid ssid;
+
+       /* Authentication type for the network */
+       enum auth_type authentication;
+
+       /* Encryption type for the network */
+       enum ed_type encryption;
+
+       /* SSID broadcast type, normal, hidden or unknown */
+       enum ssid_bcast_type bcast_network_type;
+
+       /* Indicate the channel on which the Network can be found 0 - if
+        * all channels */
+       u8 channel_count;
+       u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS];
+
+       /* Indicates the RSSI threshold for the network to be considered */
+       u8 rssi_threshold;
+};
+
+/* Preferred network list request new */
+struct set_pref_netw_list_req_new {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Enable PNO */
+       u32 enable;
+
+       /* Immediate,  On Suspend,   On Resume */
+       enum pno_mode mode;
+
+       /* Number of networks sent for PNO */
+       u32 networks_count;
+
+       /* The networks that PNO needs to look for */
+       struct network_type_new networks[WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS];
+
+       /* The scan timers required for PNO */
+       struct scan_timers_type scan_timers;
+
+       /* Probe template for 2.4GHz band */
+       u16 band_24g_probe_size;
+       u8 band_24g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE];
+
+       /* Probe template for 5GHz band */
+       u16 band_5g_probe_size;
+       u8 band_5g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE];
+};
+
+/* Preferred network list response */
+struct set_pref_netw_list_resp {
+       struct wcn36xx_hal_msg_header header;
+
+       /* status of the request - just to indicate that PNO has
+        * acknowledged the request and will start scanning */
+       u32 status;
+};
+
+/* Preferred network found indication */
+struct pref_netw_found_ind {
+
+       struct wcn36xx_hal_msg_header header;
+
+       /* Network that was found with the highest RSSI */
+       struct wcn36xx_hal_mac_ssid ssid;
+
+       /* Indicates the RSSI */
+       u8 rssi;
+};
+
+/* RSSI Filter request */
+struct set_rssi_filter_req {
+       struct wcn36xx_hal_msg_header header;
+
+       /* RSSI Threshold */
+       u8 rssi_threshold;
+};
+
+/* Set RSSI filter resp */
+struct set_rssi_filter_resp {
+       struct wcn36xx_hal_msg_header header;
+
+       /* status of the request */
+       u32 status;
+};
+
+/* Update scan params - sent from host to PNO to be used during PNO
+ * scanningx */
+struct wcn36xx_hal_update_scan_params_req {
+
+       struct wcn36xx_hal_msg_header header;
+
+       /* Host setting for 11d */
+       u8 dot11d_enabled;
+
+       /* Lets PNO know that host has determined the regulatory domain */
+       u8 dot11d_resolved;
+
+       /* Channels on which PNO is allowed to scan */
+       u8 channel_count;
+       u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS];
+
+       /* Minimum channel time */
+       u16 active_min_ch_time;
+
+       /* Maximum channel time */
+       u16 active_max_ch_time;
+
+       /* Minimum channel time */
+       u16 passive_min_ch_time;
+
+       /* Maximum channel time */
+       u16 passive_max_ch_time;
+
+       /* Cb State */
+       enum phy_chan_bond_state state;
+} __packed;
+
+/* Update scan params - sent from host to PNO to be used during PNO
+ * scanningx */
+struct update_scan_params_req_ex {
+
+       struct wcn36xx_hal_msg_header header;
+
+       /* Host setting for 11d */
+       u8 dot11d_enabled;
+
+       /* Lets PNO know that host has determined the regulatory domain */
+       u8 dot11d_resolved;
+
+       /* Channels on which PNO is allowed to scan */
+       u8 channel_count;
+       u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX];
+
+       /* Minimum channel time */
+       u16 active_min_ch_time;
+
+       /* Maximum channel time */
+       u16 active_max_ch_time;
+
+       /* Minimum channel time */
+       u16 passive_min_ch_time;
+
+       /* Maximum channel time */
+       u16 passive_max_ch_time;
+
+       /* Cb State */
+       enum phy_chan_bond_state state;
+};
+
+/* Update scan params - sent from host to PNO to be used during PNO
+ * scanningx */
+struct wcn36xx_hal_update_scan_params_resp {
+
+       struct wcn36xx_hal_msg_header header;
+
+       /* status of the request */
+       u32 status;
+} __packed;
+
+struct wcn36xx_hal_set_tx_per_tracking_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* 0: disable, 1:enable */
+       u8 tx_per_tracking_enable;
+
+       /* Check period, unit is sec. */
+       u8 tx_per_tracking_period;
+
+       /* (Fail TX packet)/(Total TX packet) ratio, the unit is 10%. */
+       u8 tx_per_tracking_ratio;
+
+       /* A watermark of check number, once the tx packet exceed this
+        * number, we do the check, default is 5 */
+       u32 tx_per_tracking_watermark;
+};
+
+struct wcn36xx_hal_set_tx_per_tracking_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+};
+
+struct tx_per_hit_ind_msg {
+       struct wcn36xx_hal_msg_header header;
+};
+
+/* Packet Filtering Definitions Begin */
+#define    WCN36XX_HAL_PROTOCOL_DATA_LEN                  8
+#define    WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS        240
+#define    WCN36XX_HAL_MAX_NUM_FILTERS                   20
+#define    WCN36XX_HAL_MAX_CMP_PER_FILTER                10
+
+enum wcn36xx_hal_receive_packet_filter_type {
+       HAL_RCV_FILTER_TYPE_INVALID,
+       HAL_RCV_FILTER_TYPE_FILTER_PKT,
+       HAL_RCV_FILTER_TYPE_BUFFER_PKT,
+       HAL_RCV_FILTER_TYPE_MAX_ENUM_SIZE
+};
+
+enum wcn36xx_hal_rcv_pkt_flt_protocol_type {
+       HAL_FILTER_PROTO_TYPE_INVALID,
+       HAL_FILTER_PROTO_TYPE_MAC,
+       HAL_FILTER_PROTO_TYPE_ARP,
+       HAL_FILTER_PROTO_TYPE_IPV4,
+       HAL_FILTER_PROTO_TYPE_IPV6,
+       HAL_FILTER_PROTO_TYPE_UDP,
+       HAL_FILTER_PROTO_TYPE_MAX
+};
+
+enum wcn36xx_hal_rcv_pkt_flt_cmp_flag_type {
+       HAL_FILTER_CMP_TYPE_INVALID,
+       HAL_FILTER_CMP_TYPE_EQUAL,
+       HAL_FILTER_CMP_TYPE_MASK_EQUAL,
+       HAL_FILTER_CMP_TYPE_NOT_EQUAL,
+       HAL_FILTER_CMP_TYPE_MAX
+};
+
+struct wcn36xx_hal_rcv_pkt_filter_params {
+       u8 protocol_layer;
+       u8 cmp_flag;
+
+       /* Length of the data to compare */
+       u16 data_length;
+
+       /* from start of the respective frame header */
+       u8 data_offset;
+
+       /* Reserved field */
+       u8 reserved;
+
+       /* Data to compare */
+       u8 compare_data[WCN36XX_HAL_PROTOCOL_DATA_LEN];
+
+       /* Mask to be applied on the received packet data before compare */
+       u8 data_mask[WCN36XX_HAL_PROTOCOL_DATA_LEN];
+};
+
+struct wcn36xx_hal_sessionized_rcv_pkt_filter_cfg_type {
+       u8 id;
+       u8 type;
+       u8 params_count;
+       u32 coleasce_time;
+       u8 bss_index;
+       struct wcn36xx_hal_rcv_pkt_filter_params params[1];
+};
+
+struct wcn36xx_hal_set_rcv_pkt_filter_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 id;
+       u8 type;
+       u8 params_count;
+       u32 coalesce_time;
+       struct wcn36xx_hal_rcv_pkt_filter_params params[1];
+};
+
+struct wcn36xx_hal_rcv_flt_mc_addr_list_type {
+       /* from start of the respective frame header */
+       u8 data_offset;
+
+       u32 mc_addr_count;
+       u8 mc_addr[ETH_ALEN][WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS];
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_set_pkt_filter_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_match_cnt_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_match_cnt {
+       u8 id;
+       u32 match_cnt;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_match_cnt_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Success or Failure */
+       u32 status;
+
+       u32 match_count;
+       struct wcn36xx_hal_rcv_flt_pkt_match_cnt
+               matches[WCN36XX_HAL_MAX_NUM_FILTERS];
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_clear_param {
+       /* only valid for response message */
+       u32 status;
+       u8 id;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_clear_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_rcv_flt_pkt_clear_param param;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_clear_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_rcv_flt_pkt_clear_param param;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_set_mc_list_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       struct wcn36xx_hal_rcv_flt_mc_addr_list_type mc_addr_list;
+};
+
+struct wcn36xx_hal_rcv_flt_pkt_set_mc_list_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+       u32 status;
+       u8 bss_index;
+};
+
+/* Packet Filtering Definitions End */
+
+struct wcn36xx_hal_set_power_params_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /*  Ignore DTIM */
+       u32 ignore_dtim;
+
+       /* DTIM Period */
+       u32 dtim_period;
+
+       /* Listen Interval */
+       u32 listen_interval;
+
+       /* Broadcast Multicast Filter  */
+       u32 bcast_mcast_filter;
+
+       /* Beacon Early Termination */
+       u32 enable_bet;
+
+       /* Beacon Early Termination Interval */
+       u32 bet_interval;
+} __packed;
+
+struct wcn36xx_hal_set_power_params_resp {
+
+       struct wcn36xx_hal_msg_header header;
+
+       /* status of the request */
+       u32 status;
+} __packed;
+
+/* Capability bitmap exchange definitions and macros starts */
+
+enum place_holder_in_cap_bitmap {
+       MCC = 0,
+       P2P = 1,
+       DOT11AC = 2,
+       SLM_SESSIONIZATION = 3,
+       DOT11AC_OPMODE = 4,
+       SAP32STA = 5,
+       TDLS = 6,
+       P2P_GO_NOA_DECOUPLE_INIT_SCAN = 7,
+       WLANACTIVE_OFFLOAD = 8,
+       BEACON_OFFLOAD = 9,
+       SCAN_OFFLOAD = 10,
+       ROAM_OFFLOAD = 11,
+       BCN_MISS_OFFLOAD = 12,
+       STA_POWERSAVE = 13,
+       STA_ADVANCED_PWRSAVE = 14,
+       AP_UAPSD = 15,
+       AP_DFS = 16,
+       BLOCKACK = 17,
+       PHY_ERR = 18,
+       BCN_FILTER = 19,
+       RTT = 20,
+       RATECTRL = 21,
+       WOW = 22,
+       MAX_FEATURE_SUPPORTED = 128,
+};
+
+struct wcn36xx_hal_feat_caps_msg {
+
+       struct wcn36xx_hal_msg_header header;
+
+       u32 feat_caps[4];
+} __packed;
+
+/* status codes to help debug rekey failures */
+enum gtk_rekey_status {
+       WCN36XX_HAL_GTK_REKEY_STATUS_SUCCESS = 0,
+
+       /* rekey detected, but not handled */
+       WCN36XX_HAL_GTK_REKEY_STATUS_NOT_HANDLED = 1,
+
+       /* MIC check error on M1 */
+       WCN36XX_HAL_GTK_REKEY_STATUS_MIC_ERROR = 2,
+
+       /* decryption error on M1  */
+       WCN36XX_HAL_GTK_REKEY_STATUS_DECRYPT_ERROR = 3,
+
+       /* M1 replay detected */
+       WCN36XX_HAL_GTK_REKEY_STATUS_REPLAY_ERROR = 4,
+
+       /* missing GTK key descriptor in M1 */
+       WCN36XX_HAL_GTK_REKEY_STATUS_MISSING_KDE = 5,
+
+       /* missing iGTK key descriptor in M1 */
+       WCN36XX_HAL_GTK_REKEY_STATUS_MISSING_IGTK_KDE = 6,
+
+       /* key installation error */
+       WCN36XX_HAL_GTK_REKEY_STATUS_INSTALL_ERROR = 7,
+
+       /* iGTK key installation error */
+       WCN36XX_HAL_GTK_REKEY_STATUS_IGTK_INSTALL_ERROR = 8,
+
+       /* GTK rekey M2 response TX error */
+       WCN36XX_HAL_GTK_REKEY_STATUS_RESP_TX_ERROR = 9,
+
+       /* non-specific general error */
+       WCN36XX_HAL_GTK_REKEY_STATUS_GEN_ERROR = 255
+};
+
+/* wake reason types */
+enum wake_reason_type {
+       WCN36XX_HAL_WAKE_REASON_NONE = 0,
+
+       /* magic packet match */
+       WCN36XX_HAL_WAKE_REASON_MAGIC_PACKET = 1,
+
+       /* host defined pattern match */
+       WCN36XX_HAL_WAKE_REASON_PATTERN_MATCH = 2,
+
+       /* EAP-ID frame detected */
+       WCN36XX_HAL_WAKE_REASON_EAPID_PACKET = 3,
+
+       /* start of EAPOL 4-way handshake detected */
+       WCN36XX_HAL_WAKE_REASON_EAPOL4WAY_PACKET = 4,
+
+       /* network scan offload match */
+       WCN36XX_HAL_WAKE_REASON_NETSCAN_OFFL_MATCH = 5,
+
+       /* GTK rekey status wakeup (see status) */
+       WCN36XX_HAL_WAKE_REASON_GTK_REKEY_STATUS = 6,
+
+       /* BSS connection lost */
+       WCN36XX_HAL_WAKE_REASON_BSS_CONN_LOST = 7,
+};
+
+/*
+  Wake Packet which is saved at tWakeReasonParams.DataStart
+  This data is sent for any wake reasons that involve a packet-based wakeup :
+
+  WCN36XX_HAL_WAKE_REASON_TYPE_MAGIC_PACKET
+  WCN36XX_HAL_WAKE_REASON_TYPE_PATTERN_MATCH
+  WCN36XX_HAL_WAKE_REASON_TYPE_EAPID_PACKET
+  WCN36XX_HAL_WAKE_REASON_TYPE_EAPOL4WAY_PACKET
+  WCN36XX_HAL_WAKE_REASON_TYPE_GTK_REKEY_STATUS
+
+  The information is provided to the host for auditing and debug purposes
+
+*/
+
+/* Wake reason indication */
+struct wcn36xx_hal_wake_reason_ind {
+       struct wcn36xx_hal_msg_header header;
+
+       /* see tWakeReasonType */
+       u32 reason;
+
+       /* argument specific to the reason type */
+       u32 reason_arg;
+
+       /* length of optional data stored in this message, in case HAL
+        * truncates the data (i.e. data packets) this length will be less
+        * than the actual length */
+       u32 stored_data_len;
+
+       /* actual length of data */
+       u32 actual_data_len;
+
+       /* variable length start of data (length == storedDataLen) see
+        * specific wake type */
+       u8 data_start[1];
+
+       u32 bss_index:8;
+       u32 reserved:24;
+};
+
+#define WCN36XX_HAL_GTK_KEK_BYTES 16
+#define WCN36XX_HAL_GTK_KCK_BYTES 16
+
+#define WCN36XX_HAL_GTK_OFFLOAD_FLAGS_DISABLE (1 << 0)
+
+#define GTK_SET_BSS_KEY_TAG  0x1234AA55
+
+struct wcn36xx_hal_gtk_offload_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* optional flags */
+       u32 flags;
+
+       /* Key confirmation key */
+       u8 kck[WCN36XX_HAL_GTK_KCK_BYTES];
+
+       /* key encryption key */
+       u8 kek[WCN36XX_HAL_GTK_KEK_BYTES];
+
+       /* replay counter */
+       u64 key_replay_counter;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_gtk_offload_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_gtk_offload_get_info_req_msg {
+       struct wcn36xx_hal_msg_header header;
+       u8 bss_index;
+};
+
+struct wcn36xx_hal_gtk_offload_get_info_rsp_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+
+       /* last rekey status when the rekey was offloaded */
+       u32 last_rekey_status;
+
+       /* current replay counter value */
+       u64 key_replay_counter;
+
+       /* total rekey attempts */
+       u32 total_rekey_count;
+
+       /* successful GTK rekeys */
+       u32 gtk_rekey_count;
+
+       /* successful iGTK rekeys */
+       u32 igtk_rekey_count;
+
+       u8 bss_index;
+};
+
+struct dhcp_info {
+       /* Indicates the device mode which indicates about the DHCP activity */
+       u8 device_mode;
+
+       u8 addr[ETH_ALEN];
+};
+
+struct dhcp_ind_status {
+       struct wcn36xx_hal_msg_header header;
+
+       /* success or failure */
+       u32 status;
+};
+
+/*
+ *   Thermal Mitigation mode of operation.
+ *
+ *  WCN36XX_HAL_THERMAL_MITIGATION_MODE_0 - Based on AMPDU disabling aggregation
+ *
+ *  WCN36XX_HAL_THERMAL_MITIGATION_MODE_1 - Based on AMPDU disabling aggregation
+ *  and reducing transmit power
+ *
+ *  WCN36XX_HAL_THERMAL_MITIGATION_MODE_2 - Not supported */
+enum wcn36xx_hal_thermal_mitigation_mode_type {
+       HAL_THERMAL_MITIGATION_MODE_INVALID = -1,
+       HAL_THERMAL_MITIGATION_MODE_0,
+       HAL_THERMAL_MITIGATION_MODE_1,
+       HAL_THERMAL_MITIGATION_MODE_2,
+       HAL_THERMAL_MITIGATION_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE,
+};
+
+
+/*
+ *   Thermal Mitigation level.
+ * Note the levels are incremental i.e WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_2 =
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_0 +
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_1
+ *
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_0 - lowest level of thermal mitigation.
+ * This level indicates normal mode of operation
+ *
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_1 - 1st level of thermal mitigation
+ *
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_2 - 2nd level of thermal mitigation
+ *
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_3 - 3rd level of thermal mitigation
+ *
+ * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_4 - 4th level of thermal mitigation
+ */
+enum wcn36xx_hal_thermal_mitigation_level_type {
+       HAL_THERMAL_MITIGATION_LEVEL_INVALID = -1,
+       HAL_THERMAL_MITIGATION_LEVEL_0,
+       HAL_THERMAL_MITIGATION_LEVEL_1,
+       HAL_THERMAL_MITIGATION_LEVEL_2,
+       HAL_THERMAL_MITIGATION_LEVEL_3,
+       HAL_THERMAL_MITIGATION_LEVEL_4,
+       HAL_THERMAL_MITIGATION_LEVEL_MAX = WCN36XX_HAL_MAX_ENUM_SIZE,
+};
+
+
+/* WCN36XX_HAL_SET_THERMAL_MITIGATION_REQ */
+struct set_thermal_mitigation_req_msg {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Thermal Mitigation Operation Mode */
+       enum wcn36xx_hal_thermal_mitigation_mode_type mode;
+
+       /* Thermal Mitigation Level */
+       enum wcn36xx_hal_thermal_mitigation_level_type level;
+};
+
+struct set_thermal_mitigation_resp {
+
+       struct wcn36xx_hal_msg_header header;
+
+       /* status of the request */
+       u32 status;
+};
+
+/* Per STA Class B Statistics. Class B statistics are STA TX/RX stats
+ * provided to FW from Host via periodic messages */
+struct stats_class_b_ind {
+       struct wcn36xx_hal_msg_header header;
+
+       /* Duration over which this stats was collected */
+       u32 duration;
+
+       /* Per STA Stats */
+
+       /* TX stats */
+       u32 tx_bytes_pushed;
+       u32 tx_packets_pushed;
+
+       /* RX stats */
+       u32 rx_bytes_rcvd;
+       u32 rx_packets_rcvd;
+       u32 rx_time_total;
+};
+
+#endif /* _HAL_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
new file mode 100644 (file)
index 0000000..7839b31
--- /dev/null
@@ -0,0 +1,1036 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "wcn36xx.h"
+
+unsigned int wcn36xx_dbg_mask;
+module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+
+#define CHAN2G(_freq, _idx) { \
+       .band = IEEE80211_BAND_2GHZ, \
+       .center_freq = (_freq), \
+       .hw_value = (_idx), \
+       .max_power = 25, \
+}
+
+#define CHAN5G(_freq, _idx) { \
+       .band = IEEE80211_BAND_5GHZ, \
+       .center_freq = (_freq), \
+       .hw_value = (_idx), \
+       .max_power = 25, \
+}
+
+/* The wcn firmware expects channel values to matching
+ * their mnemonic values. So use these for .hw_value. */
+static struct ieee80211_channel wcn_2ghz_channels[] = {
+       CHAN2G(2412, 1), /* Channel 1 */
+       CHAN2G(2417, 2), /* Channel 2 */
+       CHAN2G(2422, 3), /* Channel 3 */
+       CHAN2G(2427, 4), /* Channel 4 */
+       CHAN2G(2432, 5), /* Channel 5 */
+       CHAN2G(2437, 6), /* Channel 6 */
+       CHAN2G(2442, 7), /* Channel 7 */
+       CHAN2G(2447, 8), /* Channel 8 */
+       CHAN2G(2452, 9), /* Channel 9 */
+       CHAN2G(2457, 10), /* Channel 10 */
+       CHAN2G(2462, 11), /* Channel 11 */
+       CHAN2G(2467, 12), /* Channel 12 */
+       CHAN2G(2472, 13), /* Channel 13 */
+       CHAN2G(2484, 14)  /* Channel 14 */
+
+};
+
+static struct ieee80211_channel wcn_5ghz_channels[] = {
+       CHAN5G(5180, 36),
+       CHAN5G(5200, 40),
+       CHAN5G(5220, 44),
+       CHAN5G(5240, 48),
+       CHAN5G(5260, 52),
+       CHAN5G(5280, 56),
+       CHAN5G(5300, 60),
+       CHAN5G(5320, 64),
+       CHAN5G(5500, 100),
+       CHAN5G(5520, 104),
+       CHAN5G(5540, 108),
+       CHAN5G(5560, 112),
+       CHAN5G(5580, 116),
+       CHAN5G(5600, 120),
+       CHAN5G(5620, 124),
+       CHAN5G(5640, 128),
+       CHAN5G(5660, 132),
+       CHAN5G(5700, 140),
+       CHAN5G(5745, 149),
+       CHAN5G(5765, 153),
+       CHAN5G(5785, 157),
+       CHAN5G(5805, 161),
+       CHAN5G(5825, 165)
+};
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+       .bitrate        = (_bitrate),                   \
+       .flags          = (_flags),                     \
+       .hw_value       = (_hw_rate),                   \
+       .hw_value_short = (_hw_rate)  \
+}
+
+static struct ieee80211_rate wcn_2ghz_rates[] = {
+       RATE(10, HW_RATE_INDEX_1MBPS, 0),
+       RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(60, HW_RATE_INDEX_6MBPS, 0),
+       RATE(90, HW_RATE_INDEX_9MBPS, 0),
+       RATE(120, HW_RATE_INDEX_12MBPS, 0),
+       RATE(180, HW_RATE_INDEX_18MBPS, 0),
+       RATE(240, HW_RATE_INDEX_24MBPS, 0),
+       RATE(360, HW_RATE_INDEX_36MBPS, 0),
+       RATE(480, HW_RATE_INDEX_48MBPS, 0),
+       RATE(540, HW_RATE_INDEX_54MBPS, 0)
+};
+
+static struct ieee80211_rate wcn_5ghz_rates[] = {
+       RATE(60, HW_RATE_INDEX_6MBPS, 0),
+       RATE(90, HW_RATE_INDEX_9MBPS, 0),
+       RATE(120, HW_RATE_INDEX_12MBPS, 0),
+       RATE(180, HW_RATE_INDEX_18MBPS, 0),
+       RATE(240, HW_RATE_INDEX_24MBPS, 0),
+       RATE(360, HW_RATE_INDEX_36MBPS, 0),
+       RATE(480, HW_RATE_INDEX_48MBPS, 0),
+       RATE(540, HW_RATE_INDEX_54MBPS, 0)
+};
+
+static struct ieee80211_supported_band wcn_band_2ghz = {
+       .channels       = wcn_2ghz_channels,
+       .n_channels     = ARRAY_SIZE(wcn_2ghz_channels),
+       .bitrates       = wcn_2ghz_rates,
+       .n_bitrates     = ARRAY_SIZE(wcn_2ghz_rates),
+       .ht_cap         = {
+               .cap =  IEEE80211_HT_CAP_GRN_FLD |
+                       IEEE80211_HT_CAP_SGI_20 |
+                       IEEE80211_HT_CAP_DSSSCCK40 |
+                       IEEE80211_HT_CAP_LSIG_TXOP_PROT,
+               .ht_supported = true,
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+               .mcs = {
+                       .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+                       .rx_highest = cpu_to_le16(72),
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               }
+       }
+};
+
+static struct ieee80211_supported_band wcn_band_5ghz = {
+       .channels       = wcn_5ghz_channels,
+       .n_channels     = ARRAY_SIZE(wcn_5ghz_channels),
+       .bitrates       = wcn_5ghz_rates,
+       .n_bitrates     = ARRAY_SIZE(wcn_5ghz_rates),
+       .ht_cap         = {
+               .cap =  IEEE80211_HT_CAP_GRN_FLD |
+                       IEEE80211_HT_CAP_SGI_20 |
+                       IEEE80211_HT_CAP_DSSSCCK40 |
+                       IEEE80211_HT_CAP_LSIG_TXOP_PROT |
+                       IEEE80211_HT_CAP_SGI_40 |
+                       IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+               .ht_supported = true,
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+               .mcs = {
+                       .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+                       .rx_highest = cpu_to_le16(72),
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               }
+       }
+};
+
+#ifdef CONFIG_PM
+
+static const struct wiphy_wowlan_support wowlan_support = {
+       .flags = WIPHY_WOWLAN_ANY
+};
+
+#endif
+
+static inline u8 get_sta_index(struct ieee80211_vif *vif,
+                              struct wcn36xx_sta *sta_priv)
+{
+       return NL80211_IFTYPE_STATION == vif->type ?
+              sta_priv->bss_sta_index :
+              sta_priv->sta_index;
+}
+
+static int wcn36xx_start(struct ieee80211_hw *hw)
+{
+       struct wcn36xx *wcn = hw->priv;
+       int ret;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n");
+
+       /* SMD initialization */
+       ret = wcn36xx_smd_open(wcn);
+       if (ret) {
+               wcn36xx_err("Failed to open smd channel: %d\n", ret);
+               goto out_err;
+       }
+
+       /* Allocate memory pools for Mgmt BD headers and Data BD headers */
+       ret = wcn36xx_dxe_allocate_mem_pools(wcn);
+       if (ret) {
+               wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret);
+               goto out_smd_close;
+       }
+
+       ret = wcn36xx_dxe_alloc_ctl_blks(wcn);
+       if (ret) {
+               wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret);
+               goto out_free_dxe_pool;
+       }
+
+       wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL);
+       if (!wcn->hal_buf) {
+               wcn36xx_err("Failed to allocate smd buf\n");
+               ret = -ENOMEM;
+               goto out_free_dxe_ctl;
+       }
+
+       ret = wcn36xx_smd_load_nv(wcn);
+       if (ret) {
+               wcn36xx_err("Failed to push NV to chip\n");
+               goto out_free_smd_buf;
+       }
+
+       ret = wcn36xx_smd_start(wcn);
+       if (ret) {
+               wcn36xx_err("Failed to start chip\n");
+               goto out_free_smd_buf;
+       }
+
+       /* DMA channel initialization */
+       ret = wcn36xx_dxe_init(wcn);
+       if (ret) {
+               wcn36xx_err("DXE init failed\n");
+               goto out_smd_stop;
+       }
+
+       wcn36xx_debugfs_init(wcn);
+
+       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+               ret = wcn36xx_smd_feature_caps_exchange(wcn);
+               if (ret)
+                       wcn36xx_warn("Exchange feature caps failed\n");
+       }
+       INIT_LIST_HEAD(&wcn->vif_list);
+       return 0;
+
+out_smd_stop:
+       wcn36xx_smd_stop(wcn);
+out_free_smd_buf:
+       kfree(wcn->hal_buf);
+out_free_dxe_pool:
+       wcn36xx_dxe_free_mem_pools(wcn);
+out_free_dxe_ctl:
+       wcn36xx_dxe_free_ctl_blks(wcn);
+out_smd_close:
+       wcn36xx_smd_close(wcn);
+out_err:
+       return ret;
+}
+
+static void wcn36xx_stop(struct ieee80211_hw *hw)
+{
+       struct wcn36xx *wcn = hw->priv;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
+
+       wcn36xx_debugfs_exit(wcn);
+       wcn36xx_smd_stop(wcn);
+       wcn36xx_dxe_deinit(wcn);
+       wcn36xx_smd_close(wcn);
+
+       wcn36xx_dxe_free_mem_pools(wcn);
+       wcn36xx_dxe_free_ctl_blks(wcn);
+
+       kfree(wcn->hal_buf);
+}
+
+static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct ieee80211_vif *vif = NULL;
+       struct wcn36xx_vif *tmp;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               int ch = WCN36XX_HW_CHANNEL(wcn);
+               wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
+                           ch);
+               list_for_each_entry(tmp, &wcn->vif_list, list) {
+                       vif = container_of((void *)tmp,
+                                          struct ieee80211_vif,
+                                          drv_priv);
+                       wcn36xx_smd_switch_channel(wcn, vif, ch);
+               }
+       }
+
+       return 0;
+}
+
+#define WCN36XX_SUPPORTED_FILTERS (0)
+
+static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
+                                    unsigned int changed,
+                                    unsigned int *total, u64 multicast)
+{
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
+
+       *total &= WCN36XX_SUPPORTED_FILTERS;
+}
+
+static void wcn36xx_tx(struct ieee80211_hw *hw,
+                      struct ieee80211_tx_control *control,
+                      struct sk_buff *skb)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_sta *sta_priv = NULL;
+
+       if (control->sta)
+               sta_priv = (struct wcn36xx_sta *)control->sta->drv_priv;
+
+       if (wcn36xx_start_tx(wcn, sta_priv, skb))
+               ieee80211_free_txskb(wcn->hw, skb);
+}
+
+static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                          struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta,
+                          struct ieee80211_key_conf *key_conf)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_sta *sta_priv = vif_priv->sta;
+       int ret = 0;
+       u8 key[WLAN_MAX_KEY_LEN];
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
+                   cmd, key_conf->cipher, key_conf->keyidx,
+                   key_conf->keylen, key_conf->flags);
+       wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
+                        key_conf->key,
+                        key_conf->keylen);
+
+       switch (key_conf->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+               vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
+               break;
+       case WLAN_CIPHER_SUITE_WEP104:
+               vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP;
+               break;
+       default:
+               wcn36xx_err("Unsupported key type 0x%x\n",
+                             key_conf->cipher);
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       switch (cmd) {
+       case SET_KEY:
+               if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) {
+                       /*
+                        * Supplicant is sending key in the wrong order:
+                        * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b)
+                        * but HW expects it to be in the order as described in
+                        * IEEE 802.11 spec (see chapter 11.7) like this:
+                        * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
+                        */
+                       memcpy(key, key_conf->key, 16);
+                       memcpy(key + 16, key_conf->key + 24, 8);
+                       memcpy(key + 24, key_conf->key + 16, 8);
+               } else {
+                       memcpy(key, key_conf->key, key_conf->keylen);
+               }
+
+               if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
+                       sta_priv->is_data_encrypted = true;
+                       /* Reconfigure bss with encrypt_type */
+                       if (NL80211_IFTYPE_STATION == vif->type)
+                               wcn36xx_smd_config_bss(wcn,
+                                                      vif,
+                                                      sta,
+                                                      sta->addr,
+                                                      true);
+
+                       wcn36xx_smd_set_stakey(wcn,
+                               vif_priv->encrypt_type,
+                               key_conf->keyidx,
+                               key_conf->keylen,
+                               key,
+                               get_sta_index(vif, sta_priv));
+               } else {
+                       wcn36xx_smd_set_bsskey(wcn,
+                               vif_priv->encrypt_type,
+                               key_conf->keyidx,
+                               key_conf->keylen,
+                               key);
+                       if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
+                           (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
+                               sta_priv->is_data_encrypted = true;
+                               wcn36xx_smd_set_stakey(wcn,
+                                       vif_priv->encrypt_type,
+                                       key_conf->keyidx,
+                                       key_conf->keylen,
+                                       key,
+                                       get_sta_index(vif, sta_priv));
+                       }
+               }
+               break;
+       case DISABLE_KEY:
+               if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
+                       wcn36xx_smd_remove_bsskey(wcn,
+                               vif_priv->encrypt_type,
+                               key_conf->keyidx);
+               } else {
+                       sta_priv->is_data_encrypted = false;
+                       /* do not remove key if disassociated */
+                       if (sta_priv->aid)
+                               wcn36xx_smd_remove_stakey(wcn,
+                                       vif_priv->encrypt_type,
+                                       key_conf->keyidx,
+                                       get_sta_index(vif, sta_priv));
+               }
+               break;
+       default:
+               wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
+               ret = -EOPNOTSUPP;
+               goto out;
+               break;
+       }
+
+out:
+       return ret;
+}
+
+static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
+{
+       struct wcn36xx *wcn = hw->priv;
+
+       wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN);
+       wcn36xx_smd_start_scan(wcn);
+}
+
+static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw)
+{
+       struct wcn36xx *wcn = hw->priv;
+
+       wcn36xx_smd_end_scan(wcn);
+       wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
+}
+
+static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
+                                        enum ieee80211_band band)
+{
+       int i, size;
+       u16 *rates_table;
+       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+       u32 rates = sta->supp_rates[band];
+
+       memset(&sta_priv->supported_rates, 0,
+               sizeof(sta_priv->supported_rates));
+       sta_priv->supported_rates.op_rate_mode = STA_11n;
+
+       size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
+       rates_table = sta_priv->supported_rates.dsss_rates;
+       if (band == IEEE80211_BAND_2GHZ) {
+               for (i = 0; i < size; i++) {
+                       if (rates & 0x01) {
+                               rates_table[i] = wcn_2ghz_rates[i].hw_value;
+                               rates = rates >> 1;
+                       }
+               }
+       }
+
+       size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
+       rates_table = sta_priv->supported_rates.ofdm_rates;
+       for (i = 0; i < size; i++) {
+               if (rates & 0x01) {
+                       rates_table[i] = wcn_5ghz_rates[i].hw_value;
+                       rates = rates >> 1;
+               }
+       }
+
+       if (sta->ht_cap.ht_supported) {
+               BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
+                       sizeof(sta_priv->supported_rates.supported_mcs_set));
+               memcpy(sta_priv->supported_rates.supported_mcs_set,
+                      sta->ht_cap.mcs.rx_mask,
+                      sizeof(sta->ht_cap.mcs.rx_mask));
+       }
+}
+void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
+{
+       u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
+               HW_RATE_INDEX_6MBPS,
+               HW_RATE_INDEX_9MBPS,
+               HW_RATE_INDEX_12MBPS,
+               HW_RATE_INDEX_18MBPS,
+               HW_RATE_INDEX_24MBPS,
+               HW_RATE_INDEX_36MBPS,
+               HW_RATE_INDEX_48MBPS,
+               HW_RATE_INDEX_54MBPS
+       };
+       u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
+               HW_RATE_INDEX_1MBPS,
+               HW_RATE_INDEX_2MBPS,
+               HW_RATE_INDEX_5_5MBPS,
+               HW_RATE_INDEX_11MBPS
+       };
+
+       rates->op_rate_mode = STA_11n;
+       memcpy(rates->dsss_rates, dsss_rates,
+               sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
+       memcpy(rates->ofdm_rates, ofdm_rates,
+               sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
+       rates->supported_mcs_set[0] = 0xFF;
+}
+static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_bss_conf *bss_conf,
+                                    u32 changed)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct sk_buff *skb = NULL;
+       u16 tim_off, tim_len;
+       enum wcn36xx_hal_link_state link_state;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
+                   vif, changed);
+
+       if (changed & BSS_CHANGED_BEACON_INFO) {
+               wcn36xx_dbg(WCN36XX_DBG_MAC,
+                           "mac bss changed dtim period %d\n",
+                           bss_conf->dtim_period);
+
+               vif_priv->dtim_period = bss_conf->dtim_period;
+       }
+
+       if (changed & BSS_CHANGED_PS) {
+               wcn36xx_dbg(WCN36XX_DBG_MAC,
+                           "mac bss PS set %d\n",
+                           bss_conf->ps);
+               if (bss_conf->ps) {
+                       wcn36xx_pmc_enter_bmps_state(wcn, vif);
+               } else {
+                       wcn36xx_pmc_exit_bmps_state(wcn, vif);
+               }
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
+                           bss_conf->bssid);
+
+               if (!is_zero_ether_addr(bss_conf->bssid)) {
+                       vif_priv->is_joining = true;
+                       vif_priv->bss_index = 0xff;
+                       wcn36xx_smd_join(wcn, bss_conf->bssid,
+                                        vif->addr, WCN36XX_HW_CHANNEL(wcn));
+                       wcn36xx_smd_config_bss(wcn, vif, NULL,
+                                              bss_conf->bssid, false);
+               } else {
+                       vif_priv->is_joining = false;
+                       wcn36xx_smd_delete_bss(wcn, vif);
+               }
+       }
+
+       if (changed & BSS_CHANGED_SSID) {
+               wcn36xx_dbg(WCN36XX_DBG_MAC,
+                           "mac bss changed ssid\n");
+               wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
+                                bss_conf->ssid, bss_conf->ssid_len);
+
+               vif_priv->ssid.length = bss_conf->ssid_len;
+               memcpy(&vif_priv->ssid.ssid,
+                      bss_conf->ssid,
+                      bss_conf->ssid_len);
+       }
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               vif_priv->is_joining = false;
+               if (bss_conf->assoc) {
+                       struct ieee80211_sta *sta;
+                       struct wcn36xx_sta *sta_priv;
+
+                       wcn36xx_dbg(WCN36XX_DBG_MAC,
+                                   "mac assoc bss %pM vif %pM AID=%d\n",
+                                    bss_conf->bssid,
+                                    vif->addr,
+                                    bss_conf->aid);
+
+                       rcu_read_lock();
+                       sta = ieee80211_find_sta(vif, bss_conf->bssid);
+                       if (!sta) {
+                               wcn36xx_err("sta %pM is not found\n",
+                                             bss_conf->bssid);
+                               rcu_read_unlock();
+                               goto out;
+                       }
+                       sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+                       wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
+
+                       wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
+                               vif->addr,
+                               WCN36XX_HAL_LINK_POSTASSOC_STATE);
+                       wcn36xx_smd_config_bss(wcn, vif, sta,
+                                              bss_conf->bssid,
+                                              true);
+                       sta_priv->aid = bss_conf->aid;
+                       /*
+                        * config_sta must be called from  because this is the
+                        * place where AID is available.
+                        */
+                       wcn36xx_smd_config_sta(wcn, vif, sta);
+                       rcu_read_unlock();
+               } else {
+                       wcn36xx_dbg(WCN36XX_DBG_MAC,
+                                   "disassociated bss %pM vif %pM AID=%d\n",
+                                   bss_conf->bssid,
+                                   vif->addr,
+                                   bss_conf->aid);
+                       wcn36xx_smd_set_link_st(wcn,
+                                               bss_conf->bssid,
+                                               vif->addr,
+                                               WCN36XX_HAL_LINK_IDLE_STATE);
+               }
+       }
+
+       if (changed & BSS_CHANGED_AP_PROBE_RESP) {
+               wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
+               skb = ieee80211_proberesp_get(hw, vif);
+               if (!skb) {
+                       wcn36xx_err("failed to alloc probereq skb\n");
+                       goto out;
+               }
+
+               wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
+               dev_kfree_skb(skb);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               wcn36xx_dbg(WCN36XX_DBG_MAC,
+                           "mac bss changed beacon enabled %d\n",
+                           bss_conf->enable_beacon);
+
+               if (bss_conf->enable_beacon) {
+                       vif_priv->bss_index = 0xff;
+                       wcn36xx_smd_config_bss(wcn, vif, NULL,
+                                              vif->addr, false);
+                       skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
+                                                      &tim_len);
+                       if (!skb) {
+                               wcn36xx_err("failed to alloc beacon skb\n");
+                               goto out;
+                       }
+                       wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
+                       dev_kfree_skb(skb);
+
+                       if (vif->type == NL80211_IFTYPE_ADHOC ||
+                           vif->type == NL80211_IFTYPE_MESH_POINT)
+                               link_state = WCN36XX_HAL_LINK_IBSS_STATE;
+                       else
+                               link_state = WCN36XX_HAL_LINK_AP_STATE;
+
+                       wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
+                                               link_state);
+               } else {
+                       wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
+                                               WCN36XX_HAL_LINK_IDLE_STATE);
+                       wcn36xx_smd_delete_bss(wcn, vif);
+               }
+       }
+out:
+       return;
+}
+
+/* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
+static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct wcn36xx *wcn = hw->priv;
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
+
+       wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
+       return 0;
+}
+
+static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
+
+       list_del(&vif_priv->list);
+       wcn36xx_smd_delete_sta_self(wcn, vif->addr);
+}
+
+static int wcn36xx_add_interface(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
+                   vif, vif->type);
+
+       if (!(NL80211_IFTYPE_STATION == vif->type ||
+             NL80211_IFTYPE_AP == vif->type ||
+             NL80211_IFTYPE_ADHOC == vif->type ||
+             NL80211_IFTYPE_MESH_POINT == vif->type)) {
+               wcn36xx_warn("Unsupported interface type requested: %d\n",
+                            vif->type);
+               return -EOPNOTSUPP;
+       }
+
+       list_add(&vif_priv->list, &wcn->vif_list);
+       wcn36xx_smd_add_sta_self(wcn, vif);
+
+       return 0;
+}
+
+static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
+                   vif, sta->addr);
+
+       vif_priv->sta = sta_priv;
+       sta_priv->vif = vif_priv;
+       /*
+        * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
+        * at this stage AID is not available yet.
+        */
+       if (NL80211_IFTYPE_STATION != vif->type) {
+               wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
+               sta_priv->aid = sta->aid;
+               wcn36xx_smd_config_sta(wcn, vif, sta);
+       }
+       return 0;
+}
+
+static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_sta *sta)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
+                   vif, sta->addr, sta_priv->sta_index);
+
+       wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
+       vif_priv->sta = NULL;
+       sta_priv->vif = NULL;
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
+{
+       struct wcn36xx *wcn = hw->priv;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
+
+       flush_workqueue(wcn->hal_ind_wq);
+       wcn36xx_smd_set_power_params(wcn, true);
+       return 0;
+}
+
+static int wcn36xx_resume(struct ieee80211_hw *hw)
+{
+       struct wcn36xx *wcn = hw->priv;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
+
+       flush_workqueue(wcn->hal_ind_wq);
+       wcn36xx_smd_set_power_params(wcn, false);
+       return 0;
+}
+
+#endif
+
+static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
+                   struct ieee80211_vif *vif,
+                   enum ieee80211_ampdu_mlme_action action,
+                   struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                   u8 buf_size)
+{
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_sta *sta_priv = NULL;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
+                   action, tid);
+
+       sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+       switch (action) {
+       case IEEE80211_AMPDU_RX_START:
+               sta_priv->tid = tid;
+               wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
+                       get_sta_index(vif, sta_priv));
+               wcn36xx_smd_add_ba(wcn);
+               wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
+               ieee80211_start_tx_ba_session(sta, tid, 0);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
+               break;
+       case IEEE80211_AMPDU_TX_START:
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
+                       get_sta_index(vif, sta_priv));
+               break;
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       default:
+               wcn36xx_err("Unknown AMPDU action\n");
+       }
+
+       return 0;
+}
+
+static const struct ieee80211_ops wcn36xx_ops = {
+       .start                  = wcn36xx_start,
+       .stop                   = wcn36xx_stop,
+       .add_interface          = wcn36xx_add_interface,
+       .remove_interface       = wcn36xx_remove_interface,
+#ifdef CONFIG_PM
+       .suspend                = wcn36xx_suspend,
+       .resume                 = wcn36xx_resume,
+#endif
+       .config                 = wcn36xx_config,
+       .configure_filter       = wcn36xx_configure_filter,
+       .tx                     = wcn36xx_tx,
+       .set_key                = wcn36xx_set_key,
+       .sw_scan_start          = wcn36xx_sw_scan_start,
+       .sw_scan_complete       = wcn36xx_sw_scan_complete,
+       .bss_info_changed       = wcn36xx_bss_info_changed,
+       .set_rts_threshold      = wcn36xx_set_rts_threshold,
+       .sta_add                = wcn36xx_sta_add,
+       .sta_remove             = wcn36xx_sta_remove,
+       .ampdu_action           = wcn36xx_ampdu_action,
+};
+
+static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
+{
+       int ret = 0;
+
+       static const u32 cipher_suites[] = {
+               WLAN_CIPHER_SUITE_WEP40,
+               WLAN_CIPHER_SUITE_WEP104,
+               WLAN_CIPHER_SUITE_TKIP,
+               WLAN_CIPHER_SUITE_CCMP,
+       };
+
+       wcn->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+               IEEE80211_HW_HAS_RATE_CONTROL |
+               IEEE80211_HW_SUPPORTS_PS |
+               IEEE80211_HW_CONNECTION_MONITOR |
+               IEEE80211_HW_AMPDU_AGGREGATION |
+               IEEE80211_HW_TIMING_BEACON_ONLY;
+
+       wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_MESH_POINT);
+
+       wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz;
+       wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz;
+
+       wcn->hw->wiphy->cipher_suites = cipher_suites;
+       wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+       wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+#ifdef CONFIG_PM
+       wcn->hw->wiphy->wowlan = &wowlan_support;
+#endif
+
+       wcn->hw->max_listen_interval = 200;
+
+       wcn->hw->queues = 4;
+
+       SET_IEEE80211_DEV(wcn->hw, wcn->dev);
+
+       wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
+       wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
+
+       return ret;
+}
+
+static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
+                                         struct platform_device *pdev)
+{
+       struct resource *res;
+       /* Set TX IRQ */
+       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+                                          "wcnss_wlantx_irq");
+       if (!res) {
+               wcn36xx_err("failed to get tx_irq\n");
+               return -ENOENT;
+       }
+       wcn->tx_irq = res->start;
+
+       /* Set RX IRQ */
+       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+                                          "wcnss_wlanrx_irq");
+       if (!res) {
+               wcn36xx_err("failed to get rx_irq\n");
+               return -ENOENT;
+       }
+       wcn->rx_irq = res->start;
+
+       /* Map the memory */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                "wcnss_mmio");
+       if (!res) {
+               wcn36xx_err("failed to get mmio\n");
+               return -ENOENT;
+       }
+       wcn->mmio = ioremap(res->start, resource_size(res));
+       if (!wcn->mmio) {
+               wcn36xx_err("failed to map io memory\n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int wcn36xx_probe(struct platform_device *pdev)
+{
+       struct ieee80211_hw *hw;
+       struct wcn36xx *wcn;
+       int ret;
+       u8 addr[ETH_ALEN];
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
+
+       hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
+       if (!hw) {
+               wcn36xx_err("failed to alloc hw\n");
+               ret = -ENOMEM;
+               goto out_err;
+       }
+       platform_set_drvdata(pdev, hw);
+       wcn = hw->priv;
+       wcn->hw = hw;
+       wcn->dev = &pdev->dev;
+       wcn->ctrl_ops = pdev->dev.platform_data;
+
+       mutex_init(&wcn->hal_mutex);
+
+       if (!wcn->ctrl_ops->get_hw_mac(addr)) {
+               wcn36xx_info("mac address: %pM\n", addr);
+               SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
+       }
+
+       ret = wcn36xx_platform_get_resources(wcn, pdev);
+       if (ret)
+               goto out_wq;
+
+       wcn36xx_init_ieee80211(wcn);
+       ret = ieee80211_register_hw(wcn->hw);
+       if (ret)
+               goto out_unmap;
+
+       return 0;
+
+out_unmap:
+       iounmap(wcn->mmio);
+out_wq:
+       ieee80211_free_hw(hw);
+out_err:
+       return ret;
+}
+static int wcn36xx_remove(struct platform_device *pdev)
+{
+       struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+       struct wcn36xx *wcn = hw->priv;
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
+
+       mutex_destroy(&wcn->hal_mutex);
+
+       ieee80211_unregister_hw(hw);
+       iounmap(wcn->mmio);
+       ieee80211_free_hw(hw);
+
+       return 0;
+}
+static const struct platform_device_id wcn36xx_platform_id_table[] = {
+       {
+               .name = "wcn36xx",
+               .driver_data = 0
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table);
+
+static struct platform_driver wcn36xx_driver = {
+       .probe      = wcn36xx_probe,
+       .remove     = wcn36xx_remove,
+       .driver         = {
+               .name   = "wcn36xx",
+               .owner  = THIS_MODULE,
+       },
+       .id_table    = wcn36xx_platform_id_table,
+};
+
+static int __init wcn36xx_init(void)
+{
+       platform_driver_register(&wcn36xx_driver);
+       return 0;
+}
+module_init(wcn36xx_init);
+
+static void __exit wcn36xx_exit(void)
+{
+       platform_driver_unregister(&wcn36xx_driver);
+}
+module_exit(wcn36xx_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
+MODULE_FIRMWARE(WLAN_NV_FILE);
diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.c b/drivers/net/wireless/ath/wcn36xx/pmc.c
new file mode 100644 (file)
index 0000000..28b515c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "wcn36xx.h"
+
+int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
+                                struct ieee80211_vif *vif)
+{
+       int ret = 0;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       /* TODO: Make sure the TX chain clean */
+       ret = wcn36xx_smd_enter_bmps(wcn, vif);
+       if (!ret) {
+               wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n");
+               vif_priv->pw_state = WCN36XX_BMPS;
+       } else {
+               /*
+                * One of the reasons why HW will not enter BMPS is because
+                * driver is trying to enter bmps before first beacon was
+                * received just after auth complete
+                */
+               wcn36xx_err("Can not enter BMPS!\n");
+       }
+       return ret;
+}
+
+int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
+                               struct ieee80211_vif *vif)
+{
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+
+       if (WCN36XX_BMPS != vif_priv->pw_state) {
+               wcn36xx_err("Not in BMPS mode, no need to exit from BMPS mode!\n");
+               return -EINVAL;
+       }
+       wcn36xx_smd_exit_bmps(wcn, vif);
+       vif_priv->pw_state = WCN36XX_FULL_POWER;
+       return 0;
+}
+
+int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn,
+                                         struct ieee80211_vif *vif)
+{
+       wcn36xx_dbg(WCN36XX_DBG_PMC, "%s\n", __func__);
+       return wcn36xx_smd_keep_alive_req(wcn, vif,
+                                         WCN36XX_HAL_KEEP_ALIVE_NULL_PKT);
+}
diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.h b/drivers/net/wireless/ath/wcn36xx/pmc.h
new file mode 100644 (file)
index 0000000..f72ed68
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WCN36XX_PMC_H_
+#define _WCN36XX_PMC_H_
+
+struct wcn36xx;
+
+enum wcn36xx_power_state {
+       WCN36XX_FULL_POWER,
+       WCN36XX_BMPS
+};
+
+int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
+                                struct ieee80211_vif *vif);
+int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
+                               struct ieee80211_vif *vif);
+int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn,
+                                         struct ieee80211_vif *vif);
+#endif /* _WCN36XX_PMC_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
new file mode 100644 (file)
index 0000000..de9eb2c
--- /dev/null
@@ -0,0 +1,2129 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/etherdevice.h>
+#include <linux/firmware.h>
+#include <linux/bitops.h>
+#include "smd.h"
+
+static int put_cfg_tlv_u32(struct wcn36xx *wcn, size_t *len, u32 id, u32 value)
+{
+       struct wcn36xx_hal_cfg *entry;
+       u32 *val;
+
+       if (*len + sizeof(*entry) + sizeof(u32) >= WCN36XX_HAL_BUF_SIZE) {
+               wcn36xx_err("Not enough room for TLV entry\n");
+               return -ENOMEM;
+       }
+
+       entry = (struct wcn36xx_hal_cfg *) (wcn->hal_buf + *len);
+       entry->id = id;
+       entry->len = sizeof(u32);
+       entry->pad_bytes = 0;
+       entry->reserve = 0;
+
+       val = (u32 *) (entry + 1);
+       *val = value;
+
+       *len += sizeof(*entry) + sizeof(u32);
+
+       return 0;
+}
+
+static void wcn36xx_smd_set_bss_nw_type(struct wcn36xx *wcn,
+               struct ieee80211_sta *sta,
+               struct wcn36xx_hal_config_bss_params *bss_params)
+{
+       if (IEEE80211_BAND_5GHZ == WCN36XX_BAND(wcn))
+               bss_params->nw_type = WCN36XX_HAL_11A_NW_TYPE;
+       else if (sta && sta->ht_cap.ht_supported)
+               bss_params->nw_type = WCN36XX_HAL_11N_NW_TYPE;
+       else if (sta && (sta->supp_rates[IEEE80211_BAND_2GHZ] & 0x7f))
+               bss_params->nw_type = WCN36XX_HAL_11G_NW_TYPE;
+       else
+               bss_params->nw_type = WCN36XX_HAL_11B_NW_TYPE;
+}
+
+static inline u8 is_cap_supported(unsigned long caps, unsigned long flag)
+{
+       return caps & flag ? 1 : 0;
+}
+static void wcn36xx_smd_set_bss_ht_params(struct ieee80211_vif *vif,
+               struct ieee80211_sta *sta,
+               struct wcn36xx_hal_config_bss_params *bss_params)
+{
+       if (sta && sta->ht_cap.ht_supported) {
+               unsigned long caps = sta->ht_cap.cap;
+               bss_params->ht = sta->ht_cap.ht_supported;
+               bss_params->tx_channel_width_set = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+               bss_params->lsig_tx_op_protection_full_support =
+                       is_cap_supported(caps,
+                                        IEEE80211_HT_CAP_LSIG_TXOP_PROT);
+
+               bss_params->ht_oper_mode = vif->bss_conf.ht_operation_mode;
+               bss_params->lln_non_gf_coexist =
+                       !!(vif->bss_conf.ht_operation_mode &
+                          IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+               /* IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT */
+               bss_params->dual_cts_protection = 0;
+               /* IEEE80211_HT_OP_MODE_PROTECTION_20MHZ */
+               bss_params->ht20_coexist = 0;
+       }
+}
+
+static void wcn36xx_smd_set_sta_ht_params(struct ieee80211_sta *sta,
+               struct wcn36xx_hal_config_sta_params *sta_params)
+{
+       if (sta->ht_cap.ht_supported) {
+               unsigned long caps = sta->ht_cap.cap;
+               sta_params->ht_capable = sta->ht_cap.ht_supported;
+               sta_params->tx_channel_width_set = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+               sta_params->lsig_txop_protection = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_LSIG_TXOP_PROT);
+
+               sta_params->max_ampdu_size = sta->ht_cap.ampdu_factor;
+               sta_params->max_ampdu_density = sta->ht_cap.ampdu_density;
+               sta_params->max_amsdu_size = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_MAX_AMSDU);
+               sta_params->sgi_20Mhz = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_SGI_20);
+               sta_params->sgi_40mhz = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_SGI_40);
+               sta_params->green_field_capable = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_GRN_FLD);
+               sta_params->delayed_ba_support = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_DELAY_BA);
+               sta_params->dsss_cck_mode_40mhz = is_cap_supported(caps,
+                       IEEE80211_HT_CAP_DSSSCCK40);
+       }
+}
+
+static void wcn36xx_smd_set_sta_params(struct wcn36xx *wcn,
+               struct ieee80211_vif *vif,
+               struct ieee80211_sta *sta,
+               struct wcn36xx_hal_config_sta_params *sta_params)
+{
+       struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_sta *priv_sta = NULL;
+       if (vif->type == NL80211_IFTYPE_ADHOC ||
+           vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_MESH_POINT) {
+               sta_params->type = 1;
+               sta_params->sta_index = 0xFF;
+       } else {
+               sta_params->type = 0;
+               sta_params->sta_index = 1;
+       }
+
+       sta_params->listen_interval = WCN36XX_LISTEN_INTERVAL(wcn);
+
+       /*
+        * In STA mode ieee80211_sta contains bssid and ieee80211_vif
+        * contains our mac address. In  AP mode we are bssid so vif
+        * contains bssid and ieee80211_sta contains mac.
+        */
+       if (NL80211_IFTYPE_STATION == vif->type)
+               memcpy(&sta_params->mac, vif->addr, ETH_ALEN);
+       else
+               memcpy(&sta_params->bssid, vif->addr, ETH_ALEN);
+
+       sta_params->encrypt_type = priv_vif->encrypt_type;
+       sta_params->short_preamble_supported =
+               !(WCN36XX_FLAGS(wcn) &
+                 IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE);
+
+       sta_params->rifs_mode = 0;
+       sta_params->rmf = 0;
+       sta_params->action = 0;
+       sta_params->uapsd = 0;
+       sta_params->mimo_ps = WCN36XX_HAL_HT_MIMO_PS_STATIC;
+       sta_params->max_ampdu_duration = 0;
+       sta_params->bssid_index = priv_vif->bss_index;
+       sta_params->p2p = 0;
+
+       if (sta) {
+               priv_sta = (struct wcn36xx_sta *)sta->drv_priv;
+               if (NL80211_IFTYPE_STATION == vif->type)
+                       memcpy(&sta_params->bssid, sta->addr, ETH_ALEN);
+               else
+                       memcpy(&sta_params->mac, sta->addr, ETH_ALEN);
+               sta_params->wmm_enabled = sta->wme;
+               sta_params->max_sp_len = sta->max_sp;
+               sta_params->aid = priv_sta->aid;
+               wcn36xx_smd_set_sta_ht_params(sta, sta_params);
+               memcpy(&sta_params->supported_rates, &priv_sta->supported_rates,
+                       sizeof(priv_sta->supported_rates));
+       } else {
+               wcn36xx_set_default_rates(&sta_params->supported_rates);
+       }
+}
+
+static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
+{
+       int ret = 0;
+       wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len);
+
+       init_completion(&wcn->hal_rsp_compl);
+       ret = wcn->ctrl_ops->tx(wcn->hal_buf, len);
+       if (ret) {
+               wcn36xx_err("HAL TX failed\n");
+               goto out;
+       }
+       if (wait_for_completion_timeout(&wcn->hal_rsp_compl,
+               msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) {
+               wcn36xx_err("Timeout while waiting SMD response\n");
+               ret = -ETIME;
+               goto out;
+       }
+out:
+       return ret;
+}
+
+#define INIT_HAL_MSG(msg_body, type) \
+       do {                                                            \
+               memset(&msg_body, 0, sizeof(msg_body));                 \
+               msg_body.header.msg_type = type;                        \
+               msg_body.header.msg_version = WCN36XX_HAL_MSG_VERSION0; \
+               msg_body.header.len = sizeof(msg_body);                 \
+       } while (0)                                                     \
+
+#define PREPARE_HAL_BUF(send_buf, msg_body) \
+       do {                                                    \
+               memset(send_buf, 0, msg_body.header.len);       \
+               memcpy(send_buf, &msg_body, sizeof(msg_body));  \
+       } while (0)                                             \
+
+static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
+{
+       struct wcn36xx_fw_msg_status_rsp *rsp;
+
+       if (len < sizeof(struct wcn36xx_hal_msg_header) +
+           sizeof(struct wcn36xx_fw_msg_status_rsp))
+               return -EIO;
+
+       rsp = (struct wcn36xx_fw_msg_status_rsp *)
+               (buf + sizeof(struct wcn36xx_hal_msg_header));
+
+       if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status)
+               return rsp->status;
+
+       return 0;
+}
+
+int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
+{
+       const struct firmware *nv;
+       struct nv_data *nv_d;
+       struct wcn36xx_hal_nv_img_download_req_msg msg_body;
+       int fw_bytes_left;
+       int ret;
+       u16 fm_offset = 0;
+
+       ret = request_firmware(&nv, WLAN_NV_FILE, wcn->dev);
+       if (ret) {
+               wcn36xx_err("Failed to load nv file %s: %d\n",
+                             WLAN_NV_FILE, ret);
+               goto out_free_nv;
+       }
+
+       nv_d = (struct nv_data *)nv->data;
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_DOWNLOAD_NV_REQ);
+
+       msg_body.header.len += WCN36XX_NV_FRAGMENT_SIZE;
+
+       msg_body.frag_number = 0;
+       /* hal_buf must be protected with  mutex */
+       mutex_lock(&wcn->hal_mutex);
+
+       do {
+               fw_bytes_left = nv->size - fm_offset - 4;
+               if (fw_bytes_left > WCN36XX_NV_FRAGMENT_SIZE) {
+                       msg_body.last_fragment = 0;
+                       msg_body.nv_img_buffer_size = WCN36XX_NV_FRAGMENT_SIZE;
+               } else {
+                       msg_body.last_fragment = 1;
+                       msg_body.nv_img_buffer_size = fw_bytes_left;
+
+                       /* Do not forget update general message len */
+                       msg_body.header.len = sizeof(msg_body) + fw_bytes_left;
+
+               }
+
+               /* Add load NV request message header */
+               memcpy(wcn->hal_buf, &msg_body, sizeof(msg_body));
+
+               /* Add NV body itself */
+               memcpy(wcn->hal_buf + sizeof(msg_body),
+                      &nv_d->table + fm_offset,
+                      msg_body.nv_img_buffer_size);
+
+               ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+               if (ret)
+                       goto out_unlock;
+               ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf,
+                                                  wcn->hal_rsp_len);
+               if (ret) {
+                       wcn36xx_err("hal_load_nv response failed err=%d\n",
+                                   ret);
+                       goto out_unlock;
+               }
+               msg_body.frag_number++;
+               fm_offset += WCN36XX_NV_FRAGMENT_SIZE;
+
+       } while (msg_body.last_fragment != 1);
+
+out_unlock:
+       mutex_unlock(&wcn->hal_mutex);
+out_free_nv:
+       release_firmware(nv);
+
+       return ret;
+}
+
+static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len)
+{
+       struct wcn36xx_hal_mac_start_rsp_msg *rsp;
+
+       if (len < sizeof(*rsp))
+               return -EIO;
+
+       rsp = (struct wcn36xx_hal_mac_start_rsp_msg *)buf;
+
+       if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->start_rsp_params.status)
+               return -EIO;
+
+       memcpy(wcn->crm_version, rsp->start_rsp_params.crm_version,
+              WCN36XX_HAL_VERSION_LENGTH);
+       memcpy(wcn->wlan_version, rsp->start_rsp_params.wlan_version,
+              WCN36XX_HAL_VERSION_LENGTH);
+
+       /* null terminate the strings, just in case */
+       wcn->crm_version[WCN36XX_HAL_VERSION_LENGTH] = '\0';
+       wcn->wlan_version[WCN36XX_HAL_VERSION_LENGTH] = '\0';
+
+       wcn->fw_revision = rsp->start_rsp_params.version.revision;
+       wcn->fw_version = rsp->start_rsp_params.version.version;
+       wcn->fw_minor = rsp->start_rsp_params.version.minor;
+       wcn->fw_major = rsp->start_rsp_params.version.major;
+
+       wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n",
+                    wcn->wlan_version, wcn->crm_version);
+
+       wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n",
+                    wcn->fw_major, wcn->fw_minor,
+                    wcn->fw_version, wcn->fw_revision,
+                    rsp->start_rsp_params.stations,
+                    rsp->start_rsp_params.bssids);
+
+       return 0;
+}
+
+int wcn36xx_smd_start(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_mac_start_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_REQ);
+
+       msg_body.params.type = DRIVER_TYPE_PRODUCTION;
+       msg_body.params.len = 0;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start type %d\n",
+                   msg_body.params.type);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_start failed\n");
+               goto out;
+       }
+
+       ret = wcn36xx_smd_start_rsp(wcn, wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_start response failed err=%d\n", ret);
+               goto out;
+       }
+
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_stop(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_mac_stop_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_STOP_REQ);
+
+       msg_body.stop_req_params.reason = HAL_STOP_TYPE_RF_KILL;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_stop failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_stop response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode)
+{
+       struct wcn36xx_hal_init_scan_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_INIT_SCAN_REQ);
+
+       msg_body.mode = mode;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "hal init scan mode %d\n", msg_body.mode);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_init_scan failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_init_scan response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_start_scan(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_start_scan_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_REQ);
+
+       msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start scan channel %d\n",
+                   msg_body.scan_channel);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_start_scan failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_start_scan response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_end_scan(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_end_scan_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_END_SCAN_REQ);
+
+       msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "hal end scan channel %d\n",
+                   msg_body.scan_channel);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_end_scan failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_end_scan response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
+                           enum wcn36xx_hal_sys_mode mode)
+{
+       struct wcn36xx_hal_finish_scan_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_FINISH_SCAN_REQ);
+
+       msg_body.mode = mode;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "hal finish scan mode %d\n",
+                   msg_body.mode);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_finish_scan failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_finish_scan response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static int wcn36xx_smd_switch_channel_rsp(void *buf, size_t len)
+{
+       struct wcn36xx_hal_switch_channel_rsp_msg *rsp;
+       int ret = 0;
+
+       ret = wcn36xx_smd_rsp_status_check(buf, len);
+       if (ret)
+               return ret;
+       rsp = (struct wcn36xx_hal_switch_channel_rsp_msg *)buf;
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "channel switched to: %d, status: %d\n",
+                   rsp->channel_number, rsp->status);
+       return ret;
+}
+
+int wcn36xx_smd_switch_channel(struct wcn36xx *wcn,
+                              struct ieee80211_vif *vif, int ch)
+{
+       struct wcn36xx_hal_switch_channel_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_CH_SWITCH_REQ);
+
+       msg_body.channel_number = (u8)ch;
+       msg_body.tx_mgmt_power = 0xbf;
+       msg_body.max_tx_power = 0xbf;
+       memcpy(msg_body.self_sta_mac_addr, vif->addr, ETH_ALEN);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_switch_channel failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_switch_channel_rsp(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_switch_channel response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len)
+{
+       struct wcn36xx_hal_update_scan_params_resp *rsp;
+
+       rsp = (struct wcn36xx_hal_update_scan_params_resp *)buf;
+
+       /* Remove the PNO version bit */
+       rsp->status &= (~(WCN36XX_FW_MSG_PNO_VERSION_MASK));
+
+       if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) {
+               wcn36xx_warn("error response from update scan\n");
+               return rsp->status;
+       }
+
+       return 0;
+}
+
+int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_update_scan_params_req msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ);
+
+       msg_body.dot11d_enabled = 0;
+       msg_body.dot11d_resolved = 0;
+       msg_body.channel_count = 26;
+       msg_body.active_min_ch_time = 60;
+       msg_body.active_max_ch_time = 120;
+       msg_body.passive_min_ch_time = 60;
+       msg_body.passive_max_ch_time = 110;
+       msg_body.state = 0;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal update scan params channel_count %d\n",
+                   msg_body.channel_count);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_update_scan_params failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_update_scan_params_rsp(wcn->hal_buf,
+                                                wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_update_scan_params response failed err=%d\n",
+                           ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static int wcn36xx_smd_add_sta_self_rsp(struct wcn36xx *wcn,
+                                       struct ieee80211_vif *vif,
+                                       void *buf,
+                                       size_t len)
+{
+       struct wcn36xx_hal_add_sta_self_rsp_msg *rsp;
+       struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv;
+
+       if (len < sizeof(*rsp))
+               return -EINVAL;
+
+       rsp = (struct wcn36xx_hal_add_sta_self_rsp_msg *)buf;
+
+       if (rsp->status != WCN36XX_FW_MSG_RESULT_SUCCESS) {
+               wcn36xx_warn("hal add sta self failure: %d\n",
+                            rsp->status);
+               return rsp->status;
+       }
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal add sta self status %d self_sta_index %d dpu_index %d\n",
+                   rsp->status, rsp->self_sta_index, rsp->dpu_index);
+
+       priv_vif->self_sta_index = rsp->self_sta_index;
+       priv_vif->self_dpu_desc_index = rsp->dpu_index;
+
+       return 0;
+}
+
+int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif)
+{
+       struct wcn36xx_hal_add_sta_self_req msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_STA_SELF_REQ);
+
+       memcpy(&msg_body.self_addr, vif->addr, ETH_ALEN);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal add sta self self_addr %pM status %d\n",
+                   msg_body.self_addr, msg_body.status);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_add_sta_self failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_add_sta_self_rsp(wcn,
+                                          vif,
+                                          wcn->hal_buf,
+                                          wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_add_sta_self response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr)
+{
+       struct wcn36xx_hal_del_sta_self_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_DEL_STA_SELF_REQ);
+
+       memcpy(&msg_body.self_addr, addr, ETH_ALEN);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_delete_sta_self failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_delete_sta_self response failed err=%d\n",
+                           ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index)
+{
+       struct wcn36xx_hal_delete_sta_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_STA_REQ);
+
+       msg_body.sta_index = sta_index;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal delete sta sta_index %d\n",
+                   msg_body.sta_index);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_delete_sta failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_delete_sta response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static int wcn36xx_smd_join_rsp(void *buf, size_t len)
+{
+       struct wcn36xx_hal_join_rsp_msg *rsp;
+
+       if (wcn36xx_smd_rsp_status_check(buf, len))
+               return -EIO;
+
+       rsp = (struct wcn36xx_hal_join_rsp_msg *)buf;
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal rsp join status %d tx_mgmt_power %d\n",
+                   rsp->status, rsp->tx_mgmt_power);
+
+       return 0;
+}
+
+int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch)
+{
+       struct wcn36xx_hal_join_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_JOIN_REQ);
+
+       memcpy(&msg_body.bssid, bssid, ETH_ALEN);
+       memcpy(&msg_body.self_sta_mac_addr, vif, ETH_ALEN);
+       msg_body.channel = ch;
+
+       if (conf_is_ht40_minus(&wcn->hw->conf))
+               msg_body.secondary_channel_offset =
+                       PHY_DOUBLE_CHANNEL_HIGH_PRIMARY;
+       else if (conf_is_ht40_plus(&wcn->hw->conf))
+               msg_body.secondary_channel_offset =
+                       PHY_DOUBLE_CHANNEL_LOW_PRIMARY;
+       else
+               msg_body.secondary_channel_offset =
+                       PHY_SINGLE_CHANNEL_CENTERED;
+
+       msg_body.link_state = WCN36XX_HAL_LINK_PREASSOC_STATE;
+
+       msg_body.max_tx_power = 0xbf;
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal join req bssid %pM self_sta_mac_addr %pM channel %d link_state %d\n",
+                   msg_body.bssid, msg_body.self_sta_mac_addr,
+                   msg_body.channel, msg_body.link_state);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_join failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_join_rsp(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_join response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid,
+                           const u8 *sta_mac,
+                           enum wcn36xx_hal_link_state state)
+{
+       struct wcn36xx_hal_set_link_state_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_LINK_ST_REQ);
+
+       memcpy(&msg_body.bssid, bssid, ETH_ALEN);
+       memcpy(&msg_body.self_mac_addr, sta_mac, ETH_ALEN);
+       msg_body.state = state;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal set link state bssid %pM self_mac_addr %pM state %d\n",
+                   msg_body.bssid, msg_body.self_mac_addr, msg_body.state);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_set_link_st failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_set_link_st response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static void wcn36xx_smd_convert_sta_to_v1(struct wcn36xx *wcn,
+                       const struct wcn36xx_hal_config_sta_params *orig,
+                       struct wcn36xx_hal_config_sta_params_v1 *v1)
+{
+       /* convert orig to v1 format */
+       memcpy(&v1->bssid, orig->bssid, ETH_ALEN);
+       memcpy(&v1->mac, orig->mac, ETH_ALEN);
+       v1->aid = orig->aid;
+       v1->type = orig->type;
+       v1->listen_interval = orig->listen_interval;
+       v1->ht_capable = orig->ht_capable;
+
+       v1->max_ampdu_size = orig->max_ampdu_size;
+       v1->max_ampdu_density = orig->max_ampdu_density;
+       v1->sgi_40mhz = orig->sgi_40mhz;
+       v1->sgi_20Mhz = orig->sgi_20Mhz;
+
+       memcpy(&v1->supported_rates, &orig->supported_rates,
+              sizeof(orig->supported_rates));
+       v1->sta_index = orig->sta_index;
+}
+
+static int wcn36xx_smd_config_sta_rsp(struct wcn36xx *wcn,
+                                     struct ieee80211_sta *sta,
+                                     void *buf,
+                                     size_t len)
+{
+       struct wcn36xx_hal_config_sta_rsp_msg *rsp;
+       struct config_sta_rsp_params *params;
+       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+
+       if (len < sizeof(*rsp))
+               return -EINVAL;
+
+       rsp = (struct wcn36xx_hal_config_sta_rsp_msg *)buf;
+       params = &rsp->params;
+
+       if (params->status != WCN36XX_FW_MSG_RESULT_SUCCESS) {
+               wcn36xx_warn("hal config sta response failure: %d\n",
+                            params->status);
+               return -EIO;
+       }
+
+       sta_priv->sta_index = params->sta_index;
+       sta_priv->dpu_desc_index = params->dpu_index;
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal config sta rsp status %d sta_index %d bssid_index %d p2p %d\n",
+                   params->status, params->sta_index, params->bssid_index,
+                   params->p2p);
+
+       return 0;
+}
+
+static int wcn36xx_smd_config_sta_v1(struct wcn36xx *wcn,
+                    const struct wcn36xx_hal_config_sta_req_msg *orig)
+{
+       struct wcn36xx_hal_config_sta_req_msg_v1 msg_body;
+       struct wcn36xx_hal_config_sta_params_v1 *sta = &msg_body.sta_params;
+
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_STA_REQ);
+
+       wcn36xx_smd_convert_sta_to_v1(wcn, &orig->sta_params,
+                                     &msg_body.sta_params);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal config sta v1 action %d sta_index %d bssid_index %d bssid %pM type %d mac %pM aid %d\n",
+                   sta->action, sta->sta_index, sta->bssid_index,
+                   sta->bssid, sta->type, sta->mac, sta->aid);
+
+       return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+}
+
+int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta)
+{
+       struct wcn36xx_hal_config_sta_req_msg msg;
+       struct wcn36xx_hal_config_sta_params *sta_params;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_STA_REQ);
+
+       sta_params = &msg.sta_params;
+
+       wcn36xx_smd_set_sta_params(wcn, vif, sta, sta_params);
+
+       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+               ret = wcn36xx_smd_config_sta_v1(wcn, &msg);
+       } else {
+               PREPARE_HAL_BUF(wcn->hal_buf, msg);
+
+               wcn36xx_dbg(WCN36XX_DBG_HAL,
+                           "hal config sta action %d sta_index %d bssid_index %d bssid %pM type %d mac %pM aid %d\n",
+                           sta_params->action, sta_params->sta_index,
+                           sta_params->bssid_index, sta_params->bssid,
+                           sta_params->type, sta_params->mac, sta_params->aid);
+
+               ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len);
+       }
+       if (ret) {
+               wcn36xx_err("Sending hal_config_sta failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_config_sta_rsp(wcn,
+                                        sta,
+                                        wcn->hal_buf,
+                                        wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_config_sta response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static int wcn36xx_smd_config_bss_v1(struct wcn36xx *wcn,
+                       const struct wcn36xx_hal_config_bss_req_msg *orig)
+{
+       struct wcn36xx_hal_config_bss_req_msg_v1 msg_body;
+       struct wcn36xx_hal_config_bss_params_v1 *bss = &msg_body.bss_params;
+       struct wcn36xx_hal_config_sta_params_v1 *sta = &bss->sta;
+
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_BSS_REQ);
+
+       /* convert orig to v1 */
+       memcpy(&msg_body.bss_params.bssid,
+              &orig->bss_params.bssid, ETH_ALEN);
+       memcpy(&msg_body.bss_params.self_mac_addr,
+              &orig->bss_params.self_mac_addr, ETH_ALEN);
+
+       msg_body.bss_params.bss_type = orig->bss_params.bss_type;
+       msg_body.bss_params.oper_mode = orig->bss_params.oper_mode;
+       msg_body.bss_params.nw_type = orig->bss_params.nw_type;
+
+       msg_body.bss_params.short_slot_time_supported =
+               orig->bss_params.short_slot_time_supported;
+       msg_body.bss_params.lla_coexist = orig->bss_params.lla_coexist;
+       msg_body.bss_params.llb_coexist = orig->bss_params.llb_coexist;
+       msg_body.bss_params.llg_coexist = orig->bss_params.llg_coexist;
+       msg_body.bss_params.ht20_coexist = orig->bss_params.ht20_coexist;
+       msg_body.bss_params.lln_non_gf_coexist =
+               orig->bss_params.lln_non_gf_coexist;
+
+       msg_body.bss_params.lsig_tx_op_protection_full_support =
+               orig->bss_params.lsig_tx_op_protection_full_support;
+       msg_body.bss_params.rifs_mode = orig->bss_params.rifs_mode;
+       msg_body.bss_params.beacon_interval = orig->bss_params.beacon_interval;
+       msg_body.bss_params.dtim_period = orig->bss_params.dtim_period;
+       msg_body.bss_params.tx_channel_width_set =
+               orig->bss_params.tx_channel_width_set;
+       msg_body.bss_params.oper_channel = orig->bss_params.oper_channel;
+       msg_body.bss_params.ext_channel = orig->bss_params.ext_channel;
+
+       msg_body.bss_params.reserved = orig->bss_params.reserved;
+
+       memcpy(&msg_body.bss_params.ssid,
+              &orig->bss_params.ssid,
+              sizeof(orig->bss_params.ssid));
+
+       msg_body.bss_params.action = orig->bss_params.action;
+       msg_body.bss_params.rateset = orig->bss_params.rateset;
+       msg_body.bss_params.ht = orig->bss_params.ht;
+       msg_body.bss_params.obss_prot_enabled =
+               orig->bss_params.obss_prot_enabled;
+       msg_body.bss_params.rmf = orig->bss_params.rmf;
+       msg_body.bss_params.ht_oper_mode = orig->bss_params.ht_oper_mode;
+       msg_body.bss_params.dual_cts_protection =
+               orig->bss_params.dual_cts_protection;
+
+       msg_body.bss_params.max_probe_resp_retry_limit =
+               orig->bss_params.max_probe_resp_retry_limit;
+       msg_body.bss_params.hidden_ssid = orig->bss_params.hidden_ssid;
+       msg_body.bss_params.proxy_probe_resp =
+               orig->bss_params.proxy_probe_resp;
+       msg_body.bss_params.edca_params_valid =
+               orig->bss_params.edca_params_valid;
+
+       memcpy(&msg_body.bss_params.acbe,
+              &orig->bss_params.acbe,
+              sizeof(orig->bss_params.acbe));
+       memcpy(&msg_body.bss_params.acbk,
+              &orig->bss_params.acbk,
+              sizeof(orig->bss_params.acbk));
+       memcpy(&msg_body.bss_params.acvi,
+              &orig->bss_params.acvi,
+              sizeof(orig->bss_params.acvi));
+       memcpy(&msg_body.bss_params.acvo,
+              &orig->bss_params.acvo,
+              sizeof(orig->bss_params.acvo));
+
+       msg_body.bss_params.ext_set_sta_key_param_valid =
+               orig->bss_params.ext_set_sta_key_param_valid;
+
+       memcpy(&msg_body.bss_params.ext_set_sta_key_param,
+              &orig->bss_params.ext_set_sta_key_param,
+              sizeof(orig->bss_params.acvo));
+
+       msg_body.bss_params.wcn36xx_hal_persona =
+               orig->bss_params.wcn36xx_hal_persona;
+       msg_body.bss_params.spectrum_mgt_enable =
+               orig->bss_params.spectrum_mgt_enable;
+       msg_body.bss_params.tx_mgmt_power = orig->bss_params.tx_mgmt_power;
+       msg_body.bss_params.max_tx_power = orig->bss_params.max_tx_power;
+
+       wcn36xx_smd_convert_sta_to_v1(wcn, &orig->bss_params.sta,
+                                     &msg_body.bss_params.sta);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal config bss v1 bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n",
+                   bss->bssid, bss->self_mac_addr, bss->bss_type,
+                   bss->oper_mode, bss->nw_type);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "- sta bssid %pM action %d sta_index %d bssid_index %d aid %d type %d mac %pM\n",
+                   sta->bssid, sta->action, sta->sta_index,
+                   sta->bssid_index, sta->aid, sta->type, sta->mac);
+
+       return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+}
+
+
+static int wcn36xx_smd_config_bss_rsp(struct wcn36xx *wcn,
+                                     struct ieee80211_vif *vif,
+                                     void *buf,
+                                     size_t len)
+{
+       struct wcn36xx_hal_config_bss_rsp_msg *rsp;
+       struct wcn36xx_hal_config_bss_rsp_params *params;
+       struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv;
+
+       if (len < sizeof(*rsp))
+               return -EINVAL;
+
+       rsp = (struct wcn36xx_hal_config_bss_rsp_msg *)buf;
+       params = &rsp->bss_rsp_params;
+
+       if (params->status != WCN36XX_FW_MSG_RESULT_SUCCESS) {
+               wcn36xx_warn("hal config bss response failure: %d\n",
+                            params->status);
+               return -EIO;
+       }
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal config bss rsp status %d bss_idx %d dpu_desc_index %d"
+                   " sta_idx %d self_idx %d bcast_idx %d mac %pM"
+                   " power %d ucast_dpu_signature %d\n",
+                   params->status, params->bss_index, params->dpu_desc_index,
+                   params->bss_sta_index, params->bss_self_sta_index,
+                   params->bss_bcast_sta_idx, params->mac,
+                   params->tx_mgmt_power, params->ucast_dpu_signature);
+
+       priv_vif->bss_index = params->bss_index;
+
+       if (priv_vif->sta) {
+               priv_vif->sta->bss_sta_index =  params->bss_sta_index;
+               priv_vif->sta->bss_dpu_desc_index = params->dpu_desc_index;
+       }
+
+       priv_vif->ucast_dpu_signature = params->ucast_dpu_signature;
+
+       return 0;
+}
+
+int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta, const u8 *bssid,
+                          bool update)
+{
+       struct wcn36xx_hal_config_bss_req_msg msg;
+       struct wcn36xx_hal_config_bss_params *bss;
+       struct wcn36xx_hal_config_sta_params *sta_params;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_BSS_REQ);
+
+       bss = &msg.bss_params;
+       sta_params = &bss->sta;
+
+       WARN_ON(is_zero_ether_addr(bssid));
+
+       memcpy(&bss->bssid, bssid, ETH_ALEN);
+
+       memcpy(bss->self_mac_addr, vif->addr, ETH_ALEN);
+
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               bss->bss_type = WCN36XX_HAL_INFRASTRUCTURE_MODE;
+
+               /* STA */
+               bss->oper_mode = 1;
+               bss->wcn36xx_hal_persona = WCN36XX_HAL_STA_MODE;
+       } else if (vif->type == NL80211_IFTYPE_AP) {
+               bss->bss_type = WCN36XX_HAL_INFRA_AP_MODE;
+
+               /* AP */
+               bss->oper_mode = 0;
+               bss->wcn36xx_hal_persona = WCN36XX_HAL_STA_SAP_MODE;
+       } else if (vif->type == NL80211_IFTYPE_ADHOC ||
+                  vif->type == NL80211_IFTYPE_MESH_POINT) {
+               bss->bss_type = WCN36XX_HAL_IBSS_MODE;
+
+               /* STA */
+               bss->oper_mode = 1;
+       } else {
+               wcn36xx_warn("Unknown type for bss config: %d\n", vif->type);
+       }
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               wcn36xx_smd_set_bss_nw_type(wcn, sta, bss);
+       else
+               bss->nw_type = WCN36XX_HAL_11N_NW_TYPE;
+
+       bss->short_slot_time_supported = vif->bss_conf.use_short_slot;
+       bss->lla_coexist = 0;
+       bss->llb_coexist = 0;
+       bss->llg_coexist = 0;
+       bss->rifs_mode = 0;
+       bss->beacon_interval = vif->bss_conf.beacon_int;
+       bss->dtim_period = vif_priv->dtim_period;
+
+       wcn36xx_smd_set_bss_ht_params(vif, sta, bss);
+
+       bss->oper_channel = WCN36XX_HW_CHANNEL(wcn);
+
+       if (conf_is_ht40_minus(&wcn->hw->conf))
+               bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+       else if (conf_is_ht40_plus(&wcn->hw->conf))
+               bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+       else
+               bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+
+       bss->reserved = 0;
+       wcn36xx_smd_set_sta_params(wcn, vif, sta, sta_params);
+
+       /* wcn->ssid is only valid in AP and IBSS mode */
+       bss->ssid.length = vif_priv->ssid.length;
+       memcpy(bss->ssid.ssid, vif_priv->ssid.ssid, vif_priv->ssid.length);
+
+       bss->obss_prot_enabled = 0;
+       bss->rmf = 0;
+       bss->max_probe_resp_retry_limit = 0;
+       bss->hidden_ssid = vif->bss_conf.hidden_ssid;
+       bss->proxy_probe_resp = 0;
+       bss->edca_params_valid = 0;
+
+       /* FIXME: set acbe, acbk, acvi and acvo */
+
+       bss->ext_set_sta_key_param_valid = 0;
+
+       /* FIXME: set ext_set_sta_key_param */
+
+       bss->spectrum_mgt_enable = 0;
+       bss->tx_mgmt_power = 0;
+       bss->max_tx_power = WCN36XX_MAX_POWER(wcn);
+
+       bss->action = update;
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal config bss bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n",
+                   bss->bssid, bss->self_mac_addr, bss->bss_type,
+                   bss->oper_mode, bss->nw_type);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "- sta bssid %pM action %d sta_index %d bssid_index %d aid %d type %d mac %pM\n",
+                   sta_params->bssid, sta_params->action,
+                   sta_params->sta_index, sta_params->bssid_index,
+                   sta_params->aid, sta_params->type,
+                   sta_params->mac);
+
+       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+               ret = wcn36xx_smd_config_bss_v1(wcn, &msg);
+       } else {
+               PREPARE_HAL_BUF(wcn->hal_buf, msg);
+
+               ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len);
+       }
+       if (ret) {
+               wcn36xx_err("Sending hal_config_bss failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_config_bss_rsp(wcn,
+                                        vif,
+                                        wcn->hal_buf,
+                                        wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_config_bss response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif)
+{
+       struct wcn36xx_hal_delete_bss_req_msg msg_body;
+       struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_BSS_REQ);
+
+       msg_body.bss_index = priv_vif->bss_index;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "hal delete bss %d\n", msg_body.bss_index);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_delete_bss failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_delete_bss response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif,
+                           struct sk_buff *skb_beacon, u16 tim_off,
+                           u16 p2p_off)
+{
+       struct wcn36xx_hal_send_beacon_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_SEND_BEACON_REQ);
+
+       /* TODO need to find out why this is needed? */
+       msg_body.beacon_length = skb_beacon->len + 6;
+
+       if (BEACON_TEMPLATE_SIZE > msg_body.beacon_length) {
+               memcpy(&msg_body.beacon, &skb_beacon->len, sizeof(u32));
+               memcpy(&(msg_body.beacon[4]), skb_beacon->data,
+                      skb_beacon->len);
+       } else {
+               wcn36xx_err("Beacon is to big: beacon size=%d\n",
+                             msg_body.beacon_length);
+               ret = -ENOMEM;
+               goto out;
+       }
+       memcpy(msg_body.bssid, vif->addr, ETH_ALEN);
+
+       /* TODO need to find out why this is needed? */
+       msg_body.tim_ie_offset = tim_off+4;
+       msg_body.p2p_ie_offset = p2p_off;
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal send beacon beacon_length %d\n",
+                   msg_body.beacon_length);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_send_beacon failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_send_beacon response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn,
+                                     struct ieee80211_vif *vif,
+                                     struct sk_buff *skb)
+{
+       struct wcn36xx_hal_send_probe_resp_req_msg msg;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg, WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_REQ);
+
+       if (skb->len > BEACON_TEMPLATE_SIZE) {
+               wcn36xx_warn("probe response template is too big: %d\n",
+                            skb->len);
+               ret = -E2BIG;
+               goto out;
+       }
+
+       msg.probe_resp_template_len = skb->len;
+       memcpy(&msg.probe_resp_template, skb->data, skb->len);
+
+       memcpy(msg.bssid, vif->addr, ETH_ALEN);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg);
+
+       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                   "hal update probe rsp len %d bssid %pM\n",
+                   msg.probe_resp_template_len, msg.bssid);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_update_proberesp_tmpl failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_update_proberesp_tmpl response failed err=%d\n",
+                           ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_set_stakey(struct wcn36xx *wcn,
+                          enum ani_ed_type enc_type,
+                          u8 keyidx,
+                          u8 keylen,
+                          u8 *key,
+                          u8 sta_index)
+{
+       struct wcn36xx_hal_set_sta_key_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_STAKEY_REQ);
+
+       msg_body.set_sta_key_params.sta_index = sta_index;
+       msg_body.set_sta_key_params.enc_type = enc_type;
+
+       msg_body.set_sta_key_params.key[0].id = keyidx;
+       msg_body.set_sta_key_params.key[0].unicast = 1;
+       msg_body.set_sta_key_params.key[0].direction = WCN36XX_HAL_TX_RX;
+       msg_body.set_sta_key_params.key[0].pae_role = 0;
+       msg_body.set_sta_key_params.key[0].length = keylen;
+       memcpy(msg_body.set_sta_key_params.key[0].key, key, keylen);
+       msg_body.set_sta_key_params.single_tid_rc = 1;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_set_stakey failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_set_stakey response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
+                          enum ani_ed_type enc_type,
+                          u8 keyidx,
+                          u8 keylen,
+                          u8 *key)
+{
+       struct wcn36xx_hal_set_bss_key_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_BSSKEY_REQ);
+       msg_body.bss_idx = 0;
+       msg_body.enc_type = enc_type;
+       msg_body.num_keys = 1;
+       msg_body.keys[0].id = keyidx;
+       msg_body.keys[0].unicast = 0;
+       msg_body.keys[0].direction = WCN36XX_HAL_RX_ONLY;
+       msg_body.keys[0].pae_role = 0;
+       msg_body.keys[0].length = keylen;
+       memcpy(msg_body.keys[0].key, key, keylen);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_set_bsskey failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_set_bsskey response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn,
+                             enum ani_ed_type enc_type,
+                             u8 keyidx,
+                             u8 sta_index)
+{
+       struct wcn36xx_hal_remove_sta_key_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_STAKEY_REQ);
+
+       msg_body.sta_idx = sta_index;
+       msg_body.enc_type = enc_type;
+       msg_body.key_id = keyidx;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_remove_stakey failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_remove_stakey response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
+                             enum ani_ed_type enc_type,
+                             u8 keyidx)
+{
+       struct wcn36xx_hal_remove_bss_key_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_BSSKEY_REQ);
+       msg_body.bss_idx = 0;
+       msg_body.enc_type = enc_type;
+       msg_body.key_id = keyidx;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_remove_bsskey failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_remove_bsskey response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif)
+{
+       struct wcn36xx_hal_enter_bmps_req_msg msg_body;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_ENTER_BMPS_REQ);
+
+       msg_body.bss_index = vif_priv->bss_index;
+       msg_body.tbtt = vif->bss_conf.sync_tsf;
+       msg_body.dtim_period = vif_priv->dtim_period;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_enter_bmps failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_enter_bmps response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif)
+{
+       struct wcn36xx_hal_enter_bmps_req_msg msg_body;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_EXIT_BMPS_REQ);
+
+       msg_body.bss_index = vif_priv->bss_index;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_exit_bmps failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim)
+{
+       struct wcn36xx_hal_set_power_params_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_POWER_PARAMS_REQ);
+
+       /*
+        * When host is down ignore every second dtim
+        */
+       if (ignore_dtim) {
+               msg_body.ignore_dtim = 1;
+               msg_body.dtim_period = 2;
+       }
+       msg_body.listen_interval = WCN36XX_LISTEN_INTERVAL(wcn);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_set_power_params failed\n");
+               goto out;
+       }
+
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+/* Notice: This function should be called after associated, or else it
+ * will be invalid
+ */
+int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
+                              struct ieee80211_vif *vif,
+                              int packet_type)
+{
+       struct wcn36xx_hal_keep_alive_req_msg msg_body;
+       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_KEEP_ALIVE_REQ);
+
+       if (packet_type == WCN36XX_HAL_KEEP_ALIVE_NULL_PKT) {
+               msg_body.bss_index = vif_priv->bss_index;
+               msg_body.packet_type = WCN36XX_HAL_KEEP_ALIVE_NULL_PKT;
+               msg_body.time_period = WCN36XX_KEEP_ALIVE_TIME_PERIOD;
+       } else if (packet_type == WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP) {
+               /* TODO: it also support ARP response type */
+       } else {
+               wcn36xx_warn("unknow keep alive packet type %d\n", packet_type);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_exit_bmps failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
+                            u32 arg3, u32 arg4, u32 arg5)
+{
+       struct wcn36xx_hal_dump_cmd_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_DUMP_COMMAND_REQ);
+
+       msg_body.arg1 = arg1;
+       msg_body.arg2 = arg2;
+       msg_body.arg3 = arg3;
+       msg_body.arg4 = arg4;
+       msg_body.arg5 = arg5;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_dump_cmd failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_dump_cmd response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static inline void set_feat_caps(u32 *bitmap,
+                                enum place_holder_in_cap_bitmap cap)
+{
+       int arr_idx, bit_idx;
+
+       if (cap < 0 || cap > 127) {
+               wcn36xx_warn("error cap idx %d\n", cap);
+               return;
+       }
+
+       arr_idx = cap / 32;
+       bit_idx = cap % 32;
+       bitmap[arr_idx] |= (1 << bit_idx);
+}
+
+static inline int get_feat_caps(u32 *bitmap,
+                               enum place_holder_in_cap_bitmap cap)
+{
+       int arr_idx, bit_idx;
+       int ret = 0;
+
+       if (cap < 0 || cap > 127) {
+               wcn36xx_warn("error cap idx %d\n", cap);
+               return -EINVAL;
+       }
+
+       arr_idx = cap / 32;
+       bit_idx = cap % 32;
+       ret = (bitmap[arr_idx] & (1 << bit_idx)) ? 1 : 0;
+       return ret;
+}
+
+static inline void clear_feat_caps(u32 *bitmap,
+                               enum place_holder_in_cap_bitmap cap)
+{
+       int arr_idx, bit_idx;
+
+       if (cap < 0 || cap > 127) {
+               wcn36xx_warn("error cap idx %d\n", cap);
+               return;
+       }
+
+       arr_idx = cap / 32;
+       bit_idx = cap % 32;
+       bitmap[arr_idx] &= ~(1 << bit_idx);
+}
+
+int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_feat_caps_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ);
+
+       set_feat_caps(msg_body.feat_caps, STA_POWERSAVE);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_feature_caps_exchange failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_feature_caps_exchange response failed err=%d\n",
+                           ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
+               struct ieee80211_sta *sta,
+               u16 tid,
+               u16 *ssn,
+               u8 direction,
+               u8 sta_index)
+{
+       struct wcn36xx_hal_add_ba_session_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_SESSION_REQ);
+
+       msg_body.sta_index = sta_index;
+       memcpy(&msg_body.mac_addr, sta->addr, ETH_ALEN);
+       msg_body.dialog_token = 0x10;
+       msg_body.tid = tid;
+
+       /* Immediate BA because Delayed BA is not supported */
+       msg_body.policy = 1;
+       msg_body.buffer_size = WCN36XX_AGGR_BUFFER_SIZE;
+       msg_body.timeout = 0;
+       if (ssn)
+               msg_body.ssn = *ssn;
+       msg_body.direction = direction;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_add_ba_session failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_add_ba_session response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_add_ba(struct wcn36xx *wcn)
+{
+       struct wcn36xx_hal_add_ba_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_REQ);
+
+       msg_body.session_id = 0;
+       msg_body.win_size = WCN36XX_AGGR_BUFFER_SIZE;
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_add_ba failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_add_ba response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index)
+{
+       struct wcn36xx_hal_del_ba_req_msg msg_body;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_DEL_BA_REQ);
+
+       msg_body.sta_index = sta_index;
+       msg_body.tid = tid;
+       msg_body.direction = 0;
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_del_ba failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_del_ba response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index)
+{
+       struct wcn36xx_hal_trigger_ba_req_msg msg_body;
+       struct wcn36xx_hal_trigget_ba_req_candidate *candidate;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_TRIGGER_BA_REQ);
+
+       msg_body.session_id = 0;
+       msg_body.candidate_cnt = 1;
+       msg_body.header.len += sizeof(*candidate);
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       candidate = (struct wcn36xx_hal_trigget_ba_req_candidate *)
+               (wcn->hal_buf + sizeof(msg_body));
+       candidate->sta_index = sta_index;
+       candidate->tid_bitmap = 1;
+
+       ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_trigger_ba failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_trigger_ba response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
+static int wcn36xx_smd_tx_compl_ind(struct wcn36xx *wcn, void *buf, size_t len)
+{
+       struct wcn36xx_hal_tx_compl_ind_msg *rsp = buf;
+
+       if (len != sizeof(*rsp)) {
+               wcn36xx_warn("Bad TX complete indication\n");
+               return -EIO;
+       }
+
+       wcn36xx_dxe_tx_ack_ind(wcn, rsp->status);
+
+       return 0;
+}
+
+static int wcn36xx_smd_missed_beacon_ind(struct wcn36xx *wcn,
+                                        void *buf,
+                                        size_t len)
+{
+       struct wcn36xx_hal_missed_beacon_ind_msg *rsp = buf;
+       struct ieee80211_vif *vif = NULL;
+       struct wcn36xx_vif *tmp;
+
+       /* Old FW does not have bss index */
+       if (wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+               list_for_each_entry(tmp, &wcn->vif_list, list) {
+                       wcn36xx_dbg(WCN36XX_DBG_HAL, "beacon missed bss_index %d\n",
+                                   tmp->bss_index);
+                       vif = container_of((void *)tmp,
+                                                struct ieee80211_vif,
+                                                drv_priv);
+                       ieee80211_connection_loss(vif);
+               }
+               return 0;
+       }
+
+       if (len != sizeof(*rsp)) {
+               wcn36xx_warn("Corrupted missed beacon indication\n");
+               return -EIO;
+       }
+
+       list_for_each_entry(tmp, &wcn->vif_list, list) {
+               if (tmp->bss_index == rsp->bss_index) {
+                       wcn36xx_dbg(WCN36XX_DBG_HAL, "beacon missed bss_index %d\n",
+                                   rsp->bss_index);
+                       vif = container_of((void *)tmp,
+                                                struct ieee80211_vif,
+                                                drv_priv);
+                       ieee80211_connection_loss(vif);
+                       return 0;
+               }
+       }
+
+       wcn36xx_warn("BSS index %d not found\n", rsp->bss_index);
+       return -ENOENT;
+}
+
+static int wcn36xx_smd_delete_sta_context_ind(struct wcn36xx *wcn,
+                                             void *buf,
+                                             size_t len)
+{
+       struct wcn36xx_hal_delete_sta_context_ind_msg *rsp = buf;
+       struct wcn36xx_vif *tmp;
+       struct ieee80211_sta *sta = NULL;
+
+       if (len != sizeof(*rsp)) {
+               wcn36xx_warn("Corrupted delete sta indication\n");
+               return -EIO;
+       }
+
+       list_for_each_entry(tmp, &wcn->vif_list, list) {
+               if (sta && (tmp->sta->sta_index == rsp->sta_id)) {
+                       sta = container_of((void *)tmp->sta,
+                                                struct ieee80211_sta,
+                                                drv_priv);
+                       wcn36xx_dbg(WCN36XX_DBG_HAL,
+                                   "delete station indication %pM index %d\n",
+                                   rsp->addr2,
+                                   rsp->sta_id);
+                       ieee80211_report_low_ack(sta, 0);
+                       return 0;
+               }
+       }
+
+       wcn36xx_warn("STA with addr %pM and index %d not found\n",
+                    rsp->addr2,
+                    rsp->sta_id);
+       return -ENOENT;
+}
+
+int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value)
+{
+       struct wcn36xx_hal_update_cfg_req_msg msg_body, *body;
+       size_t len;
+       int ret = 0;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_CFG_REQ);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       body = (struct wcn36xx_hal_update_cfg_req_msg *) wcn->hal_buf;
+       len = msg_body.header.len;
+
+       put_cfg_tlv_u32(wcn, &len, cfg_id, value);
+       body->header.len = len;
+       body->len = len - sizeof(*body);
+
+       ret = wcn36xx_smd_send_and_wait(wcn, body->header.len);
+       if (ret) {
+               wcn36xx_err("Sending hal_update_cfg failed\n");
+               goto out;
+       }
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("hal_update_cfg response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
+{
+       struct wcn36xx_hal_msg_header *msg_header = buf;
+       struct wcn36xx_hal_ind_msg *msg_ind;
+       wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len);
+
+       switch (msg_header->msg_type) {
+       case WCN36XX_HAL_START_RSP:
+       case WCN36XX_HAL_CONFIG_STA_RSP:
+       case WCN36XX_HAL_CONFIG_BSS_RSP:
+       case WCN36XX_HAL_ADD_STA_SELF_RSP:
+       case WCN36XX_HAL_STOP_RSP:
+       case WCN36XX_HAL_DEL_STA_SELF_RSP:
+       case WCN36XX_HAL_DELETE_STA_RSP:
+       case WCN36XX_HAL_INIT_SCAN_RSP:
+       case WCN36XX_HAL_START_SCAN_RSP:
+       case WCN36XX_HAL_END_SCAN_RSP:
+       case WCN36XX_HAL_FINISH_SCAN_RSP:
+       case WCN36XX_HAL_DOWNLOAD_NV_RSP:
+       case WCN36XX_HAL_DELETE_BSS_RSP:
+       case WCN36XX_HAL_SEND_BEACON_RSP:
+       case WCN36XX_HAL_SET_LINK_ST_RSP:
+       case WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP:
+       case WCN36XX_HAL_SET_BSSKEY_RSP:
+       case WCN36XX_HAL_SET_STAKEY_RSP:
+       case WCN36XX_HAL_RMV_STAKEY_RSP:
+       case WCN36XX_HAL_RMV_BSSKEY_RSP:
+       case WCN36XX_HAL_ENTER_BMPS_RSP:
+       case WCN36XX_HAL_SET_POWER_PARAMS_RSP:
+       case WCN36XX_HAL_EXIT_BMPS_RSP:
+       case WCN36XX_HAL_KEEP_ALIVE_RSP:
+       case WCN36XX_HAL_DUMP_COMMAND_RSP:
+       case WCN36XX_HAL_ADD_BA_SESSION_RSP:
+       case WCN36XX_HAL_ADD_BA_RSP:
+       case WCN36XX_HAL_DEL_BA_RSP:
+       case WCN36XX_HAL_TRIGGER_BA_RSP:
+       case WCN36XX_HAL_UPDATE_CFG_RSP:
+       case WCN36XX_HAL_JOIN_RSP:
+       case WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP:
+       case WCN36XX_HAL_CH_SWITCH_RSP:
+       case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP:
+               memcpy(wcn->hal_buf, buf, len);
+               wcn->hal_rsp_len = len;
+               complete(&wcn->hal_rsp_compl);
+               break;
+
+       case WCN36XX_HAL_OTA_TX_COMPL_IND:
+       case WCN36XX_HAL_MISSED_BEACON_IND:
+       case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
+               mutex_lock(&wcn->hal_ind_mutex);
+               msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL);
+               msg_ind->msg_len = len;
+               msg_ind->msg = kmalloc(len, GFP_KERNEL);
+               memcpy(msg_ind->msg, buf, len);
+               list_add_tail(&msg_ind->list, &wcn->hal_ind_queue);
+               queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work);
+               wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n");
+               mutex_unlock(&wcn->hal_ind_mutex);
+               break;
+       default:
+               wcn36xx_err("SMD_EVENT (%d) not supported\n",
+                             msg_header->msg_type);
+       }
+}
+static void wcn36xx_ind_smd_work(struct work_struct *work)
+{
+       struct wcn36xx *wcn =
+               container_of(work, struct wcn36xx, hal_ind_work);
+       struct wcn36xx_hal_msg_header *msg_header;
+       struct wcn36xx_hal_ind_msg *hal_ind_msg;
+
+       mutex_lock(&wcn->hal_ind_mutex);
+
+       hal_ind_msg = list_first_entry(&wcn->hal_ind_queue,
+                                      struct wcn36xx_hal_ind_msg,
+                                      list);
+
+       msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
+
+       switch (msg_header->msg_type) {
+       case WCN36XX_HAL_OTA_TX_COMPL_IND:
+               wcn36xx_smd_tx_compl_ind(wcn,
+                                        hal_ind_msg->msg,
+                                        hal_ind_msg->msg_len);
+               break;
+       case WCN36XX_HAL_MISSED_BEACON_IND:
+               wcn36xx_smd_missed_beacon_ind(wcn,
+                                             hal_ind_msg->msg,
+                                             hal_ind_msg->msg_len);
+               break;
+       case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
+               wcn36xx_smd_delete_sta_context_ind(wcn,
+                                                  hal_ind_msg->msg,
+                                                  hal_ind_msg->msg_len);
+               break;
+       default:
+               wcn36xx_err("SMD_EVENT (%d) not supported\n",
+                             msg_header->msg_type);
+       }
+       list_del(wcn->hal_ind_queue.next);
+       kfree(hal_ind_msg->msg);
+       kfree(hal_ind_msg);
+       mutex_unlock(&wcn->hal_ind_mutex);
+}
+int wcn36xx_smd_open(struct wcn36xx *wcn)
+{
+       int ret = 0;
+       wcn->hal_ind_wq = create_freezable_workqueue("wcn36xx_smd_ind");
+       if (!wcn->hal_ind_wq) {
+               wcn36xx_err("failed to allocate wq\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+       INIT_WORK(&wcn->hal_ind_work, wcn36xx_ind_smd_work);
+       INIT_LIST_HEAD(&wcn->hal_ind_queue);
+       mutex_init(&wcn->hal_ind_mutex);
+
+       ret = wcn->ctrl_ops->open(wcn, wcn36xx_smd_rsp_process);
+       if (ret) {
+               wcn36xx_err("failed to open control channel\n");
+               goto free_wq;
+       }
+
+       return ret;
+
+free_wq:
+       destroy_workqueue(wcn->hal_ind_wq);
+out:
+       return ret;
+}
+
+void wcn36xx_smd_close(struct wcn36xx *wcn)
+{
+       wcn->ctrl_ops->close();
+       destroy_workqueue(wcn->hal_ind_wq);
+       mutex_destroy(&wcn->hal_ind_mutex);
+}
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
new file mode 100644 (file)
index 0000000..e7c3901
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SMD_H_
+#define _SMD_H_
+
+#include "wcn36xx.h"
+
+/* Max shared size is 4k but we take less.*/
+#define WCN36XX_NV_FRAGMENT_SIZE                       3072
+
+#define WCN36XX_HAL_BUF_SIZE                           4096
+
+#define HAL_MSG_TIMEOUT 200
+#define WCN36XX_SMSM_WLAN_TX_ENABLE                    0x00000400
+#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY               0x00000200
+/* The PNO version info be contained in the rsp msg */
+#define WCN36XX_FW_MSG_PNO_VERSION_MASK                        0x8000
+
+enum wcn36xx_fw_msg_result {
+       WCN36XX_FW_MSG_RESULT_SUCCESS                   = 0,
+       WCN36XX_FW_MSG_RESULT_SUCCESS_SYNC              = 1,
+
+       WCN36XX_FW_MSG_RESULT_MEM_FAIL                  = 5,
+};
+
+/******************************/
+/* SMD requests and responses */
+/******************************/
+struct wcn36xx_fw_msg_status_rsp {
+       u32     status;
+} __packed;
+
+struct wcn36xx_hal_ind_msg {
+       struct list_head list;
+       u8 *msg;
+       size_t msg_len;
+};
+
+struct wcn36xx;
+
+int wcn36xx_smd_open(struct wcn36xx *wcn);
+void wcn36xx_smd_close(struct wcn36xx *wcn);
+
+int wcn36xx_smd_load_nv(struct wcn36xx *wcn);
+int wcn36xx_smd_start(struct wcn36xx *wcn);
+int wcn36xx_smd_stop(struct wcn36xx *wcn);
+int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode);
+int wcn36xx_smd_start_scan(struct wcn36xx *wcn);
+int wcn36xx_smd_end_scan(struct wcn36xx *wcn);
+int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
+                           enum wcn36xx_hal_sys_mode mode);
+int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn);
+int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif);
+int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr);
+int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index);
+int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch);
+int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid,
+                           const u8 *sta_mac,
+                           enum wcn36xx_hal_link_state state);
+int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta, const u8 *bssid,
+                          bool update);
+int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif);
+int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta);
+int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif,
+                           struct sk_buff *skb_beacon, u16 tim_off,
+                           u16 p2p_off);
+int wcn36xx_smd_switch_channel(struct wcn36xx *wcn,
+                              struct ieee80211_vif *vif, int ch);
+int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn,
+                                     struct ieee80211_vif *vif,
+                                     struct sk_buff *skb);
+int wcn36xx_smd_set_stakey(struct wcn36xx *wcn,
+                          enum ani_ed_type enc_type,
+                          u8 keyidx,
+                          u8 keylen,
+                          u8 *key,
+                          u8 sta_index);
+int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
+                          enum ani_ed_type enc_type,
+                          u8 keyidx,
+                          u8 keylen,
+                          u8 *key);
+int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn,
+                             enum ani_ed_type enc_type,
+                             u8 keyidx,
+                             u8 sta_index);
+int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
+                             enum ani_ed_type enc_type,
+                             u8 keyidx);
+int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
+int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
+int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim);
+int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
+                              struct ieee80211_vif *vif,
+                              int packet_type);
+int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
+                            u32 arg3, u32 arg4, u32 arg5);
+int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn);
+
+int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
+               struct ieee80211_sta *sta,
+               u16 tid,
+               u16 *ssn,
+               u8 direction,
+               u8 sta_index);
+int wcn36xx_smd_add_ba(struct wcn36xx *wcn);
+int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index);
+int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index);
+
+int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);
+#endif /* _SMD_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c
new file mode 100644 (file)
index 0000000..b2b60e3
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "txrx.h"
+
+static inline int get_rssi0(struct wcn36xx_rx_bd *bd)
+{
+       return 100 - ((bd->phy_stat0 >> 24) & 0xff);
+}
+
+int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
+{
+       struct ieee80211_rx_status status;
+       struct ieee80211_hdr *hdr;
+       struct wcn36xx_rx_bd *bd;
+       u16 fc, sn;
+
+       /*
+        * All fields must be 0, otherwise it can lead to
+        * unexpected consequences.
+        */
+       memset(&status, 0, sizeof(status));
+
+       bd = (struct wcn36xx_rx_bd *)skb->data;
+       buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
+       wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP,
+                        "BD   <<< ", (char *)bd,
+                        sizeof(struct wcn36xx_rx_bd));
+
+       skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len);
+       skb_pull(skb, bd->pdu.mpdu_header_off);
+
+       status.mactime = 10;
+       status.freq = WCN36XX_CENTER_FREQ(wcn);
+       status.band = WCN36XX_BAND(wcn);
+       status.signal = -get_rssi0(bd);
+       status.antenna = 1;
+       status.rate_idx = 1;
+       status.flag = 0;
+       status.rx_flags = 0;
+       status.flag |= RX_FLAG_IV_STRIPPED |
+                      RX_FLAG_MMIC_STRIPPED |
+                      RX_FLAG_DECRYPTED;
+
+       wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n",
+                   status.flag,  status.vendor_radiotap_len);
+
+       memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = __le16_to_cpu(hdr->frame_control);
+       sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
+
+       if (ieee80211_is_beacon(hdr->frame_control)) {
+               wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n",
+                           skb, skb->len, fc, sn);
+               wcn36xx_dbg_dump(WCN36XX_DBG_BEACON_DUMP, "SKB <<< ",
+                                (char *)skb->data, skb->len);
+       } else {
+               wcn36xx_dbg(WCN36XX_DBG_RX, "rx skb %p len %d fc %04x sn %d\n",
+                           skb, skb->len, fc, sn);
+               wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "SKB <<< ",
+                                (char *)skb->data, skb->len);
+       }
+
+       ieee80211_rx_irqsafe(wcn->hw, skb);
+
+       return 0;
+}
+
+static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd,
+                              u32 mpdu_header_len,
+                              u32 len,
+                              u16 tid)
+{
+       bd->pdu.mpdu_header_len = mpdu_header_len;
+       bd->pdu.mpdu_header_off = sizeof(*bd);
+       bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len +
+               bd->pdu.mpdu_header_off;
+       bd->pdu.mpdu_len = len;
+       bd->pdu.tid = tid;
+}
+
+static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn,
+                                                 u8 *addr)
+{
+       struct wcn36xx_vif *vif_priv = NULL;
+       struct ieee80211_vif *vif = NULL;
+       list_for_each_entry(vif_priv, &wcn->vif_list, list) {
+                       vif = container_of((void *)vif_priv,
+                                  struct ieee80211_vif,
+                                  drv_priv);
+                       if (memcmp(vif->addr, addr, ETH_ALEN) == 0)
+                               return vif_priv;
+       }
+       wcn36xx_warn("vif %pM not found\n", addr);
+       return NULL;
+}
+static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
+                               struct wcn36xx *wcn,
+                               struct wcn36xx_vif **vif_priv,
+                               struct wcn36xx_sta *sta_priv,
+                               struct ieee80211_hdr *hdr,
+                               bool bcast)
+{
+       struct ieee80211_vif *vif = NULL;
+       struct wcn36xx_vif *__vif_priv = NULL;
+       bd->bd_rate = WCN36XX_BD_RATE_DATA;
+
+       /*
+        * For not unicast frames mac80211 will not set sta pointer so use
+        * self_sta_index instead.
+        */
+       if (sta_priv) {
+               __vif_priv = sta_priv->vif;
+               vif = container_of((void *)__vif_priv,
+                                  struct ieee80211_vif,
+                                  drv_priv);
+
+               if (vif->type == NL80211_IFTYPE_STATION) {
+                       bd->sta_index = sta_priv->bss_sta_index;
+                       bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index;
+               } else if (vif->type == NL80211_IFTYPE_AP ||
+                          vif->type == NL80211_IFTYPE_ADHOC ||
+                          vif->type == NL80211_IFTYPE_MESH_POINT) {
+                       bd->sta_index = sta_priv->sta_index;
+                       bd->dpu_desc_idx = sta_priv->dpu_desc_index;
+               }
+       } else {
+               __vif_priv = get_vif_by_addr(wcn, hdr->addr2);
+               bd->sta_index = __vif_priv->self_sta_index;
+               bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
+       }
+
+       bd->dpu_sign = __vif_priv->ucast_dpu_signature;
+
+       if (ieee80211_is_nullfunc(hdr->frame_control) ||
+          (sta_priv && !sta_priv->is_data_encrypted))
+               bd->dpu_ne = 1;
+
+       if (bcast) {
+               bd->ub = 1;
+               bd->ack_policy = 1;
+       }
+       *vif_priv = __vif_priv;
+}
+
+static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
+                               struct wcn36xx *wcn,
+                               struct wcn36xx_vif **vif_priv,
+                               struct ieee80211_hdr *hdr,
+                               bool bcast)
+{
+       struct wcn36xx_vif *__vif_priv =
+               get_vif_by_addr(wcn, hdr->addr2);
+       bd->sta_index = __vif_priv->self_sta_index;
+       bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
+       bd->dpu_ne = 1;
+
+       /* default rate for unicast */
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ?
+                       WCN36XX_BD_RATE_CTRL :
+                       WCN36XX_BD_RATE_MGMT;
+       else if (ieee80211_is_ctl(hdr->frame_control))
+               bd->bd_rate = WCN36XX_BD_RATE_CTRL;
+       else
+               wcn36xx_warn("frame control type unknown\n");
+
+       /*
+        * In joining state trick hardware that probe is sent as
+        * unicast even if address is broadcast.
+        */
+       if (__vif_priv->is_joining &&
+           ieee80211_is_probe_req(hdr->frame_control))
+               bcast = false;
+
+       if (bcast) {
+               /* broadcast */
+               bd->ub = 1;
+               /* No ack needed not unicast */
+               bd->ack_policy = 1;
+               bd->queue_id = WCN36XX_TX_B_WQ_ID;
+       } else
+               bd->queue_id = WCN36XX_TX_U_WQ_ID;
+       *vif_priv = __vif_priv;
+}
+
+int wcn36xx_start_tx(struct wcn36xx *wcn,
+                    struct wcn36xx_sta *sta_priv,
+                    struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct wcn36xx_vif *vif_priv = NULL;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       unsigned long flags;
+       bool is_low = ieee80211_is_data(hdr->frame_control);
+       bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
+               is_multicast_ether_addr(hdr->addr1);
+       struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low);
+
+       if (!bd) {
+               /*
+                * TX DXE are used in pairs. One for the BD and one for the
+                * actual frame. The BD DXE's has a preallocated buffer while
+                * the skb ones does not. If this isn't true something is really
+                * wierd. TODO: Recover from this situation
+                */
+
+               wcn36xx_err("bd address may not be NULL for BD DXE\n");
+               return -EINVAL;
+       }
+
+       memset(bd, 0, sizeof(*bd));
+
+       wcn36xx_dbg(WCN36XX_DBG_TX,
+                   "tx skb %p len %d fc %04x sn %d %s %s\n",
+                   skb, skb->len, __le16_to_cpu(hdr->frame_control),
+                   IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)),
+                   is_low ? "low" : "high", bcast ? "bcast" : "ucast");
+
+       wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len);
+
+       bd->dpu_rf = WCN36XX_BMU_WQ_TX;
+
+       bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
+       if (bd->tx_comp) {
+               wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
+               spin_lock_irqsave(&wcn->dxe_lock, flags);
+               if (wcn->tx_ack_skb) {
+                       spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+                       wcn36xx_warn("tx_ack_skb already set\n");
+                       return -EINVAL;
+               }
+
+               wcn->tx_ack_skb = skb;
+               spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+
+               /* Only one at a time is supported by fw. Stop the TX queues
+                * until the ack status gets back.
+                *
+                * TODO: Add watchdog in case FW does not answer
+                */
+               ieee80211_stop_queues(wcn->hw);
+       }
+
+       /* Data frames served first*/
+       if (is_low) {
+               wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast);
+               wcn36xx_set_tx_pdu(bd,
+                          ieee80211_is_data_qos(hdr->frame_control) ?
+                          sizeof(struct ieee80211_qos_hdr) :
+                          sizeof(struct ieee80211_hdr_3addr),
+                          skb->len, sta_priv ? sta_priv->tid : 0);
+       } else {
+               /* MGMT and CTRL frames are handeld here*/
+               wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast);
+               wcn36xx_set_tx_pdu(bd,
+                          ieee80211_is_data_qos(hdr->frame_control) ?
+                          sizeof(struct ieee80211_qos_hdr) :
+                          sizeof(struct ieee80211_hdr_3addr),
+                          skb->len, WCN36XX_TID);
+       }
+
+       buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
+       bd->tx_bd_sign = 0xbdbdbdbd;
+
+       return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low);
+}
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.h b/drivers/net/wireless/ath/wcn36xx/txrx.h
new file mode 100644 (file)
index 0000000..bbfbcf8
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _TXRX_H_
+#define _TXRX_H_
+
+#include <linux/etherdevice.h>
+#include "wcn36xx.h"
+
+/* TODO describe all properties */
+#define WCN36XX_802_11_HEADER_LEN      24
+#define WCN36XX_BMU_WQ_TX              25
+#define WCN36XX_TID                    7
+/* broadcast wq ID */
+#define WCN36XX_TX_B_WQ_ID             0xA
+#define WCN36XX_TX_U_WQ_ID             0x9
+/* bd_rate */
+#define WCN36XX_BD_RATE_DATA 0
+#define WCN36XX_BD_RATE_MGMT 2
+#define WCN36XX_BD_RATE_CTRL 3
+
+struct wcn36xx_pdu {
+       u32     dpu_fb:8;
+       u32     adu_fb:8;
+       u32     pdu_id:16;
+
+       /* 0x04*/
+       u32     tail_pdu_idx:16;
+       u32     head_pdu_idx:16;
+
+       /* 0x08*/
+       u32     pdu_count:7;
+       u32     mpdu_data_off:9;
+       u32     mpdu_header_off:8;
+       u32     mpdu_header_len:8;
+
+       /* 0x0c*/
+       u32     reserved4:8;
+       u32     tid:4;
+       u32     reserved3:4;
+       u32     mpdu_len:16;
+};
+
+struct wcn36xx_rx_bd {
+       u32     bdt:2;
+       u32     ft:1;
+       u32     dpu_ne:1;
+       u32     rx_key_id:3;
+       u32     ub:1;
+       u32     rmf:1;
+       u32     uma_bypass:1;
+       u32     csr11:1;
+       u32     reserved0:1;
+       u32     scan_learn:1;
+       u32     rx_ch:4;
+       u32     rtsf:1;
+       u32     bsf:1;
+       u32     a2hf:1;
+       u32     st_auf:1;
+       u32     dpu_sign:3;
+       u32     dpu_rf:8;
+
+       struct wcn36xx_pdu pdu;
+
+       /* 0x14*/
+       u32     addr3:8;
+       u32     addr2:8;
+       u32     addr1:8;
+       u32     dpu_desc_idx:8;
+
+       /* 0x18*/
+       u32     rxp_flags:23;
+       u32     rate_id:9;
+
+       u32     phy_stat0;
+       u32     phy_stat1;
+
+       /* 0x24 */
+       u32     rx_times;
+
+       u32     pmi_cmd[6];
+
+       /* 0x40 */
+       u32     reserved7:4;
+       u32     reorder_slot_id:6;
+       u32     reorder_fwd_id:6;
+       u32     reserved6:12;
+       u32     reorder_code:4;
+
+       /* 0x44 */
+       u32     exp_seq_num:12;
+       u32     cur_seq_num:12;
+       u32     fr_type_subtype:8;
+
+       /* 0x48 */
+       u32     msdu_size:16;
+       u32     sub_fr_id:4;
+       u32     proc_order:4;
+       u32     reserved9:4;
+       u32     aef:1;
+       u32     lsf:1;
+       u32     esf:1;
+       u32     asf:1;
+};
+
+struct wcn36xx_tx_bd {
+       u32     bdt:2;
+       u32     ft:1;
+       u32     dpu_ne:1;
+       u32     fw_tx_comp:1;
+       u32     tx_comp:1;
+       u32     reserved1:1;
+       u32     ub:1;
+       u32     rmf:1;
+       u32     reserved0:12;
+       u32     dpu_sign:3;
+       u32     dpu_rf:8;
+
+       struct wcn36xx_pdu pdu;
+
+       /* 0x14*/
+       u32     reserved5:7;
+       u32     queue_id:5;
+       u32     bd_rate:2;
+       u32     ack_policy:2;
+       u32     sta_index:8;
+       u32     dpu_desc_idx:8;
+
+       u32     tx_bd_sign;
+       u32     reserved6;
+       u32     dxe_start_time;
+       u32     dxe_end_time;
+
+       /*u32   tcp_udp_start_off:10;
+       u32     header_cks:16;
+       u32     reserved7:6;*/
+};
+
+struct wcn36xx_sta;
+struct wcn36xx;
+
+int  wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb);
+int wcn36xx_start_tx(struct wcn36xx *wcn,
+                    struct wcn36xx_sta *sta_priv,
+                    struct sk_buff *skb);
+
+#endif /* _TXRX_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
new file mode 100644 (file)
index 0000000..58b6383
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WCN36XX_H_
+#define _WCN36XX_H_
+
+#include <linux/completion.h>
+#include <linux/printk.h>
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+
+#include "hal.h"
+#include "smd.h"
+#include "txrx.h"
+#include "dxe.h"
+#include "pmc.h"
+#include "debug.h"
+
+#define WLAN_NV_FILE               "wlan/prima/WCNSS_qcom_wlan_nv.bin"
+#define WCN36XX_AGGR_BUFFER_SIZE 64
+
+extern unsigned int wcn36xx_dbg_mask;
+
+enum wcn36xx_debug_mask {
+       WCN36XX_DBG_DXE         = 0x00000001,
+       WCN36XX_DBG_DXE_DUMP    = 0x00000002,
+       WCN36XX_DBG_SMD         = 0x00000004,
+       WCN36XX_DBG_SMD_DUMP    = 0x00000008,
+       WCN36XX_DBG_RX          = 0x00000010,
+       WCN36XX_DBG_RX_DUMP     = 0x00000020,
+       WCN36XX_DBG_TX          = 0x00000040,
+       WCN36XX_DBG_TX_DUMP     = 0x00000080,
+       WCN36XX_DBG_HAL         = 0x00000100,
+       WCN36XX_DBG_HAL_DUMP    = 0x00000200,
+       WCN36XX_DBG_MAC         = 0x00000400,
+       WCN36XX_DBG_BEACON      = 0x00000800,
+       WCN36XX_DBG_BEACON_DUMP = 0x00001000,
+       WCN36XX_DBG_PMC         = 0x00002000,
+       WCN36XX_DBG_PMC_DUMP    = 0x00004000,
+       WCN36XX_DBG_ANY         = 0xffffffff,
+};
+
+#define wcn36xx_err(fmt, arg...)                               \
+       printk(KERN_ERR pr_fmt("ERROR " fmt), ##arg);
+
+#define wcn36xx_warn(fmt, arg...)                              \
+       printk(KERN_WARNING pr_fmt("WARNING " fmt), ##arg)
+
+#define wcn36xx_info(fmt, arg...)              \
+       printk(KERN_INFO pr_fmt(fmt), ##arg)
+
+#define wcn36xx_dbg(mask, fmt, arg...) do {                    \
+       if (wcn36xx_dbg_mask & mask)                                    \
+               printk(KERN_DEBUG pr_fmt(fmt), ##arg);  \
+} while (0)
+
+#define wcn36xx_dbg_dump(mask, prefix_str, buf, len) do {      \
+       if (wcn36xx_dbg_mask & mask)                                    \
+               print_hex_dump(KERN_DEBUG, pr_fmt(prefix_str),  \
+                              DUMP_PREFIX_OFFSET, 32, 1,       \
+                              buf, len, false);                \
+} while (0)
+
+#define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value)
+#define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band)
+#define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq)
+#define WCN36XX_LISTEN_INTERVAL(__wcn) (__wcn->hw->conf.listen_interval)
+#define WCN36XX_FLAGS(__wcn) (__wcn->hw->flags)
+#define WCN36XX_MAX_POWER(__wcn) (__wcn->hw->conf.chandef.chan->max_power)
+
+static inline void buff_to_be(u32 *buf, size_t len)
+{
+       int i;
+       for (i = 0; i < len; i++)
+               buf[i] = cpu_to_be32(buf[i]);
+}
+
+struct nv_data {
+       int     is_valid;
+       u8      table;
+};
+
+/* Interface for platform control path
+ *
+ * @open: hook must be called when wcn36xx wants to open control channel.
+ * @tx: sends a buffer.
+ */
+struct wcn36xx_platform_ctrl_ops {
+       int (*open)(void *drv_priv, void *rsp_cb);
+       void (*close)(void);
+       int (*tx)(char *buf, size_t len);
+       int (*get_hw_mac)(u8 *addr);
+       int (*smsm_change_state)(u32 clear_mask, u32 set_mask);
+};
+
+/**
+ * struct wcn36xx_vif - holds VIF related fields
+ *
+ * @bss_index: bss_index is initially set to 0xFF. bss_index is received from
+ * HW after first config_bss call and must be used in delete_bss and
+ * enter/exit_bmps.
+ */
+struct wcn36xx_vif {
+       struct list_head list;
+       struct wcn36xx_sta *sta;
+       u8 dtim_period;
+       enum ani_ed_type encrypt_type;
+       bool is_joining;
+       struct wcn36xx_hal_mac_ssid ssid;
+
+       /* Power management */
+       enum wcn36xx_power_state pw_state;
+
+       u8 bss_index;
+       u8 ucast_dpu_signature;
+       /* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
+       u8 self_sta_index;
+       u8 self_dpu_desc_index;
+};
+
+/**
+ * struct wcn36xx_sta - holds STA related fields
+ *
+ * @tid: traffic ID that is used during AMPDU and in TX BD.
+ * @sta_index: STA index is returned from HW after config_sta call and is
+ * used in both SMD channel and TX BD.
+ * @dpu_desc_index: DPU descriptor index is returned from HW after config_sta
+ * call and is used in TX BD.
+ * @bss_sta_index: STA index is returned from HW after config_bss call and is
+ * used in both SMD channel and TX BD. See table bellow when it is used.
+ * @bss_dpu_desc_index: DPU descriptor index is returned from HW after
+ * config_bss call and is used in TX BD.
+ * ______________________________________________
+ * |             |     STA     |       AP      |
+ * |______________|_____________|_______________|
+ * |    TX BD     |bss_sta_index|   sta_index   |
+ * |______________|_____________|_______________|
+ * |all SMD calls |bss_sta_index|   sta_index  |
+ * |______________|_____________|_______________|
+ * |smd_delete_sta|  sta_index  |   sta_index  |
+ * |______________|_____________|_______________|
+ */
+struct wcn36xx_sta {
+       struct wcn36xx_vif *vif;
+       u16 aid;
+       u16 tid;
+       u8 sta_index;
+       u8 dpu_desc_index;
+       u8 bss_sta_index;
+       u8 bss_dpu_desc_index;
+       bool is_data_encrypted;
+       /* Rates */
+       struct wcn36xx_hal_supported_rates supported_rates;
+};
+struct wcn36xx_dxe_ch;
+struct wcn36xx {
+       struct ieee80211_hw     *hw;
+       struct device           *dev;
+       struct list_head        vif_list;
+
+       u8                      fw_revision;
+       u8                      fw_version;
+       u8                      fw_minor;
+       u8                      fw_major;
+
+       /* extra byte for the NULL termination */
+       u8                      crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
+       u8                      wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1];
+
+       /* IRQs */
+       int                     tx_irq;
+       int                     rx_irq;
+       void __iomem            *mmio;
+
+       struct wcn36xx_platform_ctrl_ops *ctrl_ops;
+       /*
+        * smd_buf must be protected with smd_mutex to garantee
+        * that all messages are sent one after another
+        */
+       u8                      *hal_buf;
+       size_t                  hal_rsp_len;
+       struct mutex            hal_mutex;
+       struct completion       hal_rsp_compl;
+       struct workqueue_struct *hal_ind_wq;
+       struct work_struct      hal_ind_work;
+       struct mutex            hal_ind_mutex;
+       struct list_head        hal_ind_queue;
+
+       /* DXE channels */
+       struct wcn36xx_dxe_ch   dxe_tx_l_ch;    /* TX low */
+       struct wcn36xx_dxe_ch   dxe_tx_h_ch;    /* TX high */
+       struct wcn36xx_dxe_ch   dxe_rx_l_ch;    /* RX low */
+       struct wcn36xx_dxe_ch   dxe_rx_h_ch;    /* RX high */
+
+       /* For synchronization of DXE resources from BH, IRQ and WQ contexts */
+       spinlock_t      dxe_lock;
+       bool                    queues_stopped;
+
+       /* Memory pools */
+       struct wcn36xx_dxe_mem_pool mgmt_mem_pool;
+       struct wcn36xx_dxe_mem_pool data_mem_pool;
+
+       struct sk_buff          *tx_ack_skb;
+
+#ifdef CONFIG_WCN36XX_DEBUGFS
+       /* Debug file system entry */
+       struct wcn36xx_dfs_entry    dfs;
+#endif /* CONFIG_WCN36XX_DEBUGFS */
+
+};
+
+static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
+                                        u8 major,
+                                        u8 minor,
+                                        u8 version,
+                                        u8 revision)
+{
+       return (wcn->fw_major == major &&
+               wcn->fw_minor == minor &&
+               wcn->fw_version == version &&
+               wcn->fw_revision == revision);
+}
+void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates);
+
+#endif /* _WCN36XX_H_ */
index 61c302a..5b34076 100644 (file)
@@ -316,8 +316,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
        }
        conn.channel = ch - 1;
 
-       memcpy(conn.bssid, bss->bssid, 6);
-       memcpy(conn.dst_mac, bss->bssid, 6);
+       memcpy(conn.bssid, bss->bssid, ETH_ALEN);
+       memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
        /*
         * FW don't support scan after connection attempt
         */
index eb1dc7a..eeceab3 100644 (file)
@@ -197,7 +197,6 @@ static void wil_pcie_remove(struct pci_dev *pdev)
        pci_iounmap(pdev, wil->csr);
        pci_release_region(pdev, 0);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
index b827d51..0d950f2 100644 (file)
@@ -844,18 +844,18 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
        if (priv->wep_is_on)
                frame_ctl |= IEEE80211_FCTL_PROTECTED;
        if (priv->operating_mode == IW_MODE_ADHOC) {
-               skb_copy_from_linear_data(skb, &header.addr1, 6);
-               memcpy(&header.addr2, dev->dev_addr, 6);
-               memcpy(&header.addr3, priv->BSSID, 6);
+               skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN);
+               memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
+               memcpy(&header.addr3, priv->BSSID, ETH_ALEN);
        } else {
                frame_ctl |= IEEE80211_FCTL_TODS;
-               memcpy(&header.addr1, priv->CurrentBSSID, 6);
-               memcpy(&header.addr2, dev->dev_addr, 6);
-               skb_copy_from_linear_data(skb, &header.addr3, 6);
+               memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN);
+               memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
+               skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN);
        }
 
        if (priv->use_wpa)
-               memcpy(&header.addr4, SNAP_RFC1024, 6);
+               memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN);
 
        header.frame_control = cpu_to_le16(frame_ctl);
        /* Copy the wireless header into the card */
@@ -929,11 +929,11 @@ static void fast_rx_path(struct atmel_private *priv,
                }
        }
 
-       memcpy(skbp, header->addr1, 6); /* destination address */
+       memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */
        if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
-               memcpy(&skbp[6], header->addr3, 6);
+               memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN);
        else
-               memcpy(&skbp[6], header->addr2, 6); /* source address */
+               memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */
 
        skb->protocol = eth_type_trans(skb, priv->dev);
        skb->ip_summed = CHECKSUM_NONE;
@@ -969,14 +969,14 @@ static void frag_rx_path(struct atmel_private *priv,
                         u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
                         u8 frag_no, int more_frags)
 {
-       u8 mac4[6];
-       u8 source[6];
+       u8 mac4[ETH_ALEN];
+       u8 source[ETH_ALEN];
        struct sk_buff *skb;
 
        if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
-               memcpy(source, header->addr3, 6);
+               memcpy(source, header->addr3, ETH_ALEN);
        else
-               memcpy(source, header->addr2, 6);
+               memcpy(source, header->addr2, ETH_ALEN);
 
        rx_packet_loc += 24; /* skip header */
 
@@ -984,9 +984,9 @@ static void frag_rx_path(struct atmel_private *priv,
                msdu_size -= 4;
 
        if (frag_no == 0) { /* first fragment */
-               atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, 6);
-               msdu_size -= 6;
-               rx_packet_loc += 6;
+               atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN);
+               msdu_size -= ETH_ALEN;
+               rx_packet_loc += ETH_ALEN;
 
                if (priv->do_rx_crc)
                        crc = crc32_le(crc, mac4, 6);
@@ -994,9 +994,9 @@ static void frag_rx_path(struct atmel_private *priv,
                priv->frag_seq = seq_no;
                priv->frag_no = 1;
                priv->frag_len = msdu_size;
-               memcpy(priv->frag_source, source, 6);
-               memcpy(&priv->rx_buf[6], source, 6);
-               memcpy(priv->rx_buf, header->addr1, 6);
+               memcpy(priv->frag_source, source, ETH_ALEN);
+               memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN);
+               memcpy(priv->rx_buf, header->addr1, ETH_ALEN);
 
                atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size);
 
@@ -1006,13 +1006,13 @@ static void frag_rx_path(struct atmel_private *priv,
                        atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
                        if ((crc ^ 0xffffffff) != netcrc) {
                                priv->dev->stats.rx_crc_errors++;
-                               memset(priv->frag_source, 0xff, 6);
+                               memset(priv->frag_source, 0xff, ETH_ALEN);
                        }
                }
 
        } else if (priv->frag_no == frag_no &&
                   priv->frag_seq == seq_no &&
-                  memcmp(priv->frag_source, source, 6) == 0) {
+                  memcmp(priv->frag_source, source, ETH_ALEN) == 0) {
 
                atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len],
                                   rx_packet_loc, msdu_size);
@@ -1024,7 +1024,7 @@ static void frag_rx_path(struct atmel_private *priv,
                        atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
                        if ((crc ^ 0xffffffff) != netcrc) {
                                priv->dev->stats.rx_crc_errors++;
-                               memset(priv->frag_source, 0xff, 6);
+                               memset(priv->frag_source, 0xff, ETH_ALEN);
                                more_frags = 1; /* don't send broken assembly */
                        }
                }
@@ -1033,7 +1033,7 @@ static void frag_rx_path(struct atmel_private *priv,
                priv->frag_no++;
 
                if (!more_frags) { /* last one */
-                       memset(priv->frag_source, 0xff, 6);
+                       memset(priv->frag_source, 0xff, ETH_ALEN);
                        if (!(skb = dev_alloc_skb(priv->frag_len + 14))) {
                                priv->dev->stats.rx_dropped++;
                        } else {
@@ -1129,7 +1129,7 @@ static void rx_done_irq(struct atmel_private *priv)
                        atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
 
                        /* we use the same buffer for frag reassembly and control packets */
-                       memset(priv->frag_source, 0xff, 6);
+                       memset(priv->frag_source, 0xff, ETH_ALEN);
 
                        if (priv->do_rx_crc) {
                                /* last 4 octets is crc */
@@ -1557,7 +1557,7 @@ struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
        priv->last_qual = jiffies;
        priv->last_beacon_timestamp = 0;
        memset(priv->frag_source, 0xff, sizeof(priv->frag_source));
-       memset(priv->BSSID, 0, 6);
+       memset(priv->BSSID, 0, ETH_ALEN);
        priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */
        priv->station_was_associated = 0;
 
@@ -1718,7 +1718,7 @@ static int atmel_get_wap(struct net_device *dev,
                         char *extra)
 {
        struct atmel_private *priv = netdev_priv(dev);
-       memcpy(awrq->sa_data, priv->CurrentBSSID, 6);
+       memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN);
        awrq->sa_family = ARPHRD_ETHER;
 
        return 0;
@@ -2356,7 +2356,7 @@ static int atmel_get_scan(struct net_device *dev,
        for (i = 0; i < priv->BSS_list_entries; i++) {
                iwe.cmd = SIOCGIWAP;
                iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
-               memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, 6);
+               memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN);
                current_ev = iwe_stream_add_event(info, current_ev,
                                                  extra + IW_SCAN_MAX_DATA,
                                                  &iwe, IW_EV_ADDR_LEN);
@@ -2760,7 +2760,7 @@ static void atmel_enter_state(struct atmel_private *priv, int new_state)
 static void atmel_scan(struct atmel_private *priv, int specific_ssid)
 {
        struct {
-               u8 BSSID[6];
+               u8 BSSID[ETH_ALEN];
                u8 SSID[MAX_SSID_LENGTH];
                u8 scan_type;
                u8 channel;
@@ -2771,7 +2771,7 @@ static void atmel_scan(struct atmel_private *priv, int specific_ssid)
                u8 SSID_size;
        } cmd;
 
-       memset(cmd.BSSID, 0xff, 6);
+       memset(cmd.BSSID, 0xff, ETH_ALEN);
 
        if (priv->fast_scan) {
                cmd.SSID_size = priv->SSID_size;
@@ -2816,7 +2816,7 @@ static void join(struct atmel_private *priv, int type)
 
        cmd.SSID_size = priv->SSID_size;
        memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
-       memcpy(cmd.BSSID, priv->CurrentBSSID, 6);
+       memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN);
        cmd.channel = (priv->channel & 0x7f);
        cmd.BSS_type = type;
        cmd.timeout = cpu_to_le16(2000);
@@ -2837,7 +2837,7 @@ static void start(struct atmel_private *priv, int type)
 
        cmd.SSID_size = priv->SSID_size;
        memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
-       memcpy(cmd.BSSID, priv->BSSID, 6);
+       memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN);
        cmd.BSS_type = type;
        cmd.channel = (priv->channel & 0x7f);
 
@@ -2883,9 +2883,9 @@ static void send_authentication_request(struct atmel_private *priv, u16 system,
        header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
        header.duration_id = cpu_to_le16(0x8000);
        header.seq_ctrl = 0;
-       memcpy(header.addr1, priv->CurrentBSSID, 6);
-       memcpy(header.addr2, priv->dev->dev_addr, 6);
-       memcpy(header.addr3, priv->CurrentBSSID, 6);
+       memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN);
+       memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN);
+       memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN);
 
        if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
                /* no WEP for authentication frames with TrSeqNo 1 */
@@ -2916,7 +2916,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
        struct ass_req_format {
                __le16 capability;
                __le16 listen_interval;
-               u8 ap[6]; /* nothing after here directly accessible */
+               u8 ap[ETH_ALEN]; /* nothing after here directly accessible */
                u8 ssid_el_id;
                u8 ssid_len;
                u8 ssid[MAX_SSID_LENGTH];
@@ -2930,9 +2930,9 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
        header.duration_id = cpu_to_le16(0x8000);
        header.seq_ctrl = 0;
 
-       memcpy(header.addr1, priv->CurrentBSSID, 6);
-       memcpy(header.addr2, priv->dev->dev_addr, 6);
-       memcpy(header.addr3, priv->CurrentBSSID, 6);
+       memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN);
+       memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN);
+       memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN);
 
        body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS);
        if (priv->wep_is_on)
@@ -2944,7 +2944,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
 
        /* current AP address - only in reassoc frame */
        if (is_reassoc) {
-               memcpy(body.ap, priv->CurrentBSSID, 6);
+               memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN);
                ssid_el_p = &body.ssid_el_id;
                bodysize = 18 + priv->SSID_size;
        } else {
@@ -3021,7 +3021,7 @@ static void store_bss_info(struct atmel_private *priv,
        int i, index;
 
        for (index = -1, i = 0; i < priv->BSS_list_entries; i++)
-               if (memcmp(bss, priv->BSSinfo[i].BSSID, 6) == 0)
+               if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0)
                        index = i;
 
        /* If we process a probe and an entry from this BSS exists
@@ -3032,7 +3032,7 @@ static void store_bss_info(struct atmel_private *priv,
                if (priv->BSS_list_entries == MAX_BSS_ENTRIES)
                        return;
                index = priv->BSS_list_entries++;
-               memcpy(priv->BSSinfo[index].BSSID, bss, 6);
+               memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN);
                priv->BSSinfo[index].RSSI = rssi;
        } else {
                if (rssi > priv->BSSinfo[index].RSSI)
@@ -3212,7 +3212,7 @@ static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype)
        if (subtype == IEEE80211_STYPE_REASSOC_RESP &&
            status != WLAN_STATUS_ASSOC_DENIED_RATES &&
            status != WLAN_STATUS_CAPS_UNSUPPORTED &&
-           priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
+           priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
                mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
                priv->ReAssociationRequestRetryCnt++;
                send_association_request(priv, 1);
@@ -3235,7 +3235,7 @@ static void atmel_join_bss(struct atmel_private *priv, int bss_index)
 {
        struct bss_info *bss =  &priv->BSSinfo[bss_index];
 
-       memcpy(priv->CurrentBSSID, bss->BSSID, 6);
+       memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN);
        memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize);
 
        /* The WPA stuff cares about the current AP address */
@@ -3767,7 +3767,7 @@ static int probe_atmel_card(struct net_device *dev)
                                0x00, 0x04, 0x25, 0x00, 0x00, 0x00
                        };
                        printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name);
-                       memcpy(dev->dev_addr, default_mac, 6);
+                       memcpy(dev->dev_addr, default_mac, ETH_ALEN);
                }
        }
 
@@ -3819,7 +3819,7 @@ static void build_wpa_mib(struct atmel_private *priv)
 
        struct { /* NB this is matched to the hardware, don't change. */
                u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
-               u8 receiver_address[6];
+               u8 receiver_address[ETH_ALEN];
                u8 wep_is_on;
                u8 default_key; /* 0..3 */
                u8 group_key;
@@ -3837,7 +3837,7 @@ static void build_wpa_mib(struct atmel_private *priv)
 
        mib.wep_is_on = priv->wep_is_on;
        mib.exclude_unencrypted = priv->exclude_unencrypted;
-       memcpy(mib.receiver_address, priv->CurrentBSSID, 6);
+       memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN);
 
        /* zero all the keys before adding in valid ones. */
        memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value));
index 8cb206a..4ae63f4 100644 (file)
@@ -278,7 +278,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
        else
                txhdr->phy_rate = b43_plcp_get_ratecode_cck(rate);
        txhdr->mac_frame_ctl = wlhdr->frame_control;
-       memcpy(txhdr->tx_receiver, wlhdr->addr1, 6);
+       memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN);
 
        /* Calculate duration for fallback rate */
        if ((rate_fb == rate) ||
index 849a28c..86588c9 100644 (file)
@@ -215,7 +215,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
        rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value);
 
        txhdr->mac_frame_ctl = wlhdr->frame_control;
-       memcpy(txhdr->tx_receiver, wlhdr->addr1, 6);
+       memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN);
 
        /* Calculate duration for fallback rate */
        if ((rate_fb->hw_value == rate) ||
index e13b1a6..3e10b80 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
 
 #include <defs.h>
@@ -239,7 +238,9 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
                func_num = SDIO_FUNC_1;
                reg_size = 4;
 
-               brcmf_sdio_addrprep(sdiodev, reg_size, &addr);
+               ret = brcmf_sdio_addrprep(sdiodev, reg_size, &addr);
+               if (ret)
+                       goto done;
        }
 
        do {
@@ -255,6 +256,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
                                                       func_num, addr, data, 4);
        } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
 
+done:
        if (ret != 0)
                brcmf_err("failed with %d\n", ret);
 
@@ -315,8 +317,36 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
                *ret = retval;
 }
 
+static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                            bool write, u32 addr, struct sk_buff *pkt)
+{
+       unsigned int req_sz;
+
+       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_pm_resume_error(sdiodev))
+               return -EIO;
+
+       /* Single skb use the standard mmc interface */
+       req_sz = pkt->len + 3;
+       req_sz &= (uint)~3;
+
+       if (write)
+               return sdio_memcpy_toio(sdiodev->func[fn], addr,
+                                       ((u8 *)(pkt->data)),
+                                       req_sz);
+       else if (fn == 1)
+               return sdio_memcpy_fromio(sdiodev->func[fn],
+                                         ((u8 *)(pkt->data)),
+                                         addr, req_sz);
+       else
+               /* function 2 read is FIFO operation */
+               return sdio_readsb(sdiodev->func[fn],
+                                  ((u8 *)(pkt->data)), addr,
+                                  req_sz);
+}
+
 /**
- * brcmf_sdio_buffrw - SDIO interface function for block data access
+ * brcmf_sdio_sglist_rw - SDIO interface function for block data access
  * @sdiodev: brcmfmac sdio device
  * @fn: SDIO function number
  * @write: direction flag
@@ -327,12 +357,13 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
  * stack for block data access. It assumes that the skb passed down by the
  * caller has already been padded and aligned.
  */
-static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
-                            bool write, u32 addr, struct sk_buff_head *pktlist)
+static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                               bool write, u32 addr,
+                               struct sk_buff_head *pktlist)
 {
        unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
-       unsigned int max_blks, max_req_sz, orig_offset, dst_offset;
-       unsigned short max_seg_sz, seg_sz;
+       unsigned int max_req_sz, orig_offset, dst_offset;
+       unsigned short max_seg_cnt, seg_sz;
        unsigned char *pkt_data, *orig_data, *dst_data;
        struct sk_buff *pkt_next = NULL, *local_pkt_next;
        struct sk_buff_head local_list, *target_list;
@@ -341,7 +372,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
        struct mmc_data mmc_dat;
        struct sg_table st;
        struct scatterlist *sgl;
-       struct mmc_host *host;
        int ret = 0;
 
        if (!pktlist->qlen)
@@ -351,27 +381,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
        if (brcmf_pm_resume_error(sdiodev))
                return -EIO;
 
-       /* Single skb use the standard mmc interface */
-       if (pktlist->qlen == 1) {
-               pkt_next = pktlist->next;
-               req_sz = pkt_next->len + 3;
-               req_sz &= (uint)~3;
-
-               if (write)
-                       return sdio_memcpy_toio(sdiodev->func[fn], addr,
-                                               ((u8 *)(pkt_next->data)),
-                                               req_sz);
-               else if (fn == 1)
-                       return sdio_memcpy_fromio(sdiodev->func[fn],
-                                                 ((u8 *)(pkt_next->data)),
-                                                 addr, req_sz);
-               else
-                       /* function 2 read is FIFO operation */
-                       return sdio_readsb(sdiodev->func[fn],
-                                          ((u8 *)(pkt_next->data)), addr,
-                                          req_sz);
-       }
-
        target_list = pktlist;
        /* for host with broken sg support, prepare a page aligned list */
        __skb_queue_head_init(&local_list);
@@ -398,38 +407,46 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                target_list = &local_list;
        }
 
-       host = sdiodev->func[fn]->card->host;
        func_blk_sz = sdiodev->func[fn]->cur_blksize;
-       /* Blocks per command is limited by host count, host transfer
-        * size and the maximum for IO_RW_EXTENDED of 511 blocks.
-        */
-       max_blks = min_t(unsigned int, host->max_blk_count, 511u);
-       max_req_sz = min_t(unsigned int, host->max_req_size,
-                          max_blks * func_blk_sz);
-       max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC);
-       max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen);
+       max_req_sz = sdiodev->max_request_size;
+       max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
+                           target_list->qlen);
        seg_sz = target_list->qlen;
        pkt_offset = 0;
        pkt_next = target_list->next;
 
-       if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) {
+       if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) {
                ret = -ENOMEM;
                goto exit;
        }
 
+       memset(&mmc_req, 0, sizeof(struct mmc_request));
+       memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+       memset(&mmc_dat, 0, sizeof(struct mmc_data));
+
+       mmc_dat.sg = st.sgl;
+       mmc_dat.blksz = func_blk_sz;
+       mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+       mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+       mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
+       mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
+       mmc_cmd.arg |= 1<<27;                   /* block mode */
+       /* for function 1 the addr will be incremented */
+       mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+       mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+       mmc_req.cmd = &mmc_cmd;
+       mmc_req.data = &mmc_dat;
+
        while (seg_sz) {
                req_sz = 0;
                sg_cnt = 0;
-               memset(&mmc_req, 0, sizeof(struct mmc_request));
-               memset(&mmc_cmd, 0, sizeof(struct mmc_command));
-               memset(&mmc_dat, 0, sizeof(struct mmc_data));
                sgl = st.sgl;
                /* prep sg table */
                while (pkt_next != (struct sk_buff *)target_list) {
                        pkt_data = pkt_next->data + pkt_offset;
                        sg_data_sz = pkt_next->len - pkt_offset;
-                       if (sg_data_sz > host->max_seg_size)
-                               sg_data_sz = host->max_seg_size;
+                       if (sg_data_sz > sdiodev->max_segment_size)
+                               sg_data_sz = sdiodev->max_segment_size;
                        if (sg_data_sz > max_req_sz - req_sz)
                                sg_data_sz = max_req_sz - req_sz;
 
@@ -444,7 +461,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                                pkt_next = pkt_next->next;
                        }
 
-                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz)
+                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt)
                                break;
                }
                seg_sz -= sg_cnt;
@@ -455,27 +472,17 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                        ret = -ENOTBLK;
                        goto exit;
                }
-               mmc_dat.sg = st.sgl;
+
                mmc_dat.sg_len = sg_cnt;
-               mmc_dat.blksz = func_blk_sz;
                mmc_dat.blocks = req_sz / func_blk_sz;
-               mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
-               mmc_cmd.opcode = SD_IO_RW_EXTENDED;
-               mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
-               mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
-               mmc_cmd.arg |= 1<<27;                   /* block mode */
-               /* incrementing addr for function 1 */
-               mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
                mmc_cmd.arg |= (addr & 0x1FFFF) << 9;   /* address */
                mmc_cmd.arg |= mmc_dat.blocks & 0x1FF;  /* block count */
-               mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
-               mmc_req.cmd = &mmc_cmd;
-               mmc_req.data = &mmc_dat;
+               /* incrementing addr for function 1 */
                if (fn == 1)
                        addr += req_sz;
 
                mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
-               mmc_wait_for_req(host, &mmc_req);
+               mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req);
 
                ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
                if (ret != 0) {
@@ -546,7 +553,6 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 {
        uint width;
        int err = 0;
-       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
@@ -556,19 +562,17 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        if (err)
                goto done;
 
-       skb_queue_head_init(&pkt_list);
-       skb_queue_tail(&pkt_list, pkt);
-       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list);
-       skb_dequeue_tail(&pkt_list);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt);
 
 done:
        return err;
 }
 
 int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                           uint flags, struct sk_buff_head *pktq)
+                           uint flags, struct sk_buff_head *pktq, uint totlen)
 {
-       uint incr_fix;
+       struct sk_buff *glom_skb;
+       struct sk_buff *skb;
        uint width;
        int err = 0;
 
@@ -580,8 +584,22 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        if (err)
                goto done;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq);
+       if (pktq->qlen == 1)
+               err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next);
+       else if (!sdiodev->sg_support) {
+               glom_skb = brcmu_pkt_buf_get_skb(totlen);
+               if (!glom_skb)
+                       return -ENOMEM;
+               err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb);
+               if (err)
+                       goto done;
+
+               skb_queue_walk(pktq, skb) {
+                       memcpy(skb->data, glom_skb->data, skb->len);
+                       skb_pull(glom_skb, skb->len);
+               }
+       } else
+               err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq);
 
 done:
        return err;
@@ -592,7 +610,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, u8 *buf, uint nbytes)
 {
        struct sk_buff *mypkt;
-       struct sk_buff_head pktq;
+       uint width;
        int err;
 
        mypkt = brcmu_pkt_buf_get_skb(nbytes);
@@ -603,10 +621,12 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        }
 
        memcpy(mypkt->data, buf, nbytes);
-       __skb_queue_head_init(&pktq);
-       __skb_queue_tail(&pktq, mypkt);
-       err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq);
-       __skb_dequeue_tail(&pktq);
+
+       width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+
+       if (!err)
+               err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt);
 
        brcmu_pkt_buf_free_skb(mypkt);
        return err;
@@ -617,16 +637,26 @@ int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff_head *pktq)
 {
+       struct sk_buff *skb;
        uint width;
-       int err = 0;
+       int err;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pktq->qlen);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       brcmf_sdio_addrprep(sdiodev, width, &addr);
+       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+       if (err)
+               return err;
 
-       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq);
+       if (pktq->qlen == 1 || !sdiodev->sg_support)
+               skb_queue_walk(pktq, skb) {
+                       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb);
+                       if (err)
+                               break;
+               }
+       else
+               err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq);
 
        return err;
 }
@@ -639,7 +669,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        struct sk_buff *pkt;
        u32 sdaddr;
        uint dsize;
-       struct sk_buff_head pkt_list;
 
        dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
        pkt = dev_alloc_skb(dsize);
@@ -648,7 +677,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                return -EIO;
        }
        pkt->priority = 0;
-       skb_queue_head_init(&pkt_list);
 
        /* Determine initial transfer parameters */
        sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
@@ -676,10 +704,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                skb_put(pkt, dsize);
                if (write)
                        memcpy(pkt->data, data, dsize);
-               skb_queue_tail(&pkt_list, pkt);
                bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
-                                            sdaddr, &pkt_list);
-               skb_dequeue_tail(&pkt_list);
+                                            sdaddr, pkt);
                if (bcmerror) {
                        brcmf_err("membytes transfer failed\n");
                        break;
index c3462b7..905704e 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 #include <linux/suspend.h>
 #include <linux/errno.h>
 #include <linux/sched.h>       /* request_irq() */
@@ -34,6 +35,7 @@
 #include <brcmu_utils.h>
 #include <brcmu_wifi.h>
 #include "sdio_host.h"
+#include "sdio_chip.h"
 #include "dhd_dbg.h"
 #include "dhd_bus.h"
 
 
 #define DMA_ALIGN_MASK 0x03
 
-#define SDIO_DEVICE_ID_BROADCOM_43143  43143
-#define SDIO_DEVICE_ID_BROADCOM_43241  0x4324
-#define SDIO_DEVICE_ID_BROADCOM_4329   0x4329
-#define SDIO_DEVICE_ID_BROADCOM_4330   0x4330
-#define SDIO_DEVICE_ID_BROADCOM_4334   0x4334
-#define SDIO_DEVICE_ID_BROADCOM_4335   0x4335
-
 #define SDIO_FUNC1_BLOCKSIZE           64
 #define SDIO_FUNC2_BLOCKSIZE           512
 
@@ -58,7 +53,8 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
        {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
        {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
        {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
+                    SDIO_DEVICE_ID_BROADCOM_4335_4339)},
        { /* end: all zeroes */ },
 };
 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
@@ -320,6 +316,8 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        int err;
        struct brcmf_sdio_dev *sdiodev;
        struct brcmf_bus *bus_if;
+       struct mmc_host *host;
+       uint max_blocks;
 
        brcmf_dbg(SDIO, "Enter\n");
        brcmf_dbg(SDIO, "Class=%x\n", func->class);
@@ -366,6 +364,20 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
                brcmf_err("F2 error, probe failed %d...\n", err);
                goto fail;
        }
+
+       /*
+        * determine host related variables after brcmf_sdio_probe()
+        * as func->cur_blksize is properly set and F2 init has been
+        * completed successfully.
+        */
+       host = func->card->host;
+       sdiodev->sg_support = host->max_segs > 1;
+       max_blocks = min_t(uint, host->max_blk_count, 511u);
+       sdiodev->max_request_size = min_t(uint, host->max_req_size,
+                                         max_blocks * func->cur_blksize);
+       sdiodev->max_segment_count = min_t(uint, host->max_segs,
+                                          SG_MAX_SINGLE_ALLOC);
+       sdiodev->max_segment_size = host->max_seg_size;
        brcmf_dbg(SDIO, "F2 init completed...\n");
        return 0;
 
@@ -466,7 +478,7 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
 {
        brcmf_dbg(SDIO, "Enter\n");
 
-       brcmfmac_sdio_pdata = pdev->dev.platform_data;
+       brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
 
        if (brcmfmac_sdio_pdata->power_on)
                brcmfmac_sdio_pdata->power_on();
index 2eb9e64..899a2ad 100644 (file)
@@ -97,8 +97,6 @@
 #define        WLC_PHY_TYPE_LCN        8
 #define        WLC_PHY_TYPE_NULL       0xf
 
-#define BRCMF_EVENTING_MASK_LEN        16
-
 #define TOE_TX_CSUM_OL         0x00000001
 #define TOE_RX_CSUM_OL         0x00000002
 
@@ -632,29 +630,29 @@ struct brcmf_skb_reorder_data {
        u8 *reorder;
 };
 
-extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
+int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
 
 /* Return pointer to interface name */
-extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
+char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
 
 /* Query dongle */
-extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx,
-                                      uint cmd, void *buf, uint len);
-extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
-                                   void *buf, uint len);
+int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+                              void *buf, uint len);
+int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
+                            void *buf, uint len);
 
 /* Remove any protocol-specific data header. */
-extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
-                              struct sk_buff *rxp);
+int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
+                       struct sk_buff *rxp);
 
-extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
-extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx,
-                                    s32 ifidx, char *name, u8 *mac_addr);
-extern void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
+int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+                             char *name, u8 *mac_addr);
+void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
-extern u32 brcmf_get_chip_info(struct brcmf_if *ifp);
-extern void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
-                            bool success);
+u32 brcmf_get_chip_info(struct brcmf_if *ifp);
+void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+                     bool success);
 
 #endif                         /* _BRCMF_H_ */
index 74156f8..a6eb09e 100644 (file)
@@ -132,35 +132,34 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus)
  * interface functions from common layer
  */
 
-extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
-                        struct sk_buff *pkt, int prec);
+bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt,
+                     int prec);
 
 /* Receive frame for delivery to OS.  Callee disposes of rxp. */
-extern void brcmf_rx_frames(struct device *dev, struct sk_buff_head *rxlist);
+void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
 
 /* Indication from bus module regarding presence/insertion of dongle. */
-extern int brcmf_attach(uint bus_hdrlen, struct device *dev);
+int brcmf_attach(uint bus_hdrlen, struct device *dev);
 /* Indication from bus module regarding removal/absence of dongle */
-extern void brcmf_detach(struct device *dev);
+void brcmf_detach(struct device *dev);
 /* Indication from bus module that dongle should be reset */
-extern void brcmf_dev_reset(struct device *dev);
+void brcmf_dev_reset(struct device *dev);
 /* Indication from bus module to change flow-control state */
-extern void brcmf_txflowblock(struct device *dev, bool state);
+void brcmf_txflowblock(struct device *dev, bool state);
 
 /* Notify the bus has transferred the tx packet to firmware */
-extern void brcmf_txcomplete(struct device *dev, struct sk_buff *txp,
-                            bool success);
+void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
 
-extern int brcmf_bus_start(struct device *dev);
+int brcmf_bus_start(struct device *dev);
 
 #ifdef CONFIG_BRCMFMAC_SDIO
-extern void brcmf_sdio_exit(void);
-extern void brcmf_sdio_init(void);
-extern void brcmf_sdio_register(void);
+void brcmf_sdio_exit(void);
+void brcmf_sdio_init(void);
+void brcmf_sdio_register(void);
 #endif
 #ifdef CONFIG_BRCMFMAC_USB
-extern void brcmf_usb_exit(void);
-extern void brcmf_usb_register(void);
+void brcmf_usb_exit(void);
+void brcmf_usb_register(void);
 #endif
 
 #endif                         /* _BRCMF_BUS_H_ */
index 40e7f85..64e9cff 100644 (file)
@@ -509,9 +509,8 @@ netif_rx:
        }
 }
 
-void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
+void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
 {
-       struct sk_buff *skb, *pnext;
        struct brcmf_if *ifp;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
@@ -519,29 +518,24 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
        u8 ifidx;
        int ret;
 
-       brcmf_dbg(DATA, "Enter: %s: count=%u\n", dev_name(dev),
-                 skb_queue_len(skb_list));
+       brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
 
-       skb_queue_walk_safe(skb_list, skb, pnext) {
-               skb_unlink(skb, skb_list);
-
-               /* process and remove protocol-specific header */
-               ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
-               ifp = drvr->iflist[ifidx];
-
-               if (ret || !ifp || !ifp->ndev) {
-                       if ((ret != -ENODATA) && ifp)
-                               ifp->stats.rx_errors++;
-                       brcmu_pkt_buf_free_skb(skb);
-                       continue;
-               }
+       /* process and remove protocol-specific header */
+       ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
+       ifp = drvr->iflist[ifidx];
 
-               rd = (struct brcmf_skb_reorder_data *)skb->cb;
-               if (rd->reorder)
-                       brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
-               else
-                       brcmf_netif_rx(ifp, skb);
+       if (ret || !ifp || !ifp->ndev) {
+               if ((ret != -ENODATA) && ifp)
+                       ifp->stats.rx_errors++;
+               brcmu_pkt_buf_free_skb(skb);
+               return;
        }
+
+       rd = (struct brcmf_skb_reorder_data *)skb->cb;
+       if (rd->reorder)
+               brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
+       else
+               brcmf_netif_rx(ifp, skb);
 }
 
 void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
index ef91798..53c6e71 100644 (file)
  */
 
 /* Linkage, sets prot link and updates hdrlen in pub */
-extern int brcmf_proto_attach(struct brcmf_pub *drvr);
+int brcmf_proto_attach(struct brcmf_pub *drvr);
 
 /* Unlink, frees allocated protocol memory (including brcmf_proto) */
-extern void brcmf_proto_detach(struct brcmf_pub *drvr);
+void brcmf_proto_detach(struct brcmf_pub *drvr);
 
 /* Stop protocol: sync w/dongle state. */
-extern void brcmf_proto_stop(struct brcmf_pub *drvr);
+void brcmf_proto_stop(struct brcmf_pub *drvr);
 
 /* Add any protocol-specific data header.
  * Caller must reserve prot_hdrlen prepend space.
  */
-extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, u8 offset,
-                               struct sk_buff *txp);
+void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, u8 offset,
+                        struct sk_buff *txp);
 
 /* Sets dongle media info (drv_version, mac address). */
-extern int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
 
 #endif                         /* _BRCMF_PROTO_H_ */
index 1aa75d5..b02953c 100644 (file)
@@ -275,11 +275,6 @@ struct rte_console {
 /* Flags for SDH calls */
 #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
 
-#define BRCMF_SDIO_FW_NAME     "brcm/brcmfmac-sdio.bin"
-#define BRCMF_SDIO_NV_NAME     "brcm/brcmfmac-sdio.txt"
-MODULE_FIRMWARE(BRCMF_SDIO_FW_NAME);
-MODULE_FIRMWARE(BRCMF_SDIO_NV_NAME);
-
 #define BRCMF_IDLE_IMMEDIATE   (-1)    /* Enter idle immediately */
 #define BRCMF_IDLE_ACTIVE      0       /* Do not request any SD clock change
                                         * when idle
@@ -454,9 +449,6 @@ struct brcmf_sdio {
        struct work_struct datawork;
        atomic_t dpc_tskcnt;
 
-       const struct firmware *firmware;
-       u32 fw_ptr;
-
        bool txoff;             /* Transmit flow-controlled */
        struct brcmf_sdio_count sdcnt;
        bool sr_enabled; /* SaveRestore enabled */
@@ -493,6 +485,100 @@ enum brcmf_sdio_frmtype {
        BRCMF_SDIO_FT_SUB,
 };
 
+#define BCM43143_FIRMWARE_NAME         "brcm/brcmfmac43143-sdio.bin"
+#define BCM43143_NVRAM_NAME            "brcm/brcmfmac43143-sdio.txt"
+#define BCM43241B0_FIRMWARE_NAME       "brcm/brcmfmac43241b0-sdio.bin"
+#define BCM43241B0_NVRAM_NAME          "brcm/brcmfmac43241b0-sdio.txt"
+#define BCM43241B4_FIRMWARE_NAME       "brcm/brcmfmac43241b4-sdio.bin"
+#define BCM43241B4_NVRAM_NAME          "brcm/brcmfmac43241b4-sdio.txt"
+#define BCM4329_FIRMWARE_NAME          "brcm/brcmfmac4329-sdio.bin"
+#define BCM4329_NVRAM_NAME             "brcm/brcmfmac4329-sdio.txt"
+#define BCM4330_FIRMWARE_NAME          "brcm/brcmfmac4330-sdio.bin"
+#define BCM4330_NVRAM_NAME             "brcm/brcmfmac4330-sdio.txt"
+#define BCM4334_FIRMWARE_NAME          "brcm/brcmfmac4334-sdio.bin"
+#define BCM4334_NVRAM_NAME             "brcm/brcmfmac4334-sdio.txt"
+#define BCM4335_FIRMWARE_NAME          "brcm/brcmfmac4335-sdio.bin"
+#define BCM4335_NVRAM_NAME             "brcm/brcmfmac4335-sdio.txt"
+
+MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43143_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME);
+MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM4329_NVRAM_NAME);
+MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM4330_NVRAM_NAME);
+MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM4334_NVRAM_NAME);
+MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM4335_NVRAM_NAME);
+
+struct brcmf_firmware_names {
+       u32 chipid;
+       u32 revmsk;
+       const char *bin;
+       const char *nv;
+};
+
+enum brcmf_firmware_type {
+       BRCMF_FIRMWARE_BIN,
+       BRCMF_FIRMWARE_NVRAM
+};
+
+#define BRCMF_FIRMWARE_NVRAM(name) \
+       name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
+
+static const struct brcmf_firmware_names brcmf_fwname_data[] = {
+       { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
+       { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
+       { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
+       { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
+       { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
+       { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
+       { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }
+};
+
+
+static const struct firmware *brcmf_sdbrcm_get_fw(struct brcmf_sdio *bus,
+                                                 enum brcmf_firmware_type type)
+{
+       const struct firmware *fw;
+       const char *name;
+       int err, i;
+
+       for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
+               if (brcmf_fwname_data[i].chipid == bus->ci->chip &&
+                   brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) {
+                       switch (type) {
+                       case BRCMF_FIRMWARE_BIN:
+                               name = brcmf_fwname_data[i].bin;
+                               break;
+                       case BRCMF_FIRMWARE_NVRAM:
+                               name = brcmf_fwname_data[i].nv;
+                               break;
+                       default:
+                               brcmf_err("invalid firmware type (%d)\n", type);
+                               return NULL;
+                       }
+                       goto found;
+               }
+       }
+       brcmf_err("Unknown chipid %d [%d]\n",
+                 bus->ci->chip, bus->ci->chiprev);
+       return NULL;
+
+found:
+       err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev);
+       if ((err) || (!fw)) {
+               brcmf_err("fail to request firmware %s (%d)\n", name, err);
+               return NULL;
+       }
+
+       return fw;
+}
+
 static void pkt_align(struct sk_buff *p, int len, int align)
 {
        uint datalign;
@@ -1061,6 +1147,8 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
        u8 rx_seq, fc, tx_seq_max;
        u32 swheader;
 
+       trace_brcmf_sdpcm_hdr(false, header);
+
        /* hw header */
        len = get_unaligned_le16(header);
        checksum = get_unaligned_le16(header + sizeof(u16));
@@ -1183,6 +1271,7 @@ static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
                     SDPCM_DOFFSET_MASK;
        *(((__le32 *)header) + 1) = cpu_to_le32(sw_header);
        *(((__le32 *)header) + 2) = 0;
+       trace_brcmf_sdpcm_hdr(true, header);
 }
 
 static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
@@ -1303,7 +1392,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                sdio_claim_host(bus->sdiodev->func[1]);
                errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
                                bus->sdiodev->sbwad,
-                               SDIO_FUNC_2, F2SYNC, &bus->glom);
+                               SDIO_FUNC_2, F2SYNC, &bus->glom, dlen);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->sdcnt.f2rxdata++;
 
@@ -1406,13 +1495,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                                           bus->glom.qlen, pfirst, pfirst->data,
                                           pfirst->len, pfirst->next,
                                           pfirst->prev);
+                       skb_unlink(pfirst, &bus->glom);
+                       brcmf_rx_frame(bus->sdiodev->dev, pfirst);
+                       bus->sdcnt.rxglompkts++;
                }
-               /* sent any remaining packets up */
-               if (bus->glom.qlen)
-                       brcmf_rx_frames(bus->sdiodev->dev, &bus->glom);
 
                bus->sdcnt.rxglomframes++;
-               bus->sdcnt.rxglompkts += bus->glom.qlen;
        }
        return num;
 }
@@ -1557,7 +1645,6 @@ static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
 static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
 {
        struct sk_buff *pkt;            /* Packet for event or data frames */
-       struct sk_buff_head pktlist;    /* needed for bus interface */
        u16 pad;                /* Number of pad bytes to read */
        uint rxleft = 0;        /* Remaining number of frames allowed */
        int ret;                /* Return code from calls */
@@ -1759,9 +1846,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                        continue;
                }
 
-               skb_queue_head_init(&pktlist);
-               skb_queue_tail(&pktlist, pkt);
-               brcmf_rx_frames(bus->sdiodev->dev, &pktlist);
+               brcmf_rx_frame(bus->sdiodev->dev, pkt);
        }
 
        rxcount = maxframes - rxleft;
@@ -1786,10 +1871,65 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
        return;
 }
 
+/**
+ * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for
+ * bus layer usage.
+ */
 /* flag marking a dummy skb added for DMA alignment requirement */
-#define DUMMY_SKB_FLAG         0x10000
+#define ALIGN_SKB_FLAG         0x8000
 /* bit mask of data length chopped from the previous packet */
-#define DUMMY_SKB_CHOP_LEN_MASK        0xffff
+#define ALIGN_SKB_CHOP_LEN_MASK        0x7fff
+
+static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio_dev *sdiodev,
+                                   struct sk_buff_head *pktq,
+                                   struct sk_buff *pkt, uint chan)
+{
+       struct sk_buff *pkt_pad;
+       u16 tail_pad, tail_chop, sg_align;
+       unsigned int blksize;
+       u8 *dat_buf;
+       int ntail;
+
+       blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+       sg_align = 4;
+       if (sdiodev->pdata && sdiodev->pdata->sd_sgentry_align > 4)
+               sg_align = sdiodev->pdata->sd_sgentry_align;
+       /* sg entry alignment should be a divisor of block size */
+       WARN_ON(blksize % sg_align);
+
+       /* Check tail padding */
+       pkt_pad = NULL;
+       tail_chop = pkt->len % sg_align;
+       tail_pad = sg_align - tail_chop;
+       tail_pad += blksize - (pkt->len + tail_pad) % blksize;
+       if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
+               pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop);
+               if (pkt_pad == NULL)
+                       return -ENOMEM;
+               memcpy(pkt_pad->data,
+                      pkt->data + pkt->len - tail_chop,
+                      tail_chop);
+               *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
+               skb_trim(pkt, pkt->len - tail_chop);
+               __skb_queue_after(pktq, pkt, pkt_pad);
+       } else {
+               ntail = pkt->data_len + tail_pad -
+                       (pkt->end - pkt->tail);
+               if (skb_cloned(pkt) || ntail > 0)
+                       if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
+                               return -ENOMEM;
+               if (skb_linearize(pkt))
+                       return -ENOMEM;
+               dat_buf = (u8 *)(pkt->data);
+               __skb_put(pkt, tail_pad);
+       }
+
+       if (pkt_pad)
+               return pkt->len + tail_chop;
+       else
+               return pkt->len - tail_pad;
+}
+
 /**
  * brcmf_sdio_txpkt_prep - packet preparation for transmit
  * @bus: brcmf_sdio structure pointer
@@ -1806,24 +1946,16 @@ static int
 brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
                      uint chan)
 {
-       u16 head_pad, tail_pad, tail_chop, head_align, sg_align;
-       int ntail;
-       struct sk_buff *pkt_next, *pkt_new;
+       u16 head_pad, head_align;
+       struct sk_buff *pkt_next;
        u8 *dat_buf;
-       unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+       int err;
        struct brcmf_sdio_hdrinfo hd_info = {0};
 
        /* SDIO ADMA requires at least 32 bit alignment */
        head_align = 4;
-       sg_align = 4;
-       if (bus->sdiodev->pdata) {
-               head_align = bus->sdiodev->pdata->sd_head_align > 4 ?
-                            bus->sdiodev->pdata->sd_head_align : 4;
-               sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ?
-                          bus->sdiodev->pdata->sd_sgentry_align : 4;
-       }
-       /* sg entry alignment should be a divisor of block size */
-       WARN_ON(blksize % sg_align);
+       if (bus->sdiodev->pdata && bus->sdiodev->pdata->sd_head_align > 4)
+               head_align = bus->sdiodev->pdata->sd_head_align;
 
        pkt_next = pktq->next;
        dat_buf = (u8 *)(pkt_next->data);
@@ -1842,40 +1974,20 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
                memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
        }
 
-       /* Check tail padding */
-       pkt_new = NULL;
-       tail_chop = pkt_next->len % sg_align;
-       tail_pad = sg_align - tail_chop;
-       tail_pad += blksize - (pkt_next->len + tail_pad) % blksize;
-       if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) {
-               pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop);
-               if (pkt_new == NULL)
-                       return -ENOMEM;
-               memcpy(pkt_new->data,
-                      pkt_next->data + pkt_next->len - tail_chop,
-                      tail_chop);
-               *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop;
-               skb_trim(pkt_next, pkt_next->len - tail_chop);
-               __skb_queue_after(pktq, pkt_next, pkt_new);
+       if (bus->sdiodev->sg_support && pktq->qlen > 1) {
+               err = brcmf_sdio_txpkt_prep_sg(bus->sdiodev, pktq,
+                                              pkt_next, chan);
+               if (err < 0)
+                       return err;
+               hd_info.len = (u16)err;
        } else {
-               ntail = pkt_next->data_len + tail_pad -
-                       (pkt_next->end - pkt_next->tail);
-               if (skb_cloned(pkt_next) || ntail > 0)
-                       if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC))
-                               return -ENOMEM;
-               if (skb_linearize(pkt_next))
-                       return -ENOMEM;
-               dat_buf = (u8 *)(pkt_next->data);
-               __skb_put(pkt_next, tail_pad);
+               hd_info.len = pkt_next->len;
        }
 
-       /* Now prep the header */
-       if (pkt_new)
-               hd_info.len = pkt_next->len + tail_chop;
-       else
-               hd_info.len = pkt_next->len - tail_pad;
        hd_info.channel = chan;
        hd_info.dat_offset = head_pad + bus->tx_hdrlen;
+
+       /* Now fill the header */
        brcmf_sdio_hdpack(bus, dat_buf, &hd_info);
 
        if (BRCMF_BYTES_ON() &&
@@ -1908,8 +2020,8 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
 
        skb_queue_walk_safe(pktq, pkt_next, tmp) {
                dummy_flags = *(u32 *)(pkt_next->cb);
-               if (dummy_flags & DUMMY_SKB_FLAG) {
-                       chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK;
+               if (dummy_flags & ALIGN_SKB_FLAG) {
+                       chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;
                        if (chop_len) {
                                pkt_prev = pkt_next->prev;
                                memcpy(pkt_prev->data + pkt_prev->len,
@@ -3037,69 +3149,43 @@ static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
        return true;
 }
 
-static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus)
-{
-       if (bus->firmware->size < bus->fw_ptr + len)
-               len = bus->firmware->size - bus->fw_ptr;
-
-       memcpy(buf, &bus->firmware->data[bus->fw_ptr], len);
-       bus->fw_ptr += len;
-       return len;
-}
-
 static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
 {
+       const struct firmware *fw;
+       int err;
        int offset;
-       uint len;
-       u8 *memblock = NULL, *memptr;
-       int ret;
-       u8 idx;
-
-       brcmf_dbg(INFO, "Enter\n");
-
-       ret = request_firmware(&bus->firmware, BRCMF_SDIO_FW_NAME,
-                              &bus->sdiodev->func[2]->dev);
-       if (ret) {
-               brcmf_err("Fail to request firmware %d\n", ret);
-               return ret;
-       }
-       bus->fw_ptr = 0;
-
-       memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC);
-       if (memblock == NULL) {
-               ret = -ENOMEM;
-               goto err;
-       }
-       if ((u32)(unsigned long)memblock % BRCMF_SDALIGN)
-               memptr += (BRCMF_SDALIGN -
-                          ((u32)(unsigned long)memblock % BRCMF_SDALIGN));
-
-       offset = bus->ci->rambase;
-
-       /* Download image */
-       len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus);
-       idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4);
-       if (BRCMF_MAX_CORENUM != idx)
-               memcpy(&bus->ci->rst_vec, memptr, sizeof(bus->ci->rst_vec));
-       while (len) {
-               ret = brcmf_sdio_ramrw(bus->sdiodev, true, offset, memptr, len);
-               if (ret) {
+       int address;
+       int len;
+
+       fw = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_BIN);
+       if (fw == NULL)
+               return -ENOENT;
+
+       if (brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4) !=
+           BRCMF_MAX_CORENUM)
+               memcpy(&bus->ci->rst_vec, fw->data, sizeof(bus->ci->rst_vec));
+
+       err = 0;
+       offset = 0;
+       address = bus->ci->rambase;
+       while (offset < fw->size) {
+               len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK :
+                     fw->size - offset;
+               err = brcmf_sdio_ramrw(bus->sdiodev, true, address,
+                                      (u8 *)&fw->data[offset], len);
+               if (err) {
                        brcmf_err("error %d on writing %d membytes at 0x%08x\n",
-                                 ret, MEMBLOCK, offset);
-                       goto err;
+                                 err, len, address);
+                       goto failure;
                }
-
-               offset += MEMBLOCK;
-               len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus);
+               offset += len;
+               address += len;
        }
 
-err:
-       kfree(memblock);
-
-       release_firmware(bus->firmware);
-       bus->fw_ptr = 0;
+failure:
+       release_firmware(fw);
 
-       return ret;
+       return err;
 }
 
 /*
@@ -3111,7 +3197,8 @@ err:
  * by two NULs.
 */
 
-static int brcmf_process_nvram_vars(struct brcmf_sdio *bus)
+static int brcmf_process_nvram_vars(struct brcmf_sdio *bus,
+                                   const struct firmware *nv)
 {
        char *varbuf;
        char *dp;
@@ -3120,12 +3207,12 @@ static int brcmf_process_nvram_vars(struct brcmf_sdio *bus)
        int ret = 0;
        uint buf_len, n, len;
 
-       len = bus->firmware->size;
+       len = nv->size;
        varbuf = vmalloc(len);
        if (!varbuf)
                return -ENOMEM;
 
-       memcpy(varbuf, bus->firmware->data, len);
+       memcpy(varbuf, nv->data, len);
        dp = varbuf;
 
        findNewline = false;
@@ -3177,18 +3264,16 @@ err:
 
 static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus)
 {
+       const struct firmware *nv;
        int ret;
 
-       ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME,
-                              &bus->sdiodev->func[2]->dev);
-       if (ret) {
-               brcmf_err("Fail to request nvram %d\n", ret);
-               return ret;
-       }
+       nv = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
+       if (nv == NULL)
+               return -ENOENT;
 
-       ret = brcmf_process_nvram_vars(bus);
+       ret = brcmf_process_nvram_vars(bus, nv);
 
-       release_firmware(bus->firmware);
+       release_firmware(nv);
 
        return ret;
 }
index e679214..14bc24d 100644 (file)
@@ -102,7 +102,8 @@ struct brcmf_event;
        BRCMF_ENUM_DEF(DCS_REQUEST, 73) \
        BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
        BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
-       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
+       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
+       BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128)
 
 #define BRCMF_ENUM_DEF(id, val) \
        BRCMF_E_##id = (val),
@@ -114,6 +115,8 @@ enum brcmf_fweh_event_code {
 };
 #undef BRCMF_ENUM_DEF
 
+#define BRCMF_EVENTING_MASK_LEN                DIV_ROUND_UP(BRCMF_E_LAST, 8)
+
 /* flags field values in struct brcmf_event_msg */
 #define BRCMF_EVENT_MSG_LINK           0x01
 #define BRCMF_EVENT_MSG_FLUSHTXQ       0x02
index 82f9140..d0cd0bf 100644 (file)
@@ -168,6 +168,7 @@ enum brcmf_fws_skb_state {
 /**
  * struct brcmf_skbuff_cb - control buffer associated with skbuff.
  *
+ * @bus_flags: 2 bytes reserved for bus specific parameters
  * @if_flags: holds interface index and packet related flags.
  * @htod: host to device packet identifier (used in PKTTAG tlv).
  * @state: transmit state of the packet.
@@ -177,6 +178,7 @@ enum brcmf_fws_skb_state {
  * provides 48 bytes of storage so this structure should not exceed that.
  */
 struct brcmf_skbuff_cb {
+       u16 bus_flags;
        u16 if_flags;
        u32 htod;
        enum brcmf_fws_skb_state state;
index ca72177..2096a14 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/types.h>
 #include <linux/netdevice.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
 #include <linux/ssb/ssb_regs.h>
 #include <linux/bcma/bcma.h>
 
@@ -136,6 +137,8 @@ brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
        u8 idx;
 
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
+       if (idx == BRCMF_MAX_CORENUM)
+               return false;
 
        regdata = brcmf_sdio_regrl(sdiodev,
                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
@@ -154,6 +157,8 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
        bool ret;
 
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
+       if (idx == BRCMF_MAX_CORENUM)
+               return false;
 
        regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
                                   NULL);
@@ -261,6 +266,8 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
        u32 regdata;
 
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
+       if (idx == BRCMF_MAX_CORENUM)
+               return;
 
        /* if core is already in reset, just return */
        regdata = brcmf_sdio_regrl(sdiodev,
@@ -304,6 +311,8 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
        u8 idx;
 
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
+       if (idx == BRCMF_MAX_CORENUM)
+               return;
 
        /*
         * Must do the disable sequence first to work for
@@ -368,6 +377,8 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
        u32 regdata;
 
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
+       if (idx == BRCMF_MAX_CORENUM)
+               return;
 
        /* must disable first to work for arbitrary current core state */
        brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits);
@@ -444,6 +455,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
                                   NULL);
        ci->chip = regdata & CID_ID_MASK;
        ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+       if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+           ci->chiprev >= 2)
+               ci->chip = BCM4339_CHIP_ID;
        ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
 
        brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev);
@@ -541,6 +555,20 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
                ci->ramsize = 0xc0000;
                ci->rambase = 0x180000;
                break;
+       case BCM4339_CHIP_ID:
+               ci->c_inf[0].wrapbase = 0x18100000;
+               ci->c_inf[0].cib = 0x2e084411;
+               ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
+               ci->c_inf[1].base = 0x18005000;
+               ci->c_inf[1].wrapbase = 0x18105000;
+               ci->c_inf[1].cib = 0x15004211;
+               ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
+               ci->c_inf[2].base = 0x18002000;
+               ci->c_inf[2].wrapbase = 0x18102000;
+               ci->c_inf[2].cib = 0x04084411;
+               ci->ramsize = 0xc0000;
+               ci->rambase = 0x180000;
+               break;
        default:
                brcmf_err("chipid 0x%x is not supported\n", ci->chip);
                return -ENODEV;
index 83c041f..507c61c 100644 (file)
 
 #define BRCMF_MAX_CORENUM      6
 
+/* SDIO device ID */
+#define SDIO_DEVICE_ID_BROADCOM_43143          43143
+#define SDIO_DEVICE_ID_BROADCOM_43241          0x4324
+#define SDIO_DEVICE_ID_BROADCOM_4329           0x4329
+#define SDIO_DEVICE_ID_BROADCOM_4330           0x4330
+#define SDIO_DEVICE_ID_BROADCOM_4334           0x4334
+#define SDIO_DEVICE_ID_BROADCOM_4335_4339      0x4335
+
 struct chip_core_info {
        u16 id;
        u16 rev;
@@ -215,17 +223,16 @@ struct sdpcmd_regs {
        u16 PAD[0x80];
 };
 
-extern int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
-                                 struct chip_info **ci_ptr, u32 regs);
-extern void brcmf_sdio_chip_detach(struct chip_info **ci_ptr);
-extern void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
-                                             struct chip_info *ci,
-                                             u32 drivestrength);
-extern u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid);
-extern void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
-                                          struct chip_info *ci);
-extern bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
-                                         struct chip_info *ci, char *nvram_dat,
-                                         uint nvram_sz);
+int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
+                          struct chip_info **ci_ptr, u32 regs);
+void brcmf_sdio_chip_detach(struct chip_info **ci_ptr);
+void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
+                                      struct chip_info *ci, u32 drivestrength);
+u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid);
+void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
+                                   struct chip_info *ci);
+bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
+                                  struct chip_info *ci, char *nvram_dat,
+                                  uint nvram_sz);
 
 #endif         /* _BRCMFMAC_SDIO_CHIP_H_ */
index 2b5407f..fc0d4f0 100644 (file)
@@ -178,21 +178,25 @@ struct brcmf_sdio_dev {
        bool irq_en;                    /* irq enable flags */
        spinlock_t irq_en_lock;
        bool irq_wake;                  /* irq wake enable flags */
+       bool sg_support;
+       uint max_request_size;
+       ushort max_segment_count;
+       uint max_segment_size;
 };
 
 /* Register/deregister interrupt handler. */
-extern int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev);
-extern int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev);
 
 /* sdio device register access interface */
-extern u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
-extern u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
-extern void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
-                            u8 data, int *ret);
-extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
-                            u32 data, int *ret);
-extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
-                                  void *data, bool write);
+u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
+                     int *ret);
+void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
+                     int *ret);
+int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
+                           void *data, bool write);
 
 /* Buffer transfer to/from device (client) core via cmd53.
  *   fn:       function number
@@ -206,22 +210,17 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
  * Returns 0 or error code.
  * NOTE: Async operation is not currently supported.
  */
-extern int
-brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, struct sk_buff_head *pktq);
-extern int
-brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, u8 *buf, uint nbytes);
-
-extern int
-brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, struct sk_buff *pkt);
-extern int
-brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, u8 *buf, uint nbytes);
-extern int
-brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                       uint flags, struct sk_buff_head *pktq);
+int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+                         uint flags, struct sk_buff_head *pktq);
+int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+                         uint flags, u8 *buf, uint nbytes);
+
+int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+                         uint flags, struct sk_buff *pkt);
+int brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+                         uint flags, u8 *buf, uint nbytes);
+int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
+                           uint flags, struct sk_buff_head *pktq, uint totlen);
 
 /* Flags bits */
 
@@ -237,46 +236,43 @@ brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
  *   nbytes:   number of bytes to transfer to/from buf
  * Returns 0 or error code.
  */
-extern int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw,
-                              u32 addr, u8 *buf, uint nbytes);
-extern int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write,
-                           u32 address, u8 *data, uint size);
+int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
+                       u8 *buf, uint nbytes);
+int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+                    u8 *data, uint size);
 
 /* Issue an abort to the specified function */
-extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
+int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
 
 /* platform specific/high level functions */
-extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
-extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev);
 
 /* attach, return handler on success, NULL if failed.
  *  The handler shall be provided by all subsequent calls. No local cache
  *  cfghdl points to the starting address of pci device mapped memory
  */
-extern int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev);
-extern void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev);
 
 /* read or write one byte using cmd52 */
-extern int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw,
-                                   uint fnc, uint addr, u8 *byte);
+int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc,
+                            uint addr, u8 *byte);
 
 /* read or write 2/4 bytes using cmd53 */
-extern int
-brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
-                        uint rw, uint fnc, uint addr,
-                        u32 *word, uint nbyte);
+int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc,
+                            uint addr, u32 *word, uint nbyte);
 
 /* Watchdog timer interface for pm ops */
-extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev,
-                                   bool enable);
+void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable);
 
-extern void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev);
-extern void brcmf_sdbrcm_disconnect(void *ptr);
-extern void brcmf_sdbrcm_isr(void *arg);
+void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdbrcm_disconnect(void *ptr);
+void brcmf_sdbrcm_isr(void *arg);
 
-extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick);
 
-extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
-                                wait_queue_head_t *wq);
-extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev);
+void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
+                         wait_queue_head_t *wq);
+bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev);
 #endif                         /* _BRCM_SDH_H_ */
index bc29171..3c67529 100644 (file)
@@ -78,13 +78,15 @@ TRACE_EVENT(brcmf_hexdump,
        TP_ARGS(data, len),
        TP_STRUCT__entry(
                __field(unsigned long, len)
+               __field(unsigned long, addr)
                __dynamic_array(u8, hdata, len)
        ),
        TP_fast_assign(
                __entry->len = len;
+               __entry->addr = (unsigned long)data;
                memcpy(__get_dynamic_array(hdata), data, len);
        ),
-       TP_printk("hexdump [length=%lu]", __entry->len)
+       TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len)
 );
 
 TRACE_EVENT(brcmf_bdchdr,
@@ -108,6 +110,23 @@ TRACE_EVENT(brcmf_bdchdr,
        TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen)
 );
 
+TRACE_EVENT(brcmf_sdpcm_hdr,
+       TP_PROTO(bool tx, void *data),
+       TP_ARGS(tx, data),
+       TP_STRUCT__entry(
+               __field(u8, tx)
+               __field(u16, len)
+               __array(u8, hdr, 12)
+       ),
+       TP_fast_assign(
+               memcpy(__entry->hdr, data, 12);
+               __entry->len = __entry->hdr[0] | (__entry->hdr[1] << 8);
+               __entry->tx = tx ? 1 : 0;
+       ),
+       TP_printk("sdpcm: %s len %u, seq %d", __entry->tx ? "TX" : "RX",
+                 __entry->len, __entry->hdr[4])
+);
+
 #ifdef CONFIG_BRCM_TRACING
 
 #undef TRACE_INCLUDE_PATH
index f4aea47..422f44c 100644 (file)
@@ -435,7 +435,6 @@ static void brcmf_usb_rx_complete(struct urb *urb)
        struct brcmf_usbreq  *req = (struct brcmf_usbreq *)urb->context;
        struct brcmf_usbdev_info *devinfo = req->devinfo;
        struct sk_buff *skb;
-       struct sk_buff_head skbq;
 
        brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
        brcmf_usb_del_fromq(devinfo, req);
@@ -450,10 +449,8 @@ static void brcmf_usb_rx_complete(struct urb *urb)
        }
 
        if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
-               skb_queue_head_init(&skbq);
-               skb_queue_tail(&skbq, skb);
                skb_put(skb, urb->actual_length);
-               brcmf_rx_frames(devinfo->dev, &skbq);
+               brcmf_rx_frame(devinfo->dev, skb);
                brcmf_usb_rx_refill(devinfo, req);
        } else {
                brcmu_pkt_buf_free_skb(skb);
index a8a267b..2d08c15 100644 (file)
@@ -172,19 +172,19 @@ struct si_info {
 
 
 /* AMBA Interconnect exported externs */
-extern u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val);
+u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val);
 
 /* === exported functions === */
-extern struct si_pub *ai_attach(struct bcma_bus *pbus);
-extern void ai_detach(struct si_pub *sih);
-extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val);
-extern void ai_clkctl_init(struct si_pub *sih);
-extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
-extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode);
-extern bool ai_deviceremoved(struct si_pub *sih);
+struct si_pub *ai_attach(struct bcma_bus *pbus);
+void ai_detach(struct si_pub *sih);
+uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val);
+void ai_clkctl_init(struct si_pub *sih);
+u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
+bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode);
+bool ai_deviceremoved(struct si_pub *sih);
 
 /* Enable Ex-PA for 4313 */
-extern void ai_epa_4313war(struct si_pub *sih);
+void ai_epa_4313war(struct si_pub *sih);
 
 static inline u32 ai_get_cccaps(struct si_pub *sih)
 {
index 73d01e5..03bdcf2 100644 (file)
@@ -37,17 +37,17 @@ struct brcms_ampdu_session {
        u16 dma_len;
 };
 
-extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
-                                       struct brcms_c_info *wlc);
-extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
-                                  struct sk_buff *p);
-extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session);
+void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
+                                struct brcms_c_info *wlc);
+int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
+                           struct sk_buff *p);
+void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session);
 
-extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc);
-extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu);
-extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
-                                struct sk_buff *p, struct tx_status *txs);
-extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc);
-extern void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu);
+struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc);
+void brcms_c_ampdu_detach(struct ampdu_info *ampdu);
+void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+                             struct sk_buff *p, struct tx_status *txs);
+void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc);
+void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu);
 
 #endif                         /* _BRCM_AMPDU_H_ */
index 97ea388..a3d487a 100644 (file)
 #ifndef _BRCM_ANTSEL_H_
 #define _BRCM_ANTSEL_H_
 
-extern struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc);
-extern void brcms_c_antsel_detach(struct antsel_info *asi);
-extern void brcms_c_antsel_init(struct antsel_info *asi);
-extern void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef,
-                                 bool sel,
-                                 u8 id, u8 fbid, u8 *antcfg,
-                                 u8 *fbantcfg);
-extern u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
+struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc);
+void brcms_c_antsel_detach(struct antsel_info *asi);
+void brcms_c_antsel_init(struct antsel_info *asi);
+void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
+                              u8 id, u8 fbid, u8 *antcfg, u8 *fbantcfg);
+u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
 
 #endif /* _BRCM_ANTSEL_H_ */
index 006483a..39dd3a5 100644 (file)
 
 #define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */
 
-extern struct brcms_cm_info *
-brcms_c_channel_mgr_attach(struct brcms_c_info *wlc);
+struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc);
 
-extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm);
+void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm);
 
-extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm,
-                                     u16 chspec);
+bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec);
 
-extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm,
-                                  u16 chanspec,
-                                  struct txpwr_limits *txpwr);
-extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm,
-                                    u16 chanspec,
-                                    u8 local_constraint_qdbm);
-extern void brcms_c_regd_init(struct brcms_c_info *wlc);
+void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec,
+                               struct txpwr_limits *txpwr);
+void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
+                                 u8 local_constraint_qdbm);
+void brcms_c_regd_init(struct brcms_c_info *wlc);
 
 #endif                         /* _WLC_CHANNEL_H */
index 4090032..198053d 100644 (file)
@@ -88,26 +88,26 @@ struct brcms_info {
 };
 
 /* misc callbacks */
-extern void brcms_init(struct brcms_info *wl);
-extern uint brcms_reset(struct brcms_info *wl);
-extern void brcms_intrson(struct brcms_info *wl);
-extern u32 brcms_intrsoff(struct brcms_info *wl);
-extern void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask);
-extern int brcms_up(struct brcms_info *wl);
-extern void brcms_down(struct brcms_info *wl);
-extern void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
-                               bool state, int prio);
-extern bool brcms_rfkill_set_hw_state(struct brcms_info *wl);
+void brcms_init(struct brcms_info *wl);
+uint brcms_reset(struct brcms_info *wl);
+void brcms_intrson(struct brcms_info *wl);
+u32 brcms_intrsoff(struct brcms_info *wl);
+void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask);
+int brcms_up(struct brcms_info *wl);
+void brcms_down(struct brcms_info *wl);
+void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
+                        bool state, int prio);
+bool brcms_rfkill_set_hw_state(struct brcms_info *wl);
 
 /* timer functions */
-extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
-                                     void (*fn) (void *arg), void *arg,
-                                     const char *name);
-extern void brcms_free_timer(struct brcms_timer *timer);
-extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic);
-extern bool brcms_del_timer(struct brcms_timer *timer);
-extern void brcms_dpc(unsigned long data);
-extern void brcms_timer(struct brcms_timer *t);
-extern void brcms_fatal_error(struct brcms_info *wl);
+struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
+                                    void (*fn) (void *arg), void *arg,
+                                    const char *name);
+void brcms_free_timer(struct brcms_timer *timer);
+void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic);
+bool brcms_del_timer(struct brcms_timer *timer);
+void brcms_dpc(unsigned long data);
+void brcms_timer(struct brcms_timer *t);
+void brcms_fatal_error(struct brcms_info *wl);
 
 #endif                         /* _BRCM_MAC80211_IF_H_ */
index 4608e0e..8138f1c 100644 (file)
@@ -1906,14 +1906,14 @@ static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_
 
        /* If macaddr exists, use it (Sromrev4, CIS, ...). */
        if (!is_zero_ether_addr(sprom->il0mac)) {
-               memcpy(etheraddr, sprom->il0mac, 6);
+               memcpy(etheraddr, sprom->il0mac, ETH_ALEN);
                return;
        }
 
        if (wlc_hw->_nbands > 1)
-               memcpy(etheraddr, sprom->et1mac, 6);
+               memcpy(etheraddr, sprom->et1mac, ETH_ALEN);
        else
-               memcpy(etheraddr, sprom->il0mac, 6);
+               memcpy(etheraddr, sprom->il0mac, ETH_ALEN);
 }
 
 /* power both the pll and external oscillator on/off */
@@ -5695,7 +5695,7 @@ static bool brcms_c_chipmatch_pci(struct bcma_device *core)
                return true;
        if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
                return true;
-       if (device == BCM4313_D11N2G_ID)
+       if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID)
                return true;
        if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
                return true;
index b5d7a38..c4d135c 100644 (file)
@@ -616,66 +616,54 @@ struct brcms_bss_cfg {
        struct brcms_bss_info *current_bss;
 };
 
-extern int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo,
-                          struct sk_buff *p);
-extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
-                  uint *blocks);
-
-extern int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config);
-extern void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags);
-extern u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec,
-                               uint mac_len);
-extern u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc,
-                                            u32 rspec,
-                                            bool use_rspec, u16 mimo_ctlchbw);
-extern u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
-                                     u32 rts_rate,
-                                     u32 frame_rate,
-                                     u8 rts_preamble_type,
-                                     u8 frame_preamble_type, uint frame_len,
-                                     bool ba);
-extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw,
-                              struct ieee80211_sta *sta,
-                              void (*dma_callback_fn));
-extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend);
-extern int brcms_c_set_nmode(struct brcms_c_info *wlc);
-extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc,
-                                         u32 bcn_rate);
-extern void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw,
-                                    u8 antsel_type);
-extern void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw,
-                                 u16 chanspec,
-                                 bool mute, struct txpwr_limits *txpwr);
-extern void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset,
-                             u16 v);
-extern u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset);
-extern void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask,
-                       u16 val, int bands);
-extern void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags);
-extern void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val);
-extern void brcms_b_phy_reset(struct brcms_hardware *wlc_hw);
-extern void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw);
-extern void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw);
-extern void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
-                                       u32 override_bit);
-extern void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
-                                         u32 override_bit);
-extern void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw,
-                                      int offset, int len, void *buf);
-extern u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate);
-extern void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw,
-                                  uint offset, const void *buf, int len,
-                                  u32 sel);
-extern void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset,
-                                    void *buf, int len, u32 sel);
-extern void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode);
-extern u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw);
-extern void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk);
-extern void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk);
-extern void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on);
-extern void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant);
-extern void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw,
-                                   u8 stf_mode);
-extern void brcms_c_init_scb(struct scb *scb);
+int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p);
+int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
+                          uint *blocks);
+
+int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config);
+void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags);
+u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len);
+u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec,
+                              bool use_rspec, u16 mimo_ctlchbw);
+u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
+                              u32 rts_rate, u32 frame_rate,
+                              u8 rts_preamble_type, u8 frame_preamble_type,
+                              uint frame_len, bool ba);
+void brcms_c_inval_dma_pkts(struct brcms_hardware *hw,
+                           struct ieee80211_sta *sta, void (*dma_callback_fn));
+void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend);
+int brcms_c_set_nmode(struct brcms_c_info *wlc);
+void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rate);
+void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type);
+void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec,
+                         bool mute, struct txpwr_limits *txpwr);
+void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v);
+u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset);
+void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val,
+                int bands);
+void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags);
+void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val);
+void brcms_b_phy_reset(struct brcms_hardware *wlc_hw);
+void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw);
+void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw);
+void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
+                                    u32 override_bit);
+void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
+                                      u32 override_bit);
+void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset,
+                               int len, void *buf);
+u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate);
+void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset,
+                          const void *buf, int len, u32 sel);
+void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset,
+                            void *buf, int len, u32 sel);
+void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode);
+u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw);
+void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk);
+void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk);
+void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on);
+void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant);
+void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode);
+void brcms_c_init_scb(struct scb *scb);
 
 #endif                         /* _BRCM_MAIN_H_ */
index e34a71e..4d3734f 100644 (file)
@@ -179,121 +179,106 @@ struct shared_phy_params {
 };
 
 
-extern struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp);
-extern struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh,
-                                           struct bcma_device *d11core,
-                                           int bandtype, struct wiphy *wiphy);
-extern void wlc_phy_detach(struct brcms_phy_pub *ppi);
-
-extern bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype,
-                                  u16 *phyrev, u16 *radioid,
-                                  u16 *radiover);
-extern bool wlc_phy_get_encore(struct brcms_phy_pub *pih);
-extern u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih);
-
-extern void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate);
-extern void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate);
-extern void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec);
-extern void wlc_phy_watchdog(struct brcms_phy_pub *ppi);
-extern int wlc_phy_down(struct brcms_phy_pub *ppi);
-extern u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih);
-extern void wlc_phy_cal_init(struct brcms_phy_pub *ppi);
-extern void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init);
-
-extern void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi,
-                                u16 chanspec);
-extern u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi);
-extern void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi,
-                                      u16 newch);
-extern u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi);
-extern void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw);
-
-extern int wlc_phy_rssi_compute(struct brcms_phy_pub *pih,
-                               struct d11rxhdr *rxh);
-extern void wlc_phy_por_inform(struct brcms_phy_pub *ppi);
-extern void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi);
-extern bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi);
-
-extern void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag);
-
-extern void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on);
-extern void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on);
-
-
-extern void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi);
-
-extern void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi,
-                                                bool wide_filter);
-extern void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band,
-                                         struct brcms_chanvec *channels);
-extern u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi,
-                                        uint band);
-
-extern void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan,
-                                     u8 *_min_, u8 *_max_, int rate);
-extern void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi,
-                                             uint chan, u8 *_max_, u8 *_min_);
-extern void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi,
-                                           uint band, s32 *, s32 *, u32 *);
-extern void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi,
-                                     struct txpwr_limits *,
-                                     u16 chanspec);
-extern int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm,
-                              bool *override);
-extern int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm,
-                              bool override);
-extern void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi,
-                                      struct txpwr_limits *);
-extern bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi);
-extern void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi,
-                                       bool hwpwrctrl);
-extern u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi);
-extern u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi);
-extern bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih);
-
-extern void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain,
-                                  u8 rxchain);
-extern void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain,
-                                 u8 rxchain);
-extern void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain,
-                                 u8 *rxchain);
-extern u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih);
-extern s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih,
-                                u16 chanspec);
-extern void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val);
-
-extern void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason);
-extern void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi);
-extern void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock);
-extern void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi);
-
-extern void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val);
-extern void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi);
-extern void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val);
-extern void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags);
-
-extern void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type);
-
-extern void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi,
-                                       struct tx_power *power, uint channel);
-
-extern void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal);
-extern bool wlc_phy_test_ison(struct brcms_phy_pub *ppi);
-extern void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi,
-                                     u8 txpwr_percent);
-extern void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war);
-extern void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih,
-                                     bool bf_preempt);
-extern void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap);
-
-extern void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end);
-
-extern void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi);
-extern void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi);
-
-extern const u8 *wlc_phy_get_ofdm_rate_lookup(void);
-
-extern s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi,
-                                            u8 mcs_offset);
-extern s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset);
+struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp);
+struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh,
+                                    struct bcma_device *d11core, int bandtype,
+                                    struct wiphy *wiphy);
+void wlc_phy_detach(struct brcms_phy_pub *ppi);
+
+bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype,
+                           u16 *phyrev, u16 *radioid, u16 *radiover);
+bool wlc_phy_get_encore(struct brcms_phy_pub *pih);
+u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih);
+
+void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate);
+void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate);
+void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec);
+void wlc_phy_watchdog(struct brcms_phy_pub *ppi);
+int wlc_phy_down(struct brcms_phy_pub *ppi);
+u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih);
+void wlc_phy_cal_init(struct brcms_phy_pub *ppi);
+void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init);
+
+void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec);
+u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi);
+void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch);
+u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi);
+void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw);
+
+int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh);
+void wlc_phy_por_inform(struct brcms_phy_pub *ppi);
+void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi);
+bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi);
+
+void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag);
+
+void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on);
+void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on);
+
+
+void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi);
+
+void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi,
+                                         bool wide_filter);
+void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band,
+                                  struct brcms_chanvec *channels);
+u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band);
+
+void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, u8 *_min_,
+                              u8 *_max_, int rate);
+void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan,
+                                      u8 *_max_, u8 *_min_);
+void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint band,
+                                    s32 *, s32 *, u32 *);
+void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *,
+                              u16 chanspec);
+int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override);
+int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override);
+void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi,
+                               struct txpwr_limits *);
+bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi);
+void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl);
+u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi);
+u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi);
+bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih);
+
+void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain);
+void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain);
+void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain);
+u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih);
+s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec);
+void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val);
+
+void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason);
+void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi);
+void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock);
+void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi);
+
+void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val);
+void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi);
+void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val);
+void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags);
+
+void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type);
+
+void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi,
+                                struct tx_power *power, uint channel);
+
+void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal);
+bool wlc_phy_test_ison(struct brcms_phy_pub *ppi);
+void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent);
+void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war);
+void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt);
+void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap);
+
+void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end);
+
+void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi);
+void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi);
+
+const u8 *wlc_phy_get_ofdm_rate_lookup(void);
+
+s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi,
+                                     u8 mcs_offset);
+s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset);
 #endif                          /* _BRCM_PHY_HAL_H_ */
index 1dc767c..4960f7d 100644 (file)
@@ -910,113 +910,103 @@ struct lcnphy_radio_regs {
        u8 do_init_g;
 };
 
-extern u16 read_phy_reg(struct brcms_phy *pi, u16 addr);
-extern void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
-extern void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
-extern void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
-extern void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val);
-
-extern u16 read_radio_reg(struct brcms_phy *pi, u16 addr);
-extern void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
-extern void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
-extern void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask,
-                         u16 val);
-extern void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask);
-
-extern void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
-
-extern void wlc_phyreg_enter(struct brcms_phy_pub *pih);
-extern void wlc_phyreg_exit(struct brcms_phy_pub *pih);
-extern void wlc_radioreg_enter(struct brcms_phy_pub *pih);
-extern void wlc_radioreg_exit(struct brcms_phy_pub *pih);
-
-extern void wlc_phy_read_table(struct brcms_phy *pi,
-                              const struct phytbl_info *ptbl_info,
-                              u16 tblAddr, u16 tblDataHi,
-                              u16 tblDatalo);
-extern void wlc_phy_write_table(struct brcms_phy *pi,
-                               const struct phytbl_info *ptbl_info,
-                               u16 tblAddr, u16 tblDataHi, u16 tblDatalo);
-extern void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id,
-                              uint tbl_offset, u16 tblAddr, u16 tblDataHi,
-                              u16 tblDataLo);
-extern void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val);
-
-extern void write_phy_channel_reg(struct brcms_phy *pi, uint val);
-extern void wlc_phy_txpower_update_shm(struct brcms_phy *pi);
-
-extern u8 wlc_phy_nbits(s32 value);
-extern void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core);
-
-extern uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi,
-                                            struct radio_20xx_regs *radioregs);
-extern uint wlc_phy_init_radio_regs(struct brcms_phy *pi,
-                                   const struct radio_regs *radioregs,
-                                   u16 core_offset);
-
-extern void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi);
-
-extern void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on);
-extern void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real,
-                                       s32 *eps_imag);
-
-extern void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi);
-extern void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi);
-
-extern bool wlc_phy_attach_nphy(struct brcms_phy *pi);
-extern bool wlc_phy_attach_lcnphy(struct brcms_phy *pi);
-
-extern void wlc_phy_detach_lcnphy(struct brcms_phy *pi);
-
-extern void wlc_phy_init_nphy(struct brcms_phy *pi);
-extern void wlc_phy_init_lcnphy(struct brcms_phy *pi);
-
-extern void wlc_phy_cal_init_nphy(struct brcms_phy *pi);
-extern void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi);
-
-extern void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi,
-                                     u16 chanspec);
-extern void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi,
-                                       u16 chanspec);
-extern void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi,
-                                             u16 chanspec);
-extern int wlc_phy_channel2freq(uint channel);
-extern int wlc_phy_chanspec_freq2bandrange_lpssn(uint);
-extern int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec);
-
-extern void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode);
-extern s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi);
-
-extern void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi);
-extern void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi);
-extern void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi);
-
-extern void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index);
-extern void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable);
-extern void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi);
-extern void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz,
-                                    u16 max_val, bool iqcalmode);
-
-extern void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan,
-                                              u8 *max_pwr, u8 rate_id);
-extern void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start,
-                                           u8 rate_mcs_end,
-                                           u8 rate_ofdm_start);
-extern void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power,
-                                           u8 rate_ofdm_start,
-                                           u8 rate_ofdm_end,
-                                           u8 rate_mcs_start);
-
-extern u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode);
-extern s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode);
-extern s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode);
-extern s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode);
-extern void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi);
-extern void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel);
-extern void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode);
-extern void wlc_2064_vco_cal(struct brcms_phy *pi);
-
-extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi);
+u16 read_phy_reg(struct brcms_phy *pi, u16 addr);
+void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
+void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
+void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val);
+void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val);
+
+u16 read_radio_reg(struct brcms_phy *pi, u16 addr);
+void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
+void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
+void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val);
+void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask);
+
+void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val);
+
+void wlc_phyreg_enter(struct brcms_phy_pub *pih);
+void wlc_phyreg_exit(struct brcms_phy_pub *pih);
+void wlc_radioreg_enter(struct brcms_phy_pub *pih);
+void wlc_radioreg_exit(struct brcms_phy_pub *pih);
+
+void wlc_phy_read_table(struct brcms_phy *pi,
+                       const struct phytbl_info *ptbl_info,
+                       u16 tblAddr, u16 tblDataHi, u16 tblDatalo);
+void wlc_phy_write_table(struct brcms_phy *pi,
+                        const struct phytbl_info *ptbl_info,
+                        u16 tblAddr, u16 tblDataHi, u16 tblDatalo);
+void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset,
+                       u16 tblAddr, u16 tblDataHi, u16 tblDataLo);
+void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val);
+
+void write_phy_channel_reg(struct brcms_phy *pi, uint val);
+void wlc_phy_txpower_update_shm(struct brcms_phy *pi);
+
+u8 wlc_phy_nbits(s32 value);
+void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core);
+
+uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi,
+                                     struct radio_20xx_regs *radioregs);
+uint wlc_phy_init_radio_regs(struct brcms_phy *pi,
+                            const struct radio_regs *radioregs,
+                            u16 core_offset);
+
+void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi);
+
+void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on);
+void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag);
+
+void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi);
+void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi);
+
+bool wlc_phy_attach_nphy(struct brcms_phy *pi);
+bool wlc_phy_attach_lcnphy(struct brcms_phy *pi);
+
+void wlc_phy_detach_lcnphy(struct brcms_phy *pi);
+
+void wlc_phy_init_nphy(struct brcms_phy *pi);
+void wlc_phy_init_lcnphy(struct brcms_phy *pi);
+
+void wlc_phy_cal_init_nphy(struct brcms_phy *pi);
+void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi);
+
+void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec);
+void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec);
+void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, u16 chanspec);
+int wlc_phy_channel2freq(uint channel);
+int wlc_phy_chanspec_freq2bandrange_lpssn(uint);
+int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec);
+
+void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode);
+s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi);
+
+void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi);
+void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi);
+void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi);
+
+void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index);
+void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable);
+void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi);
+void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val,
+                             bool iqcalmode);
+
+void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan,
+                                       u8 *max_pwr, u8 rate_id);
+void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start,
+                                    u8 rate_mcs_end, u8 rate_ofdm_start);
+void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start,
+                                    u8 rate_ofdm_end, u8 rate_mcs_start);
+
+u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode);
+s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode);
+s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode);
+s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode);
+void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi);
+void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel);
+void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode);
+void wlc_2064_vco_cal(struct brcms_phy *pi);
+
+void wlc_phy_txpower_recalc_target(struct brcms_phy *pi);
 
 #define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18
 #define LCNPHY_TX_POWER_TABLE_SIZE     128
@@ -1030,26 +1020,24 @@ extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi);
 
 #define LCNPHY_TX_PWR_CTRL_TEMPBASED   0xE001
 
-extern void wlc_lcnphy_write_table(struct brcms_phy *pi,
-                                  const struct phytbl_info *pti);
-extern void wlc_lcnphy_read_table(struct brcms_phy *pi,
-                                 struct phytbl_info *pti);
-extern void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b);
-extern void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq);
-extern void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b);
-extern u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi);
-extern void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0,
-                                     u8 *eq0, u8 *fi0, u8 *fq0);
-extern void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode);
-extern void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode);
-extern bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi);
-extern void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi);
-extern s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1);
-extern void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr,
-                               s8 *cck_pwr);
-extern void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi);
-
-extern s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index);
+void wlc_lcnphy_write_table(struct brcms_phy *pi,
+                           const struct phytbl_info *pti);
+void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti);
+void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b);
+void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq);
+void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b);
+u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi);
+void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0,
+                              u8 *fq0);
+void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode);
+void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode);
+bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi);
+void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi);
+s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1);
+void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr);
+void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi);
+
+s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index);
 
 #define NPHY_MAX_HPVGA1_INDEX          10
 #define NPHY_DEF_HPVGA1_INDEXLIMIT     7
@@ -1060,9 +1048,8 @@ struct phy_iq_est {
        u32 q_pwr;
 };
 
-extern void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi,
-                                              bool enable);
-extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode);
+void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable);
+void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode);
 
 #define wlc_phy_write_table_nphy(pi, pti) \
        wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73)
@@ -1076,10 +1063,10 @@ extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode);
 #define wlc_nphy_table_data_write(pi, w, v) \
        wlc_phy_table_data_write((pi), (w), (v))
 
-extern void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o,
-                                   u32 w, void *d);
-extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32,
-                                    u32, const void *);
+void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, u32 w,
+                            void *d);
+void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, u32,
+                             const void *);
 
 #define        PHY_IPA(pi) \
        ((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \
@@ -1089,73 +1076,67 @@ extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32,
        if (NREV_LT((pi)->pubpi.phy_rev, 3)) \
                (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol))
 
-extern void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype);
-extern void wlc_phy_aci_reset_nphy(struct brcms_phy *pi);
-extern void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en);
-
-extern u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan);
-extern void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on);
-
-extern void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi);
-
-extern void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd);
-extern s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi);
-
-extern u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val);
-
-extern void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est,
-                                  u16 num_samps, u8 wait_time,
-                                  u8 wait_for_crs);
-
-extern void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write,
-                                     struct nphy_iq_comp *comp);
-extern void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi);
-
-extern void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih,
-                                        u8 rxcore_bitmask);
-extern u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih);
-
-extern void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type);
-extern void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi);
-extern void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi);
-extern void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi);
-extern u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi);
-
-extern struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi);
-extern int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi,
-                                  struct nphy_txgains target_gain,
-                                  bool full, bool m);
-extern int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi,
-                                struct nphy_txgains target_gain,
-                                u8 type, bool d);
-extern void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask,
-                                    s8 txpwrindex, bool res);
-extern void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type);
-extern int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type,
-                                 s32 *rssi_buf, u8 nsamps);
-extern void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi);
-extern int wlc_phy_aci_scan_nphy(struct brcms_phy *pi);
-extern void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi,
-                                       s32 dBm_targetpower, bool debug);
-extern int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val,
-                               u8 mode, u8, bool);
-extern void wlc_phy_stopplayback_nphy(struct brcms_phy *pi);
-extern void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf,
-                                    u8 num_samps);
-extern void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi);
-
-extern int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi,
-                                    struct d11rxhdr *rxh);
+void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype);
+void wlc_phy_aci_reset_nphy(struct brcms_phy *pi);
+void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en);
+
+u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan);
+void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on);
+
+void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi);
+
+void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd);
+s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi);
+
+u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val);
+
+void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est,
+                           u16 num_samps, u8 wait_time, u8 wait_for_crs);
+
+void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write,
+                              struct nphy_iq_comp *comp);
+void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi);
+
+void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask);
+u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih);
+
+void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type);
+void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi);
+void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi);
+void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi);
+u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi);
+
+struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi);
+int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi,
+                           struct nphy_txgains target_gain, bool full, bool m);
+int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain,
+                         u8 type, bool d);
+void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask,
+                             s8 txpwrindex, bool res);
+void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type);
+int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type,
+                          s32 *rssi_buf, u8 nsamps);
+void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi);
+int wlc_phy_aci_scan_nphy(struct brcms_phy *pi);
+void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower,
+                                bool debug);
+int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 mode,
+                        u8, bool);
+void wlc_phy_stopplayback_nphy(struct brcms_phy *pi);
+void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf,
+                             u8 num_samps);
+void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi);
+
+int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh);
 
 #define NPHY_TESTPATTERN_BPHY_EVM   0
 #define NPHY_TESTPATTERN_BPHY_RFCS  1
 
-extern void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs);
+void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs);
 
 void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset,
                                s8 *ofdmoffset);
-extern s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi,
-                                 u16 chanspec);
+s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec);
 
-extern bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih);
+bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih);
 #endif                         /* _BRCM_PHY_INT_H_ */
index 2c5b66b..dd87747 100644 (file)
 
 struct brcms_phy;
 
-extern struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw,
-                                                struct brcms_info *wl,
-                                                struct brcms_c_info *wlc);
-extern void wlc_phy_shim_detach(struct phy_shim_info *physhim);
+struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw,
+                                         struct brcms_info *wl,
+                                         struct brcms_c_info *wlc);
+void wlc_phy_shim_detach(struct phy_shim_info *physhim);
 
 /* PHY to WL utility functions */
-extern struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim,
-                                           void (*fn) (struct brcms_phy *pi),
-                                           void *arg, const char *name);
-extern void wlapi_free_timer(struct wlapi_timer *t);
-extern void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic);
-extern bool wlapi_del_timer(struct wlapi_timer *t);
-extern void wlapi_intrson(struct phy_shim_info *physhim);
-extern u32 wlapi_intrsoff(struct phy_shim_info *physhim);
-extern void wlapi_intrsrestore(struct phy_shim_info *physhim,
-                              u32 macintmask);
-
-extern void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset,
-                                u16 v);
-extern u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset);
-extern void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx,
-                          u16 mask, u16 val, int bands);
-extern void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags);
-extern void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim);
-extern void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode);
-extern void wlapi_enable_mac(struct phy_shim_info *physhim);
-extern void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask,
-                            u32 val);
-extern void wlapi_bmac_phy_reset(struct phy_shim_info *physhim);
-extern void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw);
-extern void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk);
-extern void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk);
-extern void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on);
-extern void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim);
-extern void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *
-                                                     physhim);
-extern void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *
-                                                       physhim);
-extern void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o,
-                                         int len, void *buf);
-extern u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim,
-                                        u8 rate);
-extern void wlapi_ucode_sample_init(struct phy_shim_info *physhim);
-extern void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint,
-                                 void *buf, int, u32 sel);
-extern void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint,
-                               const void *buf, int, u32);
-
-extern void wlapi_high_update_phy_mode(struct phy_shim_info *physhim,
-                                      u32 phy_mode);
-extern u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim);
+struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim,
+                                    void (*fn)(struct brcms_phy *pi),
+                                    void *arg, const char *name);
+void wlapi_free_timer(struct wlapi_timer *t);
+void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic);
+bool wlapi_del_timer(struct wlapi_timer *t);
+void wlapi_intrson(struct phy_shim_info *physhim);
+u32 wlapi_intrsoff(struct phy_shim_info *physhim);
+void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask);
+
+void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v);
+u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset);
+void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val,
+                   int bands);
+void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags);
+void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim);
+void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode);
+void wlapi_enable_mac(struct phy_shim_info *physhim);
+void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val);
+void wlapi_bmac_phy_reset(struct phy_shim_info *physhim);
+void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw);
+void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk);
+void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk);
+void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on);
+void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim);
+void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim);
+void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim);
+void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o,
+                                  int len, void *buf);
+u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate);
+void wlapi_ucode_sample_init(struct phy_shim_info *physhim);
+void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, void *buf,
+                          int, u32 sel);
+void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, const void *buf,
+                        int, u32);
+
+void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, u32 phy_mode);
+u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim);
 
 #endif                         /* _BRCM_PHY_SHIM_H_ */
index 20e2012..a014bbc 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "types.h"
 
-extern u16 si_pmu_fast_pwrup_delay(struct si_pub *sih);
-extern u32 si_pmu_measure_alpclk(struct si_pub *sih);
+u16 si_pmu_fast_pwrup_delay(struct si_pub *sih);
+u32 si_pmu_measure_alpclk(struct si_pub *sih);
 
 #endif /* _BRCM_PMU_H_ */
index d36ea5e..4da38cb 100644 (file)
@@ -266,83 +266,76 @@ struct brcms_antselcfg {
 };
 
 /* common functions for every port */
-extern struct brcms_c_info *
-brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit,
-              bool piomode, uint *perr);
-extern uint brcms_c_detach(struct brcms_c_info *wlc);
-extern int brcms_c_up(struct brcms_c_info *wlc);
-extern uint brcms_c_down(struct brcms_c_info *wlc);
-
-extern bool brcms_c_chipmatch(struct bcma_device *core);
-extern void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx);
-extern void brcms_c_reset(struct brcms_c_info *wlc);
-
-extern void brcms_c_intrson(struct brcms_c_info *wlc);
-extern u32 brcms_c_intrsoff(struct brcms_c_info *wlc);
-extern void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask);
-extern bool brcms_c_intrsupd(struct brcms_c_info *wlc);
-extern bool brcms_c_isr(struct brcms_c_info *wlc);
-extern bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded);
-extern bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc,
-                                    struct sk_buff *sdu,
-                                    struct ieee80211_hw *hw);
-extern bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid);
-extern void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx,
-                                  int val);
-extern int brcms_c_get_header_len(void);
-extern void brcms_c_set_addrmatch(struct brcms_c_info *wlc,
-                                 int match_reg_offset,
-                                 const u8 *addr);
-extern void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
-                             const struct ieee80211_tx_queue_params *arg,
-                             bool suspend);
-extern struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc);
-extern void brcms_c_ampdu_flush(struct brcms_c_info *wlc,
-                           struct ieee80211_sta *sta, u16 tid);
-extern void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
-                                        u8 ba_wsize, uint max_rx_ampdu_bytes);
-extern int brcms_c_module_register(struct brcms_pub *pub,
-                                  const char *name, struct brcms_info *hdl,
-                                  int (*down_fn)(void *handle));
-extern int brcms_c_module_unregister(struct brcms_pub *pub, const char *name,
-                                    struct brcms_info *hdl);
-extern void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc);
-extern void brcms_c_enable_mac(struct brcms_c_info *wlc);
-extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state);
-extern void brcms_c_scan_start(struct brcms_c_info *wlc);
-extern void brcms_c_scan_stop(struct brcms_c_info *wlc);
-extern int brcms_c_get_curband(struct brcms_c_info *wlc);
-extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel);
-extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl);
-extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc,
+struct brcms_c_info *brcms_c_attach(struct brcms_info *wl,
+                                   struct bcma_device *core, uint unit,
+                                   bool piomode, uint *perr);
+uint brcms_c_detach(struct brcms_c_info *wlc);
+int brcms_c_up(struct brcms_c_info *wlc);
+uint brcms_c_down(struct brcms_c_info *wlc);
+
+bool brcms_c_chipmatch(struct bcma_device *core);
+void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx);
+void brcms_c_reset(struct brcms_c_info *wlc);
+
+void brcms_c_intrson(struct brcms_c_info *wlc);
+u32 brcms_c_intrsoff(struct brcms_c_info *wlc);
+void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask);
+bool brcms_c_intrsupd(struct brcms_c_info *wlc);
+bool brcms_c_isr(struct brcms_c_info *wlc);
+bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded);
+bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
+                             struct ieee80211_hw *hw);
+bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid);
+void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val);
+int brcms_c_get_header_len(void);
+void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset,
+                          const u8 *addr);
+void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
+                          const struct ieee80211_tx_queue_params *arg,
+                          bool suspend);
+struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc);
+void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta,
+                        u16 tid);
+void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
+                                 u8 ba_wsize, uint max_rx_ampdu_bytes);
+int brcms_c_module_register(struct brcms_pub *pub, const char *name,
+                           struct brcms_info *hdl,
+                           int (*down_fn)(void *handle));
+int brcms_c_module_unregister(struct brcms_pub *pub, const char *name,
+                             struct brcms_info *hdl);
+void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc);
+void brcms_c_enable_mac(struct brcms_c_info *wlc);
+void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state);
+void brcms_c_scan_start(struct brcms_c_info *wlc);
+void brcms_c_scan_stop(struct brcms_c_info *wlc);
+int brcms_c_get_curband(struct brcms_c_info *wlc);
+int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel);
+int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl);
+void brcms_c_get_current_rateset(struct brcms_c_info *wlc,
                                 struct brcm_rateset *currs);
-extern int brcms_c_set_rateset(struct brcms_c_info *wlc,
-                                       struct brcm_rateset *rs);
-extern int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period);
-extern u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx);
-extern void brcms_c_set_shortslot_override(struct brcms_c_info *wlc,
+int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs);
+int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period);
+u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx);
+void brcms_c_set_shortslot_override(struct brcms_c_info *wlc,
                                    s8 sslot_override);
-extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc,
-                                       u8 interval);
-extern u64 brcms_c_tsf_get(struct brcms_c_info *wlc);
-extern void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf);
-extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr);
-extern int brcms_c_get_tx_power(struct brcms_c_info *wlc);
-extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc);
-extern void brcms_c_mute(struct brcms_c_info *wlc, bool on);
-extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc);
-extern void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr);
-extern void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr,
-                            const u8 *bssid, u8 *ssid, size_t ssid_len);
-extern void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr);
-extern void brcms_c_update_beacon(struct brcms_c_info *wlc);
-extern void brcms_c_set_new_beacon(struct brcms_c_info *wlc,
-                                  struct sk_buff *beacon, u16 tim_offset,
-                                  u16 dtim_period);
-extern void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc,
-                                      struct sk_buff *probe_resp);
-extern void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable);
-extern void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid,
-                            size_t ssid_len);
+void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval);
+u64 brcms_c_tsf_get(struct brcms_c_info *wlc);
+void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf);
+int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr);
+int brcms_c_get_tx_power(struct brcms_c_info *wlc);
+bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc);
+void brcms_c_mute(struct brcms_c_info *wlc, bool on);
+bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc);
+void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr);
+void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid,
+                     u8 *ssid, size_t ssid_len);
+void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr);
+void brcms_c_update_beacon(struct brcms_c_info *wlc);
+void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon,
+                           u16 tim_offset, u16 dtim_period);
+void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc,
+                               struct sk_buff *probe_resp);
+void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable);
+void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len);
 
 #endif                         /* _BRCM_PUB_H_ */
index 980d578..5bb88b7 100644 (file)
@@ -216,34 +216,30 @@ static inline u8 cck_phy2mac_rate(u8 signal)
 
 /* sanitize, and sort a rateset with the basic bit(s) preserved, validate
  * rateset */
-extern bool
-brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs,
-                                      const struct brcms_c_rateset *hw_rs,
-                                      bool check_brate, u8 txstreams);
+bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs,
+                                           const struct brcms_c_rateset *hw_rs,
+                                           bool check_brate, u8 txstreams);
 /* copy rateset src to dst as-is (no masking or sorting) */
-extern void brcms_c_rateset_copy(const struct brcms_c_rateset *src,
-                            struct brcms_c_rateset *dst);
+void brcms_c_rateset_copy(const struct brcms_c_rateset *src,
+                         struct brcms_c_rateset *dst);
 
 /* would be nice to have these documented ... */
-extern u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp);
-
-extern void brcms_c_rateset_filter(struct brcms_c_rateset *src,
-       struct brcms_c_rateset *dst, bool basic_only, u8 rates, uint xmask,
-       bool mcsallow);
-
-extern void
-brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt,
-                       const struct brcms_c_rateset *rs_hw, uint phy_type,
-                       int bandtype, bool cck_only, uint rate_mask,
-                       bool mcsallow, u8 bw, u8 txstreams);
-
-extern s16 brcms_c_rate_legacy_phyctl(uint rate);
-
-extern void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams);
-extern void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset);
-extern void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset,
-                                     u8 txstreams);
-extern void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset,
-                                         u8 bw);
+u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp);
+
+void brcms_c_rateset_filter(struct brcms_c_rateset *src,
+                           struct brcms_c_rateset *dst, bool basic_only,
+                           u8 rates, uint xmask, bool mcsallow);
+
+void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt,
+                            const struct brcms_c_rateset *rs_hw, uint phy_type,
+                            int bandtype, bool cck_only, uint rate_mask,
+                            bool mcsallow, u8 bw, u8 txstreams);
+
+s16 brcms_c_rate_legacy_phyctl(uint rate);
+
+void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams);
+void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset);
+void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams);
+void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw);
 
 #endif                         /* _BRCM_RATE_H_ */
index 19f6580..ba94930 100644 (file)
 
 #include "types.h"
 
-extern int brcms_c_stf_attach(struct brcms_c_info *wlc);
-extern void brcms_c_stf_detach(struct brcms_c_info *wlc);
+int brcms_c_stf_attach(struct brcms_c_info *wlc);
+void brcms_c_stf_detach(struct brcms_c_info *wlc);
 
-extern void brcms_c_tempsense_upd(struct brcms_c_info *wlc);
-extern void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc,
-                                       u16 *ss_algo_channel,
-                                       u16 chanspec);
-extern int brcms_c_stf_ss_update(struct brcms_c_info *wlc,
-                            struct brcms_band *band);
-extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc);
-extern int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val,
-                              bool force);
-extern bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val);
-extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc);
-extern void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc);
-extern u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc,
-                                     u32 rspec);
-extern u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc,
-                                       u32 rspec);
+void brcms_c_tempsense_upd(struct brcms_c_info *wlc);
+void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc,
+                                    u16 *ss_algo_channel, u16 chanspec);
+int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band);
+void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc);
+int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force);
+bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val);
+void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc);
+void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc);
+u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec);
+u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec);
 
 #endif                         /* _BRCM_STF_H_ */
index 18750a8..c87dd89 100644 (file)
@@ -43,16 +43,14 @@ struct brcms_ucode {
        u32 *bcm43xx_bomminor;
 };
 
-extern int
-brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode);
+int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode);
 
-extern void brcms_ucode_data_free(struct brcms_ucode *ucode);
+void brcms_ucode_data_free(struct brcms_ucode *ucode);
 
-extern int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf,
-                               unsigned int idx);
-extern int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes,
-                                unsigned int idx);
-extern void brcms_ucode_free_buf(void *);
-extern int  brcms_check_firmwares(struct brcms_info *wl);
+int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, unsigned int idx);
+int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes,
+                         unsigned int idx);
+void brcms_ucode_free_buf(void *);
+int  brcms_check_firmwares(struct brcms_info *wl);
 
 #endif /* _BRCM_UCODE_H_ */
index c1fe245..84113ea 100644 (file)
@@ -41,5 +41,6 @@
 #define BCM4331_CHIP_ID                0x4331
 #define BCM4334_CHIP_ID                0x4334
 #define BCM4335_CHIP_ID                0x4335
+#define BCM4339_CHIP_ID                0x4339
 
 #endif                         /* _BRCM_HW_IDS_H_ */
index 92623f0..8660a2c 100644 (file)
@@ -140,6 +140,6 @@ struct brcmu_d11inf {
        void (*decchspec)(struct brcmu_chan *ch);
 };
 
-extern void brcmu_d11_attach(struct brcmu_d11inf *d11inf);
+void brcmu_d11_attach(struct brcmu_d11inf *d11inf);
 
 #endif /* _BRCMU_CHANNELS_H_ */
index 898cacb..8ba445b 100644 (file)
@@ -114,31 +114,29 @@ static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec)
        return skb_peek_tail(&pq->q[prec].skblist);
 }
 
-extern struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec,
-                                struct sk_buff *p);
-extern struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
-                                     struct sk_buff *p);
-extern struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec);
-extern struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec);
-extern struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
-                                            bool (*match_fn)(struct sk_buff *p,
-                                                             void *arg),
-                                            void *arg);
+struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p);
+struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec,
+                                    struct sk_buff *p);
+struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec);
+struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec);
+struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec,
+                                     bool (*match_fn)(struct sk_buff *p,
+                                                      void *arg),
+                                     void *arg);
 
 /* packet primitives */
-extern struct sk_buff *brcmu_pkt_buf_get_skb(uint len);
-extern void brcmu_pkt_buf_free_skb(struct sk_buff *skb);
+struct sk_buff *brcmu_pkt_buf_get_skb(uint len);
+void brcmu_pkt_buf_free_skb(struct sk_buff *skb);
 
 /* Empty the queue at particular precedence level */
 /* callback function fn(pkt, arg) returns true if pkt belongs to if */
-extern void brcmu_pktq_pflush(struct pktq *pq, int prec,
-       bool dir, bool (*fn)(struct sk_buff *, void *), void *arg);
+void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir,
+                      bool (*fn)(struct sk_buff *, void *), void *arg);
 
 /* operations on a set of precedences in packet queue */
 
-extern int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp);
-extern struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp,
-       int *prec_out);
+int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp);
+struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out);
 
 /* operations on packet queue as a whole */
 
@@ -167,11 +165,11 @@ static inline bool pktq_empty(struct pktq *pq)
        return pq->len == 0;
 }
 
-extern void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len);
+void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len);
 /* prec_out may be NULL if caller is not interested in return value */
-extern struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out);
-extern void brcmu_pktq_flush(struct pktq *pq, bool dir,
-               bool (*fn)(struct sk_buff *, void *), void *arg);
+struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out);
+void brcmu_pktq_flush(struct pktq *pq, bool dir,
+                     bool (*fn)(struct sk_buff *, void *), void *arg);
 
 /* externs */
 /* ip address */
@@ -204,13 +202,13 @@ static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift)
 /* externs */
 /* format/print */
 #ifdef DEBUG
-extern void brcmu_prpkt(const char *msg, struct sk_buff *p0);
+void brcmu_prpkt(const char *msg, struct sk_buff *p0);
 #else
 #define brcmu_prpkt(a, b)
 #endif                         /* DEBUG */
 
 #ifdef DEBUG
-extern __printf(3, 4)
+__printf(3, 4)
 void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...);
 #else
 __printf(3, 4)
index 755a0c8..40078f5 100644 (file)
@@ -365,7 +365,7 @@ static struct hwbus_ops cw1200_spi_hwbus_ops = {
 static int cw1200_spi_probe(struct spi_device *func)
 {
        const struct cw1200_platform_data_spi *plat_data =
-               func->dev.platform_data;
+               dev_get_platdata(&func->dev);
        struct hwbus_priv *self;
        int status;
 
@@ -443,7 +443,7 @@ static int cw1200_spi_disconnect(struct spi_device *func)
                }
                kfree(self);
        }
-       cw1200_spi_off(func->dev.platform_data);
+       cw1200_spi_off(dev_get_platdata(&func->dev));
 
        return 0;
 }
index 970a48b..de7c4ff 100644 (file)
@@ -217,7 +217,7 @@ static void prism2_host_roaming(local_info_t *local)
                }
        }
 
-       memcpy(req.bssid, selected->bssid, 6);
+       memcpy(req.bssid, selected->bssid, ETH_ALEN);
        req.channel = selected->chid;
        spin_unlock_irqrestore(&local->lock, flags);
 
index 6b823a1..81903e3 100644 (file)
@@ -2698,7 +2698,7 @@ static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
 /* data's copy of the eeprom data                                 */
 static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
 {
-       memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6);
+       memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN);
 }
 
 static void ipw_read_eeprom(struct ipw_priv *priv)
@@ -11885,7 +11885,6 @@ static int ipw_pci_probe(struct pci_dev *pdev,
        pci_release_regions(pdev);
       out_pci_disable_device:
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
       out_free_libipw:
        free_libipw(priv->net_dev, 0);
       out:
@@ -11966,7 +11965,6 @@ static void ipw_pci_remove(struct pci_dev *pdev)
        iounmap(priv->hw_base);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        /* wiphy_unregister needs to be here, before free_libipw */
        wiphy_unregister(priv->ieee->wdev.wiphy);
        kfree(priv->ieee->a_band.channels);
index 6eede52..5ce2f59 100644 (file)
@@ -950,66 +950,55 @@ static inline int libipw_is_cck_rate(u8 rate)
 }
 
 /* libipw.c */
-extern void free_libipw(struct net_device *dev, int monitor);
-extern struct net_device *alloc_libipw(int sizeof_priv, int monitor);
-extern int libipw_change_mtu(struct net_device *dev, int new_mtu);
+void free_libipw(struct net_device *dev, int monitor);
+struct net_device *alloc_libipw(int sizeof_priv, int monitor);
+int libipw_change_mtu(struct net_device *dev, int new_mtu);
 
-extern void libipw_networks_age(struct libipw_device *ieee,
-                                  unsigned long age_secs);
+void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs);
 
-extern int libipw_set_encryption(struct libipw_device *ieee);
+int libipw_set_encryption(struct libipw_device *ieee);
 
 /* libipw_tx.c */
-extern netdev_tx_t libipw_xmit(struct sk_buff *skb,
-                              struct net_device *dev);
-extern void libipw_txb_free(struct libipw_txb *);
+netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev);
+void libipw_txb_free(struct libipw_txb *);
 
 /* libipw_rx.c */
-extern void libipw_rx_any(struct libipw_device *ieee,
-                    struct sk_buff *skb, struct libipw_rx_stats *stats);
-extern int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
-                       struct libipw_rx_stats *rx_stats);
+void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb,
+                  struct libipw_rx_stats *stats);
+int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb,
+             struct libipw_rx_stats *rx_stats);
 /* make sure to set stats->len */
-extern void libipw_rx_mgt(struct libipw_device *ieee,
-                            struct libipw_hdr_4addr *header,
-                            struct libipw_rx_stats *stats);
-extern void libipw_network_reset(struct libipw_network *network);
+void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header,
+                  struct libipw_rx_stats *stats);
+void libipw_network_reset(struct libipw_network *network);
 
 /* libipw_geo.c */
-extern const struct libipw_geo *libipw_get_geo(struct libipw_device
-                                                    *ieee);
-extern void libipw_set_geo(struct libipw_device *ieee,
-                            const struct libipw_geo *geo);
-
-extern int libipw_is_valid_channel(struct libipw_device *ieee,
-                                     u8 channel);
-extern int libipw_channel_to_index(struct libipw_device *ieee,
-                                     u8 channel);
-extern u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq);
-extern u8 libipw_get_channel_flags(struct libipw_device *ieee,
-                                     u8 channel);
-extern const struct libipw_channel *libipw_get_channel(struct
-                                                            libipw_device
-                                                            *ieee, u8 channel);
-extern u32 libipw_channel_to_freq(struct libipw_device * ieee,
-                                     u8 channel);
+const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee);
+void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo);
+
+int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel);
+int libipw_channel_to_index(struct libipw_device *ieee, u8 channel);
+u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq);
+u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel);
+const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee,
+                                               u8 channel);
+u32 libipw_channel_to_freq(struct libipw_device *ieee, u8 channel);
 
 /* libipw_wx.c */
-extern int libipw_wx_get_scan(struct libipw_device *ieee,
-                                struct iw_request_info *info,
-                                union iwreq_data *wrqu, char *key);
-extern int libipw_wx_set_encode(struct libipw_device *ieee,
-                                  struct iw_request_info *info,
-                                  union iwreq_data *wrqu, char *key);
-extern int libipw_wx_get_encode(struct libipw_device *ieee,
-                                  struct iw_request_info *info,
-                                  union iwreq_data *wrqu, char *key);
-extern int libipw_wx_set_encodeext(struct libipw_device *ieee,
-                                     struct iw_request_info *info,
-                                     union iwreq_data *wrqu, char *extra);
-extern int libipw_wx_get_encodeext(struct libipw_device *ieee,
-                                     struct iw_request_info *info,
-                                     union iwreq_data *wrqu, char *extra);
+int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info,
+                      union iwreq_data *wrqu, char *key);
+int libipw_wx_set_encode(struct libipw_device *ieee,
+                        struct iw_request_info *info, union iwreq_data *wrqu,
+                        char *key);
+int libipw_wx_get_encode(struct libipw_device *ieee,
+                        struct iw_request_info *info, union iwreq_data *wrqu,
+                        char *key);
+int libipw_wx_set_encodeext(struct libipw_device *ieee,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra);
+int libipw_wx_get_encodeext(struct libipw_device *ieee,
+                           struct iw_request_info *info,
+                           union iwreq_data *wrqu, char *extra);
 
 static inline void libipw_increment_scans(struct libipw_device *ieee)
 {
index 9581d07..dea3b50 100644 (file)
@@ -3811,7 +3811,6 @@ out_iounmap:
 out_pci_release_regions:
        pci_release_regions(pdev);
 out_pci_disable_device:
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 out_ieee80211_free_hw:
        ieee80211_free_hw(il->hw);
@@ -3888,7 +3887,6 @@ il3945_pci_remove(struct pci_dev *pdev)
        iounmap(il->hw_base);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        il_free_channel_map(il);
        il_free_geos(il);
index 9a8703d..00030d4 100644 (file)
@@ -189,15 +189,14 @@ struct il3945_ibss_seq {
  * for use by iwl-*.c
  *
  *****************************************************************************/
-extern int il3945_calc_db_from_ratio(int sig_ratio);
-extern void il3945_rx_replenish(void *data);
-extern void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq);
-extern unsigned int il3945_fill_beacon_frame(struct il_priv *il,
-                                            struct ieee80211_hdr *hdr,
-                                            int left);
-extern int il3945_dump_nic_event_log(struct il_priv *il, bool full_log,
-                                    char **buf, bool display);
-extern void il3945_dump_nic_error_log(struct il_priv *il);
+int il3945_calc_db_from_ratio(int sig_ratio);
+void il3945_rx_replenish(void *data);
+void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq);
+unsigned int il3945_fill_beacon_frame(struct il_priv *il,
+                                     struct ieee80211_hdr *hdr, int left);
+int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, char **buf,
+                             bool display);
+void il3945_dump_nic_error_log(struct il_priv *il);
 
 /******************************************************************************
  *
@@ -215,39 +214,36 @@ extern void il3945_dump_nic_error_log(struct il_priv *il);
  * il3945_mac_     <-- mac80211 callback
  *
  ****************************************************************************/
-extern void il3945_hw_handler_setup(struct il_priv *il);
-extern void il3945_hw_setup_deferred_work(struct il_priv *il);
-extern void il3945_hw_cancel_deferred_work(struct il_priv *il);
-extern int il3945_hw_rxq_stop(struct il_priv *il);
-extern int il3945_hw_set_hw_params(struct il_priv *il);
-extern int il3945_hw_nic_init(struct il_priv *il);
-extern int il3945_hw_nic_stop_master(struct il_priv *il);
-extern void il3945_hw_txq_ctx_free(struct il_priv *il);
-extern void il3945_hw_txq_ctx_stop(struct il_priv *il);
-extern int il3945_hw_nic_reset(struct il_priv *il);
-extern int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il,
-                                          struct il_tx_queue *txq,
-                                          dma_addr_t addr, u16 len, u8 reset,
-                                          u8 pad);
-extern void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq);
-extern int il3945_hw_get_temperature(struct il_priv *il);
-extern int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq);
-extern unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il,
-                                            struct il3945_frame *frame,
-                                            u8 rate);
+void il3945_hw_handler_setup(struct il_priv *il);
+void il3945_hw_setup_deferred_work(struct il_priv *il);
+void il3945_hw_cancel_deferred_work(struct il_priv *il);
+int il3945_hw_rxq_stop(struct il_priv *il);
+int il3945_hw_set_hw_params(struct il_priv *il);
+int il3945_hw_nic_init(struct il_priv *il);
+int il3945_hw_nic_stop_master(struct il_priv *il);
+void il3945_hw_txq_ctx_free(struct il_priv *il);
+void il3945_hw_txq_ctx_stop(struct il_priv *il);
+int il3945_hw_nic_reset(struct il_priv *il);
+int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq,
+                                   dma_addr_t addr, u16 len, u8 reset, u8 pad);
+void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq);
+int il3945_hw_get_temperature(struct il_priv *il);
+int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq);
+unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il,
+                                     struct il3945_frame *frame, u8 rate);
 void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd,
                                 struct ieee80211_tx_info *info,
                                 struct ieee80211_hdr *hdr, int sta_id);
-extern int il3945_hw_reg_send_txpower(struct il_priv *il);
-extern int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power);
-extern void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb);
+int il3945_hw_reg_send_txpower(struct il_priv *il);
+int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power);
+void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb);
 void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb);
-extern void il3945_disable_events(struct il_priv *il);
-extern int il4965_get_temperature(const struct il_priv *il);
-extern void il3945_post_associate(struct il_priv *il);
-extern void il3945_config_ap(struct il_priv *il);
+void il3945_disable_events(struct il_priv *il);
+int il4965_get_temperature(const struct il_priv *il);
+void il3945_post_associate(struct il_priv *il);
+void il3945_config_ap(struct il_priv *il);
 
-extern int il3945_commit_rxon(struct il_priv *il);
+int il3945_commit_rxon(struct il_priv *il);
 
 /**
  * il3945_hw_find_station - Find station id for a given BSSID
@@ -257,14 +253,14 @@ extern int il3945_commit_rxon(struct il_priv *il);
  * not yet been merged into a single common layer for managing the
  * station tables.
  */
-extern u8 il3945_hw_find_station(struct il_priv *il, const u8 * bssid);
+u8 il3945_hw_find_station(struct il_priv *il, const u8 *bssid);
 
-extern __le32 il3945_get_antenna_flags(const struct il_priv *il);
-extern int il3945_init_hw_rate_table(struct il_priv *il);
-extern void il3945_reg_txpower_periodic(struct il_priv *il);
-extern int il3945_txpower_set_from_eeprom(struct il_priv *il);
+__le32 il3945_get_antenna_flags(const struct il_priv *il);
+int il3945_init_hw_rate_table(struct il_priv *il);
+void il3945_reg_txpower_periodic(struct il_priv *il);
+int il3945_txpower_set_from_eeprom(struct il_priv *il);
 
-extern int il3945_rs_next_rate(struct il_priv *il, int rate);
+int il3945_rs_next_rate(struct il_priv *il, int rate);
 
 /* scanning */
 int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif);
index 5ab50a5..3982ab7 100644 (file)
@@ -6706,7 +6706,6 @@ out_free_eeprom:
 out_iounmap:
        iounmap(il->hw_base);
 out_pci_release_regions:
-       pci_set_drvdata(pdev, NULL);
        pci_release_regions(pdev);
 out_pci_disable_device:
        pci_disable_device(pdev);
@@ -6787,7 +6786,6 @@ il4965_pci_remove(struct pci_dev *pdev)
        iounmap(il->hw_base);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        il4965_uninit_drv(il);
 
index 1b15b0b..337dfcf 100644 (file)
@@ -272,7 +272,7 @@ il4965_hw_valid_rtc_data_addr(u32 addr)
        ((t) < IL_TX_POWER_TEMPERATURE_MIN || \
         (t) > IL_TX_POWER_TEMPERATURE_MAX)
 
-extern void il4965_temperature_calib(struct il_priv *il);
+void il4965_temperature_calib(struct il_priv *il);
 /********************* END TEMPERATURE ***************************************/
 
 /********************* START TXPOWER *****************************************/
index 83f8ed8..ad123d6 100644 (file)
@@ -858,9 +858,9 @@ struct il_hw_params {
  * il4965_mac_     <-- mac80211 callback
  *
  ****************************************************************************/
-extern void il4965_update_chain_flags(struct il_priv *il);
+void il4965_update_chain_flags(struct il_priv *il);
 extern const u8 il_bcast_addr[ETH_ALEN];
-extern int il_queue_space(const struct il_queue *q);
+int il_queue_space(const struct il_queue *q);
 static inline int
 il_queue_used(const struct il_queue *q, int i)
 {
@@ -1727,7 +1727,7 @@ int il_alloc_txq_mem(struct il_priv *il);
 void il_free_txq_mem(struct il_priv *il);
 
 #ifdef CONFIG_IWLEGACY_DEBUGFS
-extern void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len);
+void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len);
 #else
 static inline void
 il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len)
@@ -1760,12 +1760,12 @@ void il_chswitch_done(struct il_priv *il, bool is_success);
 /*****************************************************
 * TX
 ******************************************************/
-extern void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq);
-extern int il_tx_queue_init(struct il_priv *il, u32 txq_id);
-extern void il_tx_queue_reset(struct il_priv *il, u32 txq_id);
-extern void il_tx_queue_unmap(struct il_priv *il, int txq_id);
-extern void il_tx_queue_free(struct il_priv *il, int txq_id);
-extern void il_setup_watchdog(struct il_priv *il);
+void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq);
+int il_tx_queue_init(struct il_priv *il, u32 txq_id);
+void il_tx_queue_reset(struct il_priv *il, u32 txq_id);
+void il_tx_queue_unmap(struct il_priv *il, int txq_id);
+void il_tx_queue_free(struct il_priv *il, int txq_id);
+void il_setup_watchdog(struct il_priv *il);
 /*****************************************************
  * TX power
  ****************************************************/
@@ -1931,10 +1931,10 @@ il_is_ready_rf(struct il_priv *il)
        return il_is_ready(il);
 }
 
-extern void il_send_bt_config(struct il_priv *il);
-extern int il_send_stats_request(struct il_priv *il, u8 flags, bool clear);
-extern void il_apm_stop(struct il_priv *il);
-extern void _il_apm_stop(struct il_priv *il);
+void il_send_bt_config(struct il_priv *il);
+int il_send_stats_request(struct il_priv *il, u8 flags, bool clear);
+void il_apm_stop(struct il_priv *il);
+void _il_apm_stop(struct il_priv *il);
 
 int il_apm_init(struct il_priv *il);
 
@@ -1968,15 +1968,15 @@ void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info,
 
 irqreturn_t il_isr(int irq, void *data);
 
-extern void il_set_bit(struct il_priv *p, u32 r, u32 m);
-extern void il_clear_bit(struct il_priv *p, u32 r, u32 m);
-extern bool _il_grab_nic_access(struct il_priv *il);
-extern int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout);
-extern int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout);
-extern u32 il_rd_prph(struct il_priv *il, u32 reg);
-extern void il_wr_prph(struct il_priv *il, u32 addr, u32 val);
-extern u32 il_read_targ_mem(struct il_priv *il, u32 addr);
-extern void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val);
+void il_set_bit(struct il_priv *p, u32 r, u32 m);
+void il_clear_bit(struct il_priv *p, u32 r, u32 m);
+bool _il_grab_nic_access(struct il_priv *il);
+int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout);
+int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout);
+u32 il_rd_prph(struct il_priv *il, u32 reg);
+void il_wr_prph(struct il_priv *il, u32 addr, u32 val);
+u32 il_read_targ_mem(struct il_priv *il, u32 addr);
+void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val);
 
 static inline void
 _il_write8(struct il_priv *il, u32 ofs, u8 val)
@@ -2868,13 +2868,13 @@ il4965_first_antenna(u8 mask)
  * The specific throughput table used is based on the type of network
  * the associated with, including A, B, G, and G w/ TGG protection
  */
-extern void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
+void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
 
 /* Initialize station's rate scaling information after adding station */
-extern void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta,
-                               u8 sta_id);
-extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta,
-                               u8 sta_id);
+void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta,
+                        u8 sta_id);
+void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta,
+                        u8 sta_id);
 
 /**
  * il_rate_control_register - Register the rate control algorithm callbacks
@@ -2886,8 +2886,8 @@ extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta,
  * ieee80211_register_hw
  *
  */
-extern int il4965_rate_control_register(void);
-extern int il3945_rate_control_register(void);
+int il4965_rate_control_register(void);
+int il3945_rate_control_register(void);
 
 /**
  * il_rate_control_unregister - Unregister the rate control callbacks
@@ -2895,11 +2895,11 @@ extern int il3945_rate_control_register(void);
  * This should be called after calling ieee80211_unregister_hw, but before
  * the driver is unloaded.
  */
-extern void il4965_rate_control_unregister(void);
-extern void il3945_rate_control_unregister(void);
+void il4965_rate_control_unregister(void);
+void il3945_rate_control_unregister(void);
 
-extern int il_power_update_mode(struct il_priv *il, bool force);
-extern void il_power_initialize(struct il_priv *il);
+int il_power_update_mode(struct il_priv *il, bool force);
+void il_power_initialize(struct il_priv *il);
 
 extern u32 il_debug_level;
 
index f2a86ff..23d5f02 100644 (file)
@@ -397,7 +397,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
        return cpu_to_le32(flags|(u32)rate);
 }
 
-extern int iwl_alive_start(struct iwl_priv *priv);
+int iwl_alive_start(struct iwl_priv *priv);
 
 #ifdef CONFIG_IWLWIFI_DEBUG
 void iwl_print_rx_config_cmd(struct iwl_priv *priv,
index a79fdd1..7434d9e 100644 (file)
@@ -270,7 +270,7 @@ struct iwl_sensitivity_ranges {
  * iwlXXXX_     <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
  *
  ****************************************************************************/
-extern void iwl_update_chain_flags(struct iwl_priv *priv);
+void iwl_update_chain_flags(struct iwl_priv *priv);
 extern const u8 iwl_bcast_addr[ETH_ALEN];
 
 #define IWL_OPERATION_MODE_AUTO     0
index 5d83cab..26fc550 100644 (file)
@@ -407,8 +407,8 @@ static inline u8 first_antenna(u8 mask)
 
 
 /* Initialize station's rate scaling information after adding station */
-extern void iwl_rs_rate_init(struct iwl_priv *priv,
-                            struct ieee80211_sta *sta, u8 sta_id);
+void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta,
+                     u8 sta_id);
 
 /**
  * iwl_rate_control_register - Register the rate control algorithm callbacks
@@ -420,7 +420,7 @@ extern void iwl_rs_rate_init(struct iwl_priv *priv,
  * ieee80211_register_hw
  *
  */
-extern int iwlagn_rate_control_register(void);
+int iwlagn_rate_control_register(void);
 
 /**
  * iwl_rate_control_unregister - Unregister the rate control callbacks
@@ -428,6 +428,6 @@ extern int iwlagn_rate_control_register(void);
  * This should be called after calling ieee80211_unregister_hw, but before
  * the driver is unloaded.
  */
-extern void iwlagn_rate_control_unregister(void);
+void iwlagn_rate_control_unregister(void);
 
 #endif /* __iwl_agn__rs__ */
index 86270b6..6363794 100644 (file)
@@ -330,15 +330,14 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
        enum iwl_ucode_type old_type;
        static const u8 alive_cmd[] = { REPLY_ALIVE };
 
-       old_type = priv->cur_ucode;
-       priv->cur_ucode = ucode_type;
        fw = iwl_get_ucode_image(priv, ucode_type);
+       if (WARN_ON(!fw))
+               return -EINVAL;
 
+       old_type = priv->cur_ucode;
+       priv->cur_ucode = ucode_type;
        priv->ucode_loaded = false;
 
-       if (!fw)
-               return -EINVAL;
-
        iwl_init_notification_wait(&priv->notif_wait, &alive_wait,
                                   alive_cmd, ARRAY_SIZE(alive_cmd),
                                   iwl_alive_fn, &alive_data);
index 76e14c0..85879db 100644 (file)
@@ -83,6 +83,8 @@
 #define IWL7260_TX_POWER_VERSION       0xffff /* meaningless */
 #define IWL3160_NVM_VERSION            0x709
 #define IWL3160_TX_POWER_VERSION       0xffff /* meaningless */
+#define IWL7265_NVM_VERSION            0x0a1d
+#define IWL7265_TX_POWER_VERSION       0xffff /* meaningless */
 
 #define IWL7260_FW_PRE "iwlwifi-7260-"
 #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
@@ -90,6 +92,9 @@
 #define IWL3160_FW_PRE "iwlwifi-3160-"
 #define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
 
+#define IWL7265_FW_PRE "iwlwifi-7265-"
+#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+
 static const struct iwl_base_params iwl7000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE,
        .num_of_queues = IWLAGN_NUM_QUEUES,
@@ -182,5 +187,14 @@ const struct iwl_cfg iwl3160_n_cfg = {
        .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
 };
 
+const struct iwl_cfg iwl7265_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC 7265",
+       .fw_name_pre = IWL7265_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7265_NVM_VERSION,
+       .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+};
+
 MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
 MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
index b03c25e..18f232e 100644 (file)
@@ -293,6 +293,7 @@ extern const struct iwl_cfg iwl7260_n_cfg;
 extern const struct iwl_cfg iwl3160_2ac_cfg;
 extern const struct iwl_cfg iwl3160_2n_cfg;
 extern const struct iwl_cfg iwl3160_n_cfg;
+extern const struct iwl_cfg iwl7265_2ac_cfg;
 #endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index a276af4..54a4fdc 100644 (file)
 #define CSR_DRAM_INT_TBL_ENABLE                (1 << 31)
 #define CSR_DRAM_INIT_TBL_WRAP_CHECK   (1 << 27)
 
+/* SECURE boot registers */
+#define CSR_SECURE_BOOT_CONFIG_ADDR    (0x100)
+enum secure_boot_config_reg {
+       CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP  = 0x00000001,
+       CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ        = 0x00000002,
+};
+
+#define CSR_SECURE_BOOT_CPU1_STATUS_ADDR       (0x100)
+#define CSR_SECURE_BOOT_CPU2_STATUS_ADDR       (0x100)
+enum secure_boot_status_reg {
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS          = 0x00000003,
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED       = 0x00000002,
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS         = 0x00000004,
+       CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL            = 0x00000008,
+       CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL       = 0x00000010,
+};
+
+#define CSR_UCODE_LOAD_STATUS_ADDR     (0x100)
+enum secure_load_status_reg {
+       CSR_CPU_STATUS_LOADING_STARTED                  = 0x00000001,
+       CSR_CPU_STATUS_LOADING_COMPLETED                = 0x00000002,
+       CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED            = 0x000000F8,
+       CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK         = 0x0000FF00,
+};
+
+#define CSR_SECURE_INSPECTOR_CODE_ADDR (0x100)
+#define CSR_SECURE_INSPECTOR_DATA_ADDR (0x100)
+
+#define CSR_SECURE_TIME_OUT    (100)
+
+#define FH_TCSR_0_REG0 (0x1D00)
+
 /*
  * HBUS (Host-side Bus)
  *
index 99e1da3..ff57002 100644 (file)
@@ -483,6 +483,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        const u8 *tlv_data;
        char buildstr[25];
        u32 build;
+       int num_of_cpus;
 
        if (len < sizeof(*ucode)) {
                IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -692,6 +693,42 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                goto invalid_tlv_len;
                        drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data);
                        break;
+                case IWL_UCODE_TLV_SECURE_SEC_RT:
+                       iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
+                                           tlv_len);
+                       drv->fw.mvm_fw = true;
+                       drv->fw.img[IWL_UCODE_REGULAR].is_secure = true;
+                       break;
+               case IWL_UCODE_TLV_SECURE_SEC_INIT:
+                       iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
+                                           tlv_len);
+                       drv->fw.mvm_fw = true;
+                       drv->fw.img[IWL_UCODE_INIT].is_secure = true;
+                       break;
+               case IWL_UCODE_TLV_SECURE_SEC_WOWLAN:
+                       iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
+                                           tlv_len);
+                       drv->fw.mvm_fw = true;
+                       drv->fw.img[IWL_UCODE_WOWLAN].is_secure = true;
+                       break;
+               case IWL_UCODE_TLV_NUM_OF_CPU:
+                       if (tlv_len != sizeof(u32))
+                               goto invalid_tlv_len;
+                       num_of_cpus =
+                               le32_to_cpup((__le32 *)tlv_data);
+
+                       if (num_of_cpus == 2) {
+                               drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus =
+                                       true;
+                               drv->fw.img[IWL_UCODE_INIT].is_dual_cpus =
+                                       true;
+                               drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus =
+                                       true;
+                       } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) {
+                               IWL_ERR(drv, "Driver support upto 2 CPUs\n");
+                               return -EINVAL;
+                       }
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
index 8b6c6fd..6c6c35c 100644 (file)
@@ -121,6 +121,10 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_SEC_WOWLAN        = 21,
        IWL_UCODE_TLV_DEF_CALIB         = 22,
        IWL_UCODE_TLV_PHY_SKU           = 23,
+       IWL_UCODE_TLV_SECURE_SEC_RT     = 24,
+       IWL_UCODE_TLV_SECURE_SEC_INIT   = 25,
+       IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
+       IWL_UCODE_TLV_NUM_OF_CPU        = 27,
 };
 
 struct iwl_ucode_tlv {
index a122368..75db087 100644 (file)
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
  * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
  * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
+ *     offload profile config command.
  * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api
  * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API.
  * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
  *     (rather than two) IPv6 addresses
  * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
+ * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
+ *     from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping
+ *     connection when going back to D0
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
+ * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan.
+ * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
+ * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
+ *     containing CAM (Continuous Active Mode) indication.
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -87,11 +99,21 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_MFP                 = BIT(2),
        IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
        IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
-       IWL_UCODE_TLV_FLAGS_UAPSD               = BIT(6),
+       IWL_UCODE_TLV_FLAGS_NEWBT_COEX          = BIT(5),
+       IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT      = BIT(6),
+       IWL_UCODE_TLV_FLAGS_SHORT_BL            = BIT(7),
        IWL_UCODE_TLV_FLAGS_RX_ENERGY_API       = BIT(8),
        IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2   = BIT(9),
        IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
        IWL_UCODE_TLV_FLAGS_BF_UPDATED          = BIT(11),
+       IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID       = BIT(12),
+       IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API   = BIT(14),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL    = BIT(15),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE    = BIT(16),
+       IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
+       IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
+       IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD       = BIT(20),
+       IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -133,7 +155,8 @@ enum iwl_ucode_sec {
  * For 16.0 uCode and above, there is no differentiation between sections,
  * just an offset to the HW address.
  */
-#define IWL_UCODE_SECTION_MAX 4
+#define IWL_UCODE_SECTION_MAX 6
+#define IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU  (IWL_UCODE_SECTION_MAX/2)
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
@@ -150,6 +173,8 @@ struct fw_desc {
 
 struct fw_img {
        struct fw_desc sec[IWL_UCODE_SECTION_MAX];
+       bool is_secure;
+       bool is_dual_cpus;
 };
 
 /* uCode version contains 4 values: Major/Minor/API/Serial */
index dfa4d2e..ad8e19a 100644 (file)
@@ -34,7 +34,6 @@
 #include "iwl-csr.h"
 #include "iwl-debug.h"
 #include "iwl-fh.h"
-#include "iwl-csr.h"
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
index ff8cc75..a70c7b9 100644 (file)
@@ -97,6 +97,8 @@
 
 #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS         (0x00000800)
 
+#define APMG_RTC_INT_STT_RFKILL                (0x10000000)
+
 /* Device system time */
 #define DEVICE_SYSTEM_TIME_REG 0xA0206C
 
index c6bac7c..143292b 100644 (file)
@@ -344,7 +344,7 @@ struct iwl_trans_config {
        u8 cmd_queue;
        u8 cmd_fifo;
        const u8 *no_reclaim_cmds;
-       int n_no_reclaim_cmds;
+       unsigned int n_no_reclaim_cmds;
 
        bool rx_buf_size_8k;
        bool bc_table_dword;
index 0fad98b..5d066cb 100644 (file)
@@ -98,126 +98,258 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
 
 #undef EVENT_PRIO_ANT
 
-/* BT Antenna Coupling Threshold (dB) */
-#define IWL_BT_ANTENNA_COUPLING_THRESHOLD      (35)
-#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD       (3)
-
 #define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD    (-62)
 #define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD   (-65)
-#define BT_REDUCED_TX_POWER_BIT                        BIT(7)
-
-static inline bool is_loose_coex(void)
-{
-       return iwlwifi_mod_params.ant_coupling >
-               IWL_BT_ANTENNA_COUPLING_THRESHOLD;
-}
+#define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
 {
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
+               return 0;
+
        return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
                                    sizeof(struct iwl_bt_coex_prio_tbl_cmd),
                                    &iwl_bt_prio_tbl);
 }
 
-static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type)
-{
-       struct iwl_bt_coex_prot_env_cmd env_cmd;
-       int ret;
-
-       env_cmd.action = action;
-       env_cmd.type = type;
-       ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC,
-                                  sizeof(env_cmd), &env_cmd);
-       if (ret)
-               IWL_ERR(mvm, "failed to send BT env command\n");
-       return ret;
-}
-
-enum iwl_bt_kill_msk {
-       BT_KILL_MSK_DEFAULT,
-       BT_KILL_MSK_SCO_HID_A2DP,
-       BT_KILL_MSK_REDUCED_TXPOW,
-       BT_KILL_MSK_MAX,
-};
-
-static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
+const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
        [BT_KILL_MSK_DEFAULT] = 0xffff0000,
        [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
        [BT_KILL_MSK_REDUCED_TXPOW] = 0,
 };
 
-static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
+const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
        [BT_KILL_MSK_DEFAULT] = 0xffff0000,
        [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
        [BT_KILL_MSK_REDUCED_TXPOW] = 0,
 };
 
-#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0)
-
-/* Tight Coex */
-static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaeaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xcc00ff28),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0xcc00aaaa),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0xc0004000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0xf0005000),
-       cpu_to_le32(0xf0005000),
+static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
+       cpu_to_le32(0xf0f0f0f0),
+       cpu_to_le32(0xc0c0c0c0),
+       cpu_to_le32(0xfcfcfcfc),
+       cpu_to_le32(0xff00ff00),
 };
 
-/* Loose Coex */
-static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xcc00ff28),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0xcc00aaaa),
-       cpu_to_le32(0x0000aaaa),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0xf0005000),
-       cpu_to_le32(0xf0005000),
+static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
 };
 
-/* Full concurrency */
-static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x00000000),
+static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               /* Tight */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaeaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Loose */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Tx Tx disabled */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xC0004000),
+               cpu_to_le32(0xC0004000),
+               cpu_to_le32(0xF0005000),
+               cpu_to_le32(0xF0005000),
+       },
 };
 
-/* single shared antenna */
-static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = {
-       cpu_to_le32(0x40000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x44000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x40000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0x44000000),
-       cpu_to_le32(0x00000000),
-       cpu_to_le32(0xC0004000),
-       cpu_to_le32(0xF0005000),
-       cpu_to_le32(0xC0004000),
-       cpu_to_le32(0xF0005000),
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+       /* dummy entry for channel 0 */
+       {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+       {
+               cpu_to_le64(0x0000001FFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000000FFFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000003FFFCULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+       },
+       {
+               cpu_to_le64(0x00001FFFE0ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+       },
+       {
+               cpu_to_le64(0x00007FFF80ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+       },
+       {
+               cpu_to_le64(0x0003FFFC00ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+       },
+       {
+               cpu_to_le64(0x000FFFF000ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+       },
+       {
+               cpu_to_le64(0x007FFF8000ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+       },
+       {
+               cpu_to_le64(0x01FFFE0000ULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+       },
+       {
+               cpu_to_le64(0x0FFFF00000ULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+               cpu_to_le64(0x0ULL),
+       },
+       {
+               cpu_to_le64(0x3FFFC00000ULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFFE000000ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFF8000000ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFE00000000ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0)
+       },
 };
 
+static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
+       cpu_to_le32(0x22002200),
+       cpu_to_le32(0x33113311),
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum iwl_bt_coex_lut_type ret;
+       u16 phy_ctx_id;
+
+       /*
+        * Checking that we hold mvm->mutex is a good idea, but the rate
+        * control can't acquire the mutex since it runs in Tx path.
+        * So this is racy in that case, but in the worst case, the AMPDU
+        * size limit will be wrong for a short time which is not a big
+        * issue.
+        */
+
+       rcu_read_lock();
+
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       if (!chanctx_conf ||
+            chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return BT_COEX_LOOSE_LUT;
+       }
+
+       ret = BT_COEX_TX_DIS_LUT;
+
+       if (mvm->cfg->bt_shared_single_ant) {
+               rcu_read_unlock();
+               return ret;
+       }
+
+       phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+
+       if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
+       else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
+       /* else - default = TX TX disallowed */
+
+       rcu_read_unlock();
+
+       return ret;
+}
+
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
 {
        struct iwl_bt_coex_cmd *bt_cmd;
@@ -228,17 +360,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                .flags = CMD_SYNC,
        };
        int ret;
+       u32 flags;
 
-       /* go to CALIB state in internal BT-Coex state machine */
-       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
-                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
-
-       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
-                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
+               return 0;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
@@ -246,40 +371,52 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
        cmd.data[0] = bt_cmd;
 
        bt_cmd->max_kill = 5;
-       bt_cmd->bt3_time_t7_value = 1;
-       bt_cmd->bt3_prio_sample_time = 2;
-       bt_cmd->bt3_timer_t2_value = 0xc;
+       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD,
+       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling,
+       bt_cmd->bt4_tx_tx_delta_freq_thr = 15,
+       bt_cmd->bt4_tx_rx_max_freq0 = 15,
 
-       bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ?
+       flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
-       bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
+       flags |= BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN | BT_SYNC_2_BT_DISABLE;
+       bt_cmd->flags = cpu_to_le32(flags);
 
-       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
+       bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
                                            BT_VALID_BT_PRIO_BOOST |
                                            BT_VALID_MAX_KILL |
                                            BT_VALID_3W_TMRS |
                                            BT_VALID_KILL_ACK |
                                            BT_VALID_KILL_CTS |
                                            BT_VALID_REDUCED_TX_POWER |
-                                           BT_VALID_LUT);
+                                           BT_VALID_LUT |
+                                           BT_VALID_WIFI_RX_SW_PRIO_BOOST |
+                                           BT_VALID_WIFI_TX_SW_PRIO_BOOST |
+                                           BT_VALID_MULTI_PRIO_LUT |
+                                           BT_VALID_CORUN_LUT_20 |
+                                           BT_VALID_CORUN_LUT_40 |
+                                           BT_VALID_ANT_ISOLATION |
+                                           BT_VALID_ANT_ISOLATION_THRS |
+                                           BT_VALID_TXTX_DELTA_FREQ_THRS |
+                                           BT_VALID_TXRX_MAX_FREQ_0);
 
        if (mvm->cfg->bt_shared_single_ant)
-               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup,
-                      sizeof(iwl_single_shared_ant_lookup));
-       else if (is_loose_coex())
-               memcpy(&bt_cmd->decision_lut, iwl_loose_lookup,
-                      sizeof(iwl_tight_lookup));
+               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
+                      sizeof(iwl_single_shared_ant));
        else
-               memcpy(&bt_cmd->decision_lut, iwl_tight_lookup,
-                      sizeof(iwl_tight_lookup));
+               memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
+                      sizeof(iwl_combined_lookup));
 
-       bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
+       memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+              sizeof(iwl_bt_prio_boost));
+       memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+              sizeof(iwl_bt_mprio_lut));
        bt_cmd->kill_ack_msk =
                cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
        bt_cmd->kill_cts_msk =
                cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
 
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+       memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
 
@@ -334,13 +471,17 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
        if (!bt_cmd)
                return -ENOMEM;
        cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
 
        bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
        bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
-       bt_cmd->valid_bit_msk =
-               cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+                                            BT_VALID_KILL_ACK |
+                                            BT_VALID_KILL_CTS);
 
-       IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
+       IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
+                      iwl_bt_ack_kill_msk[bt_kill_msk],
+                      iwl_bt_cts_kill_msk[bt_kill_msk]);
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
 
@@ -364,12 +505,16 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        struct iwl_mvm_sta *mvmsta;
        int ret;
 
-       /* This can happen if the station has been removed right now */
        if (sta_id == IWL_MVM_STATION_COUNT)
                return 0;
 
        sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                        lockdep_is_held(&mvm->mutex));
+
+       /* This can happen if the station has been removed right now */
+       if (IS_ERR_OR_NULL(sta))
+               return 0;
+
        mvmsta = (void *)sta->drv_priv;
 
        /* nothing to do */
@@ -380,8 +525,10 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        if (!bt_cmd)
                return -ENOMEM;
        cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
 
-       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
+       bt_cmd->valid_bit_msk =
+               cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
        bt_cmd->bt_reduced_tx_power = sta_id;
 
        if (enable)
@@ -403,8 +550,25 @@ struct iwl_bt_iterator_data {
        struct iwl_mvm *mvm;
        u32 num_bss_ifaces;
        bool reduced_tx_power;
+       struct ieee80211_chanctx_conf *primary;
+       struct ieee80211_chanctx_conf *secondary;
 };
 
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      bool enable, int rssi)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->bf_data.last_bt_coex_event = rssi;
+       mvmvif->bf_data.bt_coex_max_thold =
+               enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+       mvmvif->bf_data.bt_coex_min_thold =
+               enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+}
+
+/* must be called under rcu_read_lock */
 static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                                      struct ieee80211_vif *vif)
 {
@@ -413,65 +577,94 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        struct iwl_mvm *mvm = data->mvm;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum ieee80211_smps_mode smps_mode;
-       enum ieee80211_band band;
        int ave_rssi;
 
        lockdep_assert_held(&mvm->mutex);
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(vif->chanctx_conf);
-       if (chanctx_conf && chanctx_conf->def.chan)
-               band = chanctx_conf->def.chan->band;
-       else
-               band = -1;
-       rcu_read_unlock();
+       if (vif->type != NL80211_IFTYPE_STATION &&
+           vif->type != NL80211_IFTYPE_AP)
+               return;
 
        smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-       /* non associated BSSes aren't to be considered */
-       if (!vif->bss_conf.assoc)
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       /* If channel context is invalid or not on 2.4GHz .. */
+       if ((!chanctx_conf ||
+            chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+               /* ... and it is an associated STATION, relax constraints */
+               if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc)
+                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                           smps_mode);
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
+       }
+
+       /* SoftAP / GO will always be primary */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               if (!mvmvif->ap_ibss_active)
+                       return;
 
-       if (band != IEEE80211_BAND_2GHZ) {
-               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                   smps_mode);
+               /* the Ack / Cts kill mask must be default if AP / GO */
+               data->reduced_tx_power = false;
+
+               if (chanctx_conf == data->primary)
+                       return;
+
+               /* downgrade the current primary no matter what its type is */
+               data->secondary = data->primary;
+               data->primary = chanctx_conf;
                return;
        }
 
-       if (data->notif->bt_status)
-               smps_mode = IEEE80211_SMPS_DYNAMIC;
+       data->num_bss_ifaces++;
+
+       /* we are now a STA / P2P Client, and take associated ones only */
+       if (!vif->bss_conf.assoc)
+               return;
+
+       /* STA / P2P Client, try to be primary if first vif */
+       if (!data->primary || data->primary == chanctx_conf)
+               data->primary = chanctx_conf;
+       else if (!data->secondary)
+               /* if secondary is not NULL, it might be a GO */
+               data->secondary = chanctx_conf;
 
-       if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD)
+       if (le32_to_cpu(data->notif->bt_activity_grading) >= BT_HIGH_TRAFFIC)
                smps_mode = IEEE80211_SMPS_STATIC;
+       else if (le32_to_cpu(data->notif->bt_activity_grading) >=
+                BT_LOW_TRAFFIC)
+               smps_mode = IEEE80211_SMPS_DYNAMIC;
 
        IWL_DEBUG_COEX(data->mvm,
-                      "mac %d: bt_status %d traffic_load %d smps_req %d\n",
+                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
                       mvmvif->id,  data->notif->bt_status,
-                      data->notif->bt_traffic_load, smps_mode);
+                      data->notif->bt_activity_grading, smps_mode);
 
        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
 
        /* don't reduce the Tx power if in loose scheme */
-       if (is_loose_coex())
+       if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+           mvm->cfg->bt_shared_single_ant) {
+               data->reduced_tx_power = false;
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
+       }
 
-       data->num_bss_ifaces++;
-
-       /* reduced Txpower only if there are open BT connections, so ...*/
-       if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) {
+       /* reduced Txpower only if BT is on, so ...*/
+       if (!data->notif->bt_status) {
                /* ... cancel reduced Tx power ... */
                if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
                        IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
                data->reduced_tx_power = false;
 
                /* ... and there is no need to get reports on RSSI any more. */
-               ieee80211_disable_rssi_reports(vif);
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
        }
 
-       ave_rssi = ieee80211_ave_rssi(vif);
+       /* try to get the avg rssi from fw */
+       ave_rssi = mvmvif->bf_data.ave_beacon_signal;
 
        /* if the RSSI isn't valid, fake it is very low */
        if (!ave_rssi)
@@ -499,8 +692,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        }
 
        /* Begin to monitor the RSSI: it may influence the reduced Tx power */
-       ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD,
-                                     BT_ENABLE_REDUCED_TXPOWER_THRESHOLD);
+       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
 }
 
 static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
@@ -510,11 +702,72 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                .notif = &mvm->last_bt_notif,
                .reduced_tx_power = true,
        };
+       struct iwl_bt_coex_ci_cmd cmd = {};
+       u8 ci_bw_idx;
 
+       rcu_read_lock();
        ieee80211_iterate_active_interfaces_atomic(
                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                                        iwl_mvm_bt_notif_iterator, &data);
 
+       if (data.primary) {
+               struct ieee80211_chanctx_conf *chan = data.primary;
+               if (WARN_ON(!chan->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_primary = 0;
+               } else {
+                       cmd.co_run_bw_primary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_primary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+       }
+
+       if (data.secondary) {
+               struct ieee80211_chanctx_conf *chan = data.secondary;
+               if (WARN_ON(!data.secondary->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_secondary = 0;
+               } else {
+                       cmd.co_run_bw_secondary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_secondary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+       }
+
+       rcu_read_unlock();
+
+       /* Don't spam the fw with the same command over and over */
+       if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
+               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, CMD_SYNC,
+                                        sizeof(cmd), &cmd))
+                       IWL_ERR(mvm, "Failed to send BT_CI cmd");
+               memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
+       }
+
        /*
         * If there are no BSS / P2P client interfaces, reduced Tx Power is
         * irrelevant since it is based on the RSSI coming from the beacon.
@@ -536,12 +789,18 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
 
 
        IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
-       IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not ");
+       IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
+                      notif->bt_status ? "ON" : "OFF");
        IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
-       IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load);
+       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+       IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+                      le32_to_cpu(notif->primary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+                      le32_to_cpu(notif->secondary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+                      le32_to_cpu(notif->bt_activity_grading));
        IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
                       notif->bt_agg_traffic_load);
-       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
 
        /* remember this notification for future use: rssi fluctuations */
        memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
@@ -565,6 +824,18 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
        struct ieee80211_sta *sta;
        struct iwl_mvm_sta *mvmsta;
 
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       /* If channel context is invalid or not on 2.4GHz - don't count it */
+       if (!chanctx_conf ||
+           chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return;
+       }
+       rcu_read_unlock();
+
        if (vif->type != NL80211_IFTYPE_STATION ||
            mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
                return;
@@ -594,15 +865,15 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        };
        int ret;
 
-       mutex_lock(&mvm->mutex);
+       lockdep_assert_held(&mvm->mutex);
 
        /* Rssi update while not associated ?! */
        if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT))
-               goto out_unlock;
+               return;
 
-       /* No open connection - reports should be disabled */
-       if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2))
-               goto out_unlock;
+       /* No BT - reports should be disabled */
+       if (!mvm->last_bt_notif.bt_status)
+               return;
 
        IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
                       rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
@@ -611,7 +882,8 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
         * Check if rssi is good enough for reduced Tx power, but not in loose
         * scheme.
         */
-       if (rssi_event == RSSI_EVENT_LOW || is_loose_coex())
+       if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+           iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
                ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
                                                  false);
        else
@@ -633,12 +905,52 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
 
- out_unlock:
-       mutex_unlock(&mvm->mutex);
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT        (1200)
+
+u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm,
+                                  struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+           BT_LOW_TRAFFIC)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+       if (lut_type == BT_COEX_LOOSE_LUT)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       /* tight coex, high bt traffic, reduce AGG time limit */
+       return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
 }
 
-void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
+                                    struct ieee80211_sta *sta)
 {
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
+               return true;
+
+       /*
+        * In Tight, BT can't Rx while we Tx, so use both antennas since BT is
+        * already killed.
+        * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we
+        * Tx.
+        */
+       return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
+}
+
+void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
+{
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
+               return;
+
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
index 2bf29f7..4b6d670 100644 (file)
@@ -70,7 +70,9 @@
 #define IWL_MVM_UAPSD_RX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
 #define IWL_MVM_UAPSD_TX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
 #define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS       20
-#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS       20
+#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS       8
+#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS        30
+#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS        20
 #define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT       50
 #define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT       50
 #define IWL_MVM_PS_SNOOZE_INTERVAL             25
index 417639f..6f45966 100644 (file)
@@ -67,6 +67,7 @@
 #include <net/cfg80211.h>
 #include <net/ipv6.h>
 #include <net/tcp.h>
+#include <net/addrconf.h>
 #include "iwl-modparams.h"
 #include "fw-api.h"
 #include "mvm.h"
@@ -381,14 +382,74 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
        union {
                struct iwl_proto_offload_cmd_v1 v1;
                struct iwl_proto_offload_cmd_v2 v2;
+               struct iwl_proto_offload_cmd_v3_small v3s;
+               struct iwl_proto_offload_cmd_v3_large v3l;
        } cmd = {};
+       struct iwl_host_cmd hcmd = {
+               .id = PROT_OFFLOAD_CONFIG_CMD,
+               .flags = CMD_SYNC,
+               .data[0] = &cmd,
+               .dataflags[0] = IWL_HCMD_DFL_DUP,
+       };
        struct iwl_proto_offload_cmd_common *common;
        u32 enabled = 0, size;
+       u32 capa_flags = mvm->fw->ucode_capa.flags;
 #if IS_ENABLED(CONFIG_IPV6)
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int i;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+       if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
+           capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+               struct iwl_ns_config *nsc;
+               struct iwl_targ_addr *addrs;
+               int n_nsc, n_addrs;
+               int c;
+
+               if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+                       nsc = cmd.v3s.ns_config;
+                       n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
+                       addrs = cmd.v3s.targ_addrs;
+                       n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
+               } else {
+                       nsc = cmd.v3l.ns_config;
+                       n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
+                       addrs = cmd.v3l.targ_addrs;
+                       n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
+               }
+
+               if (mvmvif->num_target_ipv6_addrs)
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+
+               /*
+                * For each address we have (and that will fit) fill a target
+                * address struct and combine for NS offload structs with the
+                * solicited node addresses.
+                */
+               for (i = 0, c = 0;
+                    i < mvmvif->num_target_ipv6_addrs &&
+                    i < n_addrs && c < n_nsc; i++) {
+                       struct in6_addr solicited_addr;
+                       int j;
+
+                       addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
+                                                 &solicited_addr);
+                       for (j = 0; j < c; j++)
+                               if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
+                                                 &solicited_addr) == 0)
+                                       break;
+                       if (j == c)
+                               c++;
+                       addrs[i].addr = mvmvif->target_ipv6_addrs[i];
+                       addrs[i].config_num = cpu_to_le32(j);
+                       nsc[j].dest_ipv6_addr = solicited_addr;
+                       memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
+               }
+
+               if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
+                       cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
+               else
+                       cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
                if (mvmvif->num_target_ipv6_addrs) {
                        enabled |= IWL_D3_PROTO_OFFLOAD_NS;
                        memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
@@ -419,7 +480,13 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
        }
 #endif
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+       if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+               common = &cmd.v3s.common;
+               size = sizeof(cmd.v3s);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+               common = &cmd.v3l.common;
+               size = sizeof(cmd.v3l);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
                common = &cmd.v2.common;
                size = sizeof(cmd.v2);
        } else {
@@ -438,8 +505,8 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 
        common->enabled = cpu_to_le32(enabled);
 
-       return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
-                                   size, &cmd);
+       hcmd.len[0] = size;
+       return iwl_mvm_send_cmd(mvm, &hcmd);
 }
 
 enum iwl_mvm_tcp_packet_type {
@@ -793,6 +860,74 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return 0;
 }
 
+static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_nonqos_seq_query_cmd query_cmd = {
+               .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET),
+               .mac_id_n_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                       mvmvif->color)),
+       };
+       struct iwl_host_cmd cmd = {
+               .id = NON_QOS_TX_COUNTER_CMD,
+               .flags = CMD_SYNC | CMD_WANT_SKB,
+       };
+       int err;
+       u32 size;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
+               cmd.data[0] = &query_cmd;
+               cmd.len[0] = sizeof(query_cmd);
+       }
+
+       err = iwl_mvm_send_cmd(mvm, &cmd);
+       if (err)
+               return err;
+
+       size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+       size -= sizeof(cmd.resp_pkt->hdr);
+       if (size < sizeof(__le16)) {
+               err = -EINVAL;
+       } else {
+               err = le16_to_cpup((__le16 *)cmd.resp_pkt->data);
+               /* new API returns next, not last-used seqno */
+               if (mvm->fw->ucode_capa.flags &
+                               IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
+                       err -= 0x10;
+       }
+
+       iwl_free_resp(&cmd);
+       return err;
+}
+
+void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_nonqos_seq_query_cmd query_cmd = {
+               .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET),
+               .mac_id_n_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                       mvmvif->color)),
+               .value = cpu_to_le16(mvmvif->seqno),
+       };
+
+       /* return if called during restart, not resume from D3 */
+       if (!mvmvif->seqno_valid)
+               return;
+
+       mvmvif->seqno_valid = false;
+
+       if (!(mvm->fw->ucode_capa.flags &
+                       IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API))
+               return;
+
+       if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC,
+                                sizeof(query_cmd), &query_cmd))
+               IWL_ERR(mvm, "failed to set non-QoS seqno\n");
+}
+
 static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                             struct cfg80211_wowlan *wowlan,
                             bool test)
@@ -829,7 +964,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        };
        int ret, i;
        int len __maybe_unused;
-       u16 seq;
        u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
 
        if (!wowlan) {
@@ -872,26 +1006,15 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
 
-       /*
-        * The D3 firmware still hardcodes the AP station ID for the
-        * BSS we're associated with as 0. Store the real STA ID here
-        * and assign 0. When we leave this function, we'll restore
-        * the original value for the resume code.
-        */
-       old_ap_sta_id = mvm_ap_sta->sta_id;
-       mvm_ap_sta->sta_id = 0;
-       mvmvif->ap_sta_id = 0;
-
        /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */
 
        wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported;
 
-       /*
-        * We know the last used seqno, and the uCode expects to know that
-        * one, it will increment before TX.
-        */
-       seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ;
-       wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq);
+       /* Query the last used seqno and set it */
+       ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
+       if (ret < 0)
+               goto out_noreset;
+       wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret);
 
        /*
         * For QoS counters, we store the one to use next, so subtract 0x10
@@ -899,7 +1022,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
         * increment after using the value (i.e. store the next value to use).
         */
        for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
-               seq = mvm_ap_sta->tid_data[i].seq_number;
+               u16 seq = mvm_ap_sta->tid_data[i].seq_number;
                seq -= 0x10;
                wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq);
        }
@@ -944,6 +1067,16 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        iwl_trans_stop_device(mvm->trans);
 
+       /*
+        * The D3 firmware still hardcodes the AP station ID for the
+        * BSS we're associated with as 0. Store the real STA ID here
+        * and assign 0. When we leave this function, we'll restore
+        * the original value for the resume code.
+        */
+       old_ap_sta_id = mvm_ap_sta->sta_id;
+       mvm_ap_sta->sta_id = 0;
+       mvmvif->ap_sta_id = 0;
+
        /*
         * Set the HW restart bit -- this is mostly true as we're
         * going to load new firmware and reprogram that, though
@@ -1059,6 +1192,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
+       ret = iwl_mvm_power_update_device_mode(mvm);
+       if (ret)
+               goto out;
+
        ret = iwl_mvm_power_update_mode(mvm, vif);
        if (ret)
                goto out;
@@ -1109,16 +1246,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        return __iwl_mvm_suspend(hw, wowlan, false);
 }
 
+/* converted data from the different status responses */
+struct iwl_wowlan_status_data {
+       u16 pattern_number;
+       u16 qos_seq_ctr[8];
+       u32 wakeup_reasons;
+       u32 wake_packet_length;
+       u32 wake_packet_bufsize;
+       const u8 *wake_packet;
+};
+
 static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
                                          struct ieee80211_vif *vif,
-                                         struct iwl_wowlan_status *status)
+                                         struct iwl_wowlan_status_data *status)
 {
        struct sk_buff *pkt = NULL;
        struct cfg80211_wowlan_wakeup wakeup = {
                .pattern_idx = -1,
        };
        struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
-       u32 reasons = le32_to_cpu(status->wakeup_reasons);
+       u32 reasons = status->wakeup_reasons;
 
        if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
                wakeup_report = NULL;
@@ -1130,7 +1277,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
 
        if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
                wakeup.pattern_idx =
-                       le16_to_cpu(status->pattern_number);
+                       status->pattern_number;
 
        if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
                       IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH))
@@ -1158,8 +1305,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
                wakeup.tcp_match = true;
 
        if (status->wake_packet_bufsize) {
-               int pktsize = le32_to_cpu(status->wake_packet_bufsize);
-               int pktlen = le32_to_cpu(status->wake_packet_length);
+               int pktsize = status->wake_packet_bufsize;
+               int pktlen = status->wake_packet_length;
                const u8 *pktdata = status->wake_packet;
                struct ieee80211_hdr *hdr = (void *)pktdata;
                int truncated = pktlen - pktsize;
@@ -1239,8 +1386,229 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
        kfree_skb(pkt);
 }
 
+static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc,
+                                 struct ieee80211_key_seq *seq)
+{
+       u64 pn;
+
+       pn = le64_to_cpu(sc->pn);
+       seq->ccmp.pn[0] = pn >> 40;
+       seq->ccmp.pn[1] = pn >> 32;
+       seq->ccmp.pn[2] = pn >> 24;
+       seq->ccmp.pn[3] = pn >> 16;
+       seq->ccmp.pn[4] = pn >> 8;
+       seq->ccmp.pn[5] = pn;
+}
+
+static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc,
+                                  struct ieee80211_key_seq *seq)
+{
+       seq->tkip.iv32 = le32_to_cpu(sc->iv32);
+       seq->tkip.iv16 = le16_to_cpu(sc->iv16);
+}
+
+static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs,
+                                  struct ieee80211_key_conf *key)
+{
+       int tid;
+
+       BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
+
+       for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+               struct ieee80211_key_seq seq = {};
+
+               iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
+               ieee80211_set_key_rx_seq(key, tid, &seq);
+       }
+}
+
+static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
+                                   struct ieee80211_key_conf *key)
+{
+       int tid;
+
+       BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
+
+       for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+               struct ieee80211_key_seq seq = {};
+
+               iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq);
+               ieee80211_set_key_rx_seq(key, tid, &seq);
+       }
+}
+
+static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
+                                  struct iwl_wowlan_status_v6 *status)
+{
+       union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key);
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key);
+               break;
+       default:
+               WARN_ON(1);
+       }
+}
+
+struct iwl_mvm_d3_gtk_iter_data {
+       struct iwl_wowlan_status_v6 *status;
+       void *last_gtk;
+       u32 cipher;
+       bool find_phase, unhandled_cipher;
+       int num_keys;
+};
+
+static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_sta *sta,
+                                  struct ieee80211_key_conf *key,
+                                  void *_data)
+{
+       struct iwl_mvm_d3_gtk_iter_data *data = _data;
+
+       if (data->unhandled_cipher)
+               return;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               /* ignore WEP completely, nothing to do */
+               return;
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_TKIP:
+               /* we support these */
+               break;
+       default:
+               /* everything else (even CMAC for MFP) - disconnect from AP */
+               data->unhandled_cipher = true;
+               return;
+       }
+
+       data->num_keys++;
+
+       /*
+        * pairwise key - update sequence counters only;
+        * note that this assumes no TDLS sessions are active
+        */
+       if (sta) {
+               struct ieee80211_key_seq seq = {};
+               union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc;
+
+               if (data->find_phase)
+                       return;
+
+               switch (key->cipher) {
+               case WLAN_CIPHER_SUITE_CCMP:
+                       iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq);
+                       iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key);
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq);
+                       iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key);
+                       break;
+               }
+               ieee80211_set_key_tx_seq(key, &seq);
+
+               /* that's it for this key */
+               return;
+       }
+
+       if (data->find_phase) {
+               data->last_gtk = key;
+               data->cipher = key->cipher;
+               return;
+       }
+
+       if (data->status->num_of_gtk_rekeys)
+               ieee80211_remove_key(key);
+       else if (data->last_gtk == key)
+               iwl_mvm_set_key_rx_seq(key, data->status);
+}
+
+static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_wowlan_status_v6 *status)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_d3_gtk_iter_data gtkdata = {
+               .status = status,
+       };
+
+       if (!status || !vif->bss_conf.bssid)
+               return false;
+
+       /* find last GTK that we used initially, if any */
+       gtkdata.find_phase = true;
+       ieee80211_iter_keys(mvm->hw, vif,
+                           iwl_mvm_d3_update_gtks, &gtkdata);
+       /* not trying to keep connections with MFP/unhandled ciphers */
+       if (gtkdata.unhandled_cipher)
+               return false;
+       if (!gtkdata.num_keys)
+               return true;
+       if (!gtkdata.last_gtk)
+               return false;
+
+       /*
+        * invalidate all other GTKs that might still exist and update
+        * the one that we used
+        */
+       gtkdata.find_phase = false;
+       ieee80211_iter_keys(mvm->hw, vif,
+                           iwl_mvm_d3_update_gtks, &gtkdata);
+
+       if (status->num_of_gtk_rekeys) {
+               struct ieee80211_key_conf *key;
+               struct {
+                       struct ieee80211_key_conf conf;
+                       u8 key[32];
+               } conf = {
+                       .conf.cipher = gtkdata.cipher,
+                       .conf.keyidx = status->gtk.key_index,
+               };
+
+               switch (gtkdata.cipher) {
+               case WLAN_CIPHER_SUITE_CCMP:
+                       conf.conf.keylen = WLAN_KEY_LEN_CCMP;
+                       memcpy(conf.conf.key, status->gtk.decrypt_key,
+                              WLAN_KEY_LEN_CCMP);
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       conf.conf.keylen = WLAN_KEY_LEN_TKIP;
+                       memcpy(conf.conf.key, status->gtk.decrypt_key, 16);
+                       /* leave TX MIC key zeroed, we don't use it anyway */
+                       memcpy(conf.conf.key +
+                              NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+                              status->gtk.tkip_mic_key, 8);
+                       break;
+               }
+
+               key = ieee80211_gtk_rekey_add(vif, &conf.conf);
+               if (IS_ERR(key))
+                       return false;
+               iwl_mvm_set_key_rx_seq(key, status);
+       }
+
+       if (status->num_of_gtk_rekeys) {
+               __be64 replay_ctr =
+                       cpu_to_be64(le64_to_cpu(status->replay_ctr));
+               ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
+                                          (void *)&replay_ctr, GFP_KERNEL);
+       }
+
+       mvmvif->seqno_valid = true;
+       /* +0x10 because the set API expects next-to-use, not last-used */
+       mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10;
+
+       return true;
+}
+
 /* releases the MVM mutex */
-static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *vif)
 {
        u32 base = mvm->error_event_table;
@@ -1253,8 +1621,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                .id = WOWLAN_GET_STATUSES,
                .flags = CMD_SYNC | CMD_WANT_SKB,
        };
-       struct iwl_wowlan_status *status;
-       int ret, len;
+       struct iwl_wowlan_status_data status;
+       struct iwl_wowlan_status_v6 *status_v6;
+       int ret, len, status_size, i;
+       bool keep;
+       struct ieee80211_sta *ap_sta;
+       struct iwl_mvm_sta *mvm_ap_sta;
 
        iwl_trans_read_mem_bytes(mvm->trans, base,
                                 &err_info, sizeof(err_info));
@@ -1287,32 +1659,83 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        if (!cmd.resp_pkt)
                goto out_unlock;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
+               status_size = sizeof(struct iwl_wowlan_status_v6);
+       else
+               status_size = sizeof(struct iwl_wowlan_status_v4);
+
        len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-       if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) {
+       if (len - sizeof(struct iwl_cmd_header) < status_size) {
                IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
                goto out_free_resp;
        }
 
-       status = (void *)cmd.resp_pkt->data;
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
+               status_v6 = (void *)cmd.resp_pkt->data;
+
+               status.pattern_number = le16_to_cpu(status_v6->pattern_number);
+               for (i = 0; i < 8; i++)
+                       status.qos_seq_ctr[i] =
+                               le16_to_cpu(status_v6->qos_seq_ctr[i]);
+               status.wakeup_reasons = le32_to_cpu(status_v6->wakeup_reasons);
+               status.wake_packet_length =
+                       le32_to_cpu(status_v6->wake_packet_length);
+               status.wake_packet_bufsize =
+                       le32_to_cpu(status_v6->wake_packet_bufsize);
+               status.wake_packet = status_v6->wake_packet;
+       } else {
+               struct iwl_wowlan_status_v4 *status_v4;
+               status_v6 = NULL;
+               status_v4 = (void *)cmd.resp_pkt->data;
+
+               status.pattern_number = le16_to_cpu(status_v4->pattern_number);
+               for (i = 0; i < 8; i++)
+                       status.qos_seq_ctr[i] =
+                               le16_to_cpu(status_v4->qos_seq_ctr[i]);
+               status.wakeup_reasons = le32_to_cpu(status_v4->wakeup_reasons);
+               status.wake_packet_length =
+                       le32_to_cpu(status_v4->wake_packet_length);
+               status.wake_packet_bufsize =
+                       le32_to_cpu(status_v4->wake_packet_bufsize);
+               status.wake_packet = status_v4->wake_packet;
+       }
 
        if (len - sizeof(struct iwl_cmd_header) !=
-           sizeof(*status) +
-           ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
+           status_size + ALIGN(status.wake_packet_bufsize, 4)) {
                IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
                goto out_free_resp;
        }
 
+       /* still at hard-coded place 0 for D3 image */
+       ap_sta = rcu_dereference_protected(
+                       mvm->fw_id_to_mac_id[0],
+                       lockdep_is_held(&mvm->mutex));
+       if (IS_ERR_OR_NULL(ap_sta))
+               goto out_free_resp;
+
+       mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               u16 seq = status.qos_seq_ctr[i];
+               /* firmware stores last-used value, we store next value */
+               seq += 0x10;
+               mvm_ap_sta->tid_data[i].seq_number = seq;
+       }
+
        /* now we have all the data we need, unlock to avoid mac80211 issues */
        mutex_unlock(&mvm->mutex);
 
-       iwl_mvm_report_wakeup_reasons(mvm, vif, status);
+       iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
+
+       keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6);
+
        iwl_free_resp(&cmd);
-       return;
+       return keep;
 
  out_free_resp:
        iwl_free_resp(&cmd);
  out_unlock:
        mutex_unlock(&mvm->mutex);
+       return false;
 }
 
 static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
@@ -1335,6 +1758,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
 #endif
 }
 
+static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       /* skip the one we keep connection on */
+       if (data == vif)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               ieee80211_resume_disconnect(vif);
+}
+
 static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
 {
        struct iwl_d3_iter_data resume_iter_data = {
@@ -1343,6 +1777,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        struct ieee80211_vif *vif = NULL;
        int ret;
        enum iwl_d3_status d3_status;
+       bool keep = false;
 
        mutex_lock(&mvm->mutex);
 
@@ -1368,7 +1803,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        /* query SRAM first in case we want event logging */
        iwl_mvm_read_d3_sram(mvm);
 
-       iwl_mvm_query_wakeup_reasons(mvm, vif);
+       keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
        /* has unlocked the mutex, so skip that */
        goto out;
 
@@ -1376,8 +1811,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        mutex_unlock(&mvm->mutex);
 
  out:
-       if (!test && vif)
-               ieee80211_resume_disconnect(vif);
+       if (!test)
+               ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
+                       IEEE80211_IFACE_ITER_NORMAL,
+                       iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
 
        /* return 1 to reconfigure the device */
        set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
index aac81b8..9864d71 100644 (file)
@@ -246,58 +246,56 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file,
-                                               const char __user *user_buf,
+static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,
+                                               char __user *user_buf,
                                                size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       char buf[8] = {};
-       int allow;
-
-       if (!mvm->ucode_loaded)
-               return -EIO;
-
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
-               return -EFAULT;
-
-       if (sscanf(buf, "%d", &allow) != 1)
-               return -EINVAL;
-
-       IWL_DEBUG_POWER(mvm, "%s device power down\n",
-                       allow ? "allow" : "prevent");
+       char buf[64];
+       int bufsz = sizeof(buf);
+       int pos = 0;
 
-       /*
-        * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
-        */
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n",
+                        mvm->disable_power_off);
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n",
+                        mvm->disable_power_off_d3);
 
-       return count;
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
-                                                  const char __user *user_buf,
-                                                  size_t count, loff_t *ppos)
+static ssize_t iwl_dbgfs_disable_power_off_write(struct file *file,
+                                                const char __user *user_buf,
+                                                size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       char buf[8] = {};
-       int allow;
+       char buf[64] = {};
+       int ret;
+       int val;
 
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
+       if (!mvm->ucode_loaded)
+               return -EIO;
+
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
-       if (sscanf(buf, "%d", &allow) != 1)
+       if (!strncmp("disable_power_off_d0=", buf, 21)) {
+               if (sscanf(buf + 21, "%d", &val) != 1)
+                       return -EINVAL;
+               mvm->disable_power_off = val;
+       } else if (!strncmp("disable_power_off_d3=", buf, 21)) {
+               if (sscanf(buf + 21, "%d", &val) != 1)
+                       return -EINVAL;
+               mvm->disable_power_off_d3 = val;
+       } else {
                return -EINVAL;
+       }
 
-       IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
-                       allow ? "allow" : "prevent");
-
-       /*
-        * TODO: When WoWLAN FW alive notification happens, driver will send
-        * REPLY_DEBUG_CMD setting power_down_allow flag according to
-        * mvm->prevent_power_down_d3
-        */
-       mvm->prevent_power_down_d3 = !allow;
+       mutex_lock(&mvm->mutex);
+       ret = iwl_mvm_power_update_device_mode(mvm);
+       mutex_unlock(&mvm->mutex);
 
-       return count;
+       return ret ?: count;
 }
 
 static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
@@ -344,6 +342,7 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
        case MVM_DEBUGFS_PM_DISABLE_POWER_OFF:
                IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val);
                dbgfs_pm->disable_power_off = val;
+               break;
        case MVM_DEBUGFS_PM_LPRX_ENA:
                IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
                dbgfs_pm->lprx_ena = val;
@@ -371,7 +370,8 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
        int val;
        int ret;
 
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
        if (!strncmp("keep_alive=", buf, 11)) {
@@ -394,7 +394,9 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
                if (sscanf(buf + 16, "%d", &val) != 1)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
-       } else if (!strncmp("disable_power_off=", buf, 18)) {
+       } else if (!strncmp("disable_power_off=", buf, 18) &&
+                  !(mvm->fw->ucode_capa.flags &
+                    IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) {
                if (sscanf(buf + 18, "%d", &val) != 1)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF;
@@ -581,15 +583,21 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
        BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
 
        pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
-                                        notif->bt_status);
+                        notif->bt_status);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
-                                        notif->bt_open_conn);
+                        notif->bt_open_conn);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
-                                        notif->bt_traffic_load);
+                        notif->bt_traffic_load);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
-                                        notif->bt_agg_traffic_load);
+                        notif->bt_agg_traffic_load);
        pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
-                                        notif->bt_ci_compliance);
+                        notif->bt_ci_compliance);
+       pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+                        le32_to_cpu(notif->primary_ch_lut));
+       pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+                        le32_to_cpu(notif->secondary_ch_lut));
+       pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
+                        le32_to_cpu(notif->bt_activity_grading));
 
        mutex_unlock(&mvm->mutex);
 
@@ -600,6 +608,38 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
 }
 #undef BT_MBOX_PRINT
 
+static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
+       char buf[256];
+       int bufsz = sizeof(buf);
+       int pos = 0;
+
+       mutex_lock(&mvm->mutex);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n");
+       pos += scnprintf(buf+pos, bufsz-pos,
+                      "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n",
+                      le64_to_cpu(cmd->bt_primary_ci),
+                      !!cmd->co_run_bw_primary);
+       pos += scnprintf(buf+pos, bufsz-pos,
+                      "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n",
+                      le64_to_cpu(cmd->bt_secondary_ci),
+                      !!cmd->co_run_bw_secondary);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+       pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+                        iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
+       pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+                        iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+
+       mutex_unlock(&mvm->mutex);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 #define PRINT_STATS_LE32(_str, _val)                                   \
                         pos += scnprintf(buf + pos, bufsz - pos,       \
                                          fmt_table, _str,              \
@@ -615,9 +655,11 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
        int pos = 0;
        char *buf;
        int ret;
-       int bufsz = sizeof(struct mvm_statistics_rx_phy) * 20 +
-                   sizeof(struct mvm_statistics_rx_non_phy) * 10 +
-                   sizeof(struct mvm_statistics_rx_ht_phy) * 10 + 200;
+       /* 43 is the size of each data line, 33 is the size of each header */
+       size_t bufsz =
+               ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) +
+               (4 * 33) + 1;
+
        struct mvm_statistics_rx_phy *ofdm;
        struct mvm_statistics_rx_phy *cck;
        struct mvm_statistics_rx_non_phy *general;
@@ -712,6 +754,7 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
        PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b);
        PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c);
        PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills);
+       PRINT_STATS_LE32("mac_id", general->mac_id);
        PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu);
 
        pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
@@ -757,6 +800,59 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
        return count;
 }
 
+static ssize_t
+iwl_dbgfs_scan_ant_rxchain_read(struct file *file,
+                               char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       int pos = 0;
+       char buf[32];
+       const size_t bufsz = sizeof(buf);
+
+       /* print which antennas were set for the scan command by the user */
+       pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: ");
+       if (mvm->scan_rx_ant & ANT_A)
+               pos += scnprintf(buf + pos, bufsz - pos, "A");
+       if (mvm->scan_rx_ant & ANT_B)
+               pos += scnprintf(buf + pos, bufsz - pos, "B");
+       if (mvm->scan_rx_ant & ANT_C)
+               pos += scnprintf(buf + pos, bufsz - pos, "C");
+       pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t
+iwl_dbgfs_scan_ant_rxchain_write(struct file *file,
+                                const char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8];
+       int buf_size;
+       u8 scan_rx_ant;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+
+       /* get the argument from the user and check if it is valid */
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       if (sscanf(buf, "%hhx", &scan_rx_ant) != 1)
+               return -EINVAL;
+       if (scan_rx_ant > ANT_ABC)
+               return -EINVAL;
+       if (scan_rx_ant & ~iwl_fw_valid_rx_ant(mvm->fw))
+               return -EINVAL;
+
+       /* change the rx antennas for scan command */
+       mvm->scan_rx_ant = scan_rx_ant;
+
+       return count;
+}
+
+
 static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
                                enum iwl_dbgfs_bf_mask param, int value)
 {
@@ -968,7 +1064,8 @@ static ssize_t iwl_dbgfs_d3_sram_write(struct file *file,
        char buf[8] = {};
        int store;
 
-       if (copy_from_user(buf, user_buf, sizeof(buf)))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
        if (sscanf(buf, "%d", &store) != 1)
@@ -1063,10 +1160,12 @@ MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
 MVM_DEBUGFS_READ_FILE_OPS(stations);
 MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
-MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
-MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off);
 MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain);
+
 #ifdef CONFIG_PM_SLEEP
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
 #endif
@@ -1087,10 +1186,14 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
-       MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
-       MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)
+               MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
+                                    S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
+                            S_IWUSR | S_IRUSR);
 #ifdef CONFIG_PM_SLEEP
        MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
index 05c61d6..4ea5e24 100644 (file)
@@ -82,6 +82,8 @@
  * @BT_USE_DEFAULTS:
  * @BT_SYNC_2_BT_DISABLE:
  * @BT_COEX_CORUNNING_TBL_EN:
+ *
+ * The COEX_MODE must be set for each command. Even if it is not changed.
  */
 enum iwl_bt_coex_flags {
        BT_CH_PRIMARY_EN                = BIT(0),
@@ -95,14 +97,16 @@ enum iwl_bt_coex_flags {
        BT_COEX_NW                      = 0x3 << BT_COEX_MODE_POS,
        BT_USE_DEFAULTS                 = BIT(6),
        BT_SYNC_2_BT_DISABLE            = BIT(7),
-       /*
-        * For future use - when the flags will be enlarged
-        * BT_COEX_CORUNNING_TBL_EN     = BIT(8),
-        */
+       BT_COEX_CORUNNING_TBL_EN        = BIT(8),
+       BT_COEX_MPLUT_TBL_EN            = BIT(9),
+       /* Bit 10 is reserved */
+       BT_COEX_WF_PRIO_BOOST_CHECK_EN  = BIT(11),
 };
 
 /*
  * indicates what has changed in the BT_COEX command.
+ * BT_VALID_ENABLE must be set for each command. Commands without this bit will
+ * discarded by the firmware
  */
 enum iwl_bt_coex_valid_bit_msk {
        BT_VALID_ENABLE                 = BIT(0),
@@ -121,11 +125,8 @@ enum iwl_bt_coex_valid_bit_msk {
        BT_VALID_CORUN_LUT_40           = BIT(13),
        BT_VALID_ANT_ISOLATION          = BIT(14),
        BT_VALID_ANT_ISOLATION_THRS     = BIT(15),
-       /*
-        * For future use - when the valid flags will be enlarged
-        * BT_VALID_TXTX_DELTA_FREQ_THRS        = BIT(16),
-        * BT_VALID_TXRX_MAX_FREQ_0     = BIT(17),
-        */
+       BT_VALID_TXTX_DELTA_FREQ_THRS   = BIT(16),
+       BT_VALID_TXRX_MAX_FREQ_0        = BIT(17),
 };
 
 /**
@@ -142,48 +143,88 @@ enum iwl_bt_reduced_tx_power {
        BT_REDUCED_TX_POWER_DATA        = BIT(1),
 };
 
+enum iwl_bt_coex_lut_type {
+       BT_COEX_TIGHT_LUT = 0,
+       BT_COEX_LOOSE_LUT,
+       BT_COEX_TX_DIS_LUT,
+
+       BT_COEX_MAX_LUT,
+};
+
 #define BT_COEX_LUT_SIZE (12)
+#define BT_COEX_CORUN_LUT_SIZE (32)
+#define BT_COEX_MULTI_PRIO_LUT_SIZE (2)
+#define BT_COEX_BOOST_SIZE (4)
+#define BT_REDUCED_TX_POWER_BIT BIT(7)
 
 /**
  * struct iwl_bt_coex_cmd - bt coex configuration command
  * @flags:&enum iwl_bt_coex_flags
- * @lead_time:
  * @max_kill:
- * @bt3_time_t7_value:
- * @kill_ack_msk:
- * @kill_cts_msk:
- * @bt3_prio_sample_time:
- * @bt3_timer_t2_value:
- * @bt4_reaction_time:
- * @decision_lut[12]:
  * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
- * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
- * @bt_prio_boost: values for PTA boost register
+ * @bt4_antenna_isolation:
+ * @bt4_antenna_isolation_thr:
+ * @bt4_tx_tx_delta_freq_thr:
+ * @bt4_tx_rx_max_freq0:
+ * @bt_prio_boost:
  * @wifi_tx_prio_boost: SW boost of wifi tx priority
  * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ * @kill_ack_msk:
+ * @kill_cts_msk:
+ * @decision_lut:
+ * @bt4_multiprio_lut:
+ * @bt4_corun_lut20:
+ * @bt4_corun_lut40:
+ * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
  *
  * The structure is used for the BT_COEX command.
  */
 struct iwl_bt_coex_cmd {
-       u8 flags;
-       u8 lead_time;
+       __le32 flags;
        u8 max_kill;
-       u8 bt3_time_t7_value;
+       u8 bt_reduced_tx_power;
+       u8 reserved[2];
+
+       u8 bt4_antenna_isolation;
+       u8 bt4_antenna_isolation_thr;
+       u8 bt4_tx_tx_delta_freq_thr;
+       u8 bt4_tx_rx_max_freq0;
+
+       __le32 bt_prio_boost[BT_COEX_BOOST_SIZE];
+       __le32 wifi_tx_prio_boost;
+       __le32 wifi_rx_prio_boost;
        __le32 kill_ack_msk;
        __le32 kill_cts_msk;
-       u8 bt3_prio_sample_time;
-       u8 bt3_timer_t2_value;
-       __le16 bt4_reaction_time;
-       __le32 decision_lut[BT_COEX_LUT_SIZE];
-       u8 bt_reduced_tx_power;
-       u8 reserved;
-       __le16 valid_bit_msk;
-       __le32 bt_prio_boost;
-       u8 reserved2;
-       u8 wifi_tx_prio_boost;
-       __le16 wifi_rx_prio_boost;
+
+       __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
+       __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
+       __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+       __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+
+       __le32 valid_bit_msk;
 } __packed; /* BT_COEX_CMD_API_S_VER_3 */
 
+/**
+ * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @bt_secondary_ci:
+ * @co_run_bw_primary:
+ * @co_run_bw_secondary:
+ * @primary_ch_phy_id:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd {
+       __le64 bt_primary_ci;
+       __le64 bt_secondary_ci;
+
+       u8 co_run_bw_primary;
+       u8 co_run_bw_secondary;
+       u8 primary_ch_phy_id;
+       u8 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_1 */
+
 #define BT_MBOX(n_dw, _msg, _pos, _nbits)      \
        BT_MBOX##n_dw##_##_msg##_POS = (_pos),  \
        BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS
@@ -244,23 +285,39 @@ enum iwl_bt_mxbox_dw3 {
        ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
        >> BT_MBOX##_num##_##_field##_POS)
 
+enum iwl_bt_activity_grading {
+       BT_OFF                  = 0,
+       BT_ON_NO_CONNECTION     = 1,
+       BT_LOW_TRAFFIC          = 2,
+       BT_HIGH_TRAFFIC         = 3,
+};
+
 /**
  * struct iwl_bt_coex_profile_notif - notification about BT coex
  * @mbox_msg: message from BT to WiFi
- * @:bt_status: 0 - off, 1 - on
- * @:bt_open_conn: number of BT connections open
- * @:bt_traffic_load: load of BT traffic
- * @:bt_agg_traffic_load: aggregated load of BT traffic
- * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @msg_idx: the index of the message
+ * @bt_status: 0 - off, 1 - on
+ * @bt_open_conn: number of BT connections open
+ * @bt_traffic_load: load of BT traffic
+ * @bt_agg_traffic_load: aggregated load of BT traffic
+ * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @primary_ch_lut: LUT used for primary channel
+ * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
  */
 struct iwl_bt_coex_profile_notif {
        __le32 mbox_msg[4];
+       __le32 msg_idx;
        u8 bt_status;
        u8 bt_open_conn;
        u8 bt_traffic_load;
        u8 bt_agg_traffic_load;
        u8 bt_ci_compliance;
        u8 reserved[3];
+
+       __le32 primary_ch_lut;
+       __le32 secondary_ch_lut;
+       __le32 bt_activity_grading;
 } __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
 
 enum iwl_bt_coex_prio_table_event {
@@ -300,20 +357,4 @@ struct iwl_bt_coex_prio_tbl_cmd {
        u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
 } __packed;
 
-enum iwl_bt_coex_env_action {
-       BT_COEX_ENV_CLOSE        = 0,
-       BT_COEX_ENV_OPEN         = 1,
-}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */
-
-/**
- * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope
- * @action: enum %iwl_bt_coex_env_action
- * @type: enum %iwl_bt_coex_prio_table_event
- */
-struct iwl_bt_coex_prot_env_cmd {
-       u8 action; /* 0 = closed, 1 = open */
-       u8 type; /* 0 .. 15 */
-       u8 reserved[2];
-} __packed;
-
 #endif /* __fw_api_bt_coex_h__ */
index df72fcd..4e7dd8c 100644 (file)
@@ -100,7 +100,12 @@ enum iwl_proto_offloads {
 
 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1    2
 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2    6
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   6
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L   12
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S   4
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   12
+
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L    4
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S    2
 
 /**
  * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
@@ -155,6 +160,43 @@ struct iwl_proto_offload_cmd_v2 {
        u8 reserved2[3];
 } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
 
+struct iwl_ns_config {
+       struct in6_addr source_ipv6_addr;
+       struct in6_addr dest_ipv6_addr;
+       u8 target_mac_addr[ETH_ALEN];
+       __le16 reserved;
+} __packed; /* NS_OFFLOAD_CONFIG */
+
+struct iwl_targ_addr {
+       struct in6_addr addr;
+       __le32 config_num;
+} __packed; /* TARGET_IPV6_ADDRESS */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_small {
+       struct iwl_proto_offload_cmd_common common;
+       __le32 num_valid_ipv6_addrs;
+       struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S];
+       struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_large {
+       struct iwl_proto_offload_cmd_common common;
+       __le32 num_valid_ipv6_addrs;
+       struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L];
+       struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
 
 /*
  * WOWLAN_PATTERNS
@@ -293,7 +335,7 @@ enum iwl_wowlan_wakeup_reason {
        IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET             = BIT(12),
 }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
 
-struct iwl_wowlan_status {
+struct iwl_wowlan_status_v4 {
        __le64 replay_ctr;
        __le16 pattern_number;
        __le16 non_qos_seq_ctr;
@@ -308,6 +350,29 @@ struct iwl_wowlan_status {
        u8 wake_packet[]; /* can be truncated from _length to _bufsize */
 } __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
 
+struct iwl_wowlan_gtk_status {
+       u8 key_index;
+       u8 reserved[3];
+       u8 decrypt_key[16];
+       u8 tkip_mic_key[8];
+       struct iwl_wowlan_rsc_tsc_params_cmd rsc;
+} __packed;
+
+struct iwl_wowlan_status_v6 {
+       struct iwl_wowlan_gtk_status gtk;
+       __le64 replay_ctr;
+       __le16 pattern_number;
+       __le16 non_qos_seq_ctr;
+       __le16 qos_seq_ctr[8];
+       __le32 wakeup_reasons;
+       __le32 num_of_gtk_rekeys;
+       __le32 transmitted_ndps;
+       __le32 received_beacons;
+       __le32 wake_packet_length;
+       __le32 wake_packet_bufsize;
+       u8 wake_packet[]; /* can be truncated from _length to _bufsize */
+} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */
+
 #define IWL_WOWLAN_TCP_MAX_PACKET_LEN          64
 #define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN  128
 #define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS      2048
index 98b1feb..39c3148 100644 (file)
@@ -170,12 +170,14 @@ struct iwl_mac_data_ap {
  * @beacon_tsf: beacon transmit time in TSF
  * @bi: beacon interval in TU
  * @bi_reciprocal: 2^32 / bi
+ * @beacon_template: beacon template ID
  */
 struct iwl_mac_data_ibss {
        __le32 beacon_time;
        __le64 beacon_tsf;
        __le32 bi;
        __le32 bi_reciprocal;
+       __le32 beacon_template;
 } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
 
 /**
@@ -372,4 +374,13 @@ static inline u32 iwl_mvm_reciprocal(u32 v)
        return 0xFFFFFFFF / v;
 }
 
+#define IWL_NONQOS_SEQ_GET     0x1
+#define IWL_NONQOS_SEQ_SET     0x2
+struct iwl_nonqos_seq_query_cmd {
+       __le32 get_set_flag;
+       __le32 mac_id_n_color;
+       __le16 value;
+       __le16 reserved;
+} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */
+
 #endif /* __fw_api_mac_h__ */
index 8e7ab41..5cb93ae 100644 (file)
@@ -131,6 +131,33 @@ struct iwl_powertable_cmd {
        __le32 lprx_rssi_threshold;
 } __packed;
 
+/**
+ * enum iwl_device_power_flags - masks for device power command flags
+ * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
+ *     receiver and transmitter. '0' - does not allow. This flag should be
+ *     always set to '1' unless one need to disable actual power down for debug
+ *     purposes.
+ * @DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning
+ *     that power management is disabled. '0' Power management is enabled, one
+ *     of power schemes is applied.
+*/
+enum iwl_device_power_flags {
+       DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK   = BIT(0),
+       DEVICE_POWER_FLAGS_CAM_MSK              = BIT(13),
+};
+
+/**
+ * struct iwl_device_power_cmd - device wide power command.
+ * DEVICE_POWER_CMD = 0x77 (command, has simple generic response)
+ *
+ * @flags:     Power table command flags from DEVICE_POWER_FLAGS_*
+ */
+struct iwl_device_power_cmd {
+       /* PM_POWER_TABLE_CMD_API_S_VER_6 */
+       __le16 flags;
+       __le16 reserved;
+} __packed;
+
 /**
  * struct iwl_mac_power_cmd - New power command containing uAPSD support
  * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
@@ -290,7 +317,7 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_ESCAPE_TIMER_MIN 0
 
 #define IWL_BA_ESCAPE_TIMER_DEFAULT 6
-#define IWL_BA_ESCAPE_TIMER_D3 6
+#define IWL_BA_ESCAPE_TIMER_D3 9
 #define IWL_BA_ESCAPE_TIMER_MAX 1024
 #define IWL_BA_ESCAPE_TIMER_MIN 0
 
index fdd33bc..538f1c7 100644 (file)
@@ -68,6 +68,7 @@
 /*
  * These serve as indexes into
  * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
+ * TODO: avoid overlap between legacy and HT rates
  */
 enum {
        IWL_RATE_1M_INDEX = 0,
@@ -78,18 +79,31 @@ enum {
        IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
        IWL_RATE_6M_INDEX,
        IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+       IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX,
+       IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX,
+       IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX,
        IWL_RATE_9M_INDEX,
        IWL_RATE_12M_INDEX,
+       IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX,
        IWL_RATE_18M_INDEX,
+       IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX,
        IWL_RATE_24M_INDEX,
+       IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX,
        IWL_RATE_36M_INDEX,
+       IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX,
        IWL_RATE_48M_INDEX,
+       IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX,
        IWL_RATE_54M_INDEX,
+       IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX,
        IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
        IWL_RATE_60M_INDEX,
-       IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
+       IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX,
+       IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX,
+       IWL_RATE_MCS_8_INDEX,
+       IWL_RATE_MCS_9_INDEX,
+       IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX,
        IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
-       IWL_RATE_COUNT,
+       IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1,
 };
 
 #define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
@@ -108,6 +122,7 @@ enum {
        IWL_RATE_2M_PLCP  = 20,
        IWL_RATE_5M_PLCP  = 55,
        IWL_RATE_11M_PLCP = 110,
+       IWL_RATE_INVM_PLCP = -1,
 };
 
 /*
@@ -164,6 +179,8 @@ enum {
  * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
  */
 #define RATE_HT_MCS_RATE_CODE_MSK      0x7
+#define RATE_HT_MCS_NSS_POS             3
+#define RATE_HT_MCS_NSS_MSK             (3 << RATE_HT_MCS_NSS_POS)
 
 /* Bit 10: (1) Use Green Field preamble */
 #define RATE_HT_MCS_GF_POS             10
index 83cb9b9..c3782b4 100644 (file)
@@ -356,6 +356,7 @@ struct iwl_scan_complete_notif {
 /* scan offload */
 #define IWL_MAX_SCAN_CHANNELS          40
 #define IWL_SCAN_MAX_BLACKLIST_LEN     64
+#define IWL_SCAN_SHORT_BLACKLIST_LEN   16
 #define IWL_SCAN_MAX_PROFILES          11
 #define SCAN_OFFLOAD_PROBE_REQ_SIZE    512
 
@@ -368,6 +369,12 @@ struct iwl_scan_complete_notif {
 #define IWL_FULL_SCAN_MULTIPLIER 5
 #define IWL_FAST_SCHED_SCAN_ITERATIONS 3
 
+enum scan_framework_client {
+       SCAN_CLIENT_SCHED_SCAN          = BIT(0),
+       SCAN_CLIENT_NETDETECT           = BIT(1),
+       SCAN_CLIENT_ASSET_TRACKING      = BIT(2),
+};
+
 /**
  * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
  * @scan_flags:                see enum iwl_scan_flags
@@ -449,11 +456,12 @@ struct iwl_scan_offload_cfg {
  * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
  * @ssid:              MAC address to filter out
  * @reported_rssi:     AP rssi reported to the host
+ * @client_bitmap: clients ignore this entry  - enum scan_framework_client
  */
 struct iwl_scan_offload_blacklist {
        u8 ssid[ETH_ALEN];
        u8 reported_rssi;
-       u8 reserved;
+       u8 client_bitmap;
 } __packed;
 
 enum iwl_scan_offload_network_type {
@@ -475,6 +483,7 @@ enum iwl_scan_offload_band_selection {
  * @aut_alg:           authentication olgorithm to match - bitmap
  * @network_type:      enum iwl_scan_offload_network_type
  * @band_selection:    enum iwl_scan_offload_band_selection
+ * @client_bitmap:     clients waiting for match - enum scan_framework_client
  */
 struct iwl_scan_offload_profile {
        u8 ssid_index;
@@ -482,7 +491,8 @@ struct iwl_scan_offload_profile {
        u8 auth_alg;
        u8 network_type;
        u8 band_selection;
-       u8 reserved[3];
+       u8 client_bitmap;
+       u8 reserved[2];
 } __packed;
 
 /**
@@ -491,13 +501,18 @@ struct iwl_scan_offload_profile {
  * @profiles:          profiles to search for match
  * @blacklist_len:     length of blacklist
  * @num_profiles:      num of profiles in the list
+ * @match_notify:      clients waiting for match found notification
+ * @pass_match:                clients waiting for the results
+ * @active_clients:    active clients bitmap - enum scan_framework_client
  */
 struct iwl_scan_offload_profile_cfg {
-       struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN];
        struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
        u8 blacklist_len;
        u8 num_profiles;
-       u8 reserved[2];
+       u8 match_notify;
+       u8 pass_match;
+       u8 active_clients;
+       u8 reserved[3];
 } __packed;
 
 /**
@@ -560,4 +575,15 @@ struct iwl_scan_offload_complete {
        u8 reserved;
 } __packed;
 
+/**
+ * iwl_sched_scan_results - SCAN_OFFLOAD_MATCH_FOUND_NTF_API_S_VER_1
+ * @ssid_bitmap:       SSIDs indexes found in this iteration
+ * @client_bitmap:     clients that are active and wait for this notification
+ */
+struct iwl_sched_scan_results {
+       __le16 ssid_bitmap;
+       u8 client_bitmap;
+       u8 reserved;
+};
+
 #endif
index a30691a..4aca593 100644 (file)
@@ -247,7 +247,7 @@ struct iwl_mvm_keyinfo {
 } __packed;
 
 /**
- * struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table
+ * struct iwl_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table.
  * ( REPLY_ADD_STA = 0x18 )
  * @add_modify: 1: modify existing, 0: add new station
  * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent
@@ -286,7 +286,7 @@ struct iwl_mvm_keyinfo {
  * ADD_STA sets up the table entry for one station, either creating a new
  * entry, or modifying a pre-existing one.
  */
-struct iwl_mvm_add_sta_cmd {
+struct iwl_mvm_add_sta_cmd_v5 {
        u8 add_modify;
        u8 unicast_tx_key_id;
        u8 multicast_tx_key_id;
@@ -312,6 +312,57 @@ struct iwl_mvm_add_sta_cmd {
        __le32 tfd_queue_msk;
 } __packed; /* ADD_STA_CMD_API_S_VER_5 */
 
+/**
+ * struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station
+ * VER_6 of this command is quite similar to VER_5 except
+ * exclusion of all fields related to the security key installation.
+ */
+struct iwl_mvm_add_sta_cmd_v6 {
+       u8 add_modify;
+       u8 reserved1;
+       __le16 tid_disable_tx;
+       __le32 mac_id_n_color;
+       u8 addr[ETH_ALEN];      /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
+       __le16 reserved2;
+       u8 sta_id;
+       u8 modify_mask;
+       __le16 reserved3;
+       __le32 station_flags;
+       __le32 station_flags_msk;
+       u8 add_immediate_ba_tid;
+       u8 remove_immediate_ba_tid;
+       __le16 add_immediate_ba_ssn;
+       __le16 sleep_tx_count;
+       __le16 sleep_state_flags;
+       __le16 assoc_id;
+       __le16 beamform_flags;
+       __le32 tfd_queue_msk;
+} __packed; /* ADD_STA_CMD_API_S_VER_6 */
+
+/**
+ * struct iwl_mvm_add_sta_key_cmd - add/modify sta key
+ * ( REPLY_ADD_STA_KEY = 0x17 )
+ * @sta_id: index of station in uCode's station table
+ * @key_offset: key offset in key storage
+ * @key_flags: type %iwl_sta_key_flag
+ * @key: key material data
+ * @key2: key material data
+ * @rx_secur_seq_cnt: RX security sequence counter for the key
+ * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
+ * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
+ */
+struct iwl_mvm_add_sta_key_cmd {
+       u8 sta_id;
+       u8 key_offset;
+       __le16 key_flags;
+       u8 key[16];
+       u8 key2[16];
+       u8 rx_secur_seq_cnt[16];
+       u8 tkip_rx_tsc_byte2;
+       u8 reserved;
+       __le16 tkip_rx_ttak[5];
+} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */
+
 /**
  * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
  * @ADD_STA_SUCCESS: operation was executed successfully
index 66264cc..bad5a55 100644 (file)
 #include "fw-api-d3.h"
 #include "fw-api-bt-coex.h"
 
-/* queue and FIFO numbers by usage */
+/* maximal number of Tx queues in any platform */
+#define IWL_MVM_MAX_QUEUES     20
+
+/* Tx queue numbers */
 enum {
        IWL_MVM_OFFCHANNEL_QUEUE = 8,
        IWL_MVM_CMD_QUEUE = 9,
-       IWL_MVM_AUX_QUEUE = 15,
-       IWL_MVM_FIRST_AGG_QUEUE = 16,
-       IWL_MVM_NUM_QUEUES = 20,
-       IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1,
-       IWL_MVM_CMD_FIFO = 7
 };
 
+#define IWL_MVM_CMD_FIFO       7
+
 #define IWL_MVM_STATION_COUNT  16
 
 /* commands */
@@ -97,6 +97,7 @@ enum {
        DBG_CFG = 0x9,
 
        /* station table */
+       ADD_STA_KEY = 0x17,
        ADD_STA = 0x18,
        REMOVE_STA = 0x19,
 
@@ -114,6 +115,7 @@ enum {
        TIME_EVENT_NOTIFICATION = 0x2a,
        BINDING_CONTEXT_CMD = 0x2b,
        TIME_QUOTA_CMD = 0x2c,
+       NON_QOS_TX_COUNTER_CMD = 0x2d,
 
        LQ_CMD = 0x4e,
 
@@ -130,6 +132,7 @@ enum {
        SCAN_OFFLOAD_COMPLETE = 0x6D,
        SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
        SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
+       MATCH_FOUND_NOTIFICATION = 0xd9,
 
        /* Phy */
        PHY_CONFIGURATION_CMD = 0x6a,
@@ -178,6 +181,7 @@ enum {
        BT_COEX_PRIO_TABLE = 0xcc,
        BT_COEX_PROT_ENV = 0xcd,
        BT_PROFILE_NOTIFICATION = 0xce,
+       BT_COEX_CI = 0x5d,
 
        REPLY_BEACON_FILTERING_CMD = 0xd2,
 
index c76299a..70e5297 100644 (file)
@@ -151,13 +151,11 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        enum iwl_ucode_type old_type = mvm->cur_ucode;
        static const u8 alive_cmd[] = { MVM_ALIVE };
 
-       mvm->cur_ucode = ucode_type;
        fw = iwl_get_ucode_image(mvm, ucode_type);
-
-       mvm->ucode_loaded = false;
-
-       if (!fw)
+       if (WARN_ON(!fw))
                return -EINVAL;
+       mvm->cur_ucode = ucode_type;
+       mvm->ucode_loaded = false;
 
        iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
                                   alive_cmd, ARRAY_SIZE(alive_cmd),
@@ -199,7 +197,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
         */
 
        for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
-               if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE)
+               if (i < mvm->first_agg_queue && i != IWL_MVM_CMD_QUEUE)
                        mvm->queue_to_mac80211[i] = i;
                else
                        mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
@@ -243,7 +241,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->init_ucode_run)
+       if (mvm->init_ucode_complete)
                return 0;
 
        iwl_init_notification_wait(&mvm->notif_wait,
@@ -264,6 +262,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        if (ret)
                goto error;
 
+       /* Read the NVM only at driver load time, no need to do this twice */
        if (read_nvm) {
                /* Read nvm */
                ret = iwl_nvm_init(mvm);
@@ -273,6 +272,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                }
        }
 
+       /* In case we read the NVM from external file, load it to the NIC */
+       if (iwlwifi_mod_params.nvm_file)
+               iwl_mvm_load_nvm_to_nic(mvm);
+
        ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
        WARN_ON(ret);
 
@@ -310,7 +313,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
                        MVM_UCODE_CALIB_TIMEOUT);
        if (!ret)
-               mvm->init_ucode_run = true;
+               mvm->init_ucode_complete = true;
        goto out;
 
 error:
@@ -353,8 +356,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                return ret;
 
-       /* If we were in RFKILL during module loading, load init ucode now */
-       if (!mvm->init_ucode_run) {
+       /*
+        * If we haven't completed the run of the init ucode during
+        * module loading, load init ucode now
+        * (for example, if we were in RFKILL)
+        */
+       if (!mvm->init_ucode_complete) {
                ret = iwl_run_init_mvm_ucode(mvm, false);
                if (ret && !iwlmvm_mod_params.init_dbg) {
                        IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
@@ -424,6 +431,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                        goto error;
        }
 
+       ret = iwl_mvm_power_update_device_mode(mvm);
+       if (ret)
+               goto error;
+
        IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
        return 0;
  error:
index 5fe23a5..f41f9b0 100644 (file)
@@ -80,7 +80,7 @@ struct iwl_mvm_mac_iface_iterator_data {
        struct ieee80211_vif *vif;
        unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
        unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
-       unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_FIRST_AGG_QUEUE)];
+       unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_MAX_QUEUES)];
        enum iwl_tsf_id preferred_tsf;
        bool found_vif;
 };
@@ -218,7 +218,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                .preferred_tsf = NUM_TSF_IDS,
                .used_hw_queues = {
                        BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
-                       BIT(IWL_MVM_AUX_QUEUE) |
+                       BIT(mvm->aux_queue) |
                        BIT(IWL_MVM_CMD_QUEUE)
                },
                .found_vif = false,
@@ -242,9 +242,17 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
         * that we should share it with another interface.
         */
 
-       /* Currently, MAC ID 0 should be used only for the managed vif */
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */
+       switch (vif->type) {
+       case NL80211_IFTYPE_ADHOC:
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (!vif->p2p)
+                       break;
+               /* fall through */
+       default:
                __clear_bit(0, data.available_mac_ids);
+       }
 
        ieee80211_iterate_active_interfaces_atomic(
                mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
@@ -302,9 +310,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
        /* Find available queues, and allocate them to the ACs */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                u8 queue = find_first_zero_bit(data.used_hw_queues,
-                                              IWL_MVM_FIRST_AGG_QUEUE);
+                                              mvm->first_agg_queue);
 
-               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+               if (queue >= mvm->first_agg_queue) {
                        IWL_ERR(mvm, "Failed to allocate queue\n");
                        ret = -EIO;
                        goto exit_fail;
@@ -317,9 +325,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
        /* Allocate the CAB queue for softAP and GO interfaces */
        if (vif->type == NL80211_IFTYPE_AP) {
                u8 queue = find_first_zero_bit(data.used_hw_queues,
-                                              IWL_MVM_FIRST_AGG_QUEUE);
+                                              mvm->first_agg_queue);
 
-               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+               if (queue >= mvm->first_agg_queue) {
                        IWL_ERR(mvm, "Failed to allocate cab queue\n");
                        ret = -EIO;
                        goto exit_fail;
@@ -559,8 +567,12 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
 
        /* Don't use cts to self as the fw doesn't support it currently. */
-       if (vif->bss_conf.use_cts_prot)
+       if (vif->bss_conf.use_cts_prot) {
                cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
+               if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
+                       cmd->protection_flags |=
+                               cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
+       }
 
        /*
         * I think that we should enable these 2 flags regardless the HT PROT
@@ -707,8 +719,35 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
        cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC |
                                       MAC_FILTER_IN_CONTROL_AND_MGMT |
                                       MAC_FILTER_IN_BEACON |
+                                      MAC_FILTER_IN_PROBE_REQUEST |
+                                      MAC_FILTER_IN_CRC32);
+       mvm->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS;
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif,
+                                    u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
+
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON |
                                       MAC_FILTER_IN_PROBE_REQUEST);
 
+       /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
+       cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
+       cmd.ibss.bi_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+
+       /* TODO: Assumes that the beacon id == mac context id */
+       cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
+
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
 
@@ -721,7 +760,8 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
        struct iwl_mvm_go_iterator_data *data = _data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-       if (vif->type == NL80211_IFTYPE_AP && vif->p2p && mvmvif->ap_active)
+       if (vif->type == NL80211_IFTYPE_AP && vif->p2p &&
+           mvmvif->ap_ibss_active)
                data->go_active = true;
 }
 
@@ -833,9 +873,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
                cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
 
        /* Set up TX beacon command fields */
-       iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
-                                beacon->data,
-                                beacon_skb_len);
+       if (vif->type == NL80211_IFTYPE_AP)
+               iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
+                                        beacon->data,
+                                        beacon_skb_len);
 
        /* Submit command */
        cmd.len[0] = sizeof(beacon_cmd);
@@ -848,14 +889,15 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
        return iwl_mvm_send_cmd(mvm, &cmd);
 }
 
-/* The beacon template for the AP/GO context has changed and needs update */
+/* The beacon template for the AP/GO/IBSS has changed and needs update */
 int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif)
 {
        struct sk_buff *beacon;
        int ret;
 
-       WARN_ON(vif->type != NL80211_IFTYPE_AP);
+       WARN_ON(vif->type != NL80211_IFTYPE_AP &&
+               vif->type != NL80211_IFTYPE_ADHOC);
 
        beacon = ieee80211_beacon_get(mvm->hw, vif);
        if (!beacon)
@@ -1018,6 +1060,8 @@ static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action);
        case NL80211_IFTYPE_P2P_DEVICE:
                return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action);
+       case NL80211_IFTYPE_ADHOC:
+               return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action);
        default:
                break;
        }
@@ -1038,6 +1082,9 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        if (ret)
                return ret;
 
+       /* will only do anything at resume from D3 time */
+       iwl_mvm_set_last_nonqos_seq(mvm, vif);
+
        mvmvif->uploaded = true;
        return 0;
 }
@@ -1077,6 +1124,10 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        }
 
        mvmvif->uploaded = false;
+
+       if (vif->type == NL80211_IFTYPE_MONITOR)
+               mvm->hw->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS;
+
        return 0;
 }
 
index 9833cdf..74bc2c8 100644 (file)
@@ -77,6 +77,7 @@
 #include "iwl-eeprom-parse.h"
 #include "fw-api-scan.h"
 #include "iwl-phy-db.h"
+#include "testmode.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
@@ -138,6 +139,14 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
        }
 }
 
+static int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm)
+{
+       /* we create the 802.11 header and SSID element */
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID)
+               return mvm->fw->ucode_capa.max_probe_length - 24 - 2;
+       return mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -155,10 +164,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_TIMING_BEACON_ONLY |
                    IEEE80211_HW_CONNECTION_MONITOR |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
-                   IEEE80211_HW_SUPPORTS_STATIC_SMPS |
-                   IEEE80211_HW_SUPPORTS_UAPSD;
+                   IEEE80211_HW_SUPPORTS_STATIC_SMPS;
 
-       hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
+       hw->queues = mvm->first_agg_queue;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
@@ -171,6 +179,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
+               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
+               hw->uapsd_queues = IWL_UAPSD_AC_INFO;
+               hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+       }
+
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
        hw->chanctx_data_size = sizeof(u16);
@@ -181,6 +195,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                BIT(NL80211_IFTYPE_P2P_GO) |
                BIT(NL80211_IFTYPE_P2P_DEVICE);
 
+       /* IBSS has bugs in older versions */
+       if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
+               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+
        hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
                            WIPHY_FLAG_DISABLE_BEACON_HINTS |
                            WIPHY_FLAG_IBSS_RSN;
@@ -191,8 +209,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
-       hw->uapsd_queues = IWL_UAPSD_AC_INFO;
-       hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
@@ -212,9 +228,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        iwl_mvm_reset_phy_ctxts(mvm);
 
-       /* we create the 802.11 header and a max-length SSID element */
-       hw->wiphy->max_scan_ie_len =
-               mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+       hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
+
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
 
        if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
@@ -231,6 +246,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
+               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+               hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+               hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
+               /* we create the 802.11 header and zero length SSID IE. */
+               hw->wiphy->max_sched_scan_ie_len =
+                                       SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
+       }
+
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_P2P_GO_OPPPS;
 
@@ -548,7 +572,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
         * In short: there's not much we can do at this point, other than
         * allocating resources :)
         */
-       if (vif->type == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_ADHOC) {
                u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
                ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
                                               qmask);
@@ -698,7 +723,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
         * For AP/GO interface, the tear down of the resources allocated to the
         * interface is be handled as part of the stop_ap flow.
         */
-       if (vif->type == NL80211_IFTYPE_AP) {
+       if (vif->type == NL80211_IFTYPE_AP ||
+           vif->type == NL80211_IFTYPE_ADHOC) {
+#ifdef CONFIG_NL80211_TESTMODE
+               if (vif == mvm->noa_vif) {
+                       mvm->noa_vif = NULL;
+                       mvm->noa_duration = 0;
+               }
+#endif
                iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
                goto out_release;
        }
@@ -796,6 +828,27 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                return;
                        }
                        iwl_mvm_configure_mcast_filter(mvm, vif);
+
+                       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+                                    &mvm->status)) {
+                               /*
+                                * If we're restarting then the firmware will
+                                * obviously have lost synchronisation with
+                                * the AP. It will attempt to synchronise by
+                                * itself, but we can make it more reliable by
+                                * scheduling a session protection time event.
+                                *
+                                * The firmware needs to receive a beacon to
+                                * catch up with synchronisation, use 110% of
+                                * the beacon interval.
+                                *
+                                * Set a large maximum delay to allow for more
+                                * than a single interface.
+                                */
+                               u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
+                               iwl_mvm_protect_session(mvm, vif, dur, dur,
+                                                       5 * dur);
+                       }
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
@@ -811,7 +864,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
 
-               if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) {
+               if (!(mvm->fw->ucode_capa.flags &
+                                       IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
                        /* Workaround for FW bug, otherwise FW disables device
                         * power save upon disassociation
                         */
@@ -819,7 +873,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update power mode\n");
                }
-               iwl_mvm_bt_coex_vif_assoc(mvm, vif);
+               iwl_mvm_bt_coex_vif_change(mvm);
        } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
@@ -848,7 +902,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        }
 }
 
-static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -871,7 +926,7 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        if (ret)
                goto out_remove;
 
-       mvmvif->ap_active = true;
+       mvmvif->ap_ibss_active = true;
 
        /* Send the bcast station. At this stage the TBTT and DTIM time events
         * are added and applied to the scheduler */
@@ -883,10 +938,12 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        if (ret)
                goto out_rm_bcast;
 
-       /* Need to update the P2P Device MAC */
+       /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
+       iwl_mvm_bt_coex_vif_change(mvm);
+
        mutex_unlock(&mvm->mutex);
        return 0;
 
@@ -901,7 +958,8 @@ out_unlock:
        return ret;
 }
 
-static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -910,9 +968,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        mutex_lock(&mvm->mutex);
 
-       mvmvif->ap_active = false;
+       mvmvif->ap_ibss_active = false;
+
+       iwl_mvm_bt_coex_vif_change(mvm);
 
-       /* Need to update the P2P Device MAC */
+       /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
 
@@ -924,10 +984,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        mutex_unlock(&mvm->mutex);
 }
 
-static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif,
-                                       struct ieee80211_bss_conf *bss_conf,
-                                       u32 changes)
+static void
+iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_bss_conf *bss_conf,
+                                u32 changes)
 {
        /* Need to send a new beacon template to the FW */
        if (changes & BSS_CHANGED_BEACON) {
@@ -950,7 +1011,8 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
                iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
                break;
        case NL80211_IFTYPE_AP:
-               iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes);
+       case NL80211_IFTYPE_ADHOC:
+               iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);
                break;
        default:
                /* shouldn't happen */
@@ -1163,7 +1225,54 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
        /* Try really hard to protect the session and hear a beacon */
-       iwl_mvm_protect_session(mvm, vif, duration, min_duration);
+       iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct cfg80211_sched_scan_request *req,
+                                       struct ieee80211_sched_scan_ies *ies)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
+               IWL_DEBUG_SCAN(mvm,
+                              "SCHED SCAN request during internal scan - abort\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       mvm->scan_status = IWL_MVM_SCAN_SCHED;
+
+       ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+       if (ret)
+               goto err;
+
+       ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+       if (ret)
+               goto err;
+
+       ret = iwl_mvm_sched_scan_start(mvm, req);
+       if (!ret)
+               goto out;
+err:
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_sched_scan_stop(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1207,8 +1316,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
-               if (vif->type == NL80211_IFTYPE_AP && !sta) {
-                       /* GTK on AP interface is a TX-only key, return 0 */
+               if ((vif->type == NL80211_IFTYPE_ADHOC ||
+                    vif->type == NL80211_IFTYPE_AP) && !sta) {
+                       /*
+                        * GTK on AP interface is a TX-only key, return 0;
+                        * on IBSS they're per-station and because we're lazy
+                        * we don't support them for RX, so do the same.
+                        */
                        ret = 0;
                        key->hw_key_idx = STA_KEY_IDX_INVALID;
                        break;
@@ -1252,6 +1366,9 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+               return;
+
        iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
 }
 
@@ -1445,6 +1562,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
+       iwl_mvm_bt_coex_vif_change(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1464,14 +1582,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
                /*
                 * The AP binding flow is handled as part of the start_ap flow
-                * (in bss_info_changed).
+                * (in bss_info_changed), similarly for IBSS.
                 */
                ret = 0;
                goto out_unlock;
        case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_MONITOR:
                break;
        default:
@@ -1517,10 +1635,10 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
 
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
-       if (vif->type == NL80211_IFTYPE_AP)
-               goto out_unlock;
-
        switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+               goto out_unlock;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
                iwl_mvm_update_quotas(mvm, NULL);
@@ -1550,14 +1668,72 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
        return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
 }
 
-static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw,
+#ifdef CONFIG_NL80211_TESTMODE
+static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
+       [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
+       [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
+       [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
+};
+
+static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                                      struct ieee80211_vif *vif,
-                                     enum ieee80211_rssi_event rssi_event)
+                                     void *data, int len)
+{
+       struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
+       int err;
+       u32 noa_duration;
+
+       err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy);
+       if (err)
+               return err;
+
+       if (!tb[IWL_MVM_TM_ATTR_CMD])
+               return -EINVAL;
+
+       switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
+       case IWL_MVM_TM_CMD_SET_NOA:
+               if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
+                   !vif->bss_conf.enable_beacon ||
+                   !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
+                       return -EINVAL;
+
+               noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
+               if (noa_duration >= vif->bss_conf.beacon_int)
+                       return -EINVAL;
+
+               mvm->noa_duration = noa_duration;
+               mvm->noa_vif = vif;
+
+               return iwl_mvm_update_quotas(mvm, NULL);
+       case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
+               /* must be associated client vif - ignore authorized */
+               if (!vif || vif->type != NL80211_IFTYPE_STATION ||
+                   !vif->bss_conf.assoc || !vif->bss_conf.dtim_period ||
+                   !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
+                       return -EINVAL;
+
+               if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
+                       return iwl_mvm_enable_beacon_filter(mvm, vif);
+               return iwl_mvm_disable_beacon_filter(mvm, vif);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   void *data, int len)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int err;
 
-       iwl_mvm_bt_rssi_event(mvm, vif, rssi_event);
+       mutex_lock(&mvm->mutex);
+       err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
+       mutex_unlock(&mvm->mutex);
+
+       return err;
 }
+#endif
 
 struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
@@ -1578,23 +1754,27 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .sched_scan_start = iwl_mvm_mac_sched_scan_start,
+       .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
        .set_key = iwl_mvm_mac_set_key,
        .update_tkip_key = iwl_mvm_mac_update_tkip_key,
        .remain_on_channel = iwl_mvm_roc,
        .cancel_remain_on_channel = iwl_mvm_cancel_roc,
-       .rssi_callback = iwl_mvm_mac_rssi_callback,
-
        .add_chanctx = iwl_mvm_add_chanctx,
        .remove_chanctx = iwl_mvm_remove_chanctx,
        .change_chanctx = iwl_mvm_change_chanctx,
        .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
        .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
 
-       .start_ap = iwl_mvm_start_ap,
-       .stop_ap = iwl_mvm_stop_ap,
+       .start_ap = iwl_mvm_start_ap_ibss,
+       .stop_ap = iwl_mvm_stop_ap_ibss,
+       .join_ibss = iwl_mvm_start_ap_ibss,
+       .leave_ibss = iwl_mvm_stop_ap_ibss,
 
        .set_tim = iwl_mvm_set_tim,
 
+       CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
+
 #ifdef CONFIG_PM_SLEEP
        /* look at d3.c */
        .suspend = iwl_mvm_suspend,
index b038927..fed21ef 100644 (file)
@@ -73,7 +73,6 @@
 #include "iwl-trans.h"
 #include "iwl-notif-wait.h"
 #include "iwl-eeprom-parse.h"
-#include "iwl-trans.h"
 #include "sta.h"
 #include "fw-api.h"
 #include "constants.h"
@@ -162,6 +161,7 @@ enum iwl_power_scheme {
 struct iwl_mvm_power_ops {
        int (*power_update_mode)(struct iwl_mvm *mvm,
                                 struct ieee80211_vif *vif);
+       int (*power_update_device_mode)(struct iwl_mvm *mvm);
        int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -241,12 +241,18 @@ enum iwl_mvm_smps_type_request {
 * @last_beacon_signal: last beacon rssi signal in dbm
 * @ave_beacon_signal: average beacon signal
 * @last_cqm_event: rssi of the last cqm event
+* @bt_coex_min_thold: minimum threshold for BT coex
+* @bt_coex_max_thold: maximum threshold for BT coex
+* @last_bt_coex_event: rssi of the last BT coex event
 */
 struct iwl_mvm_vif_bf_data {
        bool bf_enabled;
        bool ba_enabled;
        s8 ave_beacon_signal;
        s8 last_cqm_event;
+       s8 bt_coex_min_thold;
+       s8 bt_coex_max_thold;
+       s8 last_bt_coex_event;
 };
 
 /**
@@ -255,8 +261,8 @@ struct iwl_mvm_vif_bf_data {
  * @color: to solve races upon MAC addition and removal
  * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
  * @uploaded: indicates the MAC context has been added to the device
- * @ap_active: indicates that ap context is configured, and that the interface
- *  should get quota etc.
+ * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
+ *     should get quota etc.
  * @monitor_active: indicates that monitor context is configured, and that the
  * interface should get quota etc.
  * @queue_params: QoS params for this MAC
@@ -272,7 +278,7 @@ struct iwl_mvm_vif {
        u8 ap_sta_id;
 
        bool uploaded;
-       bool ap_active;
+       bool ap_ibss_active;
        bool monitor_active;
        struct iwl_mvm_vif_bf_data bf_data;
 
@@ -306,6 +312,9 @@ struct iwl_mvm_vif {
 
        int tx_key_idx;
 
+       bool seqno_valid;
+       u16 seqno;
+
 #if IS_ENABLED(CONFIG_IPV6)
        /* IPv6 addresses for WoWLAN */
        struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
@@ -333,6 +342,7 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
 enum iwl_scan_status {
        IWL_MVM_SCAN_NONE,
        IWL_MVM_SCAN_OS,
+       IWL_MVM_SCAN_SCHED,
 };
 
 /**
@@ -434,7 +444,7 @@ struct iwl_mvm {
 
        enum iwl_ucode_type cur_ucode;
        bool ucode_loaded;
-       bool init_ucode_run;
+       bool init_ucode_complete;
        u32 error_event_table;
        u32 log_event_table;
 
@@ -470,6 +480,9 @@ struct iwl_mvm {
        enum iwl_scan_status scan_status;
        struct iwl_scan_cmd *scan_cmd;
 
+       /* rx chain antennas set through debugfs for the scan command */
+       u8 scan_rx_ant;
+
        /* Internal station */
        struct iwl_mvm_int_sta aux_sta;
 
@@ -479,7 +492,8 @@ struct iwl_mvm {
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct dentry *debugfs_dir;
        u32 dbgfs_sram_offset, dbgfs_sram_len;
-       bool prevent_power_down_d3;
+       bool disable_power_off;
+       bool disable_power_off_d3;
 #endif
 
        struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
@@ -523,12 +537,23 @@ struct iwl_mvm {
        /* BT-Coex */
        u8 bt_kill_msk;
        struct iwl_bt_coex_profile_notif last_bt_notif;
+       struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
 
        /* Thermal Throttling and CTkill */
        struct iwl_mvm_tt_mgmt thermal_throttle;
        s32 temperature;        /* Celsius */
 
        const struct iwl_mvm_power_ops *pm_ops;
+
+#ifdef CONFIG_NL80211_TESTMODE
+       u32 noa_duration;
+       struct ieee80211_vif *noa_vif;
+#endif
+
+       /* Tx queues */
+       u8 aux_queue;
+       u8 first_agg_queue;
+       u8 last_agg_queue;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -570,6 +595,9 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
 /* Utils */
 int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
                                        enum ieee80211_band band);
+void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
+                              enum ieee80211_band band,
+                              struct ieee80211_tx_rate *r);
 u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
 void iwl_mvm_dump_sram(struct iwl_mvm *mvm);
@@ -608,6 +636,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
 
 /* NVM */
 int iwl_nvm_init(struct iwl_mvm *mvm);
+int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
 
 int iwl_mvm_up(struct iwl_mvm *mvm);
 int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
@@ -682,6 +711,23 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                             struct iwl_device_cmd *cmd);
 void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
 
+/* Scheduled scan */
+int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
+                                          struct iwl_rx_cmd_buffer *rxb,
+                                          struct iwl_device_cmd *cmd);
+int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct cfg80211_sched_scan_request *req,
+                             struct ieee80211_sched_scan_ies *ies);
+int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
+                                      struct cfg80211_sched_scan_request *req);
+int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
+                            struct cfg80211_sched_scan_request *req);
+void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm);
+int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
+                                 struct iwl_rx_cmd_buffer *rxb,
+                                 struct iwl_device_cmd *cmd);
+
 /* MVM debugfs */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
@@ -720,6 +766,13 @@ static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
        return mvm->pm_ops->power_disable(mvm, vif);
 }
 
+static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
+{
+       if (mvm->pm_ops->power_update_device_mode)
+               return mvm->pm_ops->power_update_device_mode(mvm);
+       return 0;
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
                                            struct ieee80211_vif *vif,
@@ -745,6 +798,15 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
 void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif, int idx);
 extern const struct file_operations iwl_dbgfs_d3_test_ops;
+#ifdef CONFIG_PM_SLEEP
+void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif);
+#else
+static inline void
+iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+}
+#endif
 
 /* BT Coex */
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
@@ -754,7 +816,20 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                             struct iwl_device_cmd *cmd);
 void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                           enum ieee80211_rssi_event rssi_event);
-void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
+u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm,
+                                  struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
+                                    struct ieee80211_sta *sta);
+
+enum iwl_bt_kill_msk {
+       BT_KILL_MSK_DEFAULT,
+       BT_KILL_MSK_SCO_HID_A2DP,
+       BT_KILL_MSK_REDUCED_TXPOW,
+       BT_KILL_MSK_MAX,
+};
+extern const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX];
+extern const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX];
 
 /* beacon filtering */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
index edb94ea..2beffd0 100644 (file)
@@ -77,7 +77,7 @@ static const int nvm_to_read[] = {
 
 /* Default NVM size to read */
 #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
-#define IWL_MAX_NVM_SECTION_SIZE 6000
+#define IWL_MAX_NVM_SECTION_SIZE 7000
 
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
@@ -259,6 +259,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
 #define MAX_NVM_FILE_LEN       16384
 
 /*
+ * Reads external NVM from a file into mvm->nvm_sections
+ *
  * HOW TO CREATE THE NVM FILE FORMAT:
  * ------------------------------
  * 1. create hex file, format:
@@ -277,20 +279,23 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
  *
  * 4. save as "iNVM_xxx.bin" under /lib/firmware
  */
-static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)
+static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
 {
-       int ret, section_id, section_size;
+       int ret, section_size;
+       u16 section_id;
        const struct firmware *fw_entry;
        const struct {
                __le16 word1;
                __le16 word2;
                u8 data[];
        } *file_sec;
-       const u8 *eof;
+       const u8 *eof, *temp;
 
 #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
 #define NVM_WORD2_ID(x) (x >> 12)
 
+       IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
+
        /*
         * Obtain NVM image via request_firmware. Since we already used
         * request_firmware_nowait() for the firmware binary load and only
@@ -362,12 +367,18 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)
                        break;
                }
 
-               ret = iwl_nvm_write_section(mvm, section_id, file_sec->data,
-                                           section_size);
-               if (ret < 0) {
-                       IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+               temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
+               if (!temp) {
+                       ret = -ENOMEM;
+                       break;
+               }
+               if (WARN_ON(section_id >= NVM_NUM_OF_SECTIONS)) {
+                       IWL_ERR(mvm, "Invalid NVM section ID\n");
+                       ret = -EINVAL;
                        break;
                }
+               mvm->nvm_sections[section_id].data = temp;
+               mvm->nvm_sections[section_id].length = section_size;
 
                /* advance to the next section */
                file_sec = (void *)(file_sec->data + section_size);
@@ -377,6 +388,28 @@ out:
        return ret;
 }
 
+/* Loads the NVM data stored in mvm->nvm_sections into the NIC */
+int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
+{
+       int i, ret;
+       u16 section_id;
+       struct iwl_nvm_section *sections = mvm->nvm_sections;
+
+       IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n");
+
+       for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+               section_id = nvm_to_read[i];
+               ret = iwl_nvm_write_section(mvm, section_id,
+                                           sections[section_id].data,
+                                           sections[section_id].length);
+               if (ret < 0) {
+                       IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+                       break;
+               }
+       }
+       return ret;
+}
+
 int iwl_nvm_init(struct iwl_mvm *mvm)
 {
        int ret, i, section;
@@ -385,36 +418,36 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        /* load external NVM if configured */
        if (iwlwifi_mod_params.nvm_file) {
                /* move to External NVM flow */
-               ret = iwl_mvm_load_external_nvm(mvm);
+               ret = iwl_mvm_read_external_nvm(mvm);
                if (ret)
                        return ret;
-       }
-
-       /* Read From FW NVM */
-       IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
-
-       /* TODO: find correct NVM max size for a section */
-       nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
-                            GFP_KERNEL);
-       if (!nvm_buffer)
-               return -ENOMEM;
-       for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
-               section = nvm_to_read[i];
-               /* we override the constness for initial read */
-               ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
-               if (ret < 0)
-                       break;
-               temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
-               if (!temp) {
-                       ret = -ENOMEM;
-                       break;
+       } else {
+               /* Read From FW NVM */
+               IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
+
+               /* TODO: find correct NVM max size for a section */
+               nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+                                    GFP_KERNEL);
+               if (!nvm_buffer)
+                       return -ENOMEM;
+               for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+                       section = nvm_to_read[i];
+                       /* we override the constness for initial read */
+                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
+                       if (ret < 0)
+                               break;
+                       temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+                       if (!temp) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+                       mvm->nvm_sections[section].data = temp;
+                       mvm->nvm_sections[section].length = ret;
                }
-               mvm->nvm_sections[section].data = temp;
-               mvm->nvm_sections[section].length = ret;
+               kfree(nvm_buffer);
+               if (ret < 0)
+                       return ret;
        }
-       kfree(nvm_buffer);
-       if (ret < 0)
-               return ret;
 
        mvm->nvm_data = iwl_parse_nvm_sections(mvm);
        if (!mvm->nvm_data)
index 2fcc8ef..d86083c 100644 (file)
@@ -224,6 +224,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
 
        RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
+       RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
+                  iwl_mvm_rx_scan_offload_complete_notif, false),
+       RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
+                  false),
 
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
        RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -249,6 +253,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(TIME_EVENT_NOTIFICATION),
        CMD(BINDING_CONTEXT_CMD),
        CMD(TIME_QUOTA_CMD),
+       CMD(NON_QOS_TX_COUNTER_CMD),
        CMD(RADIO_VERSION_NOTIFICATION),
        CMD(SCAN_REQUEST_CMD),
        CMD(SCAN_ABORT_CMD),
@@ -260,10 +265,12 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(CALIB_RES_NOTIF_PHY_DB),
        CMD(SET_CALIB_DEFAULT_CMD),
        CMD(CALIBRATION_COMPLETE_NOTIFICATION),
+       CMD(ADD_STA_KEY),
        CMD(ADD_STA),
        CMD(REMOVE_STA),
        CMD(LQ_CMD),
        CMD(SCAN_OFFLOAD_CONFIG_CMD),
+       CMD(MATCH_FOUND_NOTIFICATION),
        CMD(SCAN_OFFLOAD_REQUEST_CMD),
        CMD(SCAN_OFFLOAD_ABORT_CMD),
        CMD(SCAN_OFFLOAD_COMPLETE),
@@ -303,6 +310,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REPLY_BEACON_FILTERING_CMD),
        CMD(REPLY_THERMAL_MNG_BACKOFF),
        CMD(MAC_PM_POWER_TABLE),
+       CMD(BT_COEX_CI),
 };
 #undef CMD
 
@@ -344,6 +352,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
 
+       mvm->aux_queue = 15;
+       mvm->first_agg_queue = 16;
+       mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1;
+       if (mvm->cfg->base_params->num_of_queues == 16) {
+               mvm->aux_queue = 11;
+               mvm->first_agg_queue = 12;
+       }
+
        mutex_init(&mvm->mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
@@ -401,24 +417,32 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
                 mvm->cfg->name, mvm->trans->hw_rev);
 
-       err = iwl_trans_start_hw(mvm->trans);
-       if (err)
-               goto out_free;
-
        iwl_mvm_tt_initialize(mvm);
 
-       mutex_lock(&mvm->mutex);
-       err = iwl_run_init_mvm_ucode(mvm, true);
-       mutex_unlock(&mvm->mutex);
-       /* returns 0 if successful, 1 if success but in rfkill */
-       if (err < 0 && !iwlmvm_mod_params.init_dbg) {
-               IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
-               goto out_free;
-       }
+       /*
+        * If the NVM exists in an external file,
+        * there is no need to unnecessarily power up the NIC at driver load
+        */
+       if (iwlwifi_mod_params.nvm_file) {
+                       iwl_nvm_init(mvm);
+       } else {
+               err = iwl_trans_start_hw(mvm->trans);
+               if (err)
+                       goto out_free;
+
+               mutex_lock(&mvm->mutex);
+               err = iwl_run_init_mvm_ucode(mvm, true);
+               mutex_unlock(&mvm->mutex);
+               /* returns 0 if successful, 1 if success but in rfkill */
+               if (err < 0 && !iwlmvm_mod_params.init_dbg) {
+                       IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
+                       goto out_free;
+               }
 
-       /* Stop the hw after the ALIVE and NVM has been read */
-       if (!iwlmvm_mod_params.init_dbg)
-               iwl_trans_stop_hw(mvm->trans, false);
+               /* Stop the hw after the ALIVE and NVM has been read */
+               if (!iwlmvm_mod_params.init_dbg)
+                       iwl_trans_stop_hw(mvm->trans, false);
+       }
 
        scan_size = sizeof(struct iwl_scan_cmd) +
                mvm->fw->ucode_capa.max_probe_length +
@@ -435,7 +459,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (err)
                goto out_unregister;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)
                mvm->pm_ops = &pm_mac_ops;
        else
                mvm->pm_ops = &pm_legacy_ops;
@@ -449,7 +473,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
  out_free:
        iwl_phy_db_free(mvm->phy_db);
        kfree(mvm->scan_cmd);
-       iwl_trans_stop_hw(trans, true);
+       if (!iwlwifi_mod_params.nvm_file)
+               iwl_trans_stop_hw(trans, true);
        ieee80211_free_hw(mvm->hw);
        return NULL;
 }
@@ -715,6 +740,9 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                case IWL_MVM_SCAN_OS:
                        ieee80211_scan_completed(mvm->hw, true);
                        break;
+               case IWL_MVM_SCAN_SCHED:
+                       ieee80211_sched_scan_stopped(mvm->hw);
+                       break;
                }
 
                if (mvm->restart_fw > 0)
index d58e393..550824a 100644 (file)
@@ -300,11 +300,6 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        }
 
        if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
-               cmd->rx_data_timeout_uapsd =
-                       cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
-               cmd->tx_data_timeout_uapsd =
-                       cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
-
                if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
                                            BIT(IEEE80211_AC_VI) |
                                            BIT(IEEE80211_AC_BE) |
@@ -319,10 +314,31 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                }
 
                cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
-               cmd->heavy_tx_thld_packets =
-                       IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
-               cmd->heavy_rx_thld_packets =
-                       IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+
+               if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
+                   cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+                       cmd->rx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+                       cmd->tx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+               } else {
+                       cmd->rx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
+                       cmd->tx_data_timeout_uapsd =
+                               cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
+               }
+
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+                       cmd->heavy_tx_thld_packets =
+                               IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
+                       cmd->heavy_rx_thld_packets =
+                               IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
+               } else {
+                       cmd->heavy_tx_thld_packets =
+                               IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
+                       cmd->heavy_rx_thld_packets =
+                               IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+               }
                cmd->heavy_tx_thld_percentage =
                        IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
                cmd->heavy_rx_thld_percentage =
@@ -430,6 +446,32 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
                                    sizeof(cmd), &cmd);
 }
 
+static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+{
+       struct iwl_device_power_cmd cmd = {
+               .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
+       };
+
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
+               return 0;
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+               cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 :
+           mvm->disable_power_off)
+               cmd.flags &=
+                       cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+       IWL_DEBUG_POWER(mvm,
+                       "Sending device power command with flags = 0x%X\n",
+                       cmd.flags);
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, sizeof(cmd),
+                                   &cmd);
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif, char *buf,
@@ -440,10 +482,11 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
 
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
 
-       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
-                        (cmd.flags &
-                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
-                        0 : 1);
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
+               pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                                0 : 1);
        pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
                         iwlmvm_mod_params.power_scheme);
        pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
@@ -609,6 +652,7 @@ int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
 
 const struct iwl_mvm_power_ops pm_mac_ops = {
        .power_update_mode = iwl_mvm_power_mac_update_mode,
+       .power_update_device_mode = iwl_mvm_power_update_device,
        .power_disable = iwl_mvm_power_mac_disable,
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
index 5c6ae16..17e2bc8 100644 (file)
@@ -110,7 +110,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                        data->n_interfaces[id]++;
                break;
        case NL80211_IFTYPE_AP:
-               if (mvmvif->ap_active)
+       case NL80211_IFTYPE_ADHOC:
+               if (mvmvif->ap_ibss_active)
                        data->n_interfaces[id]++;
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -119,16 +120,45 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
-       case NL80211_IFTYPE_ADHOC:
-               if (vif->bss_conf.ibss_joined)
-                       data->n_interfaces[id]++;
-               break;
        default:
                WARN_ON_ONCE(1);
                break;
        }
 }
 
+static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
+                                        struct iwl_time_quota_cmd *cmd)
+{
+#ifdef CONFIG_NL80211_TESTMODE
+       struct iwl_mvm_vif *mvmvif;
+       int i, phy_id = -1, beacon_int = 0;
+
+       if (!mvm->noa_duration || !mvm->noa_vif)
+               return;
+
+       mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
+       if (!mvmvif->ap_ibss_active)
+               return;
+
+       phy_id = mvmvif->phy_ctxt->id;
+       beacon_int = mvm->noa_vif->bss_conf.beacon_int;
+
+       for (i = 0; i < MAX_BINDINGS; i++) {
+               u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color);
+               u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
+               u32 quota = le32_to_cpu(cmd->quotas[i].quota);
+
+               if (id != phy_id)
+                       continue;
+
+               quota *= (beacon_int - mvm->noa_duration);
+               quota /= beacon_int;
+
+               cmd->quotas[i].quota = cpu_to_le32(quota);
+       }
+#endif
+}
+
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 {
        struct iwl_time_quota_cmd cmd = {};
@@ -196,6 +226,8 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
        /* Give the remainder of the session to the first binding */
        le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
 
+       iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
+
        ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
                                   sizeof(cmd), &cmd);
        if (ret)
index 4ffaa3f..a0b4cc8 100644 (file)
@@ -82,13 +82,24 @@ static const u8 ant_toggle_lookup[] = {
        [ANT_ABC] = ANT_ABC,
 };
 
-#define IWL_DECLARE_RATE_INFO(r, s, rp, rn)                   \
-       [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
-                                   IWL_RATE_SISO_##s##M_PLCP, \
-                                   IWL_RATE_MIMO2_##s##M_PLCP,\
-                                   IWL_RATE_##rp##M_INDEX,    \
+#define IWL_DECLARE_RATE_INFO(r, s, rp, rn)                          \
+       [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,             \
+                                   IWL_RATE_HT_SISO_MCS_##s##_PLCP,  \
+                                   IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \
+                                   IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \
+                                   IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\
+                                   IWL_RATE_##rp##M_INDEX,           \
                                    IWL_RATE_##rn##M_INDEX }
 
+#define IWL_DECLARE_MCS_RATE(s)                                                  \
+       [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP,                \
+                                      IWL_RATE_HT_SISO_MCS_##s##_PLCP,   \
+                                      IWL_RATE_HT_MIMO2_MCS_##s##_PLCP,  \
+                                      IWL_RATE_VHT_SISO_MCS_##s##_PLCP,  \
+                                      IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \
+                                      IWL_RATE_INVM_INDEX,               \
+                                      IWL_RATE_INVM_INDEX }
+
 /*
  * Parameter order:
  *   rate, ht rate, prev rate, next rate
@@ -102,16 +113,17 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
        IWL_DECLARE_RATE_INFO(2, INV, 1, 5),     /*  2mbps */
        IWL_DECLARE_RATE_INFO(5, INV, 2, 11),    /*5.5mbps */
        IWL_DECLARE_RATE_INFO(11, INV, 9, 12),   /* 11mbps */
-       IWL_DECLARE_RATE_INFO(6, 6, 5, 11),      /*  6mbps */
-       IWL_DECLARE_RATE_INFO(9, 6, 6, 11),      /*  9mbps */
-       IWL_DECLARE_RATE_INFO(12, 12, 11, 18),   /* 12mbps */
-       IWL_DECLARE_RATE_INFO(18, 18, 12, 24),   /* 18mbps */
-       IWL_DECLARE_RATE_INFO(24, 24, 18, 36),   /* 24mbps */
-       IWL_DECLARE_RATE_INFO(36, 36, 24, 48),   /* 36mbps */
-       IWL_DECLARE_RATE_INFO(48, 48, 36, 54),   /* 48mbps */
-       IWL_DECLARE_RATE_INFO(54, 54, 48, INV),  /* 54mbps */
-       IWL_DECLARE_RATE_INFO(60, 60, 48, INV),  /* 60mbps */
-       /* FIXME:RS:          ^^    should be INV (legacy) */
+       IWL_DECLARE_RATE_INFO(6, 0, 5, 11),      /*  6mbps ; MCS 0 */
+       IWL_DECLARE_RATE_INFO(9, INV, 6, 11),    /*  9mbps */
+       IWL_DECLARE_RATE_INFO(12, 1, 11, 18),    /* 12mbps ; MCS 1 */
+       IWL_DECLARE_RATE_INFO(18, 2, 12, 24),    /* 18mbps ; MCS 2 */
+       IWL_DECLARE_RATE_INFO(24, 3, 18, 36),    /* 24mbps ; MCS 3 */
+       IWL_DECLARE_RATE_INFO(36, 4, 24, 48),    /* 36mbps ; MCS 4 */
+       IWL_DECLARE_RATE_INFO(48, 5, 36, 54),    /* 48mbps ; MCS 5 */
+       IWL_DECLARE_RATE_INFO(54, 6, 48, INV),   /* 54mbps ; MCS 6 */
+       IWL_DECLARE_MCS_RATE(7),                 /* MCS 7 */
+       IWL_DECLARE_MCS_RATE(8),                 /* MCS 8 */
+       IWL_DECLARE_MCS_RATE(9),                 /* MCS 9 */
 };
 
 static inline u8 rs_extract_rate(u32 rate_n_flags)
@@ -124,26 +136,30 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
 {
        int idx = 0;
 
-       /* HT rate format */
        if (rate_n_flags & RATE_MCS_HT_MSK) {
-               idx = rs_extract_rate(rate_n_flags);
-
-               WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP);
-               if (idx >= IWL_RATE_MIMO2_6M_PLCP)
-                       idx = idx - IWL_RATE_MIMO2_6M_PLCP;
+               idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK;
+               idx += IWL_RATE_MCS_0_INDEX;
 
-               idx += IWL_FIRST_OFDM_RATE;
-               /* skip 9M not supported in ht*/
+               /* skip 9M not supported in HT*/
                if (idx >= IWL_RATE_9M_INDEX)
                        idx += 1;
-               if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+               if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE))
                        return idx;
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+               idx += IWL_RATE_MCS_0_INDEX;
 
-       /* legacy rate format, search for match in table */
+               /* skip 9M not supported in VHT*/
+               if (idx >= IWL_RATE_9M_INDEX)
+                       idx++;
+               if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE))
+                       return idx;
        } else {
+               /* legacy rate format, search for match in table */
+
+               u8 legacy_rate = rs_extract_rate(rate_n_flags);
                for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
-                       if (iwl_rates[idx].plcp ==
-                                       rs_extract_rate(rate_n_flags))
+                       if (iwl_rates[idx].plcp == legacy_rate)
                                return idx;
        }
 
@@ -155,6 +171,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                                   struct ieee80211_sta *sta,
                                   struct iwl_lq_sta *lq_sta);
 static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct ieee80211_sta *sta,
                             struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
 static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
 
@@ -180,35 +197,52 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
  */
 
 static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
-       7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
+       7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
 };
 
-static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202}, /* Norm */
-       {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210}, /* SGI */
-       {0, 0, 0, 0, 47, 0,  91, 133, 171, 242, 305, 334, 362}, /* AGG */
-       {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
+/* Expected TpT tables. 4 indexes:
+ * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
+ */
+static s32 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202, 216, 0},
+       {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210, 225, 0},
+       {0, 0, 0, 0, 49, 0,  97, 145, 192, 285, 375, 420, 464, 551, 0},
+       {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
 };
 
-static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
-       {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
-       {0, 0, 0, 0,  94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
-       {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
+static s32 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250,  257,  269,  275},
+       {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257,  264,  275,  280},
+       {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828,  911, 1070, 1173},
+       {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
+};
+
+static s32 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 130, 0, 191, 223, 244,  273,  288,  294,  298,  305,  308},
+       {0, 0, 0, 0, 138, 0, 200, 231, 251,  279,  293,  298,  302,  308,  312},
+       {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
+       {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
 };
 
 static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0,  74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
-       {0, 0, 0, 0,  81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
-       {0, 0, 0, 0,  89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
-       {0, 0, 0, 0,  97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
+       {0, 0, 0, 0,  74, 0, 123, 155, 179, 213, 235, 243, 250,  261, 0},
+       {0, 0, 0, 0,  81, 0, 131, 164, 187, 221, 242, 250, 256,  267, 0},
+       {0, 0, 0, 0,  98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
+       {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
 };
 
 static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
-       {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
-       {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
-       {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
+       {0, 0, 0, 0, 123, 0, 182, 214, 235,  264,  279,  285,  289,  296,  300},
+       {0, 0, 0, 0, 131, 0, 191, 222, 242,  270,  284,  289,  293,  300,  303},
+       {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
+       {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
+};
+
+static s32 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 182, 0, 240,  264,  278,  299,  308,  311,  313,  317,  319},
+       {0, 0, 0, 0, 190, 0, 247,  269,  282,  302,  310,  313,  315,  319,  320},
+       {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
+       {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545},
 };
 
 /* mbps, mcs */
@@ -263,7 +297,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
                       lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
 
        if (lq_sta->dbg_fixed_rate) {
-               rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
+               rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate);
                iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false);
        }
 }
@@ -275,17 +309,6 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
 {
        int ret = -EAGAIN;
 
-       /*
-        * Don't create TX aggregation sessions when in high
-        * BT traffic, as they would just be disrupted by BT.
-        */
-       if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) {
-               IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n",
-                              BT_MBOX_MSG(&mvm->last_bt_notif,
-                                          3, TRAFFIC_LOAD));
-               return ret;
-       }
-
        IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
                     sta->addr, tid);
        ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
@@ -416,49 +439,54 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
  */
 /* FIXME:RS:remove this function and put the flags statically in the table */
 static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm,
-                                struct iwl_scale_tbl_info *tbl,
-                                int index, u8 use_green)
+                                struct iwl_scale_tbl_info *tbl, int index)
 {
        u32 rate_n_flags = 0;
 
+       rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
+                        RATE_MCS_ANT_ABC_MSK);
+
        if (is_legacy(tbl->lq_type)) {
-               rate_n_flags = iwl_rates[index].plcp;
+               rate_n_flags |= iwl_rates[index].plcp;
                if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
                        rate_n_flags |= RATE_MCS_CCK_MSK;
-       } else if (is_Ht(tbl->lq_type)) {
-               if (index > IWL_LAST_OFDM_RATE) {
+               return rate_n_flags;
+       }
+
+       if (is_ht(tbl->lq_type)) {
+               if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) {
                        IWL_ERR(mvm, "Invalid HT rate index %d\n", index);
-                       index = IWL_LAST_OFDM_RATE;
+                       index = IWL_LAST_HT_RATE;
                }
-               rate_n_flags = RATE_MCS_HT_MSK;
+               rate_n_flags |= RATE_MCS_HT_MSK;
 
-               if (is_siso(tbl->lq_type))
-                       rate_n_flags |= iwl_rates[index].plcp_siso;
-               else if (is_mimo2(tbl->lq_type))
-                       rate_n_flags |= iwl_rates[index].plcp_mimo2;
+               if (is_ht_siso(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_ht_siso;
+               else if (is_ht_mimo2(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_ht_mimo2;
                else
                        WARN_ON_ONCE(1);
+       } else if (is_vht(tbl->lq_type)) {
+               if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) {
+                       IWL_ERR(mvm, "Invalid VHT rate index %d\n", index);
+                       index = IWL_LAST_VHT_RATE;
+               }
+               rate_n_flags |= RATE_MCS_VHT_MSK;
+               if (is_vht_siso(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_vht_siso;
+               else if (is_vht_mimo2(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_vht_mimo2;
+               else
+                       WARN_ON_ONCE(1);
+
        } else {
                IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type);
        }
 
-       rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
-                                                    RATE_MCS_ANT_ABC_MSK);
-
-       if (is_Ht(tbl->lq_type)) {
-               if (tbl->is_ht40)
-                       rate_n_flags |= RATE_MCS_CHAN_WIDTH_40;
-               if (tbl->is_SGI)
-                       rate_n_flags |= RATE_MCS_SGI_MSK;
-
-               if (use_green) {
-                       rate_n_flags |= RATE_HT_MCS_GF_MSK;
-                       if (is_siso(tbl->lq_type) && tbl->is_SGI) {
-                               rate_n_flags &= ~RATE_MCS_SGI_MSK;
-                               IWL_ERR(mvm, "GF was set with SGI:SISO\n");
-                       }
-               }
-       }
+       rate_n_flags |= tbl->bw;
+       if (tbl->is_SGI)
+               rate_n_flags |= RATE_MCS_SGI_MSK;
+
        return rate_n_flags;
 }
 
@@ -473,7 +501,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
 {
        u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK);
        u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
-       u8 mcs;
+       u8 nss;
 
        memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win));
        *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
@@ -483,41 +511,62 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
                return -EINVAL;
        }
        tbl->is_SGI = 0;        /* default legacy setup */
-       tbl->is_ht40 = 0;
+       tbl->bw = 0;
        tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS);
        tbl->lq_type = LQ_NONE;
        tbl->max_search = IWL_MAX_SEARCH;
 
-       /* legacy rate format */
-       if (!(rate_n_flags & RATE_MCS_HT_MSK)) {
+       /* Legacy */
+       if (!(rate_n_flags & RATE_MCS_HT_MSK) &&
+           !(rate_n_flags & RATE_MCS_VHT_MSK)) {
                if (num_of_ant == 1) {
                        if (band == IEEE80211_BAND_5GHZ)
-                               tbl->lq_type = LQ_A;
+                               tbl->lq_type = LQ_LEGACY_A;
                        else
-                               tbl->lq_type = LQ_G;
+                               tbl->lq_type = LQ_LEGACY_G;
                }
-       /* HT rate format */
-       } else {
-               if (rate_n_flags & RATE_MCS_SGI_MSK)
-                       tbl->is_SGI = 1;
-
-               if (rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
-                       tbl->is_ht40 = 1;
-
-               mcs = rs_extract_rate(rate_n_flags);
-
-               /* SISO */
-               if (mcs <= IWL_RATE_SISO_60M_PLCP) {
-                       if (num_of_ant == 1)
-                               tbl->lq_type = LQ_SISO; /*else NONE*/
-               /* MIMO2 */
-               } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
-                       if (num_of_ant == 2)
-                               tbl->lq_type = LQ_MIMO2;
+
+               return 0;
+       }
+
+       /* HT or VHT */
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               tbl->is_SGI = 1;
+
+       tbl->bw = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
+
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               nss = ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >>
+                      RATE_HT_MCS_NSS_POS) + 1;
+
+               if (nss == 1) {
+                       tbl->lq_type = LQ_HT_SISO;
+                       WARN_ON_ONCE(num_of_ant != 1);
+               } else if (nss == 2) {
+                       tbl->lq_type = LQ_HT_MIMO2;
+                       WARN_ON_ONCE(num_of_ant != 2);
+               } else {
+                       WARN_ON_ONCE(1);
+               }
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                      RATE_VHT_MCS_NSS_POS) + 1;
+
+               if (nss == 1) {
+                       tbl->lq_type = LQ_VHT_SISO;
+                       WARN_ON_ONCE(num_of_ant != 1);
+               } else if (nss == 2) {
+                       tbl->lq_type = LQ_VHT_MIMO2;
+                       WARN_ON_ONCE(num_of_ant != 2);
                } else {
-                       WARN_ON_ONCE(num_of_ant == 3);
+                       WARN_ON_ONCE(1);
                }
        }
+
+       WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_160);
+       WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_80 &&
+                    !is_vht(tbl->lq_type));
+
        return 0;
 }
 
@@ -549,22 +598,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
        return 1;
 }
 
-/**
- * Green-field mode is valid if the station supports it and
- * there are no non-GF stations present in the BSS.
- */
-static bool rs_use_green(struct ieee80211_sta *sta)
-{
-       /*
-        * There's a bug somewhere in this code that causes the
-        * scaling to get stuck because GF+SGI can't be combined
-        * in SISO rates. Until we find that bug, disable GF, it
-        * has only limited benefit and we still interoperate with
-        * GF APs since we can always receive GF transmissions.
-        */
-       return false;
-}
-
 /**
  * rs_get_supported_rates - get the available rates
  *
@@ -576,16 +609,15 @@ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
                                  struct ieee80211_hdr *hdr,
                                  enum iwl_table_type rate_type)
 {
-       if (is_legacy(rate_type)) {
+       if (is_legacy(rate_type))
                return lq_sta->active_legacy_rate;
-       } else {
-               if (is_siso(rate_type))
-                       return lq_sta->active_siso_rate;
-               else {
-                       WARN_ON_ONCE(!is_mimo2(rate_type));
-                       return lq_sta->active_mimo2_rate;
-               }
-       }
+       else if (is_siso(rate_type))
+               return lq_sta->active_siso_rate;
+       else if (is_mimo2(rate_type))
+               return lq_sta->active_mimo2_rate;
+
+       WARN_ON_ONCE(1);
+       return 0;
 }
 
 static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
@@ -652,7 +684,6 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
        u16 rate_mask;
        u16 high_low;
        u8 switch_to_legacy = 0;
-       u8 is_green = lq_sta->is_green;
        struct iwl_mvm *mvm = lq_sta->drv;
 
        /* check if we need to switch from HT to legacy rates.
@@ -662,15 +693,15 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
                switch_to_legacy = 1;
                scale_index = rs_ht_to_legacy[scale_index];
                if (lq_sta->band == IEEE80211_BAND_5GHZ)
-                       tbl->lq_type = LQ_A;
+                       tbl->lq_type = LQ_LEGACY_A;
                else
-                       tbl->lq_type = LQ_G;
+                       tbl->lq_type = LQ_LEGACY_G;
 
                if (num_of_ant(tbl->ant_type) > 1)
                        tbl->ant_type =
                            first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
 
-               tbl->is_ht40 = 0;
+               tbl->bw = 0;
                tbl->is_SGI = 0;
                tbl->max_search = IWL_MAX_SEARCH;
        }
@@ -701,7 +732,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
                low = scale_index;
 
 out:
-       return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
+       return rate_n_flags_from_tbl(lq_sta->drv, tbl, low);
 }
 
 /*
@@ -714,6 +745,18 @@ static bool table_type_matches(struct iwl_scale_tbl_info *a,
                (a->is_SGI == b->is_SGI);
 }
 
+static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags)
+{
+       if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+               return RATE_MCS_CHAN_WIDTH_40;
+       else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+               return RATE_MCS_CHAN_WIDTH_80;
+       else if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+               return RATE_MCS_CHAN_WIDTH_160;
+
+       return RATE_MCS_CHAN_WIDTH_20;
+}
+
 /*
  * mac80211 sends us Tx status
  */
@@ -783,16 +826,23 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                 */
                if (info->band == IEEE80211_BAND_2GHZ)
                        mac_index += IWL_FIRST_OFDM_RATE;
+       } else if (mac_flags & IEEE80211_TX_RC_VHT_MCS) {
+               mac_index &= RATE_VHT_MCS_RATE_CODE_MSK;
+               if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
+                       mac_index++;
        }
+
        /* Here we actually compare this rate to the latest LQ command */
        if ((mac_index < 0) ||
            (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
-           (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
+           (tbl_type.bw != rs_ch_width_from_mac_flags(mac_flags)) ||
            (tbl_type.ant_type != info->status.antenna) ||
            (!!(tx_rate & RATE_MCS_HT_MSK) !=
-                               !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+            !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+           (!!(tx_rate & RATE_MCS_VHT_MSK) !=
+            !!(mac_flags & IEEE80211_TX_RC_VHT_MCS)) ||
            (!!(tx_rate & RATE_HT_MCS_GF_MSK) !=
-                               !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
+            !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
            (rs_index != mac_index)) {
                IWL_DEBUG_RATE(mvm,
                               "initial rate %d does not match %d (0x%x)\n",
@@ -947,7 +997,8 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
        s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
 
        /* Check for invalid LQ type */
-       if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
+       if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_ht(tbl->lq_type) &&
+                        !(is_vht(tbl->lq_type)))) {
                tbl->expected_tpt = expected_tpt_legacy;
                return;
        }
@@ -958,18 +1009,40 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
                return;
        }
 
+       ht_tbl_pointer = expected_tpt_mimo2_20MHz;
        /* Choose among many HT tables depending on number of streams
-        * (SISO/MIMO2), channel width (20/40), SGI, and aggregation
+        * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation
         * status */
-       if (is_siso(tbl->lq_type) && !tbl->is_ht40)
-               ht_tbl_pointer = expected_tpt_siso20MHz;
-       else if (is_siso(tbl->lq_type))
-               ht_tbl_pointer = expected_tpt_siso40MHz;
-       else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40)
-               ht_tbl_pointer = expected_tpt_mimo2_20MHz;
-       else {
-               WARN_ON_ONCE(!is_mimo2(tbl->lq_type));
-               ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+       if (is_siso(tbl->lq_type)) {
+               switch (tbl->bw) {
+               case RATE_MCS_CHAN_WIDTH_20:
+                       ht_tbl_pointer = expected_tpt_siso_20MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_40:
+                       ht_tbl_pointer = expected_tpt_siso_40MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_80:
+                       ht_tbl_pointer = expected_tpt_siso_80MHz;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+               }
+       } else if (is_mimo2(tbl->lq_type)) {
+               switch (tbl->bw) {
+               case RATE_MCS_CHAN_WIDTH_20:
+                       ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_40:
+                       ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+                       break;
+               case RATE_MCS_CHAN_WIDTH_80:
+                       ht_tbl_pointer = expected_tpt_mimo2_80MHz;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+               }
+       } else {
+               WARN_ON_ONCE(1);
        }
 
        if (!tbl->is_SGI && !lq_sta->is_agg)            /* Normal */
@@ -1084,9 +1157,47 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
        return new_rate;
 }
 
-static bool iwl_is_ht40_tx_allowed(struct ieee80211_sta *sta)
+/* Move to the next action and wrap around to the first action in case
+ * we're at the last action. Assumes actions start at 0.
+ */
+static inline void rs_move_next_action(struct iwl_scale_tbl_info *tbl,
+                                      u8 last_action)
+{
+       BUILD_BUG_ON(IWL_LEGACY_FIRST_ACTION != 0);
+       BUILD_BUG_ON(IWL_SISO_FIRST_ACTION != 0);
+       BUILD_BUG_ON(IWL_MIMO2_FIRST_ACTION != 0);
+
+       tbl->action = (tbl->action + 1) % (last_action + 1);
+}
+
+static void rs_set_bw_from_sta(struct iwl_scale_tbl_info *tbl,
+                              struct ieee80211_sta *sta)
+{
+       if (sta->bandwidth >= IEEE80211_STA_RX_BW_80)
+               tbl->bw = RATE_MCS_CHAN_WIDTH_80;
+       else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40)
+               tbl->bw = RATE_MCS_CHAN_WIDTH_40;
+       else
+               tbl->bw = RATE_MCS_CHAN_WIDTH_20;
+}
+
+static bool rs_sgi_allowed(struct iwl_scale_tbl_info *tbl,
+                          struct ieee80211_sta *sta)
 {
-       return sta->bandwidth >= IEEE80211_STA_RX_BW_40;
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+       if (is_ht20(tbl) && (ht_cap->cap &
+                            IEEE80211_HT_CAP_SGI_20))
+               return true;
+       if (is_ht40(tbl) && (ht_cap->cap &
+                            IEEE80211_HT_CAP_SGI_40))
+               return true;
+       if (is_ht80(tbl) && (vht_cap->cap &
+                            IEEE80211_VHT_CAP_SHORT_GI_80))
+               return true;
+
+       return false;
 }
 
 /*
@@ -1099,7 +1210,6 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
 {
        u16 rate_mask;
        s32 rate;
-       s8 is_green = lq_sta->is_green;
 
        if (!sta->ht_cap.ht_supported)
                return -1;
@@ -1113,16 +1223,12 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
 
        IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n");
 
-       tbl->lq_type = LQ_MIMO2;
+       tbl->lq_type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
        tbl->action = 0;
        tbl->max_search = IWL_MAX_SEARCH;
        rate_mask = lq_sta->active_mimo2_rate;
 
-       if (iwl_is_ht40_tx_allowed(sta))
-               tbl->is_ht40 = 1;
-       else
-               tbl->is_ht40 = 0;
-
+       rs_set_bw_from_sta(tbl, sta);
        rs_set_expected_tpt_table(lq_sta, tbl);
 
        rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
@@ -1134,10 +1240,10 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
                               rate, rate_mask);
                return -1;
        }
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate);
 
-       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
-                      tbl->current_rate, is_green);
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n",
+                      tbl->current_rate);
        return 0;
 }
 
@@ -1150,7 +1256,6 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm,
                             struct iwl_scale_tbl_info *tbl, int index)
 {
        u16 rate_mask;
-       u8 is_green = lq_sta->is_green;
        s32 rate;
 
        if (!sta->ht_cap.ht_supported)
@@ -1158,19 +1263,12 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm,
 
        IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n");
 
-       tbl->lq_type = LQ_SISO;
+       tbl->lq_type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
        tbl->action = 0;
        tbl->max_search = IWL_MAX_SEARCH;
        rate_mask = lq_sta->active_siso_rate;
 
-       if (iwl_is_ht40_tx_allowed(sta))
-               tbl->is_ht40 = 1;
-       else
-               tbl->is_ht40 = 0;
-
-       if (is_green)
-               tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/
-
+       rs_set_bw_from_sta(tbl, sta);
        rs_set_expected_tpt_table(lq_sta, tbl);
        rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
 
@@ -1181,9 +1279,9 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm,
                               rate, rate_mask);
                return -1;
        }
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
-       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
-                      tbl->current_rate, is_green);
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate);
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n",
+                      tbl->current_rate);
        return 0;
 }
 
@@ -1211,14 +1309,10 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
        while (1) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_LEGACY_SWITCH_ANTENNA1:
-               case IWL_LEGACY_SWITCH_ANTENNA2:
+               case IWL_LEGACY_SWITCH_ANTENNA:
                        IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n");
 
-                       if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
-                            tx_chains_num <= 1) ||
-                           (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
-                            tx_chains_num <= 2))
+                       if (tx_chains_num <= 1)
                                break;
 
                        /* Don't change antenna if success has been great */
@@ -1273,9 +1367,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
                default:
                        WARN_ON_ONCE(1);
                }
-               tbl->action++;
-               if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
-                       tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+               rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION);
 
                if (tbl->action == start_action)
                        break;
@@ -1285,9 +1377,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
 
 out:
        lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
-               tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+       rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION);
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
        return 0;
@@ -1300,12 +1390,10 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                 struct iwl_lq_sta *lq_sta,
                                 struct ieee80211_sta *sta, int index)
 {
-       u8 is_green = lq_sta->is_green;
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct iwl_scale_tbl_info *search_tbl =
                                &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
        struct iwl_rate_scale_data *window = &(tbl->win[index]);
-       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action;
@@ -1314,40 +1402,17 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
        u8 update_search_tbl_counter = 0;
        int ret;
 
-       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
-       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
-               /* nothing */
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
-               /* avoid antenna B unless MIMO */
-               if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
-                       tbl->action = IWL_SISO_SWITCH_MIMO2;
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
-       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
-               /* avoid antenna B and MIMO */
-               valid_tx_ant =
-                       first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
-               if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
-                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
-               break;
-       default:
-               IWL_ERR(mvm, "Invalid BT load %d",
-                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
-               break;
-       }
+       if (tbl->action == IWL_SISO_SWITCH_MIMO2 &&
+           !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+               tbl->action = IWL_SISO_SWITCH_ANTENNA;
 
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_SISO_SWITCH_ANTENNA1:
-               case IWL_SISO_SWITCH_ANTENNA2:
+               case IWL_SISO_SWITCH_ANTENNA:
                        IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n");
-                       if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
-                            tx_chains_num <= 1) ||
-                           (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
-                            tx_chains_num <= 2))
+                       if (tx_chains_num <= 1)
                                break;
 
                        if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
@@ -1380,23 +1445,12 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                goto out;
                        break;
                case IWL_SISO_SWITCH_GI:
-                       if (!tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_20))
-                               break;
-                       if (tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_40))
+                       if (!rs_sgi_allowed(tbl, sta))
                                break;
 
                        IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n");
 
                        memcpy(search_tbl, tbl, sz);
-                       if (is_green) {
-                               if (!tbl->is_SGI)
-                                       break;
-                               else
-                                       IWL_ERR(mvm,
-                                               "SGI was set in GF+SISO\n");
-                       }
                        search_tbl->is_SGI = !tbl->is_SGI;
                        rs_set_expected_tpt_table(lq_sta, search_tbl);
                        if (tbl->is_SGI) {
@@ -1405,16 +1459,13 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                        break;
                        }
                        search_tbl->current_rate =
-                               rate_n_flags_from_tbl(mvm, search_tbl,
-                                                     index, is_green);
+                               rate_n_flags_from_tbl(mvm, search_tbl, index);
                        update_search_tbl_counter = 1;
                        goto out;
                default:
                        WARN_ON_ONCE(1);
                }
-               tbl->action++;
-               if (tbl->action > IWL_SISO_SWITCH_GI)
-                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+               rs_move_next_action(tbl, IWL_SISO_LAST_ACTION);
 
                if (tbl->action == start_action)
                        break;
@@ -1424,9 +1475,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
 
  out:
        lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_SISO_SWITCH_GI)
-               tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+       rs_move_next_action(tbl, IWL_SISO_LAST_ACTION);
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
 
@@ -1440,63 +1489,20 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                                 struct iwl_lq_sta *lq_sta,
                                 struct ieee80211_sta *sta, int index)
 {
-       s8 is_green = lq_sta->is_green;
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct iwl_scale_tbl_info *search_tbl =
                                &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
-       struct iwl_rate_scale_data *window = &(tbl->win[index]);
-       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action;
        u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
-       u8 tx_chains_num = num_of_ant(valid_tx_ant);
        u8 update_search_tbl_counter = 0;
        int ret;
 
-       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
-       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
-               /* nothing */
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
-       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
-               /* avoid antenna B and MIMO */
-               if (tbl->action != IWL_MIMO2_SWITCH_SISO_A)
-                       tbl->action = IWL_MIMO2_SWITCH_SISO_A;
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
-               /* avoid antenna B unless MIMO */
-               if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
-                       tbl->action = IWL_MIMO2_SWITCH_SISO_A;
-               break;
-       default:
-               IWL_ERR(mvm, "Invalid BT load %d",
-                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
-               break;
-       }
-
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_MIMO2_SWITCH_ANTENNA1:
-               case IWL_MIMO2_SWITCH_ANTENNA2:
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle Antennas\n");
-
-                       if (tx_chains_num <= 2)
-                               break;
-
-                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
-                               break;
-
-                       memcpy(search_tbl, tbl, sz);
-                       if (rs_toggle_antenna(valid_tx_ant,
-                                             &search_tbl->current_rate,
-                                             search_tbl)) {
-                               update_search_tbl_counter = 1;
-                               goto out;
-                       }
-                       break;
                case IWL_MIMO2_SWITCH_SISO_A:
                case IWL_MIMO2_SWITCH_SISO_B:
                        IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n");
@@ -1521,11 +1527,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                        break;
 
                case IWL_MIMO2_SWITCH_GI:
-                       if (!tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_20))
-                               break;
-                       if (tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_40))
+                       if (!rs_sgi_allowed(tbl, sta))
                                break;
 
                        IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n");
@@ -1546,16 +1548,13 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                                        break;
                        }
                        search_tbl->current_rate =
-                               rate_n_flags_from_tbl(mvm, search_tbl,
-                                                     index, is_green);
+                               rate_n_flags_from_tbl(mvm, search_tbl, index);
                        update_search_tbl_counter = 1;
                        goto out;
                default:
                        WARN_ON_ONCE(1);
                }
-               tbl->action++;
-               if (tbl->action > IWL_MIMO2_SWITCH_GI)
-                       tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+               rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION);
 
                if (tbl->action == start_action)
                        break;
@@ -1564,9 +1563,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
        return 0;
  out:
        lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_MIMO2_SWITCH_GI)
-               tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+       rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION);
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
 
@@ -1660,15 +1657,16 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
  * setup rate table in uCode
  */
 static void rs_update_rate_tbl(struct iwl_mvm *mvm,
+                              struct ieee80211_sta *sta,
                               struct iwl_lq_sta *lq_sta,
                               struct iwl_scale_tbl_info *tbl,
-                              int index, u8 is_green)
+                              int index)
 {
        u32 rate;
 
        /* Update uCode's rate table. */
-       rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
-       rs_fill_link_cmd(mvm, lq_sta, rate);
+       rate = rate_n_flags_from_tbl(mvm, tbl, index);
+       rs_fill_link_cmd(mvm, sta, lq_sta, rate);
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
 }
 
@@ -1712,7 +1710,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        u8 update_lq = 0;
        struct iwl_scale_tbl_info *tbl, *tbl1;
        u16 rate_scale_index_msk = 0;
-       u8 is_green = 0;
        u8 active_tbl = 0;
        u8 done_search = 0;
        u16 high_low;
@@ -1754,11 +1751,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                active_tbl = 1 - lq_sta->active_tbl;
 
        tbl = &(lq_sta->lq_info[active_tbl]);
-       if (is_legacy(tbl->lq_type))
-               lq_sta->is_green = 0;
-       else
-               lq_sta->is_green = rs_use_green(sta);
-       is_green = lq_sta->is_green;
 
        /* current tx rate */
        index = lq_sta->last_txrate_idx;
@@ -1797,7 +1789,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                        tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
                        /* get "active" rate info */
                        index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
-                       rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+                       rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index);
                }
                return;
        }
@@ -1978,24 +1970,24 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
             (current_tpt > (100 * tbl->expected_tpt[low]))))
                scale_action = 0;
 
-       if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+       if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >=
             IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) {
                if (lq_sta->last_bt_traffic >
-                   BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+                   le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) {
                        /*
                         * don't set scale_action, don't want to scale up if
                         * the rate scale doesn't otherwise think that is a
                         * good idea.
                         */
                } else if (lq_sta->last_bt_traffic <=
-                          BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+                          le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) {
                        scale_action = -1;
                }
        }
        lq_sta->last_bt_traffic =
-               BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD);
+               le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
 
-       if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+       if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >=
             IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) {
                /* search for a new modulation */
                rs_stay_in_table(lq_sta, true);
@@ -2032,7 +2024,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
 lq_update:
        /* Replace uCode's rate table for the destination station. */
        if (update_lq)
-               rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+               rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index);
 
        rs_stay_in_table(lq_sta, false);
 
@@ -2071,7 +2063,7 @@ lq_update:
                        IWL_DEBUG_RATE(mvm,
                                       "Switch current  mcs: %X index: %d\n",
                                       tbl->current_rate, index);
-                       rs_fill_link_cmd(mvm, lq_sta, tbl->current_rate);
+                       rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate);
                        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
                } else {
                        done_search = 1;
@@ -2113,7 +2105,7 @@ lq_update:
        }
 
 out:
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index);
        lq_sta->last_txrate_idx = index;
 }
 
@@ -2140,7 +2132,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        int rate_idx;
        int i;
        u32 rate;
-       u8 use_green = rs_use_green(sta);
        u8 active_tbl = 0;
        u8 valid_tx_ant;
 
@@ -2172,10 +2163,10 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
                rs_toggle_antenna(valid_tx_ant, &rate, tbl);
 
-       rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx, use_green);
+       rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx);
        tbl->current_rate = rate;
        rs_set_expected_tpt_table(lq_sta, tbl);
-       rs_fill_link_cmd(NULL, lq_sta, rate);
+       rs_fill_link_cmd(NULL, NULL, lq_sta, rate);
        /* TODO restore station should remember the lq cmd */
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true);
 }
@@ -2190,7 +2181,6 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
        struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct iwl_lq_sta *lq_sta = mvm_sta;
-       int rate_idx;
 
        IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n");
 
@@ -2215,36 +2205,9 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
        if (rate_control_send_low(sta, mvm_sta, txrc))
                return;
 
-       rate_idx  = lq_sta->last_txrate_idx;
-
-       if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
-               rate_idx -= IWL_FIRST_OFDM_RATE;
-               /* 6M and 9M shared same MCS index */
-               rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
-               WARN_ON_ONCE(rs_extract_rate(lq_sta->last_rate_n_flags) >=
-                            IWL_RATE_MIMO3_6M_PLCP);
-               if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
-                   IWL_RATE_MIMO2_6M_PLCP)
-                       rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
-               info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
-               if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
-                       info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
-               if (lq_sta->last_rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
-                       info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
-               if (lq_sta->last_rate_n_flags & RATE_HT_MCS_GF_MSK)
-                       info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
-       } else {
-               /* Check for invalid rates */
-               if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) ||
-                   ((sband->band == IEEE80211_BAND_5GHZ) &&
-                    (rate_idx < IWL_FIRST_OFDM_RATE)))
-                       rate_idx = rate_lowest_index(sband, sta);
-               /* On valid 5 GHz rate, adjust index */
-               else if (sband->band == IEEE80211_BAND_5GHZ)
-                       rate_idx -= IWL_FIRST_OFDM_RATE;
-               info->control.rates[0].flags = 0;
-       }
-       info->control.rates[0].idx = rate_idx;
+       iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags,
+                                 info->band, &info->control.rates[0]);
+
        info->control.rates[0].count = 1;
 }
 
@@ -2261,6 +2224,24 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
        return &sta_priv->lq_sta;
 }
 
+static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
+                                      int nss)
+{
+       u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
+               (0x3 << (2 * (nss - 1)));
+       rx_mcs >>= (2 * (nss - 1));
+
+       if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7)
+               return IWL_RATE_MCS_7_INDEX;
+       else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8)
+               return IWL_RATE_MCS_8_INDEX;
+       else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9)
+               return IWL_RATE_MCS_9_INDEX;
+
+       WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED);
+       return -1;
+}
+
 /*
  * Called after adding a new station to initialize rate scaling
  */
@@ -2270,6 +2251,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        int i, j;
        struct ieee80211_hw *hw = mvm->hw;
        struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
        struct iwl_mvm_sta *sta_priv;
        struct iwl_lq_sta *lq_sta;
        struct ieee80211_supported_band *sband;
@@ -2298,7 +2280,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        lq_sta->max_rate_idx = -1;
        lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
-       lq_sta->is_green = rs_use_green(sta);
        lq_sta->band = sband->band;
        /*
         * active legacy rates as per supported rates bitmap
@@ -2308,25 +2289,54 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        for_each_set_bit(i, &supp, BITS_PER_LONG)
                lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
 
-       /*
-        * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
-        * supp_rates[] does not; shift to convert format, force 9 MBits off.
-        */
-       lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
-       lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
-       lq_sta->active_siso_rate &= ~((u16)0x2);
-       lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+       /* TODO: should probably account for rx_highest for both HT/VHT */
+       if (!vht_cap || !vht_cap->vht_supported) {
+               /* active_siso_rate mask includes 9 MBits (bit 5),
+                * and CCK (bits 0-3), supp_rates[] does not;
+                * shift to convert format, force 9 MBits off.
+                */
+               lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+               lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+               lq_sta->active_siso_rate &= ~((u16)0x2);
+               lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+               /* Same here */
+               lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+               lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+               lq_sta->active_mimo2_rate &= ~((u16)0x2);
+               lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+               lq_sta->is_vht = false;
+       } else {
+               int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1);
+               if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
+                       for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
+                               if (i == IWL_RATE_9M_INDEX)
+                                       continue;
+
+                               lq_sta->active_siso_rate |= BIT(i);
+                       }
+               }
+
+               highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2);
+               if (highest_mcs >= IWL_RATE_MCS_0_INDEX) {
+                       for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) {
+                               if (i == IWL_RATE_9M_INDEX)
+                                       continue;
 
-       /* Same here */
-       lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
-       lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
-       lq_sta->active_mimo2_rate &= ~((u16)0x2);
-       lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+                               lq_sta->active_mimo2_rate |= BIT(i);
+                       }
+               }
+
+               /* TODO: avoid MCS9 in 20Mhz which isn't valid for 11ac */
+               lq_sta->is_vht = true;
+       }
 
        IWL_DEBUG_RATE(mvm,
-                      "SISO-RATE=%X MIMO2-RATE=%X\n",
+                      "SISO-RATE=%X MIMO2-RATE=%X VHT=%d\n",
                       lq_sta->active_siso_rate,
-                      lq_sta->active_mimo2_rate);
+                      lq_sta->active_mimo2_rate,
+                      lq_sta->is_vht);
 
        /* These values will be overridden later */
        lq_sta->lq.single_stream_ant_msk =
@@ -2358,6 +2368,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 }
 
 static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct ieee80211_sta *sta,
                             struct iwl_lq_sta *lq_sta, u32 new_rate)
 {
        struct iwl_scale_tbl_info tbl_type;
@@ -2429,7 +2440,6 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
                rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type,
                                         &rate_idx);
 
-
                /* Indicate to uCode which entries might be MIMO.
                 * If initial rate was MIMO, this will finally end up
                 * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
@@ -2455,7 +2465,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
                }
 
                /* Don't allow HT rates after next pass.
-                * rs_get_lower_rate() will change type to LQ_A or LQ_G. */
+                * rs_get_lower_rate() will change type to LQ_LEGACY_A
+                * or LQ_LEGACY_G.
+                */
                use_ht_possible = 0;
 
                /* Override next rate if needed for debug purposes */
@@ -2474,12 +2486,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
        lq_cmd->agg_time_limit =
                cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
 
-       /*
-        * overwrite if needed, pass aggregation time limit
-        * to uCode in uSec - This is racy - but heh, at least it helps...
-        */
-       if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2)
-               lq_cmd->agg_time_limit = cpu_to_le16(1200);
+       if (sta)
+               lq_cmd->agg_time_limit =
+                       cpu_to_le16(iwl_mvm_bt_coex_agg_time_limit(mvm, sta));
 }
 
 static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
@@ -2586,16 +2595,18 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
            (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "",
            (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : "");
        desc += sprintf(buff+desc, "lq type %s\n",
-          (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
-       if (is_Ht(tbl->lq_type)) {
+                       (is_legacy(tbl->lq_type)) ? "legacy" :
+                       is_vht(tbl->lq_type) ? "VHT" : "HT");
+       if (is_ht(tbl->lq_type)) {
                desc += sprintf(buff+desc, " %s",
                   (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2");
                   desc += sprintf(buff+desc, " %s",
-                  (tbl->is_ht40) ? "40MHz" : "20MHz");
-                  desc += sprintf(buff+desc, " %s %s %s\n",
+                                  (is_ht20(tbl)) ? "20MHz" :
+                                  (is_ht40(tbl)) ? "40MHz" :
+                                  (is_ht80(tbl)) ? "80Mhz" : "BAD BW");
+                  desc += sprintf(buff+desc, " %s %s\n",
                                   (tbl->is_SGI) ? "SGI" : "",
-                  (lq_sta->is_green) ? "GF enabled" : "",
-                  (lq_sta->is_agg) ? "AGG on" : "");
+                                  (lq_sta->is_agg) ? "AGG on" : "");
        }
        desc += sprintf(buff+desc, "last tx rate=0x%X\n",
                        lq_sta->last_rate_n_flags);
@@ -2653,7 +2664,7 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
        int desc = 0;
        int i, j;
        ssize_t ret;
-
+       struct iwl_scale_tbl_info *tbl;
        struct iwl_lq_sta *lq_sta = file->private_data;
 
        buff = kmalloc(1024, GFP_KERNEL);
@@ -2661,21 +2672,23 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
                return -ENOMEM;
 
        for (i = 0; i < LQ_SIZE; i++) {
+               tbl = &(lq_sta->lq_info[i]);
                desc += sprintf(buff+desc,
-                               "%s type=%d SGI=%d HT40=%d DUP=0 GF=%d\n"
+                               "%s type=%d SGI=%d BW=%s DUP=0\n"
                                "rate=0x%X\n",
                                lq_sta->active_tbl == i ? "*" : "x",
-                               lq_sta->lq_info[i].lq_type,
-                               lq_sta->lq_info[i].is_SGI,
-                               lq_sta->lq_info[i].is_ht40,
-                               lq_sta->is_green,
-                               lq_sta->lq_info[i].current_rate);
+                               tbl->lq_type,
+                               tbl->is_SGI,
+                               is_ht20(tbl) ? "20Mhz" :
+                               is_ht40(tbl) ? "40Mhz" :
+                               is_ht80(tbl) ? "80Mhz" : "ERR",
+                               tbl->current_rate);
                for (j = 0; j < IWL_RATE_COUNT; j++) {
                        desc += sprintf(buff+desc,
                                "counter=%d success=%d %%=%d\n",
-                               lq_sta->lq_info[i].win[j].counter,
-                               lq_sta->lq_info[i].win[j].success_counter,
-                               lq_sta->lq_info[i].win[j].success_ratio);
+                               tbl->win[j].counter,
+                               tbl->win[j].success_counter,
+                               tbl->win[j].success_ratio);
                }
        }
        ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
index 335cf16..5d5344f 100644 (file)
 #include "iwl-trans.h"
 
 struct iwl_rs_rate_info {
-       u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
-       u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
-       u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp;          /* uCode API:  IWL_RATE_6M_PLCP, etc. */
+       u8 plcp_ht_siso;  /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
+       u8 plcp_ht_mimo2; /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp_vht_siso;
+       u8 plcp_vht_mimo2;
        u8 prev_rs;      /* previous rate used in rs algo */
        u8 next_rs;      /* next rate used in rs algo */
 };
@@ -83,35 +85,52 @@ enum {
 #define        IWL_RATE_11M_MASK  (1 << IWL_RATE_11M_INDEX)
 
 
-/* uCode API values for OFDM high-throughput (HT) bit rates */
+/* uCode API values for HT/VHT bit rates */
 enum {
-       IWL_RATE_SISO_6M_PLCP = 0,
-       IWL_RATE_SISO_12M_PLCP = 1,
-       IWL_RATE_SISO_18M_PLCP = 2,
-       IWL_RATE_SISO_24M_PLCP = 3,
-       IWL_RATE_SISO_36M_PLCP = 4,
-       IWL_RATE_SISO_48M_PLCP = 5,
-       IWL_RATE_SISO_54M_PLCP = 6,
-       IWL_RATE_SISO_60M_PLCP = 7,
-       IWL_RATE_MIMO2_6M_PLCP  = 0x8,
-       IWL_RATE_MIMO2_12M_PLCP = 0x9,
-       IWL_RATE_MIMO2_18M_PLCP = 0xa,
-       IWL_RATE_MIMO2_24M_PLCP = 0xb,
-       IWL_RATE_MIMO2_36M_PLCP = 0xc,
-       IWL_RATE_MIMO2_48M_PLCP = 0xd,
-       IWL_RATE_MIMO2_54M_PLCP = 0xe,
-       IWL_RATE_MIMO2_60M_PLCP = 0xf,
-       IWL_RATE_MIMO3_6M_PLCP  = 0x10,
-       IWL_RATE_MIMO3_12M_PLCP = 0x11,
-       IWL_RATE_MIMO3_18M_PLCP = 0x12,
-       IWL_RATE_MIMO3_24M_PLCP = 0x13,
-       IWL_RATE_MIMO3_36M_PLCP = 0x14,
-       IWL_RATE_MIMO3_48M_PLCP = 0x15,
-       IWL_RATE_MIMO3_54M_PLCP = 0x16,
-       IWL_RATE_MIMO3_60M_PLCP = 0x17,
-       IWL_RATE_SISO_INVM_PLCP,
-       IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
-       IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+       IWL_RATE_HT_SISO_MCS_0_PLCP = 0,
+       IWL_RATE_HT_SISO_MCS_1_PLCP = 1,
+       IWL_RATE_HT_SISO_MCS_2_PLCP = 2,
+       IWL_RATE_HT_SISO_MCS_3_PLCP = 3,
+       IWL_RATE_HT_SISO_MCS_4_PLCP = 4,
+       IWL_RATE_HT_SISO_MCS_5_PLCP = 5,
+       IWL_RATE_HT_SISO_MCS_6_PLCP = 6,
+       IWL_RATE_HT_SISO_MCS_7_PLCP = 7,
+       IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8,
+       IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9,
+       IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA,
+       IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB,
+       IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC,
+       IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD,
+       IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE,
+       IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF,
+       IWL_RATE_VHT_SISO_MCS_0_PLCP = 0,
+       IWL_RATE_VHT_SISO_MCS_1_PLCP = 1,
+       IWL_RATE_VHT_SISO_MCS_2_PLCP = 2,
+       IWL_RATE_VHT_SISO_MCS_3_PLCP = 3,
+       IWL_RATE_VHT_SISO_MCS_4_PLCP = 4,
+       IWL_RATE_VHT_SISO_MCS_5_PLCP = 5,
+       IWL_RATE_VHT_SISO_MCS_6_PLCP = 6,
+       IWL_RATE_VHT_SISO_MCS_7_PLCP = 7,
+       IWL_RATE_VHT_SISO_MCS_8_PLCP = 8,
+       IWL_RATE_VHT_SISO_MCS_9_PLCP = 9,
+       IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10,
+       IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11,
+       IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12,
+       IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13,
+       IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14,
+       IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15,
+       IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16,
+       IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17,
+       IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18,
+       IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19,
+       IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
+       IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP,
 };
 
 #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
@@ -139,25 +158,33 @@ enum {
 #define IWL_RATE_DECREASE_TH           1920    /*  15% */
 
 /* possible actions when in legacy mode */
-#define IWL_LEGACY_SWITCH_ANTENNA1      0
-#define IWL_LEGACY_SWITCH_ANTENNA2      1
-#define IWL_LEGACY_SWITCH_SISO          2
-#define IWL_LEGACY_SWITCH_MIMO2         3
+enum {
+       IWL_LEGACY_SWITCH_ANTENNA,
+       IWL_LEGACY_SWITCH_SISO,
+       IWL_LEGACY_SWITCH_MIMO2,
+       IWL_LEGACY_FIRST_ACTION = IWL_LEGACY_SWITCH_ANTENNA,
+       IWL_LEGACY_LAST_ACTION = IWL_LEGACY_SWITCH_MIMO2,
+};
 
 /* possible actions when in siso mode */
-#define IWL_SISO_SWITCH_ANTENNA1        0
-#define IWL_SISO_SWITCH_ANTENNA2        1
-#define IWL_SISO_SWITCH_MIMO2           2
-#define IWL_SISO_SWITCH_GI              3
+enum {
+       IWL_SISO_SWITCH_ANTENNA,
+       IWL_SISO_SWITCH_MIMO2,
+       IWL_SISO_SWITCH_GI,
+       IWL_SISO_FIRST_ACTION = IWL_SISO_SWITCH_ANTENNA,
+       IWL_SISO_LAST_ACTION = IWL_SISO_SWITCH_GI,
+};
 
 /* possible actions when in mimo mode */
-#define IWL_MIMO2_SWITCH_ANTENNA1       0
-#define IWL_MIMO2_SWITCH_ANTENNA2       1
-#define IWL_MIMO2_SWITCH_SISO_A         2
-#define IWL_MIMO2_SWITCH_SISO_B         3
-#define IWL_MIMO2_SWITCH_GI             4
+enum {
+       IWL_MIMO2_SWITCH_SISO_A,
+       IWL_MIMO2_SWITCH_SISO_B,
+       IWL_MIMO2_SWITCH_GI,
+       IWL_MIMO2_FIRST_ACTION = IWL_MIMO2_SWITCH_SISO_A,
+       IWL_MIMO2_LAST_ACTION = IWL_MIMO2_SWITCH_GI,
+};
 
-#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI
+#define IWL_MAX_SEARCH IWL_MIMO2_LAST_ACTION
 
 #define IWL_ACTION_LIMIT               3       /* # possible actions */
 
@@ -188,20 +215,31 @@ enum {
 
 enum iwl_table_type {
        LQ_NONE,
-       LQ_G,           /* legacy types */
-       LQ_A,
-       LQ_SISO,        /* high-throughput types */
-       LQ_MIMO2,
+       LQ_LEGACY_G,    /* legacy types */
+       LQ_LEGACY_A,
+       LQ_HT_SISO,     /* HT types */
+       LQ_HT_MIMO2,
+       LQ_VHT_SISO,    /* VHT types */
+       LQ_VHT_MIMO2,
        LQ_MAX,
 };
 
-#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
-#define is_siso(tbl) ((tbl) == LQ_SISO)
-#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
-#define is_mimo(tbl) is_mimo2(tbl)
-#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
-#define is_a_band(tbl) ((tbl) == LQ_A)
-#define is_g_and(tbl) ((tbl) == LQ_G)
+#define is_legacy(tbl) (((tbl) == LQ_LEGACY_G) || ((tbl) == LQ_LEGACY_A))
+#define is_ht_siso(tbl) ((tbl) == LQ_HT_SISO)
+#define is_ht_mimo2(tbl) ((tbl) == LQ_HT_MIMO2)
+#define is_vht_siso(tbl) ((tbl) == LQ_VHT_SISO)
+#define is_vht_mimo2(tbl) ((tbl) == LQ_VHT_MIMO2)
+#define is_siso(tbl) (is_ht_siso(tbl) || is_vht_siso(tbl))
+#define is_mimo2(tbl) (is_ht_mimo2(tbl) || is_vht_mimo2(tbl))
+#define is_mimo(tbl) (is_mimo2(tbl))
+#define is_ht(tbl) (is_ht_siso(tbl) || is_ht_mimo2(tbl))
+#define is_vht(tbl) (is_vht_siso(tbl) || is_vht_mimo2(tbl))
+#define is_a_band(tbl) ((tbl) == LQ_LEGACY_A)
+#define is_g_band(tbl) ((tbl) == LQ_LEGACY_G)
+
+#define is_ht20(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_20)
+#define is_ht40(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_40)
+#define is_ht80(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_80)
 
 #define IWL_MAX_MCS_DISPLAY_SIZE       12
 
@@ -232,7 +270,7 @@ struct iwl_scale_tbl_info {
        enum iwl_table_type lq_type;
        u8 ant_type;
        u8 is_SGI;      /* 1 = short guard interval */
-       u8 is_ht40;     /* 1 = 40 MHz channel width */
+       u32 bw;         /* channel bandwidth; RATE_MCS_CHAN_WIDTH_XX */
        u8 action;      /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
        u8 max_search;  /* maximun number of tables we can search */
        s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
@@ -262,7 +300,7 @@ struct iwl_lq_sta {
        u64 flush_timer;        /* time staying in mode before new search */
 
        u8 action_counter;      /* # mode-switch actions tried */
-       u8 is_green;
+       bool is_vht;
        enum ieee80211_band band;
 
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
@@ -314,9 +352,8 @@ static inline u8 num_of_ant(u8 mask)
 }
 
 /* Initialize station's rate scaling information after adding station */
-extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm,
-                                struct ieee80211_sta *sta,
-                                enum ieee80211_band band);
+void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                         enum ieee80211_band band);
 
 /**
  * iwl_rate_control_register - Register the rate control algorithm callbacks
@@ -328,7 +365,7 @@ extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm,
  * ieee80211_register_hw
  *
  */
-extern int iwl_mvm_rate_control_register(void);
+int iwl_mvm_rate_control_register(void);
 
 /**
  * iwl_rate_control_unregister - Unregister the rate control callbacks
@@ -336,7 +373,7 @@ extern int iwl_mvm_rate_control_register(void);
  * This should be called after calling ieee80211_unregister_hw, but before
  * the driver is unloaded.
  */
-extern void iwl_mvm_rate_control_unregister(void);
+void iwl_mvm_rate_control_unregister(void);
 
 struct iwl_mvm_sta;
 
index 2a8cb5a..3a1f398 100644 (file)
@@ -300,10 +300,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                return 0;
        }
 
+       /*
+        * Keep packets with CRC errors (and with overrun) for monitor mode
+        * (otherwise the firmware discards them) but mark them as bad.
+        */
        if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
            !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
                IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
-               return 0;
+               rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
        }
 
        /* This will be used in several places later */
@@ -422,6 +426,27 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
 
        mvmvif->bf_data.ave_beacon_signal = sig;
 
+       /* BT Coex */
+       if (mvmvif->bf_data.bt_coex_min_thold !=
+           mvmvif->bf_data.bt_coex_max_thold) {
+               last_event = mvmvif->bf_data.last_bt_coex_event;
+               if (sig > mvmvif->bf_data.bt_coex_max_thold &&
+                   (last_event <= mvmvif->bf_data.bt_coex_min_thold ||
+                    last_event == 0)) {
+                       mvmvif->bf_data.last_bt_coex_event = sig;
+                       IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n",
+                                    sig);
+                       iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH);
+               } else if (sig < mvmvif->bf_data.bt_coex_min_thold &&
+                          (last_event >= mvmvif->bf_data.bt_coex_max_thold ||
+                           last_event == 0)) {
+                       mvmvif->bf_data.last_bt_coex_event = sig;
+                       IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n",
+                                    sig);
+                       iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW);
+               }
+       }
+
        if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
                return;
 
index 3a06832..dff7592 100644 (file)
 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
 {
        u16 rx_chain;
-       u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
+       u8 rx_ant;
 
+       if (mvm->scan_rx_ant != ANT_NONE)
+               rx_ant = mvm->scan_rx_ant;
+       else
+               rx_ant = iwl_fw_valid_rx_ant(mvm->fw);
        rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
        rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
        rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
@@ -133,11 +137,12 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
  * request.
  */
 static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
-                                   struct cfg80211_scan_request *req)
+                                   struct cfg80211_scan_request *req,
+                                   int first)
 {
        int fw_idx, req_idx;
 
-       for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx > 0;
+       for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first;
             req_idx--, fw_idx++) {
                cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
                cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
@@ -153,9 +158,9 @@ static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
  * just to notify that this scan is active and not passive.
  * In order to notify the FW of the number of SSIDs we wish to scan (including
  * the zero-length one), we need to set the corresponding bits in chan->type,
- * one for each SSID, and set the active bit (first). The first SSID is already
- * included in the probe template, so we need to set only req->n_ssids - 1 bits
- * in addition to the first bit.
+ * one for each SSID, and set the active bit (first). If the first SSID is
+ * already included in the probe template, so we need to set only
+ * req->n_ssids - 1 bits in addition to the first bit.
  */
 static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids)
 {
@@ -170,7 +175,8 @@ static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
 }
 
 static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
-                                      struct cfg80211_scan_request *req)
+                                      struct cfg80211_scan_request *req,
+                                      bool basic_ssid)
 {
        u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band);
        u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band,
@@ -178,10 +184,14 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
        struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
                (cmd->data + le16_to_cpu(cmd->tx_cmd.len));
        int i;
+       int type = BIT(req->n_ssids) - 1;
+
+       if (!basic_ssid)
+               type |= BIT(req->n_ssids);
 
        for (i = 0; i < cmd->channel_count; i++) {
                chan->channel = cpu_to_le16(req->channels[i]->hw_value);
-               chan->type = cpu_to_le32(BIT(req->n_ssids) - 1);
+               chan->type = cpu_to_le32(type);
                if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN)
                        chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE);
                chan->active_dwell = cpu_to_le16(active_dwell);
@@ -268,6 +278,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        u32 status;
        int ssid_len = 0;
        u8 *ssid = NULL;
+       bool basic_ssid = !(mvm->fw->ucode_capa.flags &
+                          IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
 
        lockdep_assert_held(&mvm->mutex);
        BUG_ON(mvm->scan_cmd == NULL);
@@ -302,14 +314,16 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        if (req->n_ssids > 0) {
                cmd->passive2active = cpu_to_le16(1);
                cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE;
-               ssid = req->ssids[0].ssid;
-               ssid_len = req->ssids[0].ssid_len;
+               if (basic_ssid) {
+                       ssid = req->ssids[0].ssid;
+                       ssid_len = req->ssids[0].ssid_len;
+               }
        } else {
                cmd->passive2active = 0;
                cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
 
-       iwl_mvm_scan_fill_ssids(cmd, req);
+       iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
 
        cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
        cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
@@ -326,7 +340,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                            req->ie, req->ie_len,
                            mvm->fw->ucode_capa.max_probe_length));
 
-       iwl_mvm_scan_fill_channels(cmd, req);
+       iwl_mvm_scan_fill_channels(cmd, req, basic_ssid);
 
        cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
                le16_to_cpu(cmd->tx_cmd.len) +
@@ -377,6 +391,21 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        return 0;
 }
 
+int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
+                                 struct iwl_rx_cmd_buffer *rxb,
+                                 struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_sched_scan_results *notif = (void *)pkt->data;
+
+       if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
+               IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+               ieee80211_sched_scan_results(mvm->hw);
+       }
+
+       return 0;
+}
+
 static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
                                     struct iwl_rx_packet *pkt, void *data)
 {
@@ -447,3 +476,406 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 out_remove_notif:
        iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
 }
+
+int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
+                                          struct iwl_rx_cmd_buffer *rxb,
+                                          struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
+
+       IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
+                      scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+                      "completed" : "aborted");
+
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+       ieee80211_sched_scan_stopped(mvm->hw);
+
+       return 0;
+}
+
+static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct ieee80211_sched_scan_ies *ies,
+                                         enum ieee80211_band band,
+                                         struct iwl_tx_cmd *cmd,
+                                         u8 *data)
+{
+       u16 cmd_len;
+
+       cmd->tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
+       cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       cmd->sta_id = mvm->aux_sta.sta_id;
+
+       cmd->rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, band, false);
+
+       cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
+                                        vif->addr,
+                                        1, NULL, 0,
+                                        ies->ie[band], ies->len[band],
+                                        SCAN_OFFLOAD_PROBE_REQ_SIZE);
+       cmd->len = cpu_to_le16(cmd_len);
+}
+
+static void iwl_build_scan_cmd(struct iwl_mvm *mvm,
+                              struct ieee80211_vif *vif,
+                              struct cfg80211_sched_scan_request *req,
+                              struct iwl_scan_offload_cmd *scan)
+{
+       scan->channel_count =
+               mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
+               mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+       scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
+       scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
+       scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT;
+       scan->rx_chain = iwl_mvm_scan_rx_chain(mvm);
+       scan->max_out_time = cpu_to_le32(200 * 1024);
+       scan->suspend_time = iwl_mvm_scan_suspend_time(vif);
+       scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                         MAC_FILTER_IN_BEACON);
+       scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND);
+       scan->rep_count = cpu_to_le32(1);
+}
+
+static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
+{
+       int i;
+
+       for (i = 0; i < PROBE_OPTION_MAX; i++) {
+               if (!ssid_list[i].len)
+                       break;
+               if (ssid_list[i].len == ssid_len &&
+                   !memcmp(ssid_list->ssid, ssid, ssid_len))
+                       return i;
+       }
+       return -1;
+}
+
+static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
+                                       struct iwl_scan_offload_cmd *scan,
+                                       u32 *ssid_bitmap)
+{
+       int i, j;
+       int index;
+
+       /*
+        * copy SSIDs from match list.
+        * iwl_config_sched_scan_profiles() uses the order of these ssids to
+        * config match list.
+        */
+       for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {
+               scan->direct_scan[i].id = WLAN_EID_SSID;
+               scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
+               memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
+                      scan->direct_scan[i].len);
+       }
+
+       /* add SSIDs from scan SSID list */
+       *ssid_bitmap = 0;
+       for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) {
+               index = iwl_ssid_exist(req->ssids[j].ssid,
+                                      req->ssids[j].ssid_len,
+                                      scan->direct_scan);
+               if (index < 0) {
+                       if (!req->ssids[j].ssid_len)
+                               continue;
+                       scan->direct_scan[i].id = WLAN_EID_SSID;
+                       scan->direct_scan[i].len = req->ssids[j].ssid_len;
+                       memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid,
+                              scan->direct_scan[i].len);
+                       *ssid_bitmap |= BIT(i + 1);
+                       i++;
+               } else {
+                       *ssid_bitmap |= BIT(index + 1);
+               }
+       }
+}
+
+static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct iwl_scan_channel_cfg *channels,
+                                 enum ieee80211_band band,
+                                 int *head, int *tail,
+                                 u32 ssid_bitmap)
+{
+       struct ieee80211_supported_band *s_band;
+       int n_probes = req->n_ssids;
+       int n_channels = req->n_channels;
+       u8 active_dwell, passive_dwell;
+       int i, j, index = 0;
+       bool partial;
+
+       /*
+        * We have to configure all supported channels, even if we don't want to
+        * scan on them, but we have to send channels in the order that we want
+        * to scan. So add requested channels to head of the list and others to
+        * the end.
+       */
+       active_dwell = iwl_mvm_get_active_dwell(band, n_probes);
+       passive_dwell = iwl_mvm_get_passive_dwell(band);
+       s_band = &mvm->nvm_data->bands[band];
+
+       for (i = 0; i < s_band->n_channels && *head <= *tail; i++) {
+               partial = false;
+               for (j = 0; j < n_channels; j++)
+                       if (s_band->channels[i].center_freq ==
+                                               req->channels[j]->center_freq) {
+                               index = *head;
+                               (*head)++;
+                               /*
+                                * Channels that came with the request will be
+                                * in partial scan .
+                                */
+                               partial = true;
+                               break;
+                       }
+               if (!partial) {
+                       index = *tail;
+                       (*tail)--;
+               }
+               channels->channel_number[index] =
+                       cpu_to_le16(ieee80211_frequency_to_channel(
+                                       s_band->channels[i].center_freq));
+               channels->dwell_time[index][0] = active_dwell;
+               channels->dwell_time[index][1] = passive_dwell;
+
+               channels->iter_count[index] = cpu_to_le16(1);
+               channels->iter_interval[index] = 0;
+
+               if (!(s_band->channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN))
+                       channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE);
+
+               channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL);
+               if (partial)
+                       channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
+
+               if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40)
+                       channels->type[index] |=
+                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW);
+
+               /* scan for all SSIDs from req->ssids */
+               channels->type[index] |= cpu_to_le32(ssid_bitmap);
+       }
+}
+
+int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct cfg80211_sched_scan_request *req,
+                             struct ieee80211_sched_scan_ies *ies)
+{
+       int supported_bands = 0;
+       int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
+       int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+       int head = 0;
+       int tail = band_2ghz + band_5ghz;
+       u32 ssid_bitmap;
+       int cmd_len;
+       int ret;
+
+       struct iwl_scan_offload_cfg *scan_cfg;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_CONFIG_CMD,
+               .flags = CMD_SYNC,
+       };
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (band_2ghz)
+               supported_bands++;
+       if (band_5ghz)
+               supported_bands++;
+
+       cmd_len = sizeof(struct iwl_scan_offload_cfg) +
+                               supported_bands * SCAN_OFFLOAD_PROBE_REQ_SIZE;
+
+       scan_cfg = kzalloc(cmd_len, GFP_KERNEL);
+       if (!scan_cfg)
+               return -ENOMEM;
+
+       iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd);
+       scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
+
+       iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap);
+       /* build tx frames for supported bands */
+       if (band_2ghz) {
+               iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
+                                             IEEE80211_BAND_2GHZ,
+                                             &scan_cfg->scan_cmd.tx_cmd[0],
+                                             scan_cfg->data);
+               iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
+                                     IEEE80211_BAND_2GHZ, &head, &tail,
+                                     ssid_bitmap);
+       }
+       if (band_5ghz) {
+               iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
+                                             IEEE80211_BAND_5GHZ,
+                                             &scan_cfg->scan_cmd.tx_cmd[1],
+                                             scan_cfg->data +
+                                               SCAN_OFFLOAD_PROBE_REQ_SIZE);
+               iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
+                                     IEEE80211_BAND_5GHZ, &head, &tail,
+                                     ssid_bitmap);
+       }
+
+       cmd.data[0] = scan_cfg;
+       cmd.len[0] = cmd_len;
+       cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+
+       IWL_DEBUG_SCAN(mvm, "Sending scheduled scan config\n");
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(scan_cfg);
+       return ret;
+}
+
+int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
+                                      struct cfg80211_sched_scan_request *req)
+{
+       struct iwl_scan_offload_profile *profile;
+       struct iwl_scan_offload_profile_cfg *profile_cfg;
+       struct iwl_scan_offload_blacklist *blacklist;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
+               .flags = CMD_SYNC,
+               .len[1] = sizeof(*profile_cfg),
+               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+               .dataflags[1] = IWL_HCMD_DFL_NOCOPY,
+       };
+       int blacklist_len;
+       int i;
+       int ret;
+
+       if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES))
+                       return -EIO;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL)
+               blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN;
+       else
+               blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN;
+
+       blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL);
+       if (!blacklist)
+               return -ENOMEM;
+
+       profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL);
+       if (!profile_cfg) {
+               ret = -ENOMEM;
+               goto free_blacklist;
+       }
+
+       cmd.data[0] = blacklist;
+       cmd.len[0] = sizeof(*blacklist) * blacklist_len;
+       cmd.data[1] = profile_cfg;
+
+       /* No blacklist configuration */
+
+       profile_cfg->num_profiles = req->n_match_sets;
+       profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN;
+       profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN;
+       profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN;
+
+       for (i = 0; i < req->n_match_sets; i++) {
+               profile = &profile_cfg->profiles[i];
+               profile->ssid_index = i;
+               /* Support any cipher and auth algorithm */
+               profile->unicast_cipher = 0xff;
+               profile->auth_alg = 0xff;
+               profile->network_type = IWL_NETWORK_TYPE_ANY;
+               profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY;
+               profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN;
+       }
+
+       IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n");
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(profile_cfg);
+free_blacklist:
+       kfree(blacklist);
+
+       return ret;
+}
+
+int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
+                            struct cfg80211_sched_scan_request *req)
+{
+       struct iwl_scan_offload_req scan_req = {
+               .watchdog = IWL_SCHED_SCAN_WATCHDOG,
+
+               .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS,
+               .schedule_line[0].delay = req->interval / 1000,
+               .schedule_line[0].full_scan_mul = 1,
+
+               .schedule_line[1].iterations = 0xff,
+               .schedule_line[1].delay = req->interval / 1000,
+               .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
+       };
+
+       if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending scheduled scan with filtering, filter len %d\n",
+                              req->n_match_sets);
+               scan_req.flags |=
+                               cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID);
+       } else {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending Scheduled scan without filtering\n");
+       }
+
+       return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC,
+                                   sizeof(scan_req), &scan_req);
+}
+
+static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
+{
+       int ret;
+       struct iwl_host_cmd cmd = {
+               .id = SCAN_OFFLOAD_ABORT_CMD,
+               .flags = CMD_SYNC,
+       };
+       u32 status;
+
+       /* Exit instantly with error when device is not ready
+        * to receive scan abort command or it does not perform
+        * scheduled scan currently */
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED)
+               return -EIO;
+
+       ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
+       if (ret)
+               return ret;
+
+       if (status != CAN_ABORT_STATUS) {
+               /*
+                * The scan abort will return 1 for success or
+                * 2 for "failure".  A failure condition can be
+                * due to simply not being in an active scan which
+                * can occur if we send the scan abort before the
+                * microcode has notified us that a scan is completed.
+                */
+               IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status);
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED) {
+               IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n");
+               return;
+       }
+
+       ret = iwl_mvm_send_sched_scan_abort(mvm);
+       if (ret)
+               IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret);
+       else
+               IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+}
index 44add29..3299523 100644 (file)
 #include "sta.h"
 #include "rs.h"
 
+static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6,
+                                        struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
+{
+       memset(cmd_v5, 0, sizeof(*cmd_v5));
+
+       cmd_v5->add_modify = cmd_v6->add_modify;
+       cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
+       cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
+       memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN);
+       cmd_v5->sta_id = cmd_v6->sta_id;
+       cmd_v5->modify_mask = cmd_v6->modify_mask;
+       cmd_v5->station_flags = cmd_v6->station_flags;
+       cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
+       cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
+       cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
+       cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
+       cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
+       cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
+       cmd_v5->assoc_id = cmd_v6->assoc_id;
+       cmd_v5->beamform_flags = cmd_v6->beamform_flags;
+       cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
+}
+
+static void
+iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd,
+                                     struct iwl_mvm_add_sta_cmd_v5 *sta_cmd,
+                                     u32 mac_id_n_color)
+{
+       memset(sta_cmd, 0, sizeof(*sta_cmd));
+
+       sta_cmd->sta_id = key_cmd->sta_id;
+       sta_cmd->add_modify = STA_MODE_MODIFY;
+       sta_cmd->modify_mask = STA_MODIFY_KEY;
+       sta_cmd->mac_id_n_color = cpu_to_le32(mac_id_n_color);
+
+       sta_cmd->key.key_offset = key_cmd->key_offset;
+       sta_cmd->key.key_flags = key_cmd->key_flags;
+       memcpy(sta_cmd->key.key, key_cmd->key, sizeof(sta_cmd->key.key));
+       sta_cmd->key.tkip_rx_tsc_byte2 = key_cmd->tkip_rx_tsc_byte2;
+       memcpy(sta_cmd->key.tkip_rx_ttak, key_cmd->tkip_rx_ttak,
+              sizeof(sta_cmd->key.tkip_rx_ttak));
+}
+
+static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
+                                          struct iwl_mvm_add_sta_cmd_v6 *cmd,
+                                          int *status)
+{
+       struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
+                                                  cmd, status);
+
+       iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+
+       return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
+                                          &cmd_v5, status);
+}
+
+static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
+                                   struct iwl_mvm_add_sta_cmd_v6 *cmd)
+{
+       struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
+                                           sizeof(*cmd), cmd);
+
+       iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
+
+       return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
+                                   &cmd_v5);
+}
+
+static int
+iwl_mvm_send_add_sta_key_cmd_status(struct iwl_mvm *mvm,
+                                   struct iwl_mvm_add_sta_key_cmd *cmd,
+                                   u32 mac_id_n_color,
+                                   int *status)
+{
+       struct iwl_mvm_add_sta_cmd_v5 sta_cmd;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY,
+                                                  sizeof(*cmd), cmd, status);
+
+       iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color);
+
+       return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(sta_cmd),
+                                          &sta_cmd, status);
+}
+
+static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm,
+                                       u32 flags,
+                                       struct iwl_mvm_add_sta_key_cmd *cmd,
+                                       u32 mac_id_n_color)
+{
+       struct iwl_mvm_add_sta_cmd_v5 sta_cmd;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
+               return iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, flags,
+                                           sizeof(*cmd), cmd);
+
+       iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color);
+
+       return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(sta_cmd),
+                                   &sta_cmd);
+}
+
 static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
 {
        int sta_id;
@@ -87,7 +196,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                           bool update)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd add_sta_cmd;
+       struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd;
        int ret;
        u32 status;
        u32 agg_size = 0, mpdu_dens = 0;
@@ -175,8 +284,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
-                                         &add_sta_cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &add_sta_cmd, &status);
        if (ret)
                return ret;
 
@@ -229,8 +337,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
                        mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
 
-       /* for HW restart - need to reset the seq_number etc... */
-       memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
+       /* for HW restart - reset everything but the sequence number */
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               u16 seq = mvm_sta->tid_data[i].seq_number;
+               memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i]));
+               mvm_sta->tid_data[i].seq_number = seq;
+       }
 
        ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
        if (ret)
@@ -256,7 +368,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm,
 int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain)
 {
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
        int ret;
        u32 status;
 
@@ -269,8 +381,7 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
        cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -469,13 +580,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
                                      const u8 *addr,
                                      u16 mac_id, u16 color)
 {
-       struct iwl_mvm_add_sta_cmd cmd;
+       struct iwl_mvm_add_sta_cmd_v6 cmd;
        int ret;
        u32 status;
 
        lockdep_assert_held(&mvm->mutex);
 
-       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd));
+       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6));
        cmd.sta_id = sta->sta_id;
        cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
                                                             color));
@@ -485,8 +596,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
        if (addr)
                memcpy(cmd.addr, addr, ETH_ALEN);
 
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -534,10 +644,14 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                           struct iwl_mvm_int_sta *bsta)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+       static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+       static const u8 *baddr = _baddr;
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (vif->type == NL80211_IFTYPE_ADHOC)
+               baddr = vif->bss_conf.bssid;
+
        if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))
                return -ENOSPC;
 
@@ -614,7 +728,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                       int tid, u16 ssn, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
        int ret;
        u32 status;
 
@@ -638,8 +752,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                                  STA_MODIFY_REMOVE_BA_TID;
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -674,7 +787,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                              int tid, u8 queue, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {};
        int ret;
        u32 status;
 
@@ -696,8 +809,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
        if (ret)
                return ret;
 
@@ -743,13 +855,13 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        lockdep_assert_held(&mvm->mutex);
 
-       for (txq_id = IWL_MVM_FIRST_AGG_QUEUE;
-            txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++)
+       for (txq_id = mvm->first_agg_queue;
+            txq_id <= mvm->last_agg_queue; txq_id++)
                if (mvm->queue_to_mac80211[txq_id] ==
                    IWL_INVALID_MAC80211_QUEUE)
                        break;
 
-       if (txq_id > IWL_MVM_LAST_AGG_QUEUE) {
+       if (txq_id > mvm->last_agg_queue) {
                IWL_ERR(mvm, "Failed to allocate agg queue\n");
                return -EIO;
        }
@@ -987,10 +1099,11 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                                u32 cmd_flags)
 {
        __le16 key_flags;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_key_cmd cmd = {};
        int ret, status;
        u16 keyidx;
        int i;
+       u32 mac_id_n_color = mvm_sta->mac_id_n_color;
 
        keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
                 STA_KEY_FLG_KEYID_MSK;
@@ -1000,14 +1113,14 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
        switch (keyconf->cipher) {
        case WLAN_CIPHER_SUITE_TKIP:
                key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP);
-               cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
+               cmd.tkip_rx_tsc_byte2 = tkip_iv32;
                for (i = 0; i < 5; i++)
-                       cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
-               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+                       cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
-               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        default:
                WARN_ON(1);
@@ -1017,20 +1130,18 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
                key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
 
-       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
-       cmd.key.key_offset = keyconf->hw_key_idx;
-       cmd.key.key_flags = key_flags;
-       cmd.add_modify = STA_MODE_MODIFY;
-       cmd.modify_mask = STA_MODIFY_KEY;
+       cmd.key_offset = keyconf->hw_key_idx;
+       cmd.key_flags = key_flags;
        cmd.sta_id = sta_id;
 
        status = ADD_STA_SUCCESS;
        if (cmd_flags == CMD_SYNC)
-               ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                                 &cmd, &status);
+               ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
+                                                         mac_id_n_color,
+                                                         &status);
        else
-               ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC,
-                                          sizeof(cmd), &cmd);
+               ret = iwl_mvm_send_add_sta_key_cmd(mvm, CMD_ASYNC, &cmd,
+                                                  mac_id_n_color);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1197,7 +1308,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
                           struct ieee80211_key_conf *keyconf)
 {
        struct iwl_mvm_sta *mvm_sta;
-       struct iwl_mvm_add_sta_cmd cmd = {};
+       struct iwl_mvm_add_sta_key_cmd cmd = {};
        __le16 key_flags;
        int ret, status;
        u8 sta_id;
@@ -1252,17 +1363,14 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
                key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
 
-       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
-       cmd.key.key_flags = key_flags;
-       cmd.key.key_offset = keyconf->hw_key_idx;
+       cmd.key_flags = key_flags;
+       cmd.key_offset = keyconf->hw_key_idx;
        cmd.sta_id = sta_id;
 
-       cmd.modify_mask = STA_MODIFY_KEY;
-       cmd.add_modify = STA_MODE_MODIFY;
-
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
-                                         &cmd, &status);
+       ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
+                                                 mvm_sta->mac_id_n_color,
+                                                 &status);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1309,7 +1417,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .station_flags_msk = cpu_to_le32(STA_FLG_PS),
@@ -1317,7 +1425,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        };
        int ret;
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
@@ -1331,7 +1439,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
                        STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
        struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd cmd = {
+       struct iwl_mvm_add_sta_cmd_v6 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
@@ -1346,7 +1454,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
        int ret;
 
        /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
-       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
index 94b265e..4dfc359 100644 (file)
@@ -293,10 +293,6 @@ struct iwl_mvm_sta {
        struct iwl_lq_sta lq_sta;
        struct ieee80211_vif *vif;
 
-#ifdef CONFIG_PM_SLEEP
-       u16 last_seq_ctl;
-#endif
-
        /* Temporary, until the new TLC will control the Tx protection */
        s8 tx_protection;
        bool tt_tx_protection;
diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h
new file mode 100644 (file)
index 0000000..eb74391
--- /dev/null
@@ -0,0 +1,95 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_MVM_TESTMODE_H__
+#define __IWL_MVM_TESTMODE_H__
+
+/**
+ * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA
+ * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute)
+ * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32)
+ * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32)
+ * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32)
+ */
+enum iwl_mvm_testmode_attrs {
+       IWL_MVM_TM_ATTR_UNSPEC,
+       IWL_MVM_TM_ATTR_CMD,
+       IWL_MVM_TM_ATTR_NOA_DURATION,
+       IWL_MVM_TM_ATTR_BEACON_FILTER_STATE,
+
+       /* keep last */
+       NUM_IWL_MVM_TM_ATTRS,
+       IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1,
+};
+
+/**
+ * enum iwl_mvm_testmode_commands - MVM testmode commands
+ * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing
+ * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on
+ */
+enum iwl_mvm_testmode_commands {
+       IWL_MVM_TM_CMD_SET_NOA,
+       IWL_MVM_TM_CMD_SET_BEACON_FILTER,
+};
+
+#endif /* __IWL_MVM_TESTMODE_H__ */
index 76a3c17..33cf56f 100644 (file)
@@ -387,7 +387,8 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
 
 void iwl_mvm_protect_session(struct iwl_mvm *mvm,
                             struct ieee80211_vif *vif,
-                            u32 duration, u32 min_duration)
+                            u32 duration, u32 min_duration,
+                            u32 max_delay)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
@@ -426,7 +427,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
                cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
 
        time_cmd.max_frags = TE_V2_FRAG_NONE;
-       time_cmd.max_delay = cpu_to_le32(500);
+       time_cmd.max_delay = cpu_to_le32(max_delay);
        /* TODO: why do we need to interval = bi if it is not periodic? */
        time_cmd.interval = cpu_to_le32(1);
        time_cmd.duration = cpu_to_le32(duration);
index f86c510..d9c8d6c 100644 (file)
  * @duration: the duration of the session in TU.
  * @min_duration: will start a new session if the current session will end
  *     in less than min_duration.
+ * @max_delay: maximum delay before starting the time event (in TU)
  *
  * This function can be used to start a session protection which means that the
  * fw will stay on the channel for %duration_ms milliseconds. This function
  */
 void iwl_mvm_protect_session(struct iwl_mvm *mvm,
                             struct ieee80211_vif *vif,
-                            u32 duration, u32 min_duration);
+                            u32 duration, u32 min_duration,
+                            u32 max_delay);
 
 /**
  * iwl_mvm_stop_session_protection - cancel the session protection.
index e05440d..43d97c3 100644 (file)
@@ -417,7 +417,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
 
        spin_unlock(&mvmsta->lock);
 
-       if (txq_id < IWL_MVM_FIRST_AGG_QUEUE)
+       if (txq_id < mvm->first_agg_queue)
                atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);
 
        return 0;
@@ -511,16 +511,10 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status)
 }
 #endif /* CONFIG_IWLWIFI_DEBUG */
 
-/**
- * translate ucode response to mac80211 tx status control values
- */
-static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
-                                        struct ieee80211_tx_info *info)
+void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
+                              enum ieee80211_band band,
+                              struct ieee80211_tx_rate *r)
 {
-       struct ieee80211_tx_rate *r = &info->status.rates[0];
-
-       info->status.antenna =
-               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
        if (rate_n_flags & RATE_HT_MCS_GF_MSK)
                r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
        switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
@@ -549,10 +543,23 @@ static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
                r->flags |= IEEE80211_TX_RC_VHT_MCS;
        } else {
                r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
-                                                            info->band);
+                                                            band);
        }
 }
 
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags,
+                                       struct ieee80211_tx_info *info)
+{
+       struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+       info->status.antenna =
+               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+       iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r);
+}
+
 static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                                     struct iwl_rx_packet *pkt)
 {
@@ -602,11 +609,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                }
 
                info->status.rates[0].count = tx_resp->failure_frame + 1;
-               iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate),
-                                            info);
+               iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate),
+                                           info);
 
                /* Single frame failure in an AMPDU queue => send BAR */
-               if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE &&
+               if (txq_id >= mvm->first_agg_queue &&
                    !(info->flags & IEEE80211_TX_STAT_ACK))
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
@@ -619,7 +626,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                ieee80211_tx_status_ni(mvm->hw, skb);
        }
 
-       if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) {
+       if (txq_id >= mvm->first_agg_queue) {
                /* If this is an aggregation queue, we use the ssn since:
                 * ssn = wifi seq_num % 256.
                 * The seq_ctl is the sequence control of the packet to which
@@ -668,10 +675,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        iwl_mvm_check_ratid_empty(mvm, sta, tid);
                        spin_unlock_bh(&mvmsta->lock);
                }
-
-#ifdef CONFIG_PM_SLEEP
-               mvmsta->last_seq_ctl = seq_ctl;
-#endif
        } else {
                sta = NULL;
                mvmsta = NULL;
@@ -681,7 +684,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
         * If the txq is not an AMPDU queue, there is no chance we freed
         * several skbs. Check that out...
         */
-       if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) &&
+       if (txq_id < mvm->first_agg_queue && !WARN_ON(skb_freed > 1) &&
            atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) {
                if (mvmsta) {
                        /*
@@ -777,7 +780,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
        u16 sequence = le16_to_cpu(pkt->hdr.sequence);
        struct ieee80211_sta *sta;
 
-       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE))
+       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue))
                return;
 
        if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
@@ -904,8 +907,8 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                        info->flags |= IEEE80211_TX_STAT_AMPDU;
                        info->status.ampdu_ack_len = ba_notif->txed_2_done;
                        info->status.ampdu_len = ba_notif->txed;
-                       iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags,
-                                                    info);
+                       iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
+                                                   info);
                }
        }
 
index a9c3574..ed69e9b 100644 (file)
@@ -466,7 +466,7 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
        ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
        len = img->sec[IWL_UCODE_SECTION_DATA].len;
 
-       buf = kzalloc(len, GFP_KERNEL);
+       buf = kzalloc(len, GFP_ATOMIC);
        if (!buf)
                return;
 
index 26108a1..941c0c8 100644 (file)
@@ -268,7 +268,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 #endif /* CONFIG_IWLDVM */
 
 #if IS_ENABLED(CONFIG_IWLMVM)
-/* 7000 Series */
+/* 7260 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
@@ -350,6 +350,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)},
+
+/* 7265 Series */
+       {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
 #endif /* CONFIG_IWLMVM */
 
        {0}
@@ -391,7 +394,6 @@ out_free_drv:
        iwl_drv_stop(trans_pcie->drv);
 out_free_trans:
        iwl_trans_pcie_free(iwl_trans);
-       pci_set_drvdata(pdev, NULL);
        return ret;
 }
 
@@ -402,8 +404,6 @@ static void iwl_pci_remove(struct pci_dev *pdev)
 
        iwl_drv_stop(trans_pcie->drv);
        iwl_trans_pcie_free(trans);
-
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM_SLEEP
index c3f904d..5d9337b 100644 (file)
@@ -220,6 +220,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
        iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
                          APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
+       /* Clear the interrupt in APMG if the NIC is in RFKILL */
+       iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
+
        set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
 
 out:
@@ -443,22 +446,138 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
        return ret;
 }
 
+static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu)
+{
+       int shift_param;
+       u32 address;
+       int ret = 0;
+
+       if (cpu == 1) {
+               shift_param = 0;
+               address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR;
+       } else {
+               shift_param = 16;
+               address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR;
+       }
+
+       /* set CPU to started */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_LOADING_STARTED << shift_param,
+                               1);
+
+       /* set last complete descriptor number */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED
+                               << shift_param,
+                               1);
+
+       /* set last loaded block */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK
+                               << shift_param,
+                               1);
+
+       /* image loading complete */
+       iwl_trans_set_bits_mask(trans,
+                               CSR_UCODE_LOAD_STATUS_ADDR,
+                               CSR_CPU_STATUS_LOADING_COMPLETED
+                               << shift_param,
+                               1);
+
+       /* set FH_TCSR_0_REG  */
+       iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1);
+
+       /* verify image verification started  */
+       ret = iwl_poll_bit(trans, address,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
+                          CSR_SECURE_TIME_OUT);
+       if (ret < 0) {
+               IWL_ERR(trans, "secure boot process didn't start\n");
+               return ret;
+       }
+
+       /* wait for image verification to complete  */
+       ret = iwl_poll_bit(trans, address,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
+                          CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
+                          CSR_SECURE_TIME_OUT);
+
+       if (ret < 0) {
+               IWL_ERR(trans, "Time out on secure boot process\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                                const struct fw_img *image)
 {
        int i, ret = 0;
 
-       for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
+       IWL_DEBUG_FW(trans,
+                    "working with %s image\n",
+                    image->is_secure ? "Secured" : "Non Secured");
+       IWL_DEBUG_FW(trans,
+                    "working with %s CPU\n",
+                    image->is_dual_cpus ? "Dual" : "Single");
+
+       /* configure the ucode to be ready to get the secured image */
+       if (image->is_secure) {
+               /* set secure boot inspector addresses */
+               iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0);
+               iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0);
+
+               /* release CPU1 reset if secure inspector image burned in OTP */
+               iwl_write32(trans, CSR_RESET, 0);
+       }
+
+       /* load to FW the binary sections of CPU1 */
+       IWL_DEBUG_INFO(trans, "Loading CPU1\n");
+       for (i = 0;
+            i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
+            i++) {
                if (!image->sec[i].data)
                        break;
-
                ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
                if (ret)
                        return ret;
        }
 
-       /* Remove all resets to allow NIC to operate */
-       iwl_write32(trans, CSR_RESET, 0);
+       /* configure the ucode to start secure process on CPU1 */
+       if (image->is_secure) {
+               /* config CPU1 to start secure protocol */
+               ret = iwl_pcie_secure_set(trans, 1);
+               if (ret)
+                       return ret;
+       } else {
+               /* Remove all resets to allow NIC to operate */
+               iwl_write32(trans, CSR_RESET, 0);
+       }
+
+       if (image->is_dual_cpus) {
+               /* load to FW the binary sections of CPU2 */
+               IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n");
+               for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
+                       i < IWL_UCODE_SECTION_MAX; i++) {
+                       if (!image->sec[i].data)
+                               break;
+                       ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
+                       if (ret)
+                               return ret;
+               }
+
+               if (image->is_secure) {
+                       /* set CPU2 for secure protocol */
+                       ret = iwl_pcie_secure_set(trans, 2);
+                       if (ret)
+                               return ret;
+               }
+       }
 
        return 0;
 }
index 80f1956..059c5ac 100644 (file)
@@ -1499,12 +1499,11 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
 
-       if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE,
-                                    &trans_pcie->status))) {
-               IWL_ERR(trans, "Command %s: a command is already active!\n",
-                       get_cmd_string(trans_pcie, cmd->id));
+       if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE,
+                                 &trans_pcie->status),
+                "Command %s: a command is already active!\n",
+                get_cmd_string(trans_pcie, cmd->id)))
                return -EIO;
-       }
 
        IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
@@ -1562,6 +1561,9 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                                       "Clearing HCMD_ACTIVE for command %s\n",
                                       get_cmd_string(trans_pcie, cmd->id));
                        ret = -ETIMEDOUT;
+
+                       iwl_op_mode_nic_error(trans->op_mode);
+
                        goto cancel;
                }
        }
index 668dd27..cc6a0a5 100644 (file)
@@ -913,7 +913,10 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
        char *p2;
        struct debug_data *d = f->private_data;
 
-       pdata = kmalloc(cnt, GFP_KERNEL);
+       if (cnt == 0)
+               return 0;
+
+       pdata = kmalloc(cnt + 1, GFP_KERNEL);
        if (pdata == NULL)
                return 0;
 
@@ -922,6 +925,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
                kfree(pdata);
                return 0;
        }
+       pdata[cnt] = '\0';
 
        p0 = pdata;
        for (i = 0; i < num_of_items; i++) {
index c0f9e7e..51b92b5 100644 (file)
@@ -53,6 +53,11 @@ static void main_firmware_cb(const struct firmware *firmware, void *context)
 
        /* Firmware found! */
        lbs_fw_loaded(priv, 0, priv->helper_fw, firmware);
+       if (priv->helper_fw) {
+               release_firmware (priv->helper_fw);
+               priv->helper_fw = NULL;
+       }
+       release_firmware (firmware);
 }
 
 static void helper_firmware_cb(const struct firmware *firmware, void *context)
index c94dd68..f499efc 100644 (file)
@@ -754,14 +754,14 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret,
        if (ret == 0 && (card->model != MODEL_8305))
                ret = if_cs_prog_real(card, mainfw);
        if (ret)
-               goto out;
+               return;
 
        /* Now actually get the IRQ */
        ret = request_irq(card->p_dev->irq, if_cs_interrupt,
                IRQF_SHARED, DRV_NAME, card);
        if (ret) {
                pr_err("error in request_irq\n");
-               goto out;
+               return;
        }
 
        /*
@@ -777,10 +777,6 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret,
                pr_err("could not activate card\n");
                free_irq(card->p_dev->irq, card);
        }
-
-out:
-       release_firmware(helper);
-       release_firmware(mainfw);
 }
 
 
@@ -906,6 +902,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
        if (card->model == MODEL_UNKNOWN) {
                pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n",
                       p_dev->manf_id, p_dev->card_id);
+               ret = -ENODEV;
                goto out2;
        }
 
index 4557833..991238a 100644 (file)
@@ -708,20 +708,16 @@ static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret,
 
        ret = if_sdio_prog_helper(card, helper);
        if (ret)
-               goto out;
+               return;
 
        lbs_deb_sdio("Helper firmware loaded\n");
 
        ret = if_sdio_prog_real(card, mainfw);
        if (ret)
-               goto out;
+               return;
 
        lbs_deb_sdio("Firmware loaded\n");
        if_sdio_finish_power_on(card);
-
-out:
-       release_firmware(helper);
-       release_firmware(mainfw);
 }
 
 static int if_sdio_prog_firmware(struct if_sdio_card *card)
index 4bb6574..8366915 100644 (file)
@@ -1094,11 +1094,7 @@ static int if_spi_init_card(struct if_spi_card *card)
                goto out;
 
 out:
-       release_firmware(helper);
-       release_firmware(mainfw);
-
        lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
-
        return err;
 }
 
@@ -1128,7 +1124,7 @@ static int if_spi_probe(struct spi_device *spi)
 {
        struct if_spi_card *card;
        struct lbs_private *priv = NULL;
-       struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
+       struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev);
        int err = 0;
 
        lbs_deb_enter(LBS_DEB_SPI);
index 2798077..dff08a2 100644 (file)
@@ -844,7 +844,7 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
        cardp->fw = fw;
        if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
                ret = -EINVAL;
-               goto release_fw;
+               goto done;
        }
 
        /* Cancel any pending usb business */
@@ -861,7 +861,7 @@ restart:
        if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
                lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
                ret = -EIO;
-               goto release_fw;
+               goto done;
        }
 
        cardp->bootcmdresp = 0;
@@ -883,14 +883,14 @@ restart:
                usb_kill_urb(cardp->tx_urb);
                if (if_usb_submit_rx_urb(cardp) < 0)
                        ret = -EIO;
-               goto release_fw;
+               goto done;
        } else if (cardp->bootcmdresp <= 0) {
                if (--reset_count >= 0) {
                        if_usb_reset_device(cardp);
                        goto restart;
                }
                ret = -EIO;
-               goto release_fw;
+               goto done;
        }
 
        i = 0;
@@ -921,14 +921,14 @@ restart:
 
                pr_info("FW download failure, time = %d ms\n", i * 100);
                ret = -EIO;
-               goto release_fw;
+               goto done;
        }
 
        cardp->priv->fw_ready = 1;
        if_usb_submit_rx_urb(cardp);
 
        if (lbs_start_card(priv))
-               goto release_fw;
+               goto done;
 
        if_usb_setup_firmware(priv);
 
@@ -939,11 +939,8 @@ restart:
        if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL))
                priv->ehs_remove_supported = false;
 
- release_fw:
-       release_firmware(cardp->fw);
-       cardp->fw = NULL;
-
  done:
+       cardp->fw = NULL;
        lbs_deb_leave(LBS_DEB_USB);
 }
 
index 2cd3f54..de0df86 100644 (file)
@@ -167,6 +167,7 @@ struct hwsim_vif_priv {
        u32 magic;
        u8 bssid[ETH_ALEN];
        bool assoc;
+       bool bcn_en;
        u16 aid;
 };
 
@@ -1170,6 +1171,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw,
        *total_flags = data->rx_filter;
 }
 
+static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       unsigned int *count = data;
+       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+
+       if (vp->bcn_en)
+               (*count)++;
+}
+
 static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
                                            struct ieee80211_vif *vif,
                                            struct ieee80211_bss_conf *info,
@@ -1180,7 +1191,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        hwsim_check_magic(vif);
 
-       wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed);
+       wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n",
+                   __func__, changed, vif->addr);
 
        if (changed & BSS_CHANGED_BSSID) {
                wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n",
@@ -1202,6 +1214,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
                wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
+               vp->bcn_en = info->enable_beacon;
                if (data->started &&
                    !hrtimer_is_queued(&data->beacon_timer.timer) &&
                    info->enable_beacon) {
@@ -1215,8 +1228,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
                        tasklet_hrtimer_start(&data->beacon_timer,
                                              ns_to_ktime(until_tbtt * 1000),
                                              HRTIMER_MODE_REL);
-               } else if (!info->enable_beacon)
-                       tasklet_hrtimer_cancel(&data->beacon_timer);
+               } else if (!info->enable_beacon) {
+                       unsigned int count = 0;
+                       ieee80211_iterate_active_interfaces(
+                               data->hw, IEEE80211_IFACE_ITER_NORMAL,
+                               mac80211_hwsim_bcn_en_iter, &count);
+                       wiphy_debug(hw->wiphy, "  beaconing vifs remaining: %u",
+                                   count);
+                       if (count == 0)
+                               tasklet_hrtimer_cancel(&data->beacon_timer);
+               }
        }
 
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
index a6c46f3..e47f4e3 100644 (file)
@@ -1048,7 +1048,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
        struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
        unsigned long cmd_flags;
        unsigned long scan_pending_q_flags;
-       uint16_t cancel_scan_cmd = false;
+       bool cancel_scan_cmd = false;
 
        if ((adapter->curr_cmd) &&
            (adapter->curr_cmd->wait_q_enabled)) {
index f80f30b..c8385ec 100644 (file)
@@ -1020,8 +1020,8 @@ struct mwifiex_power_group {
 } __packed;
 
 struct mwifiex_types_power_group {
-       u16 type;
-       u16 length;
+       __le16 type;
+       __le16 length;
 } __packed;
 
 struct host_cmd_ds_txpwr_cfg {
index 220af4f..81ac001 100644 (file)
@@ -82,7 +82,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
                             struct mwifiex_ie_list *ie_list)
 {
        u16 travel_len, index, mask;
-       s16 input_len;
+       s16 input_len, tlv_len;
        struct mwifiex_ie *ie;
        u8 *tmp;
 
@@ -91,11 +91,13 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
 
        ie_list->len = 0;
 
-       while (input_len > 0) {
+       while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
                ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
-               input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
-               travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
+               tlv_len = le16_to_cpu(ie->ie_length);
+               travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
 
+               if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
+                       return -1;
                index = le16_to_cpu(ie->ie_index);
                mask = le16_to_cpu(ie->mgmt_subtype_mask);
 
@@ -132,6 +134,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
                le16_add_cpu(&ie_list->len,
                             le16_to_cpu(priv->mgmt_ie[index].ie_length) +
                             MWIFIEX_IE_HDR_SIZE);
+               input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
        }
 
        if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
index 37f873b..4e4686e 100644 (file)
@@ -621,7 +621,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
        int ret = 0;
        struct ieee_types_assoc_rsp *assoc_rsp;
        struct mwifiex_bssdescriptor *bss_desc;
-       u8 enable_data = true;
+       bool enable_data = true;
        u16 cap_info, status_code;
 
        assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params;
index c2b91f5..9d7c9d3 100644 (file)
@@ -882,7 +882,9 @@ mwifiex_add_card(void *card, struct semaphore *sem,
        adapter->cmd_wait_q.status = 0;
        adapter->scan_wait_q_woken = false;
 
-       adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE");
+       adapter->workqueue =
+               alloc_workqueue("MWIFIEX_WORK_QUEUE",
+                               WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
        if (!adapter->workqueue)
                goto err_kmalloc;
 
index 52da8ee..33fa943 100644 (file)
@@ -93,7 +93,7 @@ static int mwifiex_pcie_suspend(struct device *dev)
        struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev) {
-               card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+               card = pci_get_drvdata(pdev);
                if (!card || !card->adapter) {
                        pr_err("Card or adapter structure is not valid\n");
                        return 0;
@@ -128,7 +128,7 @@ static int mwifiex_pcie_resume(struct device *dev)
        struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev) {
-               card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+               card = pci_get_drvdata(pdev);
                if (!card || !card->adapter) {
                        pr_err("Card or adapter structure is not valid\n");
                        return 0;
@@ -2037,7 +2037,7 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context)
                goto exit;
        }
 
-       card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+       card = pci_get_drvdata(pdev);
        if (!card || !card->adapter) {
                pr_debug("info: %s: card=%p adapter=%p\n", __func__, card,
                         card ? card->adapter : NULL);
index 1576104..9bf8898 100644 (file)
@@ -1029,7 +1029,10 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
                                    struct sk_buff *skb, u32 upld_typ)
 {
        u8 *cmd_buf;
+       __le16 *curr_ptr = (__le16 *)skb->data;
+       u16 pkt_len = le16_to_cpu(*curr_ptr);
 
+       skb_trim(skb, pkt_len);
        skb_pull(skb, INTF_HEADER_LEN);
 
        switch (upld_typ) {
index c0268b5..2181ee2 100644 (file)
@@ -239,14 +239,14 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd,
                        memmove(cmd_txp_cfg, txp,
                                sizeof(struct host_cmd_ds_txpwr_cfg) +
                                sizeof(struct mwifiex_types_power_group) +
-                               pg_tlv->length);
+                               le16_to_cpu(pg_tlv->length));
 
                        pg_tlv = (struct mwifiex_types_power_group *) ((u8 *)
                                  cmd_txp_cfg +
                                  sizeof(struct host_cmd_ds_txpwr_cfg));
                        cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) +
                                  sizeof(struct mwifiex_types_power_group) +
-                                 pg_tlv->length);
+                                 le16_to_cpu(pg_tlv->length));
                } else {
                        memmove(cmd_txp_cfg, txp, sizeof(*txp));
                }
@@ -327,7 +327,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
 {
        struct mwifiex_adapter *adapter = priv->adapter;
        struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg;
-       u16 hs_activate = false;
+       bool hs_activate = false;
 
        if (!hscfg_param)
                /* New Activate command */
index 58a6013..2675ca7 100644 (file)
@@ -274,17 +274,20 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
        struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg;
        struct mwifiex_rate_scope *rate_scope;
        struct mwifiex_ie_types_header *head;
-       u16 tlv, tlv_buf_len;
+       u16 tlv, tlv_buf_len, tlv_buf_left;
        u8 *tlv_buf;
        u32 i;
 
-       tlv_buf = ((u8 *)rate_cfg) +
-                       sizeof(struct host_cmd_ds_tx_rate_cfg);
-       tlv_buf_len = le16_to_cpu(*(__le16 *) (tlv_buf + sizeof(u16)));
+       tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg);
+       tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg);
 
-       while (tlv_buf && tlv_buf_len > 0) {
-               tlv = (*tlv_buf);
-               tlv = tlv | (*(tlv_buf + 1) << 8);
+       while (tlv_buf_left >= sizeof(*head)) {
+               head = (struct mwifiex_ie_types_header *)tlv_buf;
+               tlv = le16_to_cpu(head->type);
+               tlv_buf_len = le16_to_cpu(head->len);
+
+               if (tlv_buf_left < (sizeof(*head) + tlv_buf_len))
+                       break;
 
                switch (tlv) {
                case TLV_TYPE_RATE_SCOPE:
@@ -304,9 +307,8 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
                        /* Add RATE_DROP tlv here */
                }
 
-               head = (struct mwifiex_ie_types_header *) tlv_buf;
-               tlv_buf += le16_to_cpu(head->len) + sizeof(*head);
-               tlv_buf_len -= le16_to_cpu(head->len);
+               tlv_buf += (sizeof(*head) + tlv_buf_len);
+               tlv_buf_left -= (sizeof(*head) + tlv_buf_len);
        }
 
        priv->is_data_rate_auto = mwifiex_is_rate_auto(priv);
@@ -340,13 +342,17 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
                ((u8 *) data_buf + sizeof(struct host_cmd_ds_txpwr_cfg));
        pg = (struct mwifiex_power_group *)
                ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group));
-       length = pg_tlv_hdr->length;
-       if (length > 0) {
-               max_power = pg->power_max;
-               min_power = pg->power_min;
-               length -= sizeof(struct mwifiex_power_group);
-       }
-       while (length) {
+       length = le16_to_cpu(pg_tlv_hdr->length);
+
+       /* At least one structure required to update power */
+       if (length < sizeof(struct mwifiex_power_group))
+               return 0;
+
+       max_power = pg->power_max;
+       min_power = pg->power_min;
+       length -= sizeof(struct mwifiex_power_group);
+
+       while (length >= sizeof(struct mwifiex_power_group)) {
                pg++;
                if (max_power < pg->power_max)
                        max_power = pg->power_max;
@@ -356,10 +362,8 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
 
                length -= sizeof(struct mwifiex_power_group);
        }
-       if (pg_tlv_hdr->length > 0) {
-               priv->min_tx_power_level = (u8) min_power;
-               priv->max_tx_power_level = (u8) max_power;
-       }
+       priv->min_tx_power_level = (u8) min_power;
+       priv->max_tx_power_level = (u8) max_power;
 
        return 0;
 }
index f084412..c8e029d 100644 (file)
@@ -638,8 +638,9 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv,
                txp_cfg->mode = cpu_to_le32(1);
                pg_tlv = (struct mwifiex_types_power_group *)
                         (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
-               pg_tlv->type = TLV_TYPE_POWER_GROUP;
-               pg_tlv->length = 4 * sizeof(struct mwifiex_power_group);
+               pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
+               pg_tlv->length =
+                       cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
                pg = (struct mwifiex_power_group *)
                     (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
                      + sizeof(struct mwifiex_types_power_group));
index 1cfe5a7..92f76d6 100644 (file)
@@ -97,6 +97,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        struct mwifiex_txinfo *tx_info;
        int hdr_chop;
        struct timeval tv;
+       struct ethhdr *p_ethhdr;
        u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 
        uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -112,14 +113,36 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        }
 
        if (!memcmp(&rx_pkt_hdr->rfc1042_hdr,
-                   rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)))
+                   rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) {
+               /* Replace the 803 header and rfc1042 header (llc/snap) with
+                * an Ethernet II header, keep the src/dst and snap_type
+                * (ethertype).
+                *
+                * The firmware only passes up SNAP frames converting all RX
+                * data from 802.11 to 802.2/LLC/SNAP frames.
+                *
+                * To create the Ethernet II, just move the src, dst address
+                * right before the snap_type.
+                */
+               p_ethhdr = (struct ethhdr *)
+                       ((u8 *)(&rx_pkt_hdr->eth803_hdr)
+                        + sizeof(rx_pkt_hdr->eth803_hdr)
+                        + sizeof(rx_pkt_hdr->rfc1042_hdr)
+                        - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
+                        - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
+                        - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
+               memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
+                      sizeof(p_ethhdr->h_source));
+               memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
+                      sizeof(p_ethhdr->h_dest));
                /* Chop off the rxpd + the excess memory from
                 * 802.2/llc/snap header that was removed.
                 */
-               hdr_chop = (u8 *)eth_hdr - (u8 *)uap_rx_pd;
-       else
+               hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
+       } else {
                /* Chop off the rxpd */
                hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
+       }
 
        /* Chop off the leading header bytes so the it points
         * to the start of either the reconstructed EthII frame
index 95fa359..13eaeed 100644 (file)
@@ -708,7 +708,7 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
 {
        u8 *curr = (u8 *) &resp->params.get_wmm_status;
        uint16_t resp_len = le16_to_cpu(resp->size), tlv_len;
-       int valid = true;
+       bool valid = true;
 
        struct mwifiex_ie_types_data *tlv_hdr;
        struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus;
@@ -722,6 +722,9 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
                tlv_hdr = (struct mwifiex_ie_types_data *) curr;
                tlv_len = le16_to_cpu(tlv_hdr->header.len);
 
+               if (resp_len < tlv_len + sizeof(tlv_hdr->header))
+                       break;
+
                switch (le16_to_cpu(tlv_hdr->header.type)) {
                case TLV_TYPE_WMMQSTATUS:
                        tlv_wmm_qstatus =
index 644d6e0..0f129d4 100644 (file)
@@ -83,11 +83,10 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead)
 }
 
 void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
-                                       struct sk_buff *skb);
+                                struct sk_buff *skb);
 void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra);
 void mwifiex_rotate_priolists(struct mwifiex_private *priv,
-                             struct mwifiex_ra_list_tbl *ra,
-                             int tid);
+                             struct mwifiex_ra_list_tbl *ra, int tid);
 
 int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter);
 void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter);
@@ -95,21 +94,18 @@ int mwifiex_is_ralist_valid(struct mwifiex_private *priv,
                            struct mwifiex_ra_list_tbl *ra_list, int tid);
 
 u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv,
-                                            const struct sk_buff *skb);
+                                    const struct sk_buff *skb);
 void mwifiex_wmm_init(struct mwifiex_adapter *adapter);
 
-extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv,
-                                                u8 **assoc_buf,
-                                                struct ieee_types_wmm_parameter
-                                                *wmmie,
-                                                struct ieee80211_ht_cap
-                                                *htcap);
+u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv,
+                                       u8 **assoc_buf,
+                                       struct ieee_types_wmm_parameter *wmmie,
+                                       struct ieee80211_ht_cap *htcap);
 
 void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv,
-                                       struct ieee_types_wmm_parameter
-                                       *wmm_ie);
+                                       struct ieee_types_wmm_parameter *wmm_ie);
 void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv);
-extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
-                                     const struct host_cmd_ds_command *resp);
+int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
+                              const struct host_cmd_ds_command *resp);
 
 #endif /* !_MWIFIEX_WMM_H_ */
index a3707fd..b953ad6 100644 (file)
@@ -6093,7 +6093,6 @@ err_iounmap:
        if (priv->sram != NULL)
                pci_iounmap(pdev, priv->sram);
 
-       pci_set_drvdata(pdev, NULL);
        ieee80211_free_hw(hw);
 
 err_free_reg:
@@ -6147,7 +6146,6 @@ static void mwl8k_remove(struct pci_dev *pdev)
 unmap:
        pci_iounmap(pdev, priv->regs);
        pci_iounmap(pdev, priv->sram);
-       pci_set_drvdata(pdev, NULL);
        ieee80211_free_hw(hw);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
index 3bb936b..eebd2be 100644 (file)
@@ -182,23 +182,20 @@ extern int orinoco_debug;
 /* Exported prototypes                                              */
 /********************************************************************/
 
-extern struct orinoco_private *alloc_orinocodev(
-       int sizeof_card, struct device *device,
-       int (*hard_reset)(struct orinoco_private *),
-       int (*stop_fw)(struct orinoco_private *, int));
-extern void free_orinocodev(struct orinoco_private *priv);
-extern int orinoco_init(struct orinoco_private *priv);
-extern int orinoco_if_add(struct orinoco_private *priv,
-                         unsigned long base_addr,
-                         unsigned int irq,
-                         const struct net_device_ops *ops);
-extern void orinoco_if_del(struct orinoco_private *priv);
-extern int orinoco_up(struct orinoco_private *priv);
-extern void orinoco_down(struct orinoco_private *priv);
-extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
-
-extern void __orinoco_ev_info(struct net_device *dev, struct hermes *hw);
-extern void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw);
+struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device,
+                                        int (*hard_reset)(struct orinoco_private *),
+                                        int (*stop_fw)(struct orinoco_private *, int));
+void free_orinocodev(struct orinoco_private *priv);
+int orinoco_init(struct orinoco_private *priv);
+int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr,
+                  unsigned int irq, const struct net_device_ops *ops);
+void orinoco_if_del(struct orinoco_private *priv);
+int orinoco_up(struct orinoco_private *priv);
+void orinoco_down(struct orinoco_private *priv);
+irqreturn_t orinoco_interrupt(int irq, void *dev_id);
+
+void __orinoco_ev_info(struct net_device *dev, struct hermes *hw);
+void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw);
 
 int orinoco_process_xmit_skb(struct sk_buff *skb,
                             struct net_device *dev,
index d73fdf6..ffb2469 100644 (file)
@@ -234,7 +234,6 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev,
        free_irq(pdev->irq, priv);
 
  fail_irq:
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
 
  fail_alloc:
@@ -265,7 +264,6 @@ static void orinoco_nortel_remove_one(struct pci_dev *pdev)
 
        orinoco_if_del(priv);
        free_irq(pdev->irq, priv);
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
        pci_iounmap(pdev, priv->hw.iobase);
        pci_iounmap(pdev, card->attr_io);
index 677bf14..5ae1191 100644 (file)
@@ -184,7 +184,6 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
        free_irq(pdev->irq, priv);
 
  fail_irq:
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
 
  fail_alloc:
@@ -205,7 +204,6 @@ static void orinoco_pci_remove_one(struct pci_dev *pdev)
 
        orinoco_if_del(priv);
        free_irq(pdev->irq, priv);
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
        pci_iounmap(pdev, priv->hw.iobase);
        pci_release_regions(pdev);
index 2559dbd..bbd36d1 100644 (file)
@@ -273,7 +273,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
        free_irq(pdev->irq, priv);
 
  fail_irq:
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
 
  fail_alloc:
@@ -301,7 +300,6 @@ static void orinoco_plx_remove_one(struct pci_dev *pdev)
 
        orinoco_if_del(priv);
        free_irq(pdev->irq, priv);
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
        pci_iounmap(pdev, priv->hw.iobase);
        pci_iounmap(pdev, card->attr_io);
index 42afeee..04b08de 100644 (file)
@@ -170,7 +170,6 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev,
        free_irq(pdev->irq, priv);
 
  fail_irq:
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
 
  fail_alloc:
@@ -195,7 +194,6 @@ static void orinoco_tmd_remove_one(struct pci_dev *pdev)
 
        orinoco_if_del(priv);
        free_irq(pdev->irq, priv);
-       pci_set_drvdata(pdev, NULL);
        free_orinocodev(priv);
        pci_iounmap(pdev, priv->hw.iobase);
        pci_iounmap(pdev, card->bridge_io);
index 57e3af8..f9a07b0 100644 (file)
@@ -631,7 +631,6 @@ static int p54p_probe(struct pci_dev *pdev,
        iounmap(priv->map);
 
  err_free_dev:
-       pci_set_drvdata(pdev, NULL);
        p54_free_common(dev);
 
  err_free_reg:
index 7fc46f2..de15171 100644 (file)
@@ -636,7 +636,7 @@ static int p54spi_probe(struct spi_device *spi)
        gpio_direction_input(p54spi_gpio_irq);
 
        ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
-                         p54spi_interrupt, IRQF_DISABLED, "p54spi",
+                         p54spi_interrupt, 0, "p54spi",
                          priv->spi);
        if (ret < 0) {
                dev_err(&priv->spi->dev, "request_irq() failed");
index 1c22b81..8863a6c 100644 (file)
@@ -183,7 +183,7 @@ prism54_update_stats(struct work_struct *work)
        data = r.ptr;
 
        /* copy this MAC to the bss */
-       memcpy(bss.address, data, 6);
+       memcpy(bss.address, data, ETH_ALEN);
        kfree(data);
 
        /* now ask for the corresponding bss */
@@ -531,7 +531,7 @@ prism54_set_wap(struct net_device *ndev, struct iw_request_info *info,
                return -EINVAL;
 
        /* prepare the structure for the set object */
-       memcpy(&bssid[0], awrq->sa_data, 6);
+       memcpy(&bssid[0], awrq->sa_data, ETH_ALEN);
 
        /* set the bssid -- does this make sense when in AP mode? */
        rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid);
@@ -550,7 +550,7 @@ prism54_get_wap(struct net_device *ndev, struct iw_request_info *info,
        int rvalue;
 
        rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
-       memcpy(awrq->sa_data, r.ptr, 6);
+       memcpy(awrq->sa_data, r.ptr, ETH_ALEN);
        awrq->sa_family = ARPHRD_ETHER;
        kfree(r.ptr);
 
@@ -582,7 +582,7 @@ prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info,
        size_t wpa_ie_len;
 
        /* The first entry must be the MAC address */
-       memcpy(iwe.u.ap_addr.sa_data, bss->address, 6);
+       memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN);
        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
        iwe.cmd = SIOCGIWAP;
        current_ev = iwe_stream_add_event(info, current_ev, end_buf,
@@ -2489,7 +2489,7 @@ prism54_set_mac_address(struct net_device *ndev, void *addr)
                              &((struct sockaddr *) addr)->sa_data);
        if (!ret)
                memcpy(priv->ndev->dev_addr,
-                      &((struct sockaddr *) addr)->sa_data, 6);
+                      &((struct sockaddr *) addr)->sa_data, ETH_ALEN);
 
        return ret;
 }
index 5970ff6..e05d9b4 100644 (file)
@@ -811,6 +811,10 @@ static const struct net_device_ops islpci_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
+static struct device_type wlan_type = {
+       .name   = "wlan",
+};
+
 struct net_device *
 islpci_setup(struct pci_dev *pdev)
 {
@@ -821,9 +825,8 @@ islpci_setup(struct pci_dev *pdev)
                return ndev;
 
        pci_set_drvdata(pdev, ndev);
-#if defined(SET_NETDEV_DEV)
        SET_NETDEV_DEV(ndev, &pdev->dev);
-#endif
+       SET_NETDEV_DEVTYPE(ndev, &wlan_type);
 
        /* setup the structure members */
        ndev->base_addr = pci_resource_start(pdev, 0);
@@ -837,7 +840,7 @@ islpci_setup(struct pci_dev *pdev)
        /* ndev->set_multicast_list = &islpci_set_multicast_list; */
        ndev->addr_len = ETH_ALEN;
        /* Get a non-zero dummy MAC address for nameif. Jean II */
-       memcpy(ndev->dev_addr, dummy_mac, 6);
+       memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN);
 
        ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT;
 
index a01606b..056af38 100644 (file)
@@ -682,7 +682,7 @@ mgt_update_addr(islpci_private *priv)
                                     isl_oid[GEN_OID_MACADDRESS].size, &res);
 
        if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR))
-               memcpy(priv->ndev->dev_addr, res->data, 6);
+               memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN);
        else
                ret = -EIO;
        if (res)
index 68dbbb9..006b8bc 100644 (file)
@@ -58,11 +58,11 @@ config RT61PCI
 
 config RT2800PCI
        tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support"
-       depends on PCI || SOC_RT288X || SOC_RT305X
+       depends on PCI
        select RT2800_LIB
+       select RT2800_LIB_MMIO
        select RT2X00_LIB_MMIO
-       select RT2X00_LIB_PCI if PCI
-       select RT2X00_LIB_SOC if SOC_RT288X || SOC_RT305X
+       select RT2X00_LIB_PCI
        select RT2X00_LIB_FIRMWARE
        select RT2X00_LIB_CRYPTO
        select CRC_CCITT
@@ -199,9 +199,30 @@ config RT2800USB_UNKNOWN
 
 endif
 
+config RT2800SOC
+       tristate "Ralink WiSoC support"
+       depends on SOC_RT288X || SOC_RT305X
+       select RT2X00_LIB_SOC
+       select RT2X00_LIB_MMIO
+       select RT2X00_LIB_CRYPTO
+       select RT2X00_LIB_FIRMWARE
+       select RT2800_LIB
+       select RT2800_LIB_MMIO
+       ---help---
+         This adds support for Ralink WiSoC devices.
+         Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352.
+
+         When compiled as a module, this driver will be called rt2800soc.
+
+
 config RT2800_LIB
        tristate
 
+config RT2800_LIB_MMIO
+       tristate
+       select RT2X00_LIB_MMIO
+       select RT2800_LIB
+
 config RT2X00_LIB_MMIO
        tristate
 
@@ -219,6 +240,7 @@ config RT2X00_LIB_USB
 
 config RT2X00_LIB
        tristate
+       select AVERAGE
 
 config RT2X00_LIB_FIRMWARE
        boolean
index f069d8b..24a6601 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_RT2X00_LIB_PCI)          += rt2x00pci.o
 obj-$(CONFIG_RT2X00_LIB_SOC)           += rt2x00soc.o
 obj-$(CONFIG_RT2X00_LIB_USB)           += rt2x00usb.o
 obj-$(CONFIG_RT2800_LIB)               += rt2800lib.o
+obj-$(CONFIG_RT2800_LIB_MMIO)          += rt2800mmio.o
 obj-$(CONFIG_RT2400PCI)                        += rt2400pci.o
 obj-$(CONFIG_RT2500PCI)                        += rt2500pci.o
 obj-$(CONFIG_RT61PCI)                  += rt61pci.o
@@ -21,3 +22,4 @@ obj-$(CONFIG_RT2800PCI)                       += rt2800pci.o
 obj-$(CONFIG_RT2500USB)                        += rt2500usb.o
 obj-$(CONFIG_RT73USB)                  += rt73usb.o
 obj-$(CONFIG_RT2800USB)                        += rt2800usb.o
+obj-$(CONFIG_RT2800SOC)                        += rt2800soc.o
index fa33b5e..aab6b5e 100644 (file)
@@ -52,6 +52,7 @@
  * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392)
  * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662)
  * RF5592 2.4G/5G 2T2R
+ * RF3070 2.4G 1T1R
  * RF5360 2.4G 1T1R
  * RF5370 2.4G 1T1R
  * RF5390 2.4G 1T1R
@@ -70,6 +71,7 @@
 #define RF3322                         0x000c
 #define RF3053                         0x000d
 #define RF5592                         0x000f
+#define RF3070                         0x3070
 #define RF3290                         0x3290
 #define RF5360                         0x5360
 #define RF5370                         0x5370
 /*
  * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number.
  */
-#define MAC_CSR0_3290                          0x0000
+#define MAC_CSR0_3290                  0x0000
 
 /*
  * E2PROM_CSR: PCI EEPROM control register.
 /*
  * COEX_CFG_0
  */
-#define COEX_CFG0                      0x0040
+#define COEX_CFG0              0x0040
 #define COEX_CFG_ANT           FIELD32(0xff000000)
 /*
  * COEX_CFG_1
  */
-#define COEX_CFG1                      0x0044
+#define COEX_CFG1              0x0044
 
 /*
  * COEX_CFG_2
  */
-#define COEX_CFG2                      0x0048
+#define COEX_CFG2              0x0048
 #define BT_COEX_CFG1           FIELD32(0xff000000)
 #define BT_COEX_CFG0           FIELD32(0x00ff0000)
 #define WL_COEX_CFG1           FIELD32(0x0000ff00)
 #define PLL_RESERVED_INPUT2    FIELD32(0x0000ff00)
 #define PLL_CONTROL            FIELD32(0x00070000)
 #define PLL_LPF_R1             FIELD32(0x00080000)
-#define PLL_LPF_C1_CTRL        FIELD32(0x00300000)
-#define PLL_LPF_C2_CTRL        FIELD32(0x00c00000)
+#define PLL_LPF_C1_CTRL                FIELD32(0x00300000)
+#define PLL_LPF_C2_CTRL                FIELD32(0x00c00000)
 #define PLL_CP_CURRENT_CTRL    FIELD32(0x03000000)
 #define PLL_PFD_DELAY_CTRL     FIELD32(0x0c000000)
 #define PLL_LOCK_CTRL          FIELD32(0x70000000)
@@ -2164,7 +2166,7 @@ struct mac_iveiv_entry {
  */
 #define RFCSR6_R1                      FIELD8(0x03)
 #define RFCSR6_R2                      FIELD8(0x40)
-#define RFCSR6_TXDIV           FIELD8(0x0c)
+#define RFCSR6_TXDIV                   FIELD8(0x0c)
 /* bits for RF3053 */
 #define RFCSR6_VCO_IC                  FIELD8(0xc0)
 
@@ -2202,13 +2204,13 @@ struct mac_iveiv_entry {
  * RFCSR 12:
  */
 #define RFCSR12_TX_POWER               FIELD8(0x1f)
-#define RFCSR12_DR0                            FIELD8(0xe0)
+#define RFCSR12_DR0                    FIELD8(0xe0)
 
 /*
  * RFCSR 13:
  */
 #define RFCSR13_TX_POWER               FIELD8(0x1f)
-#define RFCSR13_DR0                            FIELD8(0xe0)
+#define RFCSR13_DR0                    FIELD8(0xe0)
 
 /*
  * RFCSR 15:
@@ -2226,7 +2228,7 @@ struct mac_iveiv_entry {
 #define RFCSR17_TXMIXER_GAIN           FIELD8(0x07)
 #define RFCSR17_TX_LO1_EN              FIELD8(0x08)
 #define RFCSR17_R                      FIELD8(0x20)
-#define RFCSR17_CODE                   FIELD8(0x7f)
+#define RFCSR17_CODE                   FIELD8(0x7f)
 
 /* RFCSR 18 */
 #define RFCSR18_XO_TUNE_BYPASS         FIELD8(0x40)
@@ -2449,7 +2451,7 @@ enum rt2800_eeprom_word {
  */
 #define EEPROM_NIC_CONF0_RXPATH                FIELD16(0x000f)
 #define EEPROM_NIC_CONF0_TXPATH                FIELD16(0x00f0)
-#define EEPROM_NIC_CONF0_RF_TYPE               FIELD16(0x0f00)
+#define EEPROM_NIC_CONF0_RF_TYPE       FIELD16(0x0f00)
 
 /*
  * EEPROM NIC Configuration 1
@@ -2471,18 +2473,18 @@ enum rt2800_eeprom_word {
  * DAC_TEST: 0: disable, 1: enable
  */
 #define EEPROM_NIC_CONF1_HW_RADIO              FIELD16(0x0001)
-#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC               FIELD16(0x0002)
-#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G               FIELD16(0x0004)
-#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G               FIELD16(0x0008)
+#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC       FIELD16(0x0002)
+#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G       FIELD16(0x0004)
+#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G       FIELD16(0x0008)
 #define EEPROM_NIC_CONF1_CARDBUS_ACCEL         FIELD16(0x0010)
 #define EEPROM_NIC_CONF1_BW40M_SB_2G           FIELD16(0x0020)
 #define EEPROM_NIC_CONF1_BW40M_SB_5G           FIELD16(0x0040)
 #define EEPROM_NIC_CONF1_WPS_PBC               FIELD16(0x0080)
 #define EEPROM_NIC_CONF1_BW40M_2G              FIELD16(0x0100)
 #define EEPROM_NIC_CONF1_BW40M_5G              FIELD16(0x0200)
-#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA             FIELD16(0x400)
+#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA     FIELD16(0x400)
 #define EEPROM_NIC_CONF1_ANT_DIVERSITY         FIELD16(0x1800)
-#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC               FIELD16(0x2000)
+#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC       FIELD16(0x2000)
 #define EEPROM_NIC_CONF1_BT_COEXIST            FIELD16(0x4000)
 #define EEPROM_NIC_CONF1_DAC_TEST              FIELD16(0x8000)
 
@@ -2521,9 +2523,9 @@ enum rt2800_eeprom_word {
  * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream
  * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved
  */
-#define EEPROM_NIC_CONF2_RX_STREAM             FIELD16(0x000f)
-#define EEPROM_NIC_CONF2_TX_STREAM             FIELD16(0x00f0)
-#define EEPROM_NIC_CONF2_CRYSTAL               FIELD16(0x0600)
+#define EEPROM_NIC_CONF2_RX_STREAM     FIELD16(0x000f)
+#define EEPROM_NIC_CONF2_TX_STREAM     FIELD16(0x00f0)
+#define EEPROM_NIC_CONF2_CRYSTAL       FIELD16(0x0600)
 
 /*
  * EEPROM LNA
@@ -2790,7 +2792,7 @@ enum rt2800_eeprom_word {
 #define MCU_CURRENT                    0x36
 #define MCU_LED                                0x50
 #define MCU_LED_STRENGTH               0x51
-#define MCU_LED_AG_CONF                0x52
+#define MCU_LED_AG_CONF                        0x52
 #define MCU_LED_ACT_CONF               0x53
 #define MCU_LED_LED_POLARITY           0x54
 #define MCU_RADAR                      0x60
@@ -2799,7 +2801,7 @@ enum rt2800_eeprom_word {
 #define MCU_FREQ_OFFSET                        0x74
 #define MCU_BBP_SIGNAL                 0x80
 #define MCU_POWER_SAVE                 0x83
-#define MCU_BAND_SELECT                0x91
+#define MCU_BAND_SELECT                        0x91
 
 /*
  * MCU mailbox tokens
index 88ce656..776aff3 100644 (file)
@@ -278,12 +278,9 @@ static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
        [EEPROM_LNA]                    = 0x0026,
        [EEPROM_EXT_LNA2]               = 0x0027,
        [EEPROM_RSSI_BG]                = 0x0028,
-       [EEPROM_TXPOWER_DELTA]          = 0x0028, /* Overlaps with RSSI_BG */
        [EEPROM_RSSI_BG2]               = 0x0029,
-       [EEPROM_TXMIXER_GAIN_BG]        = 0x0029, /* Overlaps with RSSI_BG2 */
        [EEPROM_RSSI_A]                 = 0x002a,
        [EEPROM_RSSI_A2]                = 0x002b,
-       [EEPROM_TXMIXER_GAIN_A]         = 0x002b, /* Overlaps with RSSI_A2 */
        [EEPROM_TXPOWER_BG1]            = 0x0030,
        [EEPROM_TXPOWER_BG2]            = 0x0037,
        [EEPROM_EXT_TXPOWER_BG3]        = 0x003e,
@@ -1783,7 +1780,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
        rt2800_bbp_read(rt2x00dev, 3, &r3);
 
        if (rt2x00_rt(rt2x00dev, RT3572) &&
-           test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+           rt2x00_has_cap_bt_coexist(rt2x00dev))
                rt2800_config_3572bt_ant(rt2x00dev);
 
        /*
@@ -1795,7 +1792,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                break;
        case 2:
                if (rt2x00_rt(rt2x00dev, RT3572) &&
-                   test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+                   rt2x00_has_cap_bt_coexist(rt2x00dev))
                        rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1);
                else
                        rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
@@ -1825,7 +1822,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                break;
        case 2:
                if (rt2x00_rt(rt2x00dev, RT3572) &&
-                   test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+                   rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                        rt2x00_set_field8(&r3, BBP3_RX_ADC, 1);
                        rt2x00_set_field8(&r3, BBP3_RX_ANTENNA,
                                rt2x00dev->curr_band == IEEE80211_BAND_5GHZ);
@@ -2029,13 +2026,6 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
                          rt2x00dev->default_ant.tx_chain_num <= 2);
        rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
 
-       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
-       rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
-       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
-       msleep(1);
-       rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0);
-       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
-
        rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
        rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
        rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
@@ -2141,7 +2131,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
        rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
        rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
-       if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                if (rf->channel <= 14) {
                        rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
                        rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
@@ -2650,7 +2640,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
 
        if (rt2x00_rt(rt2x00dev, RT5392)) {
                rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
-               if (info->default_power1 > POWER_BOUND)
+               if (info->default_power2 > POWER_BOUND)
                        rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND);
                else
                        rt2x00_set_field8(&rfcsr, RFCSR50_TX,
@@ -2674,7 +2664,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
        if (rf->channel <= 14) {
                int idx = rf->channel-1;
 
-               if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                        if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) {
                                /* r55/r59 value array of channel 1~14 */
                                static const char r55_bt_rev[] = {0x83, 0x83,
@@ -3152,6 +3142,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        case RF3322:
                rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info);
                break;
+       case RF3070:
        case RF5360:
        case RF5370:
        case RF5372:
@@ -3166,7 +3157,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info);
        }
 
-       if (rt2x00_rf(rt2x00dev, RF3290) ||
+       if (rt2x00_rf(rt2x00dev, RF3070) ||
+           rt2x00_rf(rt2x00dev, RF3290) ||
            rt2x00_rf(rt2x00dev, RF3322) ||
            rt2x00_rf(rt2x00dev, RF5360) ||
            rt2x00_rf(rt2x00dev, RF5370) ||
@@ -3218,8 +3210,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        if (rf->channel <= 14) {
                if (!rt2x00_rt(rt2x00dev, RT5390) &&
                    !rt2x00_rt(rt2x00dev, RT5392)) {
-                       if (test_bit(CAPABILITY_EXTERNAL_LNA_BG,
-                                    &rt2x00dev->cap_flags)) {
+                       if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
                                rt2800_bbp_write(rt2x00dev, 82, 0x62);
                                rt2800_bbp_write(rt2x00dev, 75, 0x46);
                        } else {
@@ -3244,7 +3235,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                if (rt2x00_rt(rt2x00dev, RT3593))
                        rt2800_bbp_write(rt2x00dev, 83, 0x9a);
 
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev))
                        rt2800_bbp_write(rt2x00dev, 75, 0x46);
                else
                        rt2800_bbp_write(rt2x00dev, 75, 0x50);
@@ -3280,7 +3271,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                /* Turn on primary PAs */
                rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN,
                                   rf->channel > 14);
-               if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_bt_coexist(rt2x00dev))
                        rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
                else
                        rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
@@ -3311,33 +3302,50 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
 
        rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
 
-       if (rt2x00_rt(rt2x00dev, RT3572))
+       if (rt2x00_rt(rt2x00dev, RT3572)) {
                rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
 
+               /* AGC init */
+               if (rf->channel <= 14)
+                       reg = 0x1c + (2 * rt2x00dev->lna_gain);
+               else
+                       reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3);
+
+               rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg);
+       }
+
        if (rt2x00_rt(rt2x00dev, RT3593)) {
-               if (rt2x00_is_usb(rt2x00dev)) {
-                       rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
+               rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
 
-                       /* Band selection. GPIO #8 controls all paths */
+               /* Band selection */
+               if (rt2x00_is_usb(rt2x00dev) ||
+                   rt2x00_is_pcie(rt2x00dev)) {
+                       /* GPIO #8 controls all paths */
                        rt2x00_set_field32(&reg, GPIO_CTRL_DIR8, 0);
                        if (rf->channel <= 14)
                                rt2x00_set_field32(&reg, GPIO_CTRL_VAL8, 1);
                        else
                                rt2x00_set_field32(&reg, GPIO_CTRL_VAL8, 0);
+               }
 
+               /* LNA PE control. */
+               if (rt2x00_is_usb(rt2x00dev)) {
+                       /* GPIO #4 controls PE0 and PE1,
+                        * GPIO #7 controls PE2
+                        */
                        rt2x00_set_field32(&reg, GPIO_CTRL_DIR4, 0);
                        rt2x00_set_field32(&reg, GPIO_CTRL_DIR7, 0);
 
-                       /* LNA PE control.
-                       * GPIO #4 controls PE0 and PE1,
-                       * GPIO #7 controls PE2
-                       */
                        rt2x00_set_field32(&reg, GPIO_CTRL_VAL4, 1);
                        rt2x00_set_field32(&reg, GPIO_CTRL_VAL7, 1);
-
-                       rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
+               } else if (rt2x00_is_pcie(rt2x00dev)) {
+                       /* GPIO #4 controls PE0, PE1 and PE2 */
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR4, 0);
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL4, 1);
                }
 
+               rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
+
                /* AGC init */
                if (rf->channel <= 14)
                        reg = 0x1c + 2 * rt2x00dev->lna_gain;
@@ -3565,7 +3573,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev,
 {
        int delta;
 
-       if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_power_limit(rt2x00dev))
                return 0;
 
        /*
@@ -3594,7 +3602,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
        if (rt2x00_rt(rt2x00dev, RT3593))
                return min_t(u8, txpower, 0xc);
 
-       if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_power_limit(rt2x00dev)) {
                /*
                 * Check if eirp txpower exceed txpower_limit.
                 * We use OFDM 6M as criterion and its eirp txpower
@@ -4264,6 +4272,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
                rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
                break;
        case RF3053:
+       case RF3070:
        case RF3290:
        case RF5360:
        case RF5370:
@@ -4405,6 +4414,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
                    rt2x00_rt(rt2x00dev, RT3290) ||
                    rt2x00_rt(rt2x00dev, RT3390) ||
                    rt2x00_rt(rt2x00dev, RT3572) ||
+                   rt2x00_rt(rt2x00dev, RT3593) ||
                    rt2x00_rt(rt2x00dev, RT5390) ||
                    rt2x00_rt(rt2x00dev, RT5392) ||
                    rt2x00_rt(rt2x00dev, RT5592))
@@ -4412,8 +4422,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
                else
                        vgc = 0x2e + rt2x00dev->lna_gain;
        } else { /* 5GHZ band */
-               if (rt2x00_rt(rt2x00dev, RT3572))
-                       vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3;
+               if (rt2x00_rt(rt2x00dev, RT3593))
+                       vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3;
                else if (rt2x00_rt(rt2x00dev, RT5592))
                        vgc = 0x24 + (2 * rt2x00dev->lna_gain);
                else {
@@ -4431,11 +4441,17 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
                                  struct link_qual *qual, u8 vgc_level)
 {
        if (qual->vgc_level != vgc_level) {
-               if (rt2x00_rt(rt2x00dev, RT5592)) {
+               if (rt2x00_rt(rt2x00dev, RT3572) ||
+                   rt2x00_rt(rt2x00dev, RT3593)) {
+                       rt2800_bbp_write_with_rx_chain(rt2x00dev, 66,
+                                                      vgc_level);
+               } else if (rt2x00_rt(rt2x00dev, RT5592)) {
                        rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a);
                        rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level);
-               } else
+               } else {
                        rt2800_bbp_write(rt2x00dev, 66, vgc_level);
+               }
+
                qual->vgc_level = vgc_level;
                qual->vgc_level_reg = vgc_level;
        }
@@ -4454,17 +4470,35 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
 
        if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C))
                return;
-       /*
-        * When RSSI is better then -80 increase VGC level with 0x10, except
-        * for rt5592 chip.
+
+       /* When RSSI is better than a certain threshold, increase VGC
+        * with a chip specific value in order to improve the balance
+        * between sensibility and noise isolation.
         */
 
        vgc = rt2800_get_default_vgc(rt2x00dev);
 
-       if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65)
-               vgc += 0x20;
-       else if (qual->rssi > -80)
-               vgc += 0x10;
+       switch (rt2x00dev->chip.rt) {
+       case RT3572:
+       case RT3593:
+               if (qual->rssi > -65) {
+                       if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)
+                               vgc += 0x20;
+                       else
+                               vgc += 0x10;
+               }
+               break;
+
+       case RT5592:
+               if (qual->rssi > -65)
+                       vgc += 0x20;
+               break;
+
+       default:
+               if (qual->rssi > -80)
+                       vgc += 0x10;
+               break;
+       }
 
        rt2800_set_vgc(rt2x00dev, qual, vgc);
 }
@@ -5489,7 +5523,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
        ant = (div_mode == 3) ? 1 : 0;
 
        /* check if this is a Bluetooth combo card */
-       if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
                u32 reg;
 
                rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
@@ -5798,7 +5832,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
            rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
            rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
            rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
-               if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
+               if (!rt2x00_has_cap_external_lna_bg(rt2x00dev))
                        rt2x00_set_field8(&rfcsr, RFCSR17_R, 1);
        }
 
@@ -5985,7 +6019,7 @@ static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev)
        rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
        rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
        rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
-       rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+       rt2800_rfcsr_write(rt2x00dev, 25, 0x03);
        rt2800_rfcsr_write(rt2x00dev, 29, 0x1f);
 
        if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) {
@@ -6441,7 +6475,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
        rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
        rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
 
-       rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
        rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
@@ -6479,7 +6513,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
        rt2800_rfcsr_write(rt2x00dev, 56, 0x22);
        rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
-       rt2800_rfcsr_write(rt2x00dev, 59, 0x63);
+       rt2800_rfcsr_write(rt2x00dev, 59, 0x8f);
 
        rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
        if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
@@ -6499,7 +6533,6 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev)
        rt2800_rf_init_calibration(rt2x00dev, 2);
 
        rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
-       rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
        rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
        rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
        rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
@@ -6653,17 +6686,20 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
        u16 word;
 
        /*
-        * Initialize all registers.
+        * Initialize MAC registers.
         */
        if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) ||
                     rt2800_init_registers(rt2x00dev)))
                return -EIO;
 
+       /*
+        * Wait BBP/RF to wake up.
+        */
        if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev)))
                return -EIO;
 
        /*
-        * Send signal to firmware during boot time.
+        * Send signal during boot time to initialize firmware.
         */
        rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
        rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
@@ -6672,9 +6708,15 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
        rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
        msleep(1);
 
+       /*
+        * Make sure BBP is up and running.
+        */
        if (unlikely(rt2800_wait_bbp_ready(rt2x00dev)))
                return -EIO;
 
+       /*
+        * Initialize BBP/RF registers.
+        */
        rt2800_init_bbp(rt2x00dev);
        rt2800_init_rfcsr(rt2x00dev);
 
@@ -7021,6 +7063,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        case RF3022:
        case RF3052:
        case RF3053:
+       case RF3070:
        case RF3290:
        case RF3320:
        case RF3322:
@@ -7203,7 +7246,7 @@ static const struct rf_channel rf_vals[] = {
 
 /*
  * RF value list for rt3xxx
- * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052)
+ * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053)
  */
 static const struct rf_channel rf_vals_3x[] = {
        {1,  241, 2, 2 },
@@ -7399,72 +7442,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
        {196, 83, 0, 12, 1},
 };
 
-static const struct rf_channel rf_vals_3053[] = {
-       /* Channel, N, R, K */
-       {1, 241, 2, 2},
-       {2, 241, 2, 7},
-       {3, 242, 2, 2},
-       {4, 242, 2, 7},
-       {5, 243, 2, 2},
-       {6, 243, 2, 7},
-       {7, 244, 2, 2},
-       {8, 244, 2, 7},
-       {9, 245, 2, 2},
-       {10, 245, 2, 7},
-       {11, 246, 2, 2},
-       {12, 246, 2, 7},
-       {13, 247, 2, 2},
-       {14, 248, 2, 4},
-
-       {36, 0x56, 0, 4},
-       {38, 0x56, 0, 6},
-       {40, 0x56, 0, 8},
-       {44, 0x57, 0, 0},
-       {46, 0x57, 0, 2},
-       {48, 0x57, 0, 4},
-       {52, 0x57, 0, 8},
-       {54, 0x57, 0, 10},
-       {56, 0x58, 0, 0},
-       {60, 0x58, 0, 4},
-       {62, 0x58, 0, 6},
-       {64, 0x58, 0, 8},
-
-       {100, 0x5B, 0, 8},
-       {102, 0x5B, 0, 10},
-       {104, 0x5C, 0, 0},
-       {108, 0x5C, 0, 4},
-       {110, 0x5C, 0, 6},
-       {112, 0x5C, 0, 8},
-
-       /* NOTE: Channel 114 has been removed intentionally.
-        * The EEPROM contains no TX power values for that,
-        * and it is disabled in the vendor driver as well.
-        */
-
-       {116, 0x5D, 0, 0},
-       {118, 0x5D, 0, 2},
-       {120, 0x5D, 0, 4},
-       {124, 0x5D, 0, 8},
-       {126, 0x5D, 0, 10},
-       {128, 0x5E, 0, 0},
-       {132, 0x5E, 0, 4},
-       {134, 0x5E, 0, 6},
-       {136, 0x5E, 0, 8},
-       {140, 0x5F, 0, 0},
-
-       {149, 0x5F, 0, 9},
-       {151, 0x5F, 0, 11},
-       {153, 0x60, 0, 1},
-       {157, 0x60, 0, 5},
-       {159, 0x60, 0, 7},
-       {161, 0x60, 0, 9},
-       {165, 0x61, 0, 1},
-       {167, 0x61, 0, 3},
-       {169, 0x61, 0, 5},
-       {171, 0x61, 0, 7},
-       {173, 0x61, 0, 9},
-};
-
 static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
        struct hw_mode_spec *spec = &rt2x00dev->spec;
@@ -7473,7 +7450,6 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        char *default_power2;
        char *default_power3;
        unsigned int i;
-       u16 eeprom;
        u32 reg;
 
        /*
@@ -7522,48 +7498,48 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->max_report_rates = 7;
        rt2x00dev->hw->max_rate_tries = 1;
 
-       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
-
        /*
         * Initialize hw_mode information.
         */
-       spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (rt2x00_rf(rt2x00dev, RF2820) ||
-           rt2x00_rf(rt2x00dev, RF2720)) {
+       switch (rt2x00dev->chip.rf) {
+       case RF2720:
+       case RF2820:
                spec->num_channels = 14;
                spec->channels = rf_vals;
-       } else if (rt2x00_rf(rt2x00dev, RF2850) ||
-                  rt2x00_rf(rt2x00dev, RF2750)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               break;
+
+       case RF2750:
+       case RF2850:
                spec->num_channels = ARRAY_SIZE(rf_vals);
                spec->channels = rf_vals;
-       } else if (rt2x00_rf(rt2x00dev, RF3020) ||
-                  rt2x00_rf(rt2x00dev, RF2020) ||
-                  rt2x00_rf(rt2x00dev, RF3021) ||
-                  rt2x00_rf(rt2x00dev, RF3022) ||
-                  rt2x00_rf(rt2x00dev, RF3290) ||
-                  rt2x00_rf(rt2x00dev, RF3320) ||
-                  rt2x00_rf(rt2x00dev, RF3322) ||
-                  rt2x00_rf(rt2x00dev, RF5360) ||
-                  rt2x00_rf(rt2x00dev, RF5370) ||
-                  rt2x00_rf(rt2x00dev, RF5372) ||
-                  rt2x00_rf(rt2x00dev, RF5390) ||
-                  rt2x00_rf(rt2x00dev, RF5392)) {
+               break;
+
+       case RF2020:
+       case RF3020:
+       case RF3021:
+       case RF3022:
+       case RF3070:
+       case RF3290:
+       case RF3320:
+       case RF3322:
+       case RF5360:
+       case RF5370:
+       case RF5372:
+       case RF5390:
+       case RF5392:
                spec->num_channels = 14;
                spec->channels = rf_vals_3x;
-       } else if (rt2x00_rf(rt2x00dev, RF3052)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               break;
+
+       case RF3052:
+       case RF3053:
                spec->num_channels = ARRAY_SIZE(rf_vals_3x);
                spec->channels = rf_vals_3x;
-       } else if (rt2x00_rf(rt2x00dev, RF3053)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
-               spec->num_channels = ARRAY_SIZE(rf_vals_3053);
-               spec->channels = rf_vals_3053;
-       } else if (rt2x00_rf(rt2x00dev, RF5592)) {
-               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               break;
 
+       case RF5592:
                rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, &reg);
                if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) {
                        spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40);
@@ -7572,11 +7548,16 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                        spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20);
                        spec->channels = rf_vals_5592_xtal20;
                }
+               break;
        }
 
        if (WARN_ON_ONCE(!spec->channels))
                return -ENODEV;
 
+       spec->supported_bands = SUPPORT_BAND_2GHZ;
+       if (spec->num_channels > 14)
+               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+
        /*
         * Initialize HT information.
         */
@@ -7591,22 +7572,21 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
            IEEE80211_HT_CAP_SGI_20 |
            IEEE80211_HT_CAP_SGI_40;
 
-       if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) >= 2)
+       if (rt2x00dev->default_ant.tx_chain_num >= 2)
                spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC;
 
-       spec->ht.cap |=
-           rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) <<
-               IEEE80211_HT_CAP_RX_STBC_SHIFT;
+       spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num <<
+                       IEEE80211_HT_CAP_RX_STBC_SHIFT;
 
        spec->ht.ampdu_factor = 3;
        spec->ht.ampdu_density = 4;
        spec->ht.mcs.tx_params =
            IEEE80211_HT_MCS_TX_DEFINED |
            IEEE80211_HT_MCS_TX_RX_DIFF |
-           ((rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) - 1) <<
-               IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+           ((rt2x00dev->default_ant.tx_chain_num - 1) <<
+            IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
 
-       switch (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH)) {
+       switch (rt2x00dev->default_ant.rx_chain_num) {
        case 3:
                spec->ht.mcs.rx_mask[2] = 0xff;
        case 2:
@@ -7671,6 +7651,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        case RF3320:
        case RF3052:
        case RF3053:
+       case RF3070:
        case RF3290:
        case RF5360:
        case RF5370:
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c
new file mode 100644 (file)
index 0000000..ae15228
--- /dev/null
@@ -0,0 +1,873 @@
+/*     Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
+ *     Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ *     Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *     Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ *     Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ *     Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ *     Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ *     Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ *     <http://rt2x00.serialmonkey.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.
+ *
+ *     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.
+ */
+
+/*     Module: rt2800mmio
+ *     Abstract: rt2800 MMIO device routines.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/export.h>
+
+#include "rt2x00.h"
+#include "rt2x00mmio.h"
+#include "rt2800.h"
+#include "rt2800lib.h"
+#include "rt2800mmio.h"
+
+/*
+ * TX descriptor initialization
+ */
+__le32 *rt2800mmio_get_txwi(struct queue_entry *entry)
+{
+       return (__le32 *) entry->skb->data;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi);
+
+void rt2800mmio_write_tx_desc(struct queue_entry *entry,
+                             struct txentry_desc *txdesc)
+{
+       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       __le32 *txd = entry_priv->desc;
+       u32 word;
+       const unsigned int txwi_size = entry->queue->winfo_size;
+
+       /*
+        * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1
+        * must contains a TXWI structure + 802.11 header + padding + 802.11
+        * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and
+        * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11
+        * data. It means that LAST_SEC0 is always 0.
+        */
+
+       /*
+        * Initialize TX descriptor
+        */
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma);
+       rt2x00_desc_write(txd, 0, word);
+
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len);
+       rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
+                          !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
+       rt2x00_set_field32(&word, TXD_W1_BURST,
+                          test_bit(ENTRY_TXD_BURST, &txdesc->flags));
+       rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size);
+       rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0);
+       rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
+       rt2x00_desc_write(txd, 1, word);
+
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
+                          skbdesc->skb_dma + txwi_size);
+       rt2x00_desc_write(txd, 2, word);
+
+       word = 0;
+       rt2x00_set_field32(&word, TXD_W3_WIV,
+                          !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
+       rt2x00_set_field32(&word, TXD_W3_QSEL, 2);
+       rt2x00_desc_write(txd, 3, word);
+
+       /*
+        * Register descriptor details in skb frame descriptor.
+        */
+       skbdesc->desc = txd;
+       skbdesc->desc_len = TXD_DESC_SIZE;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc);
+
+/*
+ * RX control handlers
+ */
+void rt2800mmio_fill_rxdone(struct queue_entry *entry,
+                           struct rxdone_entry_desc *rxdesc)
+{
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       __le32 *rxd = entry_priv->desc;
+       u32 word;
+
+       rt2x00_desc_read(rxd, 3, &word);
+
+       if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR))
+               rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
+
+       /*
+        * Unfortunately we don't know the cipher type used during
+        * decryption. This prevents us from correct providing
+        * correct statistics through debugfs.
+        */
+       rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR);
+
+       if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) {
+               /*
+                * Hardware has stripped IV/EIV data from 802.11 frame during
+                * decryption. Unfortunately the descriptor doesn't contain
+                * any fields with the EIV/IV data either, so they can't
+                * be restored by rt2x00lib.
+                */
+               rxdesc->flags |= RX_FLAG_IV_STRIPPED;
+
+               /*
+                * The hardware has already checked the Michael Mic and has
+                * stripped it from the frame. Signal this to mac80211.
+                */
+               rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
+
+               if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
+                       rxdesc->flags |= RX_FLAG_DECRYPTED;
+               else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
+                       rxdesc->flags |= RX_FLAG_MMIC_ERROR;
+       }
+
+       if (rt2x00_get_field32(word, RXD_W3_MY_BSS))
+               rxdesc->dev_flags |= RXDONE_MY_BSS;
+
+       if (rt2x00_get_field32(word, RXD_W3_L2PAD))
+               rxdesc->dev_flags |= RXDONE_L2PAD;
+
+       /*
+        * Process the RXWI structure that is at the start of the buffer.
+        */
+       rt2800_process_rxwi(entry, rxdesc);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone);
+
+/*
+ * Interrupt functions.
+ */
+static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev)
+{
+       struct ieee80211_conf conf = { .flags = 0 };
+       struct rt2x00lib_conf libconf = { .conf = &conf };
+
+       rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
+}
+
+static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status)
+{
+       __le32 *txwi;
+       u32 word;
+       int wcid, tx_wcid;
+
+       wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
+
+       txwi = rt2800_drv_get_txwi(entry);
+       rt2x00_desc_read(txwi, 1, &word);
+       tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+
+       return (tx_wcid == wcid);
+}
+
+static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data)
+{
+       u32 status = *(u32 *)data;
+
+       /*
+        * rt2800pci hardware might reorder frames when exchanging traffic
+        * with multiple BA enabled STAs.
+        *
+        * For example, a tx queue
+        *    [ STA1 | STA2 | STA1 | STA2 ]
+        * can result in tx status reports
+        *    [ STA1 | STA1 | STA2 | STA2 ]
+        * when the hw decides to aggregate the frames for STA1 into one AMPDU.
+        *
+        * To mitigate this effect, associate the tx status to the first frame
+        * in the tx queue with a matching wcid.
+        */
+       if (rt2800mmio_txdone_entry_check(entry, status) &&
+           !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               /*
+                * Got a matching frame, associate the tx status with
+                * the frame
+                */
+               entry->status = status;
+               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
+               return true;
+       }
+
+       /* Check the next frame */
+       return false;
+}
+
+static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data)
+{
+       u32 status = *(u32 *)data;
+
+       /*
+        * Find the first frame without tx status and assign this status to it
+        * regardless if it matches or not.
+        */
+       if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               /*
+                * Got a matching frame, associate the tx status with
+                * the frame
+                */
+               entry->status = status;
+               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
+               return true;
+       }
+
+       /* Check the next frame */
+       return false;
+}
+static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry,
+                                             void *data)
+{
+       if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
+               rt2800_txdone_entry(entry, entry->status,
+                                   rt2800mmio_get_txwi(entry));
+               return false;
+       }
+
+       /* No more frames to release */
+       return true;
+}
+
+static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev)
+{
+       struct data_queue *queue;
+       u32 status;
+       u8 qid;
+       int max_tx_done = 16;
+
+       while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) {
+               qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE);
+               if (unlikely(qid >= QID_RX)) {
+                       /*
+                        * Unknown queue, this shouldn't happen. Just drop
+                        * this tx status.
+                        */
+                       rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n",
+                                   qid);
+                       break;
+               }
+
+               queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
+               if (unlikely(queue == NULL)) {
+                       /*
+                        * The queue is NULL, this shouldn't happen. Stop
+                        * processing here and drop the tx status
+                        */
+                       rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n",
+                                   qid);
+                       break;
+               }
+
+               if (unlikely(rt2x00queue_empty(queue))) {
+                       /*
+                        * The queue is empty. Stop processing here
+                        * and drop the tx status.
+                        */
+                       rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
+                                   qid);
+                       break;
+               }
+
+               /*
+                * Let's associate this tx status with the first
+                * matching frame.
+                */
+               if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                               Q_INDEX, &status,
+                                               rt2800mmio_txdone_find_entry)) {
+                       /*
+                        * We cannot match the tx status to any frame, so just
+                        * use the first one.
+                        */
+                       if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                                       Q_INDEX, &status,
+                                                       rt2800mmio_txdone_match_first)) {
+                               rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n",
+                                           qid);
+                               break;
+                       }
+               }
+
+               /*
+                * Release all frames with a valid tx status.
+                */
+               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
+                                          Q_INDEX, NULL,
+                                          rt2800mmio_txdone_release_entries);
+
+               if (--max_tx_done == 0)
+                       break;
+       }
+
+       return !max_tx_done;
+}
+
+static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev,
+                                              struct rt2x00_field32 irq_field)
+{
+       u32 reg;
+
+       /*
+        * Enable a single interrupt. The interrupt mask register
+        * access needs locking.
+        */
+       spin_lock_irq(&rt2x00dev->irqmask_lock);
+       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+       rt2x00_set_field32(&reg, irq_field, 1);
+       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock_irq(&rt2x00dev->irqmask_lock);
+}
+
+void rt2800mmio_txstatus_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       if (rt2800mmio_txdone(rt2x00dev))
+               tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+
+       /*
+        * No need to enable the tx status interrupt here as we always
+        * leave it enabled to minimize the possibility of a tx status
+        * register overflow. See comment in interrupt handler.
+        */
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet);
+
+void rt2800mmio_pretbtt_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2x00lib_pretbtt(rt2x00dev);
+       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet);
+
+void rt2800mmio_tbtt_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u32 reg;
+
+       rt2x00lib_beacondone(rt2x00dev);
+
+       if (rt2x00dev->intf_ap_count) {
+               /*
+                * The rt2800pci hardware tbtt timer is off by 1us per tbtt
+                * causing beacon skew and as a result causing problems with
+                * some powersaving clients over time. Shorten the beacon
+                * interval every 64 beacons by 64us to mitigate this effect.
+                */
+               if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) {
+                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+                                          (rt2x00dev->beacon_int * 16) - 1);
+                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+               } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) {
+                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+                                          (rt2x00dev->beacon_int * 16));
+                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+               }
+               drv_data->tbtt_tick++;
+               drv_data->tbtt_tick %= BCN_TBTT_OFFSET;
+       }
+
+       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet);
+
+void rt2800mmio_rxdone_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       if (rt2x00mmio_rxdone(rt2x00dev))
+               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
+       else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet);
+
+void rt2800mmio_autowake_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2800mmio_wakeup(rt2x00dev);
+       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               rt2800mmio_enable_interrupt(rt2x00dev,
+                                           INT_MASK_CSR_AUTO_WAKEUP);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet);
+
+static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
+{
+       u32 status;
+       int i;
+
+       /*
+        * The TX_FIFO_STATUS interrupt needs special care. We should
+        * read TX_STA_FIFO but we should do it immediately as otherwise
+        * the register can overflow and we would lose status reports.
+        *
+        * Hence, read the TX_STA_FIFO register and copy all tx status
+        * reports into a kernel FIFO which is handled in the txstatus
+        * tasklet. We use a tasklet to process the tx status reports
+        * because we can schedule the tasklet multiple times (when the
+        * interrupt fires again during tx status processing).
+        *
+        * Furthermore we don't disable the TX_FIFO_STATUS
+        * interrupt here but leave it enabled so that the TX_STA_FIFO
+        * can also be read while the tx status tasklet gets executed.
+        *
+        * Since we have only one producer and one consumer we don't
+        * need to lock the kfifo.
+        */
+       for (i = 0; i < rt2x00dev->tx->limit; i++) {
+               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
+
+               if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
+                       break;
+
+               if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) {
+                       rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
+                       break;
+               }
+       }
+
+       /* Schedule the tasklet for processing the tx status. */
+       tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+}
+
+irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
+{
+       struct rt2x00_dev *rt2x00dev = dev_instance;
+       u32 reg, mask;
+
+       /* Read status and ACK all interrupts */
+       rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
+       rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+
+       if (!reg)
+               return IRQ_NONE;
+
+       if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+               return IRQ_HANDLED;
+
+       /*
+        * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits
+        * for interrupts and interrupt masks we can just use the value of
+        * INT_SOURCE_CSR to create the interrupt mask.
+        */
+       mask = ~reg;
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
+               rt2800mmio_txstatus_interrupt(rt2x00dev);
+               /*
+                * Never disable the TX_FIFO_STATUS interrupt.
+                */
+               rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1);
+       }
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
+               tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
+               tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
+               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
+
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
+               tasklet_schedule(&rt2x00dev->autowake_tasklet);
+
+       /*
+        * Disable all interrupts for which a tasklet was scheduled right now,
+        * the tasklet will reenable the appropriate interrupts.
+        */
+       spin_lock(&rt2x00dev->irqmask_lock);
+       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+       reg &= mask;
+       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock(&rt2x00dev->irqmask_lock);
+
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_interrupt);
+
+void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev,
+                          enum dev_state state)
+{
+       u32 reg;
+       unsigned long flags;
+
+       /*
+        * When interrupts are being enabled, the interrupt registers
+        * should clear the register to assure a clean state.
+        */
+       if (state == STATE_RADIO_IRQ_ON) {
+               rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
+               rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+       }
+
+       spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
+       reg = 0;
+       if (state == STATE_RADIO_IRQ_ON) {
+               rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
+               rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, 1);
+       }
+       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+
+       if (state == STATE_RADIO_IRQ_OFF) {
+               /*
+                * Wait for possibly running tasklets to finish.
+                */
+               tasklet_kill(&rt2x00dev->txstatus_tasklet);
+               tasklet_kill(&rt2x00dev->rxdone_tasklet);
+               tasklet_kill(&rt2x00dev->autowake_tasklet);
+               tasklet_kill(&rt2x00dev->tbtt_tasklet);
+               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq);
+
+/*
+ * Queue handlers.
+ */
+void rt2800mmio_start_queue(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       u32 reg;
+
+       switch (queue->qid) {
+       case QID_RX:
+               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
+               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+               break;
+       case QID_BEACON:
+               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
+               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
+               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 1);
+               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_start_queue);
+
+void rt2800mmio_kick_queue(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       struct queue_entry *entry;
+
+       switch (queue->qid) {
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               entry = rt2x00queue_get_entry(queue, Q_INDEX);
+               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid),
+                                         entry->entry_idx);
+               break;
+       case QID_MGMT:
+               entry = rt2x00queue_get_entry(queue, Q_INDEX);
+               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5),
+                                         entry->entry_idx);
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue);
+
+void rt2800mmio_stop_queue(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       u32 reg;
+
+       switch (queue->qid) {
+       case QID_RX:
+               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+               break;
+       case QID_BEACON:
+               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
+               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
+               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 0);
+               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
+
+               /*
+                * Wait for current invocation to finish. The tasklet
+                * won't be scheduled anymore afterwards since we disabled
+                * the TBTT and PRE TBTT timer.
+                */
+               tasklet_kill(&rt2x00dev->tbtt_tasklet);
+               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
+
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue);
+
+void rt2800mmio_queue_init(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       unsigned short txwi_size, rxwi_size;
+
+       rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
+
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->winfo_size = rxwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 64;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_queue_init);
+
+/*
+ * Initialization functions.
+ */
+bool rt2800mmio_get_entry_state(struct queue_entry *entry)
+{
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       u32 word;
+
+       if (entry->queue->qid == QID_RX) {
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+
+               return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE));
+       } else {
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+
+               return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE));
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state);
+
+void rt2800mmio_clear_entry(struct queue_entry *entry)
+{
+       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
+       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+       u32 word;
+
+       if (entry->queue->qid == QID_RX) {
+               rt2x00_desc_read(entry_priv->desc, 0, &word);
+               rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma);
+               rt2x00_desc_write(entry_priv->desc, 0, word);
+
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+               rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0);
+               rt2x00_desc_write(entry_priv->desc, 1, word);
+
+               /*
+                * Set RX IDX in register to inform hardware that we have
+                * handled this entry and it is available for reuse again.
+                */
+               rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
+                                         entry->entry_idx);
+       } else {
+               rt2x00_desc_read(entry_priv->desc, 1, &word);
+               rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1);
+               rt2x00_desc_write(entry_priv->desc, 1, word);
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry);
+
+int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev)
+{
+       struct queue_entry_priv_mmio *entry_priv;
+
+       /*
+        * Initialize registers.
+        */
+       entry_priv = rt2x00dev->tx[0].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0,
+                                 rt2x00dev->tx[0].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0);
+
+       entry_priv = rt2x00dev->tx[1].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1,
+                                 rt2x00dev->tx[1].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0);
+
+       entry_priv = rt2x00dev->tx[2].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2,
+                                 rt2x00dev->tx[2].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0);
+
+       entry_priv = rt2x00dev->tx[3].entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3,
+                                 rt2x00dev->tx[3].limit);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0);
+
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0);
+
+       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0);
+
+       entry_priv = rt2x00dev->rx->entries[0].priv_data;
+       rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR,
+                                 entry_priv->desc_dma);
+       rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT,
+                                 rt2x00dev->rx[0].limit);
+       rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
+                                 rt2x00dev->rx[0].limit - 1);
+       rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0);
+
+       rt2800_disable_wpdma(rt2x00dev);
+
+       rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_init_queues);
+
+int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev)
+{
+       u32 reg;
+
+       /*
+        * Reset DMA indexes
+        */
+       rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
+       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
+       rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
+
+       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
+       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
+
+       if (rt2x00_is_pcie(rt2x00dev) &&
+           (rt2x00_rt(rt2x00dev, RT3090) ||
+            rt2x00_rt(rt2x00dev, RT3390) ||
+            rt2x00_rt(rt2x00dev, RT3572) ||
+            rt2x00_rt(rt2x00dev, RT3593) ||
+            rt2x00_rt(rt2x00dev, RT5390) ||
+            rt2x00_rt(rt2x00dev, RT5392) ||
+            rt2x00_rt(rt2x00dev, RT5592))) {
+               rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, &reg);
+               rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
+               rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
+               rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg);
+       }
+
+       rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
+
+       reg = 0;
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
+       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_init_registers);
+
+/*
+ * Device state switch handlers.
+ */
+int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev)
+{
+       /* Wait for DMA, ignore error until we initialize queues. */
+       rt2800_wait_wpdma_ready(rt2x00dev);
+
+       if (unlikely(rt2800mmio_init_queues(rt2x00dev)))
+               return -EIO;
+
+       return rt2800_enable_radio(rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio);
+
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("rt2800 MMIO library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h
new file mode 100644 (file)
index 0000000..6a10de3
--- /dev/null
@@ -0,0 +1,165 @@
+/*     Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
+ *     Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ *     Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *     Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ *     Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ *     Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ *     Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ *     Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ *     <http://rt2x00.serialmonkey.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.
+ *
+ *     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.
+ */
+
+/*     Module: rt2800mmio
+ *     Abstract: forward declarations for the rt2800mmio module.
+ */
+
+#ifndef RT2800MMIO_H
+#define RT2800MMIO_H
+
+/*
+ * Queue register offset macros
+ */
+#define TX_QUEUE_REG_OFFSET    0x10
+#define TX_BASE_PTR(__x)       (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET))
+#define TX_MAX_CNT(__x)                (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET))
+#define TX_CTX_IDX(__x)                (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
+#define TX_DTX_IDX(__x)                (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
+
+/*
+ * DMA descriptor defines.
+ */
+#define TXD_DESC_SIZE                  (4 * sizeof(__le32))
+#define RXD_DESC_SIZE                  (4 * sizeof(__le32))
+
+/*
+ * TX descriptor format for TX, PRIO and Beacon Ring.
+ */
+
+/*
+ * Word0
+ */
+#define TXD_W0_SD_PTR0                 FIELD32(0xffffffff)
+
+/*
+ * Word1
+ */
+#define TXD_W1_SD_LEN1                 FIELD32(0x00003fff)
+#define TXD_W1_LAST_SEC1               FIELD32(0x00004000)
+#define TXD_W1_BURST                   FIELD32(0x00008000)
+#define TXD_W1_SD_LEN0                 FIELD32(0x3fff0000)
+#define TXD_W1_LAST_SEC0               FIELD32(0x40000000)
+#define TXD_W1_DMA_DONE                        FIELD32(0x80000000)
+
+/*
+ * Word2
+ */
+#define TXD_W2_SD_PTR1                 FIELD32(0xffffffff)
+
+/*
+ * Word3
+ * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI
+ * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler.
+ *       0:MGMT, 1:HCCA 2:EDCA
+ */
+#define TXD_W3_WIV                     FIELD32(0x01000000)
+#define TXD_W3_QSEL                    FIELD32(0x06000000)
+#define TXD_W3_TCO                     FIELD32(0x20000000)
+#define TXD_W3_UCO                     FIELD32(0x40000000)
+#define TXD_W3_ICO                     FIELD32(0x80000000)
+
+/*
+ * RX descriptor format for RX Ring.
+ */
+
+/*
+ * Word0
+ */
+#define RXD_W0_SDP0                    FIELD32(0xffffffff)
+
+/*
+ * Word1
+ */
+#define RXD_W1_SDL1                    FIELD32(0x00003fff)
+#define RXD_W1_SDL0                    FIELD32(0x3fff0000)
+#define RXD_W1_LS0                     FIELD32(0x40000000)
+#define RXD_W1_DMA_DONE                        FIELD32(0x80000000)
+
+/*
+ * Word2
+ */
+#define RXD_W2_SDP1                    FIELD32(0xffffffff)
+
+/*
+ * Word3
+ * AMSDU: RX with 802.3 header, not 802.11 header.
+ * DECRYPTED: This frame is being decrypted.
+ */
+#define RXD_W3_BA                      FIELD32(0x00000001)
+#define RXD_W3_DATA                    FIELD32(0x00000002)
+#define RXD_W3_NULLDATA                        FIELD32(0x00000004)
+#define RXD_W3_FRAG                    FIELD32(0x00000008)
+#define RXD_W3_UNICAST_TO_ME           FIELD32(0x00000010)
+#define RXD_W3_MULTICAST               FIELD32(0x00000020)
+#define RXD_W3_BROADCAST               FIELD32(0x00000040)
+#define RXD_W3_MY_BSS                  FIELD32(0x00000080)
+#define RXD_W3_CRC_ERROR               FIELD32(0x00000100)
+#define RXD_W3_CIPHER_ERROR            FIELD32(0x00000600)
+#define RXD_W3_AMSDU                   FIELD32(0x00000800)
+#define RXD_W3_HTC                     FIELD32(0x00001000)
+#define RXD_W3_RSSI                    FIELD32(0x00002000)
+#define RXD_W3_L2PAD                   FIELD32(0x00004000)
+#define RXD_W3_AMPDU                   FIELD32(0x00008000)
+#define RXD_W3_DECRYPTED               FIELD32(0x00010000)
+#define RXD_W3_PLCP_SIGNAL             FIELD32(0x00020000)
+#define RXD_W3_PLCP_RSSI               FIELD32(0x00040000)
+
+/* TX descriptor initialization */
+__le32 *rt2800mmio_get_txwi(struct queue_entry *entry);
+void rt2800mmio_write_tx_desc(struct queue_entry *entry,
+                             struct txentry_desc *txdesc);
+
+/* RX control handlers */
+void rt2800mmio_fill_rxdone(struct queue_entry *entry,
+                           struct rxdone_entry_desc *rxdesc);
+
+/* Interrupt functions */
+void rt2800mmio_txstatus_tasklet(unsigned long data);
+void rt2800mmio_pretbtt_tasklet(unsigned long data);
+void rt2800mmio_tbtt_tasklet(unsigned long data);
+void rt2800mmio_rxdone_tasklet(unsigned long data);
+void rt2800mmio_autowake_tasklet(unsigned long data);
+irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance);
+void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev,
+                          enum dev_state state);
+
+/* Queue handlers */
+void rt2800mmio_start_queue(struct data_queue *queue);
+void rt2800mmio_kick_queue(struct data_queue *queue);
+void rt2800mmio_stop_queue(struct data_queue *queue);
+void rt2800mmio_queue_init(struct data_queue *queue);
+
+/* Initialization functions */
+bool rt2800mmio_get_entry_state(struct queue_entry *entry);
+void rt2800mmio_clear_entry(struct queue_entry *entry);
+int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev);
+int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev);
+
+/* Device state switch handlers. */
+int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev);
+
+#endif /* RT2800MMIO_H */
index f8f2abb..b504455 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/platform_device.h>
 #include <linux/eeprom_93cx6.h>
 
 #include "rt2x00.h"
 #include "rt2x00mmio.h"
 #include "rt2x00pci.h"
-#include "rt2x00soc.h"
 #include "rt2800lib.h"
+#include "rt2800mmio.h"
 #include "rt2800.h"
 #include "rt2800pci.h"
 
@@ -90,27 +89,6 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token)
        rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);
 }
 
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
-{
-       void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
-
-       if (!base_addr)
-               return -ENOMEM;
-
-       memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
-
-       iounmap(base_addr);
-       return 0;
-}
-#else
-static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
-{
-       return -ENOMEM;
-}
-#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */
-
-#ifdef CONFIG_PCI
 static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
 {
        struct rt2x00_dev *rt2x00dev = eeprom->data;
@@ -183,112 +161,6 @@ static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 {
        return rt2800_read_eeprom_efuse(rt2x00dev);
 }
-#else
-static inline int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
-{
-       return 0;
-}
-
-static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
-{
-       return -EOPNOTSUPP;
-}
-#endif /* CONFIG_PCI */
-
-/*
- * Queue handlers.
- */
-static void rt2800pci_start_queue(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       u32 reg;
-
-       switch (queue->qid) {
-       case QID_RX:
-               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
-               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
-               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-               break;
-       case QID_BEACON:
-               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-
-               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
-               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 1);
-               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
-               break;
-       default:
-               break;
-       }
-}
-
-static void rt2800pci_kick_queue(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       struct queue_entry *entry;
-
-       switch (queue->qid) {
-       case QID_AC_VO:
-       case QID_AC_VI:
-       case QID_AC_BE:
-       case QID_AC_BK:
-               entry = rt2x00queue_get_entry(queue, Q_INDEX);
-               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid),
-                                         entry->entry_idx);
-               break;
-       case QID_MGMT:
-               entry = rt2x00queue_get_entry(queue, Q_INDEX);
-               rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5),
-                                         entry->entry_idx);
-               break;
-       default:
-               break;
-       }
-}
-
-static void rt2800pci_stop_queue(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       u32 reg;
-
-       switch (queue->qid) {
-       case QID_RX:
-               rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
-               rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
-               rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-               break;
-       case QID_BEACON:
-               rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
-               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
-               rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-
-               rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, &reg);
-               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 0);
-               rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg);
-
-               /*
-                * Wait for current invocation to finish. The tasklet
-                * won't be scheduled anymore afterwards since we disabled
-                * the TBTT and PRE TBTT timer.
-                */
-               tasklet_kill(&rt2x00dev->tbtt_tasklet);
-               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
-
-               break;
-       default:
-               break;
-       }
-}
 
 /*
  * Firmware functions
@@ -331,218 +203,14 @@ static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev,
        return 0;
 }
 
-/*
- * Initialization functions.
- */
-static bool rt2800pci_get_entry_state(struct queue_entry *entry)
-{
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       u32 word;
-
-       if (entry->queue->qid == QID_RX) {
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-
-               return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE));
-       } else {
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-
-               return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE));
-       }
-}
-
-static void rt2800pci_clear_entry(struct queue_entry *entry)
-{
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
-       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       u32 word;
-
-       if (entry->queue->qid == QID_RX) {
-               rt2x00_desc_read(entry_priv->desc, 0, &word);
-               rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma);
-               rt2x00_desc_write(entry_priv->desc, 0, word);
-
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-               rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0);
-               rt2x00_desc_write(entry_priv->desc, 1, word);
-
-               /*
-                * Set RX IDX in register to inform hardware that we have
-                * handled this entry and it is available for reuse again.
-                */
-               rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
-                                         entry->entry_idx);
-       } else {
-               rt2x00_desc_read(entry_priv->desc, 1, &word);
-               rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1);
-               rt2x00_desc_write(entry_priv->desc, 1, word);
-       }
-}
-
-static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev)
-{
-       struct queue_entry_priv_mmio *entry_priv;
-
-       /*
-        * Initialize registers.
-        */
-       entry_priv = rt2x00dev->tx[0].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0,
-                                 rt2x00dev->tx[0].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0);
-
-       entry_priv = rt2x00dev->tx[1].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1,
-                                 rt2x00dev->tx[1].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0);
-
-       entry_priv = rt2x00dev->tx[2].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2,
-                                 rt2x00dev->tx[2].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0);
-
-       entry_priv = rt2x00dev->tx[3].entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3,
-                                 rt2x00dev->tx[3].limit);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0);
-
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0);
-
-       rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0);
-       rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0);
-
-       entry_priv = rt2x00dev->rx->entries[0].priv_data;
-       rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR,
-                                 entry_priv->desc_dma);
-       rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT,
-                                 rt2x00dev->rx[0].limit);
-       rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX,
-                                 rt2x00dev->rx[0].limit - 1);
-       rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0);
-
-       rt2800_disable_wpdma(rt2x00dev);
-
-       rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0);
-
-       return 0;
-}
-
 /*
  * Device state switch handlers.
  */
-static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
-                                enum dev_state state)
-{
-       u32 reg;
-       unsigned long flags;
-
-       /*
-        * When interrupts are being enabled, the interrupt registers
-        * should clear the register to assure a clean state.
-        */
-       if (state == STATE_RADIO_IRQ_ON) {
-               rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
-               rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
-       }
-
-       spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
-       reg = 0;
-       if (state == STATE_RADIO_IRQ_ON) {
-               rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, 1);
-       }
-       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
-       spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
-
-       if (state == STATE_RADIO_IRQ_OFF) {
-               /*
-                * Wait for possibly running tasklets to finish.
-                */
-               tasklet_kill(&rt2x00dev->txstatus_tasklet);
-               tasklet_kill(&rt2x00dev->rxdone_tasklet);
-               tasklet_kill(&rt2x00dev->autowake_tasklet);
-               tasklet_kill(&rt2x00dev->tbtt_tasklet);
-               tasklet_kill(&rt2x00dev->pretbtt_tasklet);
-       }
-}
-
-static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev)
-{
-       u32 reg;
-
-       /*
-        * Reset DMA indexes
-        */
-       rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
-       rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
-
-       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
-       rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
-
-       if (rt2x00_is_pcie(rt2x00dev) &&
-           (rt2x00_rt(rt2x00dev, RT3090) ||
-            rt2x00_rt(rt2x00dev, RT3390) ||
-            rt2x00_rt(rt2x00dev, RT3572) ||
-            rt2x00_rt(rt2x00dev, RT3593) ||
-            rt2x00_rt(rt2x00dev, RT5390) ||
-            rt2x00_rt(rt2x00dev, RT5392) ||
-            rt2x00_rt(rt2x00dev, RT5592))) {
-               rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, &reg);
-               rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
-               rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
-               rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg);
-       }
-
-       rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
-
-       reg = 0;
-       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
-       rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
-       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
-       rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
-
-       return 0;
-}
-
 static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
 
-       /* Wait for DMA, ignore error until we initialize queues. */
-       rt2800_wait_wpdma_ready(rt2x00dev);
-
-       if (unlikely(rt2800pci_init_queues(rt2x00dev)))
-               return -EIO;
-
-       retval = rt2800_enable_radio(rt2x00dev);
+       retval = rt2800mmio_enable_radio(rt2x00dev);
        if (retval)
                return retval;
 
@@ -559,15 +227,6 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
        return retval;
 }
 
-static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev)
-{
-       if (rt2x00_is_soc(rt2x00dev)) {
-               rt2800_disable_radio(rt2x00dev);
-               rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0);
-               rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0);
-       }
-}
-
 static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev,
                               enum dev_state state)
 {
@@ -601,12 +260,11 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
                 * After the radio has been disabled, the device should
                 * be put to sleep for powersaving.
                 */
-               rt2800pci_disable_radio(rt2x00dev);
                rt2800pci_set_state(rt2x00dev, STATE_SLEEP);
                break;
        case STATE_RADIO_IRQ_ON:
        case STATE_RADIO_IRQ_OFF:
-               rt2800pci_toggle_irq(rt2x00dev, state);
+               rt2800mmio_toggle_irq(rt2x00dev, state);
                break;
        case STATE_DEEP_SLEEP:
        case STATE_SLEEP:
@@ -626,470 +284,6 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
        return retval;
 }
 
-/*
- * TX descriptor initialization
- */
-static __le32 *rt2800pci_get_txwi(struct queue_entry *entry)
-{
-       return (__le32 *) entry->skb->data;
-}
-
-static void rt2800pci_write_tx_desc(struct queue_entry *entry,
-                                   struct txentry_desc *txdesc)
-{
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       __le32 *txd = entry_priv->desc;
-       u32 word;
-       const unsigned int txwi_size = entry->queue->winfo_size;
-
-       /*
-        * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1
-        * must contains a TXWI structure + 802.11 header + padding + 802.11
-        * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and
-        * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11
-        * data. It means that LAST_SEC0 is always 0.
-        */
-
-       /*
-        * Initialize TX descriptor
-        */
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma);
-       rt2x00_desc_write(txd, 0, word);
-
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len);
-       rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
-                          !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W1_BURST,
-                          test_bit(ENTRY_TXD_BURST, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size);
-       rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0);
-       rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
-       rt2x00_desc_write(txd, 1, word);
-
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
-                          skbdesc->skb_dma + txwi_size);
-       rt2x00_desc_write(txd, 2, word);
-
-       word = 0;
-       rt2x00_set_field32(&word, TXD_W3_WIV,
-                          !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W3_QSEL, 2);
-       rt2x00_desc_write(txd, 3, word);
-
-       /*
-        * Register descriptor details in skb frame descriptor.
-        */
-       skbdesc->desc = txd;
-       skbdesc->desc_len = TXD_DESC_SIZE;
-}
-
-/*
- * RX control handlers
- */
-static void rt2800pci_fill_rxdone(struct queue_entry *entry,
-                                 struct rxdone_entry_desc *rxdesc)
-{
-       struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
-       __le32 *rxd = entry_priv->desc;
-       u32 word;
-
-       rt2x00_desc_read(rxd, 3, &word);
-
-       if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR))
-               rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
-
-       /*
-        * Unfortunately we don't know the cipher type used during
-        * decryption. This prevents us from correct providing
-        * correct statistics through debugfs.
-        */
-       rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR);
-
-       if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) {
-               /*
-                * Hardware has stripped IV/EIV data from 802.11 frame during
-                * decryption. Unfortunately the descriptor doesn't contain
-                * any fields with the EIV/IV data either, so they can't
-                * be restored by rt2x00lib.
-                */
-               rxdesc->flags |= RX_FLAG_IV_STRIPPED;
-
-               /*
-                * The hardware has already checked the Michael Mic and has
-                * stripped it from the frame. Signal this to mac80211.
-                */
-               rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
-
-               if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
-                       rxdesc->flags |= RX_FLAG_DECRYPTED;
-               else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
-                       rxdesc->flags |= RX_FLAG_MMIC_ERROR;
-       }
-
-       if (rt2x00_get_field32(word, RXD_W3_MY_BSS))
-               rxdesc->dev_flags |= RXDONE_MY_BSS;
-
-       if (rt2x00_get_field32(word, RXD_W3_L2PAD))
-               rxdesc->dev_flags |= RXDONE_L2PAD;
-
-       /*
-        * Process the RXWI structure that is at the start of the buffer.
-        */
-       rt2800_process_rxwi(entry, rxdesc);
-}
-
-/*
- * Interrupt functions.
- */
-static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
-{
-       struct ieee80211_conf conf = { .flags = 0 };
-       struct rt2x00lib_conf libconf = { .conf = &conf };
-
-       rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
-}
-
-static bool rt2800pci_txdone_entry_check(struct queue_entry *entry, u32 status)
-{
-       __le32 *txwi;
-       u32 word;
-       int wcid, tx_wcid;
-
-       wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
-
-       txwi = rt2800_drv_get_txwi(entry);
-       rt2x00_desc_read(txwi, 1, &word);
-       tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
-
-       return (tx_wcid == wcid);
-}
-
-static bool rt2800pci_txdone_find_entry(struct queue_entry *entry, void *data)
-{
-       u32 status = *(u32 *)data;
-
-       /*
-        * rt2800pci hardware might reorder frames when exchanging traffic
-        * with multiple BA enabled STAs.
-        *
-        * For example, a tx queue
-        *    [ STA1 | STA2 | STA1 | STA2 ]
-        * can result in tx status reports
-        *    [ STA1 | STA1 | STA2 | STA2 ]
-        * when the hw decides to aggregate the frames for STA1 into one AMPDU.
-        *
-        * To mitigate this effect, associate the tx status to the first frame
-        * in the tx queue with a matching wcid.
-        */
-       if (rt2800pci_txdone_entry_check(entry, status) &&
-           !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
-               /*
-                * Got a matching frame, associate the tx status with
-                * the frame
-                */
-               entry->status = status;
-               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
-               return true;
-       }
-
-       /* Check the next frame */
-       return false;
-}
-
-static bool rt2800pci_txdone_match_first(struct queue_entry *entry, void *data)
-{
-       u32 status = *(u32 *)data;
-
-       /*
-        * Find the first frame without tx status and assign this status to it
-        * regardless if it matches or not.
-        */
-       if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
-               /*
-                * Got a matching frame, associate the tx status with
-                * the frame
-                */
-               entry->status = status;
-               set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
-               return true;
-       }
-
-       /* Check the next frame */
-       return false;
-}
-static bool rt2800pci_txdone_release_entries(struct queue_entry *entry,
-                                            void *data)
-{
-       if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
-               rt2800_txdone_entry(entry, entry->status,
-                                   rt2800pci_get_txwi(entry));
-               return false;
-       }
-
-       /* No more frames to release */
-       return true;
-}
-
-static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
-{
-       struct data_queue *queue;
-       u32 status;
-       u8 qid;
-       int max_tx_done = 16;
-
-       while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) {
-               qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE);
-               if (unlikely(qid >= QID_RX)) {
-                       /*
-                        * Unknown queue, this shouldn't happen. Just drop
-                        * this tx status.
-                        */
-                       rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n",
-                                   qid);
-                       break;
-               }
-
-               queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
-               if (unlikely(queue == NULL)) {
-                       /*
-                        * The queue is NULL, this shouldn't happen. Stop
-                        * processing here and drop the tx status
-                        */
-                       rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n",
-                                   qid);
-                       break;
-               }
-
-               if (unlikely(rt2x00queue_empty(queue))) {
-                       /*
-                        * The queue is empty. Stop processing here
-                        * and drop the tx status.
-                        */
-                       rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
-                                   qid);
-                       break;
-               }
-
-               /*
-                * Let's associate this tx status with the first
-                * matching frame.
-                */
-               if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
-                                               Q_INDEX, &status,
-                                               rt2800pci_txdone_find_entry)) {
-                       /*
-                        * We cannot match the tx status to any frame, so just
-                        * use the first one.
-                        */
-                       if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
-                                                       Q_INDEX, &status,
-                                                       rt2800pci_txdone_match_first)) {
-                               rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n",
-                                           qid);
-                               break;
-                       }
-               }
-
-               /*
-                * Release all frames with a valid tx status.
-                */
-               rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
-                                          Q_INDEX, NULL,
-                                          rt2800pci_txdone_release_entries);
-
-               if (--max_tx_done == 0)
-                       break;
-       }
-
-       return !max_tx_done;
-}
-
-static inline void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev,
-                                             struct rt2x00_field32 irq_field)
-{
-       u32 reg;
-
-       /*
-        * Enable a single interrupt. The interrupt mask register
-        * access needs locking.
-        */
-       spin_lock_irq(&rt2x00dev->irqmask_lock);
-       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-       rt2x00_set_field32(&reg, irq_field, 1);
-       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
-       spin_unlock_irq(&rt2x00dev->irqmask_lock);
-}
-
-static void rt2800pci_txstatus_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       if (rt2800pci_txdone(rt2x00dev))
-               tasklet_schedule(&rt2x00dev->txstatus_tasklet);
-
-       /*
-        * No need to enable the tx status interrupt here as we always
-        * leave it enabled to minimize the possibility of a tx status
-        * register overflow. See comment in interrupt handler.
-        */
-}
-
-static void rt2800pci_pretbtt_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       rt2x00lib_pretbtt(rt2x00dev);
-       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
-}
-
-static void rt2800pci_tbtt_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
-       u32 reg;
-
-       rt2x00lib_beacondone(rt2x00dev);
-
-       if (rt2x00dev->intf_ap_count) {
-               /*
-                * The rt2800pci hardware tbtt timer is off by 1us per tbtt
-                * causing beacon skew and as a result causing problems with
-                * some powersaving clients over time. Shorten the beacon
-                * interval every 64 beacons by 64us to mitigate this effect.
-                */
-               if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) {
-                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-                                          (rt2x00dev->beacon_int * 16) - 1);
-                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-               } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) {
-                       rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-                       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-                                          (rt2x00dev->beacon_int * 16));
-                       rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-               }
-               drv_data->tbtt_tick++;
-               drv_data->tbtt_tick %= BCN_TBTT_OFFSET;
-       }
-
-       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
-}
-
-static void rt2800pci_rxdone_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       if (rt2x00mmio_rxdone(rt2x00dev))
-               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
-       else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE);
-}
-
-static void rt2800pci_autowake_tasklet(unsigned long data)
-{
-       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
-       rt2800pci_wakeup(rt2x00dev);
-       if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP);
-}
-
-static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
-{
-       u32 status;
-       int i;
-
-       /*
-        * The TX_FIFO_STATUS interrupt needs special care. We should
-        * read TX_STA_FIFO but we should do it immediately as otherwise
-        * the register can overflow and we would lose status reports.
-        *
-        * Hence, read the TX_STA_FIFO register and copy all tx status
-        * reports into a kernel FIFO which is handled in the txstatus
-        * tasklet. We use a tasklet to process the tx status reports
-        * because we can schedule the tasklet multiple times (when the
-        * interrupt fires again during tx status processing).
-        *
-        * Furthermore we don't disable the TX_FIFO_STATUS
-        * interrupt here but leave it enabled so that the TX_STA_FIFO
-        * can also be read while the tx status tasklet gets executed.
-        *
-        * Since we have only one producer and one consumer we don't
-        * need to lock the kfifo.
-        */
-       for (i = 0; i < rt2x00dev->tx->limit; i++) {
-               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
-
-               if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
-                       break;
-
-               if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) {
-                       rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
-                       break;
-               }
-       }
-
-       /* Schedule the tasklet for processing the tx status. */
-       tasklet_schedule(&rt2x00dev->txstatus_tasklet);
-}
-
-static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
-{
-       struct rt2x00_dev *rt2x00dev = dev_instance;
-       u32 reg, mask;
-
-       /* Read status and ACK all interrupts */
-       rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
-       rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
-
-       if (!reg)
-               return IRQ_NONE;
-
-       if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-               return IRQ_HANDLED;
-
-       /*
-        * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits
-        * for interrupts and interrupt masks we can just use the value of
-        * INT_SOURCE_CSR to create the interrupt mask.
-        */
-       mask = ~reg;
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
-               rt2800pci_txstatus_interrupt(rt2x00dev);
-               /*
-                * Never disable the TX_FIFO_STATUS interrupt.
-                */
-               rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1);
-       }
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
-               tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
-               tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
-               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
-
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
-               tasklet_schedule(&rt2x00dev->autowake_tasklet);
-
-       /*
-        * Disable all interrupts for which a tasklet was scheduled right now,
-        * the tasklet will reenable the appropriate interrupts.
-        */
-       spin_lock(&rt2x00dev->irqmask_lock);
-       rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-       reg &= mask;
-       rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg);
-       spin_unlock(&rt2x00dev->irqmask_lock);
-
-       return IRQ_HANDLED;
-}
-
 /*
  * Device probe functions.
  */
@@ -1097,9 +291,7 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
 
-       if (rt2x00_is_soc(rt2x00dev))
-               retval = rt2800pci_read_eeprom_soc(rt2x00dev);
-       else if (rt2800pci_efuse_detect(rt2x00dev))
+       if (rt2800pci_efuse_detect(rt2x00dev))
                retval = rt2800pci_read_eeprom_efuse(rt2x00dev);
        else
                retval = rt2800pci_read_eeprom_pci(rt2x00dev);
@@ -1145,25 +337,25 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
        .read_eeprom            = rt2800pci_read_eeprom,
        .hwcrypt_disabled       = rt2800pci_hwcrypt_disabled,
        .drv_write_firmware     = rt2800pci_write_firmware,
-       .drv_init_registers     = rt2800pci_init_registers,
-       .drv_get_txwi           = rt2800pci_get_txwi,
+       .drv_init_registers     = rt2800mmio_init_registers,
+       .drv_get_txwi           = rt2800mmio_get_txwi,
 };
 
 static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
-       .irq_handler            = rt2800pci_interrupt,
-       .txstatus_tasklet       = rt2800pci_txstatus_tasklet,
-       .pretbtt_tasklet        = rt2800pci_pretbtt_tasklet,
-       .tbtt_tasklet           = rt2800pci_tbtt_tasklet,
-       .rxdone_tasklet         = rt2800pci_rxdone_tasklet,
-       .autowake_tasklet       = rt2800pci_autowake_tasklet,
+       .irq_handler            = rt2800mmio_interrupt,
+       .txstatus_tasklet       = rt2800mmio_txstatus_tasklet,
+       .pretbtt_tasklet        = rt2800mmio_pretbtt_tasklet,
+       .tbtt_tasklet           = rt2800mmio_tbtt_tasklet,
+       .rxdone_tasklet         = rt2800mmio_rxdone_tasklet,
+       .autowake_tasklet       = rt2800mmio_autowake_tasklet,
        .probe_hw               = rt2800_probe_hw,
        .get_firmware_name      = rt2800pci_get_firmware_name,
        .check_firmware         = rt2800_check_firmware,
        .load_firmware          = rt2800_load_firmware,
        .initialize             = rt2x00mmio_initialize,
        .uninitialize           = rt2x00mmio_uninitialize,
-       .get_entry_state        = rt2800pci_get_entry_state,
-       .clear_entry            = rt2800pci_clear_entry,
+       .get_entry_state        = rt2800mmio_get_entry_state,
+       .clear_entry            = rt2800mmio_clear_entry,
        .set_device_state       = rt2800pci_set_device_state,
        .rfkill_poll            = rt2800_rfkill_poll,
        .link_stats             = rt2800_link_stats,
@@ -1171,15 +363,15 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .link_tuner             = rt2800_link_tuner,
        .gain_calibration       = rt2800_gain_calibration,
        .vco_calibration        = rt2800_vco_calibration,
-       .start_queue            = rt2800pci_start_queue,
-       .kick_queue             = rt2800pci_kick_queue,
-       .stop_queue             = rt2800pci_stop_queue,
+       .start_queue            = rt2800mmio_start_queue,
+       .kick_queue             = rt2800mmio_kick_queue,
+       .stop_queue             = rt2800mmio_stop_queue,
        .flush_queue            = rt2x00mmio_flush_queue,
-       .write_tx_desc          = rt2800pci_write_tx_desc,
+       .write_tx_desc          = rt2800mmio_write_tx_desc,
        .write_tx_data          = rt2800_write_tx_data,
        .write_beacon           = rt2800_write_beacon,
        .clear_beacon           = rt2800_clear_beacon,
-       .fill_rxdone            = rt2800pci_fill_rxdone,
+       .fill_rxdone            = rt2800mmio_fill_rxdone,
        .config_shared_key      = rt2800_config_shared_key,
        .config_pairwise_key    = rt2800_config_pairwise_key,
        .config_filter          = rt2800_config_filter,
@@ -1191,49 +383,6 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static void rt2800pci_queue_init(struct data_queue *queue)
-{
-       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
-       unsigned short txwi_size, rxwi_size;
-
-       rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
-
-       switch (queue->qid) {
-       case QID_RX:
-               queue->limit = 128;
-               queue->data_size = AGGREGATION_SIZE;
-               queue->desc_size = RXD_DESC_SIZE;
-               queue->winfo_size = rxwi_size;
-               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
-               break;
-
-       case QID_AC_VO:
-       case QID_AC_VI:
-       case QID_AC_BE:
-       case QID_AC_BK:
-               queue->limit = 64;
-               queue->data_size = AGGREGATION_SIZE;
-               queue->desc_size = TXD_DESC_SIZE;
-               queue->winfo_size = txwi_size;
-               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
-               break;
-
-       case QID_BEACON:
-               queue->limit = 8;
-               queue->data_size = 0; /* No DMA required for beacons */
-               queue->desc_size = TXD_DESC_SIZE;
-               queue->winfo_size = txwi_size;
-               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
-               break;
-
-       case QID_ATIM:
-               /* fallthrough */
-       default:
-               BUG();
-               break;
-       }
-}
-
 static const struct rt2x00_ops rt2800pci_ops = {
        .name                   = KBUILD_MODNAME,
        .drv_data_size          = sizeof(struct rt2800_drv_data),
@@ -1241,7 +390,7 @@ static const struct rt2x00_ops rt2800pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .queue_init             = rt2800pci_queue_init,
+       .queue_init             = rt2800mmio_queue_init,
        .lib                    = &rt2800pci_rt2x00_ops,
        .drv                    = &rt2800pci_rt2800_ops,
        .hw                     = &rt2800pci_mac80211_ops,
@@ -1253,7 +402,6 @@ static const struct rt2x00_ops rt2800pci_ops = {
 /*
  * RT2800pci module information.
  */
-#ifdef CONFIG_PCI
 static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
        { PCI_DEVICE(0x1814, 0x0601) },
        { PCI_DEVICE(0x1814, 0x0681) },
@@ -1298,38 +446,15 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
 #endif
        { 0, }
 };
-#endif /* CONFIG_PCI */
 
 MODULE_AUTHOR(DRV_PROJECT);
 MODULE_VERSION(DRV_VERSION);
 MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver.");
 MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards");
-#ifdef CONFIG_PCI
 MODULE_FIRMWARE(FIRMWARE_RT2860);
 MODULE_DEVICE_TABLE(pci, rt2800pci_device_table);
-#endif /* CONFIG_PCI */
 MODULE_LICENSE("GPL");
 
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-static int rt2800soc_probe(struct platform_device *pdev)
-{
-       return rt2x00soc_probe(pdev, &rt2800pci_ops);
-}
-
-static struct platform_driver rt2800soc_driver = {
-       .driver         = {
-               .name           = "rt2800_wmac",
-               .owner          = THIS_MODULE,
-               .mod_name       = KBUILD_MODNAME,
-       },
-       .probe          = rt2800soc_probe,
-       .remove         = rt2x00soc_remove,
-       .suspend        = rt2x00soc_suspend,
-       .resume         = rt2x00soc_resume,
-};
-#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */
-
-#ifdef CONFIG_PCI
 static int rt2800pci_probe(struct pci_dev *pci_dev,
                           const struct pci_device_id *id)
 {
@@ -1344,39 +469,5 @@ static struct pci_driver rt2800pci_driver = {
        .suspend        = rt2x00pci_suspend,
        .resume         = rt2x00pci_resume,
 };
-#endif /* CONFIG_PCI */
-
-static int __init rt2800pci_init(void)
-{
-       int ret = 0;
-
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-       ret = platform_driver_register(&rt2800soc_driver);
-       if (ret)
-               return ret;
-#endif
-#ifdef CONFIG_PCI
-       ret = pci_register_driver(&rt2800pci_driver);
-       if (ret) {
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-               platform_driver_unregister(&rt2800soc_driver);
-#endif
-               return ret;
-       }
-#endif
-
-       return ret;
-}
-
-static void __exit rt2800pci_exit(void)
-{
-#ifdef CONFIG_PCI
-       pci_unregister_driver(&rt2800pci_driver);
-#endif
-#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)
-       platform_driver_unregister(&rt2800soc_driver);
-#endif
-}
 
-module_init(rt2800pci_init);
-module_exit(rt2800pci_exit);
+module_pci_driver(rt2800pci_driver);
index ab22a08..a81c9ee 100644 (file)
 #ifndef RT2800PCI_H
 #define RT2800PCI_H
 
-/*
- * Queue register offset macros
- */
-#define TX_QUEUE_REG_OFFSET            0x10
-#define TX_BASE_PTR(__x)               (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET))
-#define TX_MAX_CNT(__x)                        (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET))
-#define TX_CTX_IDX(__x)                        (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
-#define TX_DTX_IDX(__x)                        (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET))
-
 /*
  * 8051 firmware image.
  */
 #define FIRMWARE_RT3290                        "rt3290.bin"
 #define FIRMWARE_IMAGE_BASE            0x2000
 
-/*
- * DMA descriptor defines.
- */
-#define TXD_DESC_SIZE                  (4 * sizeof(__le32))
-#define RXD_DESC_SIZE                  (4 * sizeof(__le32))
-
-/*
- * TX descriptor format for TX, PRIO and Beacon Ring.
- */
-
-/*
- * Word0
- */
-#define TXD_W0_SD_PTR0                 FIELD32(0xffffffff)
-
-/*
- * Word1
- */
-#define TXD_W1_SD_LEN1                 FIELD32(0x00003fff)
-#define TXD_W1_LAST_SEC1               FIELD32(0x00004000)
-#define TXD_W1_BURST                   FIELD32(0x00008000)
-#define TXD_W1_SD_LEN0                 FIELD32(0x3fff0000)
-#define TXD_W1_LAST_SEC0               FIELD32(0x40000000)
-#define TXD_W1_DMA_DONE                        FIELD32(0x80000000)
-
-/*
- * Word2
- */
-#define TXD_W2_SD_PTR1                 FIELD32(0xffffffff)
-
-/*
- * Word3
- * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI
- * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler.
- *       0:MGMT, 1:HCCA 2:EDCA
- */
-#define TXD_W3_WIV                     FIELD32(0x01000000)
-#define TXD_W3_QSEL                    FIELD32(0x06000000)
-#define TXD_W3_TCO                     FIELD32(0x20000000)
-#define TXD_W3_UCO                     FIELD32(0x40000000)
-#define TXD_W3_ICO                     FIELD32(0x80000000)
-
-/*
- * RX descriptor format for RX Ring.
- */
-
-/*
- * Word0
- */
-#define RXD_W0_SDP0                    FIELD32(0xffffffff)
-
-/*
- * Word1
- */
-#define RXD_W1_SDL1                    FIELD32(0x00003fff)
-#define RXD_W1_SDL0                    FIELD32(0x3fff0000)
-#define RXD_W1_LS0                     FIELD32(0x40000000)
-#define RXD_W1_DMA_DONE                        FIELD32(0x80000000)
-
-/*
- * Word2
- */
-#define RXD_W2_SDP1                    FIELD32(0xffffffff)
-
-/*
- * Word3
- * AMSDU: RX with 802.3 header, not 802.11 header.
- * DECRYPTED: This frame is being decrypted.
- */
-#define RXD_W3_BA                      FIELD32(0x00000001)
-#define RXD_W3_DATA                    FIELD32(0x00000002)
-#define RXD_W3_NULLDATA                        FIELD32(0x00000004)
-#define RXD_W3_FRAG                    FIELD32(0x00000008)
-#define RXD_W3_UNICAST_TO_ME           FIELD32(0x00000010)
-#define RXD_W3_MULTICAST               FIELD32(0x00000020)
-#define RXD_W3_BROADCAST               FIELD32(0x00000040)
-#define RXD_W3_MY_BSS                  FIELD32(0x00000080)
-#define RXD_W3_CRC_ERROR               FIELD32(0x00000100)
-#define RXD_W3_CIPHER_ERROR            FIELD32(0x00000600)
-#define RXD_W3_AMSDU                   FIELD32(0x00000800)
-#define RXD_W3_HTC                     FIELD32(0x00001000)
-#define RXD_W3_RSSI                    FIELD32(0x00002000)
-#define RXD_W3_L2PAD                   FIELD32(0x00004000)
-#define RXD_W3_AMPDU                   FIELD32(0x00008000)
-#define RXD_W3_DECRYPTED               FIELD32(0x00010000)
-#define RXD_W3_PLCP_SIGNAL             FIELD32(0x00020000)
-#define RXD_W3_PLCP_RSSI               FIELD32(0x00040000)
-
 #endif /* RT2800PCI_H */
diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c
new file mode 100644 (file)
index 0000000..1359227
--- /dev/null
@@ -0,0 +1,263 @@
+/*     Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
+ *     Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ *     Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *     Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ *     Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ *     Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ *     Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ *     Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ *     <http://rt2x00.serialmonkey.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.
+ *
+ *     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.
+ */
+
+/*     Module: rt2800soc
+ *     Abstract: rt2800 WiSoC specific routines.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "rt2x00.h"
+#include "rt2x00mmio.h"
+#include "rt2x00soc.h"
+#include "rt2800.h"
+#include "rt2800lib.h"
+#include "rt2800mmio.h"
+
+/* Allow hardware encryption to be disabled. */
+static bool modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev)
+{
+       return modparam_nohwcrypt;
+}
+
+static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_disable_radio(rt2x00dev);
+       rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+       rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0);
+}
+
+static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev,
+                                     enum dev_state state)
+{
+       int retval = 0;
+
+       switch (state) {
+       case STATE_RADIO_ON:
+               retval = rt2800mmio_enable_radio(rt2x00dev);
+               break;
+
+       case STATE_RADIO_OFF:
+               rt2800soc_disable_radio(rt2x00dev);
+               break;
+
+       case STATE_RADIO_IRQ_ON:
+       case STATE_RADIO_IRQ_OFF:
+               rt2800mmio_toggle_irq(rt2x00dev, state);
+               break;
+
+       case STATE_DEEP_SLEEP:
+       case STATE_SLEEP:
+       case STATE_STANDBY:
+       case STATE_AWAKE:
+               /* These states are not supported, but don't report an error */
+               retval = 0;
+               break;
+
+       default:
+               retval = -ENOTSUPP;
+               break;
+       }
+
+       if (unlikely(retval))
+               rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n",
+                          state, retval);
+
+       return retval;
+}
+
+static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+       void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
+
+       if (!base_addr)
+               return -ENOMEM;
+
+       memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
+
+       iounmap(base_addr);
+       return 0;
+}
+
+/* Firmware functions */
+static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev)
+{
+       WARN_ON_ONCE(1);
+       return NULL;
+}
+
+static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev,
+                                  const u8 *data, const size_t len)
+{
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev,
+                                   const u8 *data, const size_t len)
+{
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev,
+                                   const u8 *data, const size_t len)
+{
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+static const struct ieee80211_ops rt2800soc_mac80211_ops = {
+       .tx                     = rt2x00mac_tx,
+       .start                  = rt2x00mac_start,
+       .stop                   = rt2x00mac_stop,
+       .add_interface          = rt2x00mac_add_interface,
+       .remove_interface       = rt2x00mac_remove_interface,
+       .config                 = rt2x00mac_config,
+       .configure_filter       = rt2x00mac_configure_filter,
+       .set_key                = rt2x00mac_set_key,
+       .sw_scan_start          = rt2x00mac_sw_scan_start,
+       .sw_scan_complete       = rt2x00mac_sw_scan_complete,
+       .get_stats              = rt2x00mac_get_stats,
+       .get_tkip_seq           = rt2800_get_tkip_seq,
+       .set_rts_threshold      = rt2800_set_rts_threshold,
+       .sta_add                = rt2x00mac_sta_add,
+       .sta_remove             = rt2x00mac_sta_remove,
+       .bss_info_changed       = rt2x00mac_bss_info_changed,
+       .conf_tx                = rt2800_conf_tx,
+       .get_tsf                = rt2800_get_tsf,
+       .rfkill_poll            = rt2x00mac_rfkill_poll,
+       .ampdu_action           = rt2800_ampdu_action,
+       .flush                  = rt2x00mac_flush,
+       .get_survey             = rt2800_get_survey,
+       .get_ringparam          = rt2x00mac_get_ringparam,
+       .tx_frames_pending      = rt2x00mac_tx_frames_pending,
+};
+
+static const struct rt2800_ops rt2800soc_rt2800_ops = {
+       .register_read          = rt2x00mmio_register_read,
+       .register_read_lock     = rt2x00mmio_register_read, /* same for SoCs */
+       .register_write         = rt2x00mmio_register_write,
+       .register_write_lock    = rt2x00mmio_register_write, /* same for SoCs */
+       .register_multiread     = rt2x00mmio_register_multiread,
+       .register_multiwrite    = rt2x00mmio_register_multiwrite,
+       .regbusy_read           = rt2x00mmio_regbusy_read,
+       .read_eeprom            = rt2800soc_read_eeprom,
+       .hwcrypt_disabled       = rt2800soc_hwcrypt_disabled,
+       .drv_write_firmware     = rt2800soc_write_firmware,
+       .drv_init_registers     = rt2800mmio_init_registers,
+       .drv_get_txwi           = rt2800mmio_get_txwi,
+};
+
+static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = {
+       .irq_handler            = rt2800mmio_interrupt,
+       .txstatus_tasklet       = rt2800mmio_txstatus_tasklet,
+       .pretbtt_tasklet        = rt2800mmio_pretbtt_tasklet,
+       .tbtt_tasklet           = rt2800mmio_tbtt_tasklet,
+       .rxdone_tasklet         = rt2800mmio_rxdone_tasklet,
+       .autowake_tasklet       = rt2800mmio_autowake_tasklet,
+       .probe_hw               = rt2800_probe_hw,
+       .get_firmware_name      = rt2800soc_get_firmware_name,
+       .check_firmware         = rt2800soc_check_firmware,
+       .load_firmware          = rt2800soc_load_firmware,
+       .initialize             = rt2x00mmio_initialize,
+       .uninitialize           = rt2x00mmio_uninitialize,
+       .get_entry_state        = rt2800mmio_get_entry_state,
+       .clear_entry            = rt2800mmio_clear_entry,
+       .set_device_state       = rt2800soc_set_device_state,
+       .rfkill_poll            = rt2800_rfkill_poll,
+       .link_stats             = rt2800_link_stats,
+       .reset_tuner            = rt2800_reset_tuner,
+       .link_tuner             = rt2800_link_tuner,
+       .gain_calibration       = rt2800_gain_calibration,
+       .vco_calibration        = rt2800_vco_calibration,
+       .start_queue            = rt2800mmio_start_queue,
+       .kick_queue             = rt2800mmio_kick_queue,
+       .stop_queue             = rt2800mmio_stop_queue,
+       .flush_queue            = rt2x00mmio_flush_queue,
+       .write_tx_desc          = rt2800mmio_write_tx_desc,
+       .write_tx_data          = rt2800_write_tx_data,
+       .write_beacon           = rt2800_write_beacon,
+       .clear_beacon           = rt2800_clear_beacon,
+       .fill_rxdone            = rt2800mmio_fill_rxdone,
+       .config_shared_key      = rt2800_config_shared_key,
+       .config_pairwise_key    = rt2800_config_pairwise_key,
+       .config_filter          = rt2800_config_filter,
+       .config_intf            = rt2800_config_intf,
+       .config_erp             = rt2800_config_erp,
+       .config_ant             = rt2800_config_ant,
+       .config                 = rt2800_config,
+       .sta_add                = rt2800_sta_add,
+       .sta_remove             = rt2800_sta_remove,
+};
+
+static const struct rt2x00_ops rt2800soc_ops = {
+       .name                   = KBUILD_MODNAME,
+       .drv_data_size          = sizeof(struct rt2800_drv_data),
+       .max_ap_intf            = 8,
+       .eeprom_size            = EEPROM_SIZE,
+       .rf_size                = RF_SIZE,
+       .tx_queues              = NUM_TX_QUEUES,
+       .queue_init             = rt2800mmio_queue_init,
+       .lib                    = &rt2800soc_rt2x00_ops,
+       .drv                    = &rt2800soc_rt2800_ops,
+       .hw                     = &rt2800soc_mac80211_ops,
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+       .debugfs                = &rt2800_rt2x00debug,
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+};
+
+static int rt2800soc_probe(struct platform_device *pdev)
+{
+       return rt2x00soc_probe(pdev, &rt2800soc_ops);
+}
+
+static struct platform_driver rt2800soc_driver = {
+       .driver         = {
+               .name           = "rt2800_wmac",
+               .owner          = THIS_MODULE,
+               .mod_name       = KBUILD_MODNAME,
+       },
+       .probe          = rt2800soc_probe,
+       .remove         = rt2x00soc_remove,
+       .suspend        = rt2x00soc_suspend,
+       .resume         = rt2x00soc_resume,
+};
+
+module_platform_driver(rt2800soc_driver);
+
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver.");
+MODULE_LICENSE("GPL");
index 4feb35a..997df03 100644 (file)
@@ -1180,6 +1180,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Linksys */
        { USB_DEVICE(0x13b1, 0x002f) },
        { USB_DEVICE(0x1737, 0x0079) },
+       /* Logitec */
+       { USB_DEVICE(0x0789, 0x0170) },
        /* Ralink */
        { USB_DEVICE(0x148f, 0x3572) },
        /* Sitecom */
@@ -1203,6 +1205,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x050d, 0x1103) },
        /* Cameo */
        { USB_DEVICE(0x148f, 0xf301) },
+       /* D-Link */
+       { USB_DEVICE(0x2001, 0x3c1f) },
        /* Edimax */
        { USB_DEVICE(0x7392, 0x7733) },
        /* Hawking */
@@ -1216,6 +1220,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x0789, 0x016b) },
        /* NETGEAR */
        { USB_DEVICE(0x0846, 0x9012) },
+       { USB_DEVICE(0x0846, 0x9013) },
        { USB_DEVICE(0x0846, 0x9019) },
        /* Planex */
        { USB_DEVICE(0x2019, 0xed19) },
@@ -1224,6 +1229,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Sitecom */
        { USB_DEVICE(0x0df6, 0x0067) },
        { USB_DEVICE(0x0df6, 0x006a) },
+       { USB_DEVICE(0x0df6, 0x006e) },
        /* ZyXEL */
        { USB_DEVICE(0x0586, 0x3421) },
 #endif
@@ -1240,6 +1246,9 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x2001, 0x3c1c) },
        { USB_DEVICE(0x2001, 0x3c1d) },
        { USB_DEVICE(0x2001, 0x3c1e) },
+       { USB_DEVICE(0x2001, 0x3c20) },
+       { USB_DEVICE(0x2001, 0x3c22) },
+       { USB_DEVICE(0x2001, 0x3c23) },
        /* LG innotek */
        { USB_DEVICE(0x043e, 0x7a22) },
        { USB_DEVICE(0x043e, 0x7a42) },
@@ -1262,12 +1271,17 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x043e, 0x7a32) },
        /* AVM GmbH */
        { USB_DEVICE(0x057c, 0x8501) },
-       /* D-Link DWA-160-B2 */
+       /* Buffalo */
+       { USB_DEVICE(0x0411, 0x0241) },
+       /* D-Link */
        { USB_DEVICE(0x2001, 0x3c1a) },
+       { USB_DEVICE(0x2001, 0x3c21) },
        /* Proware */
        { USB_DEVICE(0x043e, 0x7a13) },
        /* Ralink */
        { USB_DEVICE(0x148f, 0x5572) },
+       /* TRENDnet */
+       { USB_DEVICE(0x20f4, 0x724a) },
 #endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
@@ -1337,6 +1351,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x1d4d, 0x0010) },
        /* Planex */
        { USB_DEVICE(0x2019, 0xab24) },
+       { USB_DEVICE(0x2019, 0xab29) },
        /* Qcom */
        { USB_DEVICE(0x18e8, 0x6259) },
        /* RadioShack */
index fe4c572..e4ba2ce 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/input-polldev.h>
 #include <linux/kfifo.h>
 #include <linux/hrtimer.h>
+#include <linux/average.h>
 
 #include <net/mac80211.h>
 
 #define SHORT_EIFS             ( SIFS + SHORT_DIFS + \
                                  GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) )
 
-/*
- * Structure for average calculation
- * The avg field contains the actual average value,
- * but avg_weight is internally used during calculations
- * to prevent rounding errors.
- */
-struct avg_val {
-       int avg;
-       int avg_weight;
-};
-
 enum rt2x00_chip_intf {
        RT2X00_CHIP_INTF_PCI,
        RT2X00_CHIP_INTF_PCIE,
@@ -297,7 +287,7 @@ struct link_ant {
         * Similar to the avg_rssi in the link_qual structure
         * this value is updated by using the walking average.
         */
-       struct avg_val rssi_ant;
+       struct ewma rssi_ant;
 };
 
 /*
@@ -326,7 +316,7 @@ struct link {
        /*
         * Currently active average RSSI value
         */
-       struct avg_val avg_rssi;
+       struct ewma avg_rssi;
 
        /*
         * Work structure for scheduling periodic link tuning.
@@ -1179,6 +1169,93 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev)
        return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
 }
 
+/* Helpers for capability flags */
+
+static inline bool
+rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev,
+                   enum rt2x00_capability_flags cap_flag)
+{
+       return test_bit(cap_flag, &rt2x00dev->cap_flags);
+}
+
+static inline bool
+rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO);
+}
+
+static inline bool
+rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT);
+}
+
+static inline bool
+rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS);
+}
+
+static inline bool
+rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL);
+}
+
+static inline bool
+rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT);
+}
+
+static inline bool
+rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING);
+}
+
+static inline bool
+rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE);
+}
+
+static inline bool
+rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE);
+}
+
+static inline bool
+rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A);
+}
+
+static inline bool
+rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG);
+}
+
+static inline bool
+rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA);
+}
+
+static inline bool
+rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST);
+}
+
+static inline bool
+rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev)
+{
+       return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION);
+}
+
 /**
  * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
  * @entry: Pointer to &struct queue_entry
index 1ca4c7f..3db0d99 100644 (file)
@@ -52,7 +52,7 @@ void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
 
-       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key)
+       if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key)
                return;
 
        __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags);
@@ -80,7 +80,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
        struct ieee80211_key_conf *key = tx_info->control.hw_key;
        unsigned int overhead = 0;
 
-       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key)
+       if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key)
                return overhead;
 
        /*
index fe7a7f6..7f7baae 100644 (file)
@@ -750,7 +750,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
                                intf, &rt2x00debug_fop_queue_stats);
 
 #ifdef CONFIG_RT2X00_LIB_CRYPTO
-       if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_hw_crypto(rt2x00dev))
                intf->crypto_stats_entry =
                    debugfs_create_file("crypto", S_IRUGO, intf->queue_folder,
                                        intf, &rt2x00debug_fop_crypto_stats);
index 712eea9..080b1fc 100644 (file)
@@ -88,7 +88,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
        rt2x00queue_start_queues(rt2x00dev);
        rt2x00link_start_tuner(rt2x00dev);
        rt2x00link_start_agc(rt2x00dev);
-       if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
                rt2x00link_start_vcocal(rt2x00dev);
 
        /*
@@ -113,7 +113,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
         * Stop all queues
         */
        rt2x00link_stop_agc(rt2x00dev);
-       if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
                rt2x00link_stop_vcocal(rt2x00dev);
        rt2x00link_stop_tuner(rt2x00dev);
        rt2x00queue_stop_queues(rt2x00dev);
@@ -234,7 +234,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
         * here as they will fetch the next beacon directly prior to
         * transmission.
         */
-       if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev))
                return;
 
        /* fetch next beacon */
@@ -358,7 +358,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
         * mac80211 will expect the same data to be present it the
         * frame as it was passed to us.
         */
-       if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_hw_crypto(rt2x00dev))
                rt2x00crypto_tx_insert_iv(entry->skb, header_length);
 
        /*
index a093598..7f40ab8 100644 (file)
@@ -146,7 +146,7 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length);
  * @local: frame is not from mac80211
  */
 int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
-                              bool local);
+                              struct ieee80211_sta *sta, bool local);
 
 /**
  * rt2x00queue_update_beacon - Send new beacon from mac80211
index 8368aab..c2b3b66 100644 (file)
  */
 #define DEFAULT_RSSI           -128
 
-/*
- * Helper struct and macro to work with moving/walking averages.
- * When adding a value to the average value the following calculation
- * is needed:
- *
- *        avg_rssi = ((avg_rssi * 7) + rssi) / 8;
- *
- * The advantage of this approach is that we only need 1 variable
- * to store the average in (No need for a count and a total).
- * But more importantly, normal average values will over time
- * move less and less towards newly added values this results
- * that with link tuning, the device can have a very good RSSI
- * for a few minutes but when the device is moved away from the AP
- * the average will not decrease fast enough to compensate.
- * The walking average compensates this and will move towards
- * the new values correctly allowing a effective link tuning,
- * the speed of the average moving towards other values depends
- * on the value for the number of samples. The higher the number
- * of samples, the slower the average will move.
- * We use two variables to keep track of the average value to
- * compensate for the rounding errors. This can be a significant
- * error (>5dBm) if the factor is too low.
- */
-#define AVG_SAMPLES    8
-#define AVG_FACTOR     1000
-#define MOVING_AVERAGE(__avg, __val) \
-({ \
-       struct avg_val __new; \
-       __new.avg_weight = \
-           (__avg).avg_weight  ? \
-               ((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \
-                 ((__val) * (AVG_FACTOR))) / \
-                (AVG_SAMPLES)) : \
-               ((__val) * (AVG_FACTOR)); \
-       __new.avg = __new.avg_weight / (AVG_FACTOR); \
-       __new; \
-})
+/* Constants for EWMA calculations. */
+#define RT2X00_EWMA_FACTOR     1024
+#define RT2X00_EWMA_WEIGHT     8
+
+static inline int rt2x00link_get_avg_rssi(struct ewma *ewma)
+{
+       unsigned long avg;
+
+       avg = ewma_read(ewma);
+       if (avg)
+               return -avg;
+
+       return DEFAULT_RSSI;
+}
 
 static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
 {
        struct link_ant *ant = &rt2x00dev->link.ant;
 
-       if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success)
-               return ant->rssi_ant.avg;
+       if (rt2x00dev->link.qual.rx_success)
+               return rt2x00link_get_avg_rssi(&ant->rssi_ant);
+
        return DEFAULT_RSSI;
 }
 
@@ -100,8 +78,8 @@ static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev,
 
 static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)
 {
-       rt2x00dev->link.ant.rssi_ant.avg = 0;
-       rt2x00dev->link.ant.rssi_ant.avg_weight = 0;
+       ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR,
+                 RT2X00_EWMA_WEIGHT);
 }
 
 static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev)
@@ -249,12 +227,12 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
        /*
         * Update global RSSI
         */
-       link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi);
+       ewma_add(&link->avg_rssi, -rxdesc->rssi);
 
        /*
         * Update antenna RSSI
         */
-       ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi);
+       ewma_add(&ant->rssi_ant, -rxdesc->rssi);
 }
 
 void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
@@ -309,6 +287,8 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
         */
        rt2x00dev->link.count = 0;
        memset(qual, 0, sizeof(*qual));
+       ewma_init(&rt2x00dev->link.avg_rssi, RT2X00_EWMA_FACTOR,
+                 RT2X00_EWMA_WEIGHT);
 
        /*
         * Restore the VGC level as stored in the registers,
@@ -363,17 +343,17 @@ static void rt2x00link_tuner(struct work_struct *work)
         * collect the RSSI data we could use this. Otherwise we
         * must fallback to the default RSSI value.
         */
-       if (!link->avg_rssi.avg || !qual->rx_success)
+       if (!qual->rx_success)
                qual->rssi = DEFAULT_RSSI;
        else
-               qual->rssi = link->avg_rssi.avg;
+               qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi);
 
        /*
         * Check if link tuning is supported by the hardware, some hardware
         * do not support link tuning at all, while other devices can disable
         * the feature from the EEPROM.
         */
-       if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_link_tuning(rt2x00dev))
                rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
 
        /*
@@ -513,7 +493,7 @@ static void rt2x00link_vcocal(struct work_struct *work)
 void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
 {
        INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
-       if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
+       if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
                INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal);
        INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
        INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
index ba1de86..2183e79 100644 (file)
@@ -90,7 +90,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
                                  frag_skb->data, data_length, tx_info,
                                  (struct ieee80211_rts *)(skb->data));
 
-       retval = rt2x00queue_write_tx_frame(queue, skb, true);
+       retval = rt2x00queue_write_tx_frame(queue, skb, NULL, true);
        if (retval) {
                dev_kfree_skb_any(skb);
                rt2x00_warn(rt2x00dev, "Failed to send RTS/CTS frame\n");
@@ -151,7 +151,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw,
                        goto exit_fail;
        }
 
-       if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false)))
+       if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false)))
                goto exit_fail;
 
        /*
@@ -382,11 +382,11 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw,
         * of different types, but has no a separate filter for PS Poll frames,
         * FIF_CONTROL flag implies FIF_PSPOLL.
         */
-       if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) {
+       if (!rt2x00_has_cap_control_filters(rt2x00dev)) {
                if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL)
                        *total_flags |= FIF_CONTROL | FIF_PSPOLL;
        }
-       if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) {
+       if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) {
                if (*total_flags & FIF_CONTROL)
                        *total_flags |= FIF_PSPOLL;
        }
@@ -469,7 +469,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
                return 0;
 
-       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+       if (!rt2x00_has_cap_hw_crypto(rt2x00dev))
                return -EOPNOTSUPP;
 
        /*
index dc49e52..25da20e 100644 (file)
@@ -119,7 +119,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
        rt2x00dev->ops = ops;
        rt2x00dev->hw = hw;
        rt2x00dev->irq = pci_dev->irq;
-       rt2x00dev->name = pci_name(pci_dev);
+       rt2x00dev->name = ops->name;
 
        if (pci_is_pcie(pci_dev))
                rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE);
index 6c8a33b..a5d38e8 100644 (file)
@@ -61,7 +61,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp)
         * at least 8 bytes bytes available in headroom for IV/EIV
         * and 8 bytes for ICV data as tailroon.
         */
-       if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) {
+       if (rt2x00_has_cap_hw_crypto(rt2x00dev)) {
                head_size += 8;
                tail_size += 8;
        }
@@ -635,7 +635,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry)
 }
 
 int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
-                              bool local)
+                              struct ieee80211_sta *sta, bool local)
 {
        struct ieee80211_tx_info *tx_info;
        struct queue_entry *entry;
@@ -649,7 +649,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
         * after that we are free to use the skb->cb array
         * for our information.
         */
-       rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, NULL);
+       rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, sta);
 
        /*
         * All information is retrieved from the skb->cb array,
@@ -1033,38 +1033,21 @@ EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue);
 
 void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
 {
-       bool started;
        bool tx_queue =
                (queue->qid == QID_AC_VO) ||
                (queue->qid == QID_AC_VI) ||
                (queue->qid == QID_AC_BE) ||
                (queue->qid == QID_AC_BK);
 
-       mutex_lock(&queue->status_lock);
 
        /*
-        * If the queue has been started, we must stop it temporarily
-        * to prevent any new frames to be queued on the device. If
-        * we are not dropping the pending frames, the queue must
-        * only be stopped in the software and not the hardware,
-        * otherwise the queue will never become empty on its own.
+        * If we are not supposed to drop any pending
+        * frames, this means we must force a start (=kick)
+        * to the queue to make sure the hardware will
+        * start transmitting.
         */
-       started = test_bit(QUEUE_STARTED, &queue->flags);
-       if (started) {
-               /*
-                * Pause the queue
-                */
-               rt2x00queue_pause_queue(queue);
-
-               /*
-                * If we are not supposed to drop any pending
-                * frames, this means we must force a start (=kick)
-                * to the queue to make sure the hardware will
-                * start transmitting.
-                */
-               if (!drop && tx_queue)
-                       queue->rt2x00dev->ops->lib->kick_queue(queue);
-       }
+       if (!drop && tx_queue)
+               queue->rt2x00dev->ops->lib->kick_queue(queue);
 
        /*
         * Check if driver supports flushing, if that is the case we can
@@ -1080,14 +1063,6 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
        if (unlikely(!rt2x00queue_empty(queue)))
                rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n",
                            queue->qid);
-
-       /*
-        * Restore the queue to the previous status
-        */
-       if (started)
-               rt2x00queue_unpause_queue(queue);
-
-       mutex_unlock(&queue->status_lock);
 }
 EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue);
 
index 8828987..4e12162 100644 (file)
@@ -523,7 +523,9 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
        rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n",
                    queue->qid);
 
+       rt2x00queue_stop_queue(queue);
        rt2x00queue_flush_queue(queue, true);
+       rt2x00queue_start_queue(queue);
 }
 
 static int rt2x00usb_dma_timeout(struct data_queue *queue)
index 54d3ddf..a5b69cb 100644 (file)
@@ -685,7 +685,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
 
        rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529));
        rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
-                         !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags));
+                         !rt2x00_has_cap_frame_type(rt2x00dev));
 
        /*
         * Configure the RX antenna.
@@ -813,10 +813,10 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
                sel = antenna_sel_a;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
        } else {
                sel = antenna_sel_bg;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_bg(rt2x00dev);
        }
 
        for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
@@ -836,7 +836,7 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
        else if (rt2x00_rf(rt2x00dev, RF2527))
                rt61pci_config_antenna_2x(rt2x00dev, ant);
        else if (rt2x00_rf(rt2x00dev, RF2529)) {
-               if (test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_double_antenna(rt2x00dev))
                        rt61pci_config_antenna_2x(rt2x00dev, ant);
                else
                        rt61pci_config_antenna_2529(rt2x00dev, ant);
@@ -850,13 +850,13 @@ static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev,
        short lna_gain = 0;
 
        if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
                        lna_gain += 14;
 
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
                lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
        } else {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev))
                        lna_gain += 14;
 
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
@@ -1054,14 +1054,14 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev,
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
                low_bound = 0x28;
                up_bound = 0x48;
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
                        low_bound += 0x10;
                        up_bound += 0x10;
                }
        } else {
                low_bound = 0x20;
                up_bound = 0x40;
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
                        low_bound += 0x10;
                        up_bound += 0x10;
                }
@@ -2578,7 +2578,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
         * eeprom word.
         */
        if (rt2x00_rf(rt2x00dev, RF2529) &&
-           !test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) {
+           !rt2x00_has_cap_double_antenna(rt2x00dev)) {
                rt2x00dev->default_ant.rx =
                    ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED);
                rt2x00dev->default_ant.tx =
@@ -2793,7 +2793,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (!test_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags)) {
+       if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) {
                spec->num_channels = 14;
                spec->channels = rf_vals_noseq;
        } else {
index 1d3880e..1baf9c8 100644 (file)
@@ -595,8 +595,8 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
        switch (ant->rx) {
        case ANTENNA_HW_DIVERSITY:
                rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2);
-               temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)
-                      && (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
+               temp = !rt2x00_has_cap_frame_type(rt2x00dev) &&
+                      (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
                rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp);
                break;
        case ANTENNA_A:
@@ -636,7 +636,7 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
 
        rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0);
        rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
-                         !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags));
+                         !rt2x00_has_cap_frame_type(rt2x00dev));
 
        /*
         * Configure the RX antenna.
@@ -709,10 +709,10 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev,
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
                sel = antenna_sel_a;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
        } else {
                sel = antenna_sel_bg;
-               lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags);
+               lna = rt2x00_has_cap_external_lna_bg(rt2x00dev);
        }
 
        for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
@@ -740,7 +740,7 @@ static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev,
        short lna_gain = 0;
 
        if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
                        lna_gain += 14;
 
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
@@ -930,7 +930,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
                low_bound = 0x28;
                up_bound = 0x48;
 
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
                        low_bound += 0x10;
                        up_bound += 0x10;
                }
@@ -946,7 +946,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
                        up_bound = 0x1c;
                }
 
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
                        low_bound += 0x14;
                        up_bound += 0x10;
                }
@@ -1661,7 +1661,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
        }
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
-               if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
+               if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
                        if (lna == 3 || lna == 2)
                                offset += 10;
                } else {
index fc207b2..a91506b 100644 (file)
@@ -1122,7 +1122,6 @@ static int rtl8180_probe(struct pci_dev *pdev,
        iounmap(priv->map);
 
  err_free_dev:
-       pci_set_drvdata(pdev, NULL);
        ieee80211_free_hw(dev);
 
  err_free_reg:
index 8bb4a9a..ff78407 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <linux/ip.h>
 #include <linux/module.h>
+#include <linux/udp.h>
 
 /*
  *NOTICE!!!: This file will be very big, we should
@@ -1074,64 +1075,52 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
        if (!ieee80211_is_data(fc))
                return false;
 
+       ip = (const struct iphdr *)(skb->data + mac_hdr_len +
+                                   SNAP_SIZE + PROTOC_TYPE_SIZE);
+       ether_type = be16_to_cpup((__be16 *)
+                                 (skb->data + mac_hdr_len + SNAP_SIZE));
 
-       ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len +
-                             SNAP_SIZE + PROTOC_TYPE_SIZE);
-       ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE);
-       /*      ether_type = ntohs(ether_type); */
-
-       if (ETH_P_IP == ether_type) {
-               if (IPPROTO_UDP == ip->protocol) {
-                       struct udphdr *udp = (struct udphdr *)((u8 *) ip +
-                                                              (ip->ihl << 2));
-                       if (((((u8 *) udp)[1] == 68) &&
-                            (((u8 *) udp)[3] == 67)) ||
-                           ((((u8 *) udp)[1] == 67) &&
-                            (((u8 *) udp)[3] == 68))) {
-                               /*
-                                * 68 : UDP BOOTP client
-                                * 67 : UDP BOOTP server
-                                */
-                               RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV),
-                                        DBG_DMESG, "dhcp %s !!\n",
-                                        is_tx ? "Tx" : "Rx");
-
-                               if (is_tx) {
-                                       rtlpriv->enter_ps = false;
-                                       schedule_work(&rtlpriv->
-                                                     works.lps_change_work);
-                                       ppsc->last_delaylps_stamp_jiffies =
-                                           jiffies;
-                               }
+       switch (ether_type) {
+       case ETH_P_IP: {
+               struct udphdr *udp;
+               u16 src;
+               u16 dst;
 
-                               return true;
-                       }
-               }
-       } else if (ETH_P_ARP == ether_type) {
-               if (is_tx) {
-                       rtlpriv->enter_ps = false;
-                       schedule_work(&rtlpriv->works.lps_change_work);
-                       ppsc->last_delaylps_stamp_jiffies = jiffies;
-               }
+               if (ip->protocol != IPPROTO_UDP)
+                       return false;
+               udp = (struct udphdr *)((u8 *)ip + (ip->ihl << 2));
+               src = be16_to_cpu(udp->source);
+               dst = be16_to_cpu(udp->dest);
+
+               /* If this case involves port 68 (UDP BOOTP client) connecting
+                * with port 67 (UDP BOOTP server), then return true so that
+                * the lowest speed is used.
+                */
+               if (!((src == 68 && dst == 67) || (src == 67 && dst == 68)))
+                       return false;
 
-               return true;
-       } else if (ETH_P_PAE == ether_type) {
+               RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
+                        "dhcp %s !!\n", is_tx ? "Tx" : "Rx");
+               break;
+       }
+       case ETH_P_ARP:
+               break;
+       case ETH_P_PAE:
                RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
                         "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx");
-
-               if (is_tx) {
-                       rtlpriv->enter_ps = false;
-                       schedule_work(&rtlpriv->works.lps_change_work);
-                       ppsc->last_delaylps_stamp_jiffies = jiffies;
-               }
-
-               return true;
-       } else if (ETH_P_IPV6 == ether_type) {
-               /* IPv6 */
-               return true;
+               break;
+       case ETH_P_IPV6:
+               /* TODO: Is this right? */
+               return false;
+       default:
+               return false;
        }
-
-       return false;
+       if (is_tx) {
+               rtlpriv->enter_ps = false;
+               schedule_work(&rtlpriv->works.lps_change_work);
+               ppsc->last_delaylps_stamp_jiffies = jiffies;
+       }
+       return true;
 }
 EXPORT_SYMBOL_GPL(rtl_is_special_data);
 
@@ -1613,6 +1602,35 @@ err_free:
 }
 EXPORT_SYMBOL(rtl_send_smps_action);
 
+void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       enum io_type iotype;
+
+       if (!is_hal_stop(rtlhal)) {
+               switch (operation) {
+               case SCAN_OPT_BACKUP:
+                       iotype = IO_CMD_PAUSE_DM_BY_SCAN;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                                     HW_VAR_IO_CMD,
+                                                     (u8 *)&iotype);
+                       break;
+               case SCAN_OPT_RESTORE:
+                       iotype = IO_CMD_RESUME_DM_BY_SCAN;
+                       rtlpriv->cfg->ops->set_hw_reg(hw,
+                                                     HW_VAR_IO_CMD,
+                                                     (u8 *)&iotype);
+                       break;
+               default:
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                                "Unknown Scan Backup operation.\n");
+                       break;
+               }
+       }
+}
+EXPORT_SYMBOL(rtl_phy_scan_operation_backup);
+
 /* There seem to be issues in mac80211 regarding when del ba frames can be
  * received. As a work around, we make a fake del_ba if we receive a ba_req;
  * however, rx_agg was opened to let mac80211 release some ba related
index 0e5fe09..0cd0742 100644 (file)
@@ -114,7 +114,6 @@ void rtl_init_rfkill(struct ieee80211_hw *hw);
 void rtl_deinit_rfkill(struct ieee80211_hw *hw);
 
 void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb);
-void rtl_watch_dog_timer_callback(unsigned long data);
 void rtl_deinit_deferred_work(struct ieee80211_hw *hw);
 
 bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
@@ -153,5 +152,6 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw,
 bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb);
 struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw,
                                u8 *sa, u8 *bssid, u16 tid);
+void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation);
 
 #endif
index 35e0008..0105e6c 100644 (file)
 #define        CAM_CONFIG_USEDK                                1
 #define        CAM_CONFIG_NO_USEDK                             0
 
-extern void rtl_cam_reset_all_entry(struct ieee80211_hw *hw);
-extern u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr,
-                       u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg,
-                       u32 ul_default_key, u8 *key_content);
+void rtl_cam_reset_all_entry(struct ieee80211_hw *hw);
+u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr,
+                        u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg,
+                        u32 ul_default_key, u8 *key_content);
 int rtl_cam_delete_one_entry(struct ieee80211_hw *hw, u8 *mac_addr,
-                       u32 ul_key_id);
+                            u32 ul_key_id);
 void rtl_cam_mark_invalid(struct ieee80211_hw *hw, u8 uc_index);
 void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index);
 void rtl_cam_reset_sec_info(struct ieee80211_hw *hw);
index 733b7ce..210ce7c 100644 (file)
@@ -115,7 +115,7 @@ static void rtl_op_stop(struct ieee80211_hw *hw)
        mutex_lock(&rtlpriv->locks.conf_mutex);
 
        mac->link_state = MAC80211_NOLINK;
-       memset(mac->bssid, 0, 6);
+       memset(mac->bssid, 0, ETH_ALEN);
        mac->vendor = PEER_UNKNOWN;
 
        /*reset sec info */
@@ -280,7 +280,7 @@ static void rtl_op_remove_interface(struct ieee80211_hw *hw,
        mac->p2p = 0;
        mac->vif = NULL;
        mac->link_state = MAC80211_NOLINK;
-       memset(mac->bssid, 0, 6);
+       memset(mac->bssid, 0, ETH_ALEN);
        mac->vendor = PEER_UNKNOWN;
        mac->opmode = NL80211_IFTYPE_UNSPECIFIED;
        rtlpriv->cfg->ops->set_network_type(hw, mac->opmode);
@@ -721,7 +721,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
                        mac->link_state = MAC80211_LINKED;
                        mac->cnt_after_linked = 0;
                        mac->assoc_id = bss_conf->aid;
-                       memcpy(mac->bssid, bss_conf->bssid, 6);
+                       memcpy(mac->bssid, bss_conf->bssid, ETH_ALEN);
 
                        if (rtlpriv->cfg->ops->linked_set_reg)
                                rtlpriv->cfg->ops->linked_set_reg(hw);
@@ -750,7 +750,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
                        if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
                                rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
                        mac->link_state = MAC80211_NOLINK;
-                       memset(mac->bssid, 0, 6);
+                       memset(mac->bssid, 0, ETH_ALEN);
                        mac->vendor = PEER_UNKNOWN;
 
                        if (rtlpriv->dm.supp_phymode_switch) {
@@ -826,7 +826,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
                         bss_conf->bssid);
 
                mac->vendor = PEER_UNKNOWN;
-               memcpy(mac->bssid, bss_conf->bssid, 6);
+               memcpy(mac->bssid, bss_conf->bssid, ETH_ALEN);
                rtlpriv->cfg->ops->set_network_type(hw, vif->type);
 
                rcu_read_lock();
index 838a1ed..2ffc729 100644 (file)
@@ -262,9 +262,9 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf)
                            sizeof(u8), GFP_ATOMIC);
        if (!efuse_tbl)
                return;
-       efuse_word = kmalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC);
+       efuse_word = kzalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC);
        if (!efuse_word)
-               goto done;
+               goto out;
        for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
                efuse_word[i] = kmalloc(efuse_max_section * sizeof(u16),
                                        GFP_ATOMIC);
@@ -378,6 +378,7 @@ done:
        for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++)
                kfree(efuse_word[i]);
        kfree(efuse_word);
+out:
        kfree(efuse_tbl);
 }
 
@@ -1203,20 +1204,18 @@ static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate)
 
 static u16 efuse_get_current_size(struct ieee80211_hw *hw)
 {
-       int continual = true;
        u16 efuse_addr = 0;
        u8 hworden;
        u8 efuse_data, word_cnts;
 
-       while (continual && efuse_one_byte_read(hw, efuse_addr, &efuse_data)
-              && (efuse_addr < EFUSE_MAX_SIZE)) {
-               if (efuse_data != 0xFF) {
-                       hworden = efuse_data & 0x0F;
-                       word_cnts = efuse_calculate_word_cnts(hworden);
-                       efuse_addr = efuse_addr + (word_cnts * 2) + 1;
-               } else {
-                       continual = false;
-               }
+       while (efuse_one_byte_read(hw, efuse_addr, &efuse_data) &&
+              efuse_addr < EFUSE_MAX_SIZE) {
+               if (efuse_data == 0xFF)
+                       break;
+
+               hworden = efuse_data & 0x0F;
+               word_cnts = efuse_calculate_word_cnts(hworden);
+               efuse_addr = efuse_addr + (word_cnts * 2) + 1;
        }
 
        return efuse_addr;
index 395a326..1663b3a 100644 (file)
@@ -104,20 +104,19 @@ struct efuse_priv {
        u8 tx_power_g[14];
 };
 
-extern void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf);
-extern void efuse_initialize(struct ieee80211_hw *hw);
-extern u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address);
-extern void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value);
-extern void read_efuse(struct ieee80211_hw *hw, u16 _offset,
-                      u16 _size_byte, u8 *pbuf);
-extern void efuse_shadow_read(struct ieee80211_hw *hw, u8 type,
-                             u16 offset, u32 *value);
-extern void efuse_shadow_write(struct ieee80211_hw *hw, u8 type,
-                              u16 offset, u32 value);
-extern bool efuse_shadow_update(struct ieee80211_hw *hw);
-extern bool efuse_shadow_update_chk(struct ieee80211_hw *hw);
-extern void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw);
-extern void efuse_force_write_vendor_Id(struct ieee80211_hw *hw);
-extern void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx);
+void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf);
+void efuse_initialize(struct ieee80211_hw *hw);
+u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address);
+void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value);
+void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf);
+void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, u16 offset,
+                      u32 *value);
+void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, u16 offset,
+                       u32 value);
+bool efuse_shadow_update(struct ieee80211_hw *hw);
+bool efuse_shadow_update_chk(struct ieee80211_hw *hw);
+void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw);
+void efuse_force_write_vendor_Id(struct ieee80211_hw *hw);
+void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx);
 
 #endif
index 703f839..0f49444 100644 (file)
@@ -736,7 +736,6 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
 
        struct rtl_stats stats = {
                .signal = 0,
-               .noise = -98,
                .rate = 0,
        };
        int index = rtlpci->rx_ring[rx_queue_idx].idx;
@@ -2009,7 +2008,6 @@ fail2:
 fail1:
        if (hw)
                ieee80211_free_hw(hw);
-       pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
 
        return err;
@@ -2064,8 +2062,6 @@ void rtl_pci_disconnect(struct pci_dev *pdev)
 
        rtl_pci_disable_aspm(hw);
 
-       pci_set_drvdata(pdev, NULL);
-
        ieee80211_free_hw(hw);
 }
 EXPORT_SYMBOL(rtl_pci_disconnect);
index b68cae3..e06971b 100644 (file)
@@ -143,6 +143,7 @@ static void _rtl88ee_set_fw_clock_on(struct ieee80211_hw *hw,
                } else {
                        rtlhal->fw_clk_change_in_progress = false;
                        spin_unlock_bh(&rtlpriv->locks.fw_ps_lock);
+                       break;
                }
        }
 
index e655c04..d67f9c7 100644 (file)
@@ -1136,34 +1136,6 @@ void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel)
                                           &bw40_pwr[0], channel);
 }
 
-void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       enum io_type iotype;
-
-       if (!is_hal_stop(rtlhal)) {
-               switch (operation) {
-               case SCAN_OPT_BACKUP:
-                       iotype = IO_CMD_PAUSE_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                                     HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-                       break;
-               case SCAN_OPT_RESTORE:
-                       iotype = IO_CMD_RESUME_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                                     HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-                       break;
-               default:
-                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-                                "Unknown Scan Backup operation.\n");
-                       break;
-               }
-       }
-}
-
 void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
index f1acd6d..89f0f1e 100644 (file)
@@ -200,37 +200,35 @@ enum _ANT_DIV_TYPE {
        CGCS_RX_SW_ANTDIV               = 0x05,
 };
 
-extern u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw,
-                                  u32 regaddr, u32 bitmask);
-extern void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw,
-                                 u32 regaddr, u32 bitmask, u32 data);
-extern u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw,
-                                  enum radio_path rfpath, u32 regaddr,
-                                  u32 bitmask);
-extern void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw,
-                                 enum radio_path rfpath, u32 regaddr,
-                                 u32 bitmask, u32 data);
-extern bool rtl88e_phy_mac_config(struct ieee80211_hw *hw);
-extern bool rtl88e_phy_bb_config(struct ieee80211_hw *hw);
-extern bool rtl88e_phy_rf_config(struct ieee80211_hw *hw);
-extern void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
-extern void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw,
-                                        long *powerlevel);
-extern void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
-extern void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw,
-                                            u8 operation);
-extern void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
-extern void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw,
-                                  enum nl80211_channel_type ch_type);
-extern void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw);
-extern u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw);
-extern void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery);
+u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw,
+                           u32 regaddr, u32 bitmask);
+void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw,
+                          u32 regaddr, u32 bitmask, u32 data);
+u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw,
+                           enum radio_path rfpath, u32 regaddr,
+                           u32 bitmask);
+void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw,
+                          enum radio_path rfpath, u32 regaddr,
+                          u32 bitmask, u32 data);
+bool rtl88e_phy_mac_config(struct ieee80211_hw *hw);
+bool rtl88e_phy_bb_config(struct ieee80211_hw *hw);
+bool rtl88e_phy_rf_config(struct ieee80211_hw *hw);
+void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
+void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw,
+                                 long *powerlevel);
+void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
+void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
+void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw,
+                           enum nl80211_channel_type ch_type);
+void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw);
+u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw);
+void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery);
 void rtl88e_phy_lc_calibrate(struct ieee80211_hw *hw);
 void rtl88e_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
 bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                          enum radio_path rfpath);
 bool rtl88e_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
-extern bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw,
-                                         enum rf_pwrstate rfpwr_state);
+bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw,
+                                  enum rf_pwrstate rfpwr_state);
 
 #endif
index c254693..347af1e 100644 (file)
@@ -30,6 +30,7 @@
 #include "../wifi.h"
 #include "../core.h"
 #include "../pci.h"
+#include "../base.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -244,7 +245,7 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = {
        .set_bw_mode = rtl88e_phy_set_bw_mode,
        .switch_channel = rtl88e_phy_sw_chnl,
        .dm_watchdog = rtl88e_dm_watchdog,
-       .scan_operation_backup = rtl88e_phy_scan_operation_backup,
+       .scan_operation_backup = rtl_phy_scan_operation_backup,
        .set_rf_power_state = rtl88e_phy_set_rf_power_state,
        .led_control = rtl88ee_led_control,
        .set_desc = rtl88ee_set_desc,
index 68685a8..aece6c9 100644 (file)
@@ -478,7 +478,6 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw,
 
        /*rx_status->qual = status->signal; */
        rx_status->signal = status->recvsignalpower + 10;
-       /*rx_status->noise = -status->noise; */
        if (status->packet_report_type == TX_REPORT2) {
                status->macid_valid_entry[0] =
                         GET_RX_RPT2_DESC_MACID_VALID_1(pdesc);
index d2d57a2..e9caa5d 100644 (file)
@@ -541,29 +541,6 @@ EXPORT_SYMBOL(rtl92c_dm_write_dig);
 
 static void rtl92c_dm_pwdb_monitor(struct ieee80211_hw *hw)
 {
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       long tmpentry_max_pwdb = 0, tmpentry_min_pwdb = 0xff;
-
-       u8 h2c_parameter[3] = { 0 };
-
-       return;
-
-       if (tmpentry_max_pwdb != 0) {
-               rtlpriv->dm.entry_max_undec_sm_pwdb = tmpentry_max_pwdb;
-       } else {
-               rtlpriv->dm.entry_max_undec_sm_pwdb = 0;
-       }
-
-       if (tmpentry_min_pwdb != 0xff) {
-               rtlpriv->dm.entry_min_undec_sm_pwdb = tmpentry_min_pwdb;
-       } else {
-               rtlpriv->dm.entry_min_undec_sm_pwdb = 0;
-       }
-
-       h2c_parameter[2] = (u8) (rtlpriv->dm.undec_sm_pwdb & 0xFF);
-       h2c_parameter[0] = 0;
-
-       rtl92c_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, h2c_parameter);
 }
 
 void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw)
@@ -673,7 +650,7 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw
        s8 cck_index = 0;
        int i;
        bool is2t = IS_92C_SERIAL(rtlhal->version);
-       s8 txpwr_level[2] = {0, 0};
+       s8 txpwr_level[3] = {0, 0, 0};
        u8 ofdm_min_index = 6, rf;
 
        rtlpriv->dm.txpower_trackinginit = true;
index 246e535..0c0e782 100644 (file)
@@ -592,36 +592,6 @@ long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(_rtl92c_phy_txpwr_idx_to_dbm);
 
-void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       enum io_type iotype;
-
-       if (!is_hal_stop(rtlhal)) {
-               switch (operation) {
-               case SCAN_OPT_BACKUP:
-                       iotype = IO_CMD_PAUSE_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                                     HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-
-                       break;
-               case SCAN_OPT_RESTORE:
-                       iotype = IO_CMD_RESUME_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                                     HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-                       break;
-               default:
-                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-                                "Unknown Scan Backup operation\n");
-                       break;
-               }
-       }
-}
-EXPORT_SYMBOL(rtl92c_phy_scan_operation_backup);
-
 void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw,
                            enum nl80211_channel_type ch_type)
 {
index cec10d6..e79dabe 100644 (file)
@@ -39,9 +39,7 @@
 #define RT_CANNOT_IO(hw)               false
 #define HIGHPOWER_RADIOA_ARRAYLEN      22
 
-#define IQK_ADDA_REG_NUM               16
 #define MAX_TOLERANCE                  5
-#define        IQK_DELAY_TIME                  1
 
 #define        APK_BB_REG_NUM                  5
 #define        APK_AFE_REG_NUM                 16
@@ -205,8 +203,6 @@ void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw,
 void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
 bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw,
                                          long power_indbm);
-void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw,
-                                            u8 operation);
 void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw,
                                   enum nl80211_channel_type ch_type);
 void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw);
index 3cfa1bb..fa24de4 100644 (file)
@@ -152,8 +152,6 @@ enum version_8192c {
 #define IS_VENDOR_UMC_A_CUT(version)   ((IS_CHIP_VENDOR_UMC(version)) ? \
        ((GET_CVID_CUT_VERSION(version)) ? false : true) : false)
 #define IS_CHIP_VER_B(version)  ((version & CHIP_VER_B) ? true : false)
-#define IS_VENDOR_UMC_A_CUT(version)   ((IS_CHIP_VENDOR_UMC(version)) ? \
-       ((GET_CVID_CUT_VERSION(version)) ? false : true) : false)
 #define IS_92C_SERIAL(version)  ((version & CHIP_92C_BITMASK) ? true : false)
 #define IS_CHIP_VENDOR_UMC(version)            \
        ((version & CHIP_VENDOR_UMC) ? true : false)
index d5e3b70..94486cc 100644 (file)
@@ -39,9 +39,7 @@
 #define RT_CANNOT_IO(hw)               false
 #define HIGHPOWER_RADIOA_ARRAYLEN      22
 
-#define IQK_ADDA_REG_NUM               16
 #define MAX_TOLERANCE                  5
-#define        IQK_DELAY_TIME                  1
 
 #define        APK_BB_REG_NUM                  5
 #define        APK_AFE_REG_NUM                 16
@@ -188,36 +186,29 @@ struct tx_power_struct {
 };
 
 bool rtl92c_phy_bb_config(struct ieee80211_hw *hw);
-u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw,
-                                  u32 regaddr, u32 bitmask);
-void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw,
-                                 u32 regaddr, u32 bitmask, u32 data);
-u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw,
-                                  enum radio_path rfpath, u32 regaddr,
-                                  u32 bitmask);
-extern void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw,
-                                  enum radio_path rfpath, u32 regaddr,
-                                  u32 bitmask, u32 data);
+u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask);
+void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask,
+                          u32 data);
+u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
+                           u32 regaddr, u32 bitmask);
+void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath,
+                           u32 regaddr, u32 bitmask, u32 data);
 bool rtl92c_phy_mac_config(struct ieee80211_hw *hw);
 bool rtl92ce_phy_bb_config(struct ieee80211_hw *hw);
 bool rtl92c_phy_rf_config(struct ieee80211_hw *hw);
 bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw,
-                                                enum radio_path rfpath);
+                                         enum radio_path rfpath);
 void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
-void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw,
-                                        long *powerlevel);
+void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel);
 void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
 bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw,
                                          long power_indbm);
-void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw,
-                                            u8 operation);
 void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw,
-                                  enum nl80211_channel_type ch_type);
+                           enum nl80211_channel_type ch_type);
 void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw);
 u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw);
 void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery);
-void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw,
-                                        u16 beaconinterval);
+void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, u16 beaconinterval);
 void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta);
 void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw);
 void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t);
@@ -225,28 +216,25 @@ void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
 bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                          enum radio_path rfpath);
 bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw,
-                                             u32 rfpath);
-bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
+                                      u32 rfpath);
 bool rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw,
-                                         enum rf_pwrstate rfpwr_state);
+                                   enum rf_pwrstate rfpwr_state);
 void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw);
 bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
 void rtl92c_phy_set_io(struct ieee80211_hw *hw);
 void rtl92c_bb_block_on(struct ieee80211_hw *hw);
-u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw,
-                                     enum radio_path rfpath, u32 offset);
+u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath,
+                              u32 offset);
 u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw,
-                                        enum radio_path rfpath, u32 offset);
+                                 enum radio_path rfpath, u32 offset);
 u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask);
 void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw,
-                                       enum radio_path rfpath, u32 offset,
-                                       u32 data);
+                                enum radio_path rfpath, u32 offset, u32 data);
 void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw,
-                                          enum radio_path rfpath, u32 offset,
-                                          u32 data);
+                                   enum radio_path rfpath, u32 offset,
+                                   u32 data);
 void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw,
-                                                  u32 regaddr, u32 bitmask,
-                                                  u32 data);
+                                           u32 regaddr, u32 bitmask, u32 data);
 bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw);
 void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw);
 bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw);
index bd4aef7..8922ecb 100644 (file)
 #define        EEPROM_DEFAULT_TXPOWERLEVEL             0x22
 #define        EEPROM_DEFAULT_HT40_2SDIFF              0x0
 #define EEPROM_DEFAULT_HT20_DIFF               2
-#define        EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF      0x3
 #define EEPROM_DEFAULT_HT40_PWRMAXOFFSET       0
 #define EEPROM_DEFAULT_HT20_PWRMAXOFFSET       0
 
 
 #define        EEPROM_TXPWR_GROUP                      0x6F
 
-#define EEPROM_TSSI_A                          0x76
-#define EEPROM_TSSI_B                          0x77
-#define EEPROM_THERMAL_METER                   0x78
-
 #define EEPROM_CHANNELPLAN                     0x75
 
-#define RF_OPTION1                             0x79
-#define RF_OPTION2                             0x7A
-#define RF_OPTION3                             0x7B
-#define RF_OPTION4                             0x7C
-
 #define        STOPBECON                               BIT(6)
 #define        STOPHIGHT                               BIT(5)
 #define        STOPMGT                                 BIT(4)
 #define RSV_CTRL                               0x001C
 #define RD_CTRL                                        0x0524
 
-#define REG_USB_INFO                           0xFE17
-#define REG_USB_SPECIAL_OPTION                 0xFE55
-
-#define REG_USB_DMA_AGG_TO                     0xFE5B
-#define REG_USB_AGG_TO                         0xFE5C
-#define REG_USB_AGG_TH                         0xFE5D
-
 #define REG_USB_VID                            0xFE60
 #define REG_USB_PID                            0xFE62
 #define REG_USB_OPTIONAL                       0xFE64
 #define POLLING_LLT_THRESHOLD                  20
 #define POLLING_READY_TIMEOUT_COUNT            1000
 
-#define        MAX_MSS_DENSITY_2T                      0x13
-#define        MAX_MSS_DENSITY_1T                      0x0A
-
 #define EPROM_CMD_OPERATING_MODE_MASK  ((1<<7)|(1<<6))
 #define EPROM_CMD_CONFIG                       0x3
 #define EPROM_CMD_LOAD                         1
index 6c8d56e..d8fe68b 100644 (file)
 #define RF6052_MAX_REG                 0x3F
 #define RF6052_MAX_PATH                        2
 
-extern void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
-                                            u8 bandwidth);
-extern void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
-                                              u8 *ppowerlevel);
-extern void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
-                                               u8 *ppowerlevel, u8 channel);
-extern bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw);
+void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth);
+void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+                                       u8 *ppowerlevel);
+void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+                                        u8 *ppowerlevel, u8 channel);
+bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw);
 #endif
index 1420356..b790320 100644 (file)
@@ -30,6 +30,7 @@
 #include "../wifi.h"
 #include "../core.h"
 #include "../pci.h"
+#include "../base.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -219,7 +220,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = {
        .set_bw_mode = rtl92c_phy_set_bw_mode,
        .switch_channel = rtl92c_phy_sw_chnl,
        .dm_watchdog = rtl92c_dm_watchdog,
-       .scan_operation_backup = rtl92c_phy_scan_operation_backup,
+       .scan_operation_backup = rtl_phy_scan_operation_backup,
        .set_rf_power_state = rtl92c_phy_set_rf_power_state,
        .led_control = rtl92ce_led_control,
        .set_desc = rtl92ce_set_desc,
index 6ad23b4..52abf0a 100644 (file)
@@ -420,7 +420,6 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw,
 
        /*rx_status->qual = stats->signal; */
        rx_status->signal = stats->recvsignalpower + 10;
-       /*rx_status->noise = -stats->noise; */
 
        return true;
 }
index da4f587..3936853 100644 (file)
@@ -32,6 +32,7 @@
 #include "../usb.h"
 #include "../ps.h"
 #include "../cam.h"
+#include "../stats.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -738,16 +739,6 @@ static u8 _rtl92c_evm_db_to_percentage(char value)
        return ret_val;
 }
 
-static long _rtl92c_translate_todbm(struct ieee80211_hw *hw,
-                                    u8 signal_strength_index)
-{
-       long signal_power;
-
-       signal_power = (long)((signal_strength_index + 1) >> 1);
-       signal_power -= 95;
-       return signal_power;
-}
-
 static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw,
                long currsig)
 {
@@ -913,180 +904,6 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw,
                          (hw, total_rssi /= rf_rx_num));
 }
 
-static void _rtl92c_process_ui_rssi(struct ieee80211_hw *hw,
-               struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_phy *rtlphy = &(rtlpriv->phy);
-       u8 rfpath;
-       u32 last_rssi, tmpval;
-
-       if (pstats->packet_toself || pstats->packet_beacon) {
-               rtlpriv->stats.rssi_calculate_cnt++;
-               if (rtlpriv->stats.ui_rssi.total_num++ >=
-                   PHY_RSSI_SLID_WIN_MAX) {
-                       rtlpriv->stats.ui_rssi.total_num =
-                           PHY_RSSI_SLID_WIN_MAX;
-                       last_rssi =
-                           rtlpriv->stats.ui_rssi.elements[rtlpriv->
-                                                          stats.ui_rssi.index];
-                       rtlpriv->stats.ui_rssi.total_val -= last_rssi;
-               }
-               rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength;
-               rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi.
-                                       index++] = pstats->signalstrength;
-               if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX)
-                       rtlpriv->stats.ui_rssi.index = 0;
-               tmpval = rtlpriv->stats.ui_rssi.total_val /
-                   rtlpriv->stats.ui_rssi.total_num;
-               rtlpriv->stats.signal_strength =
-                   _rtl92c_translate_todbm(hw, (u8) tmpval);
-               pstats->rssi = rtlpriv->stats.signal_strength;
-       }
-       if (!pstats->is_cck && pstats->packet_toself) {
-               for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath;
-                    rfpath++) {
-                       if (!rtl8192_phy_check_is_legal_rfpath(hw, rfpath))
-                               continue;
-                       if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   pstats->rx_mimo_signalstrength[rfpath];
-                       }
-                       if (pstats->rx_mimo_signalstrength[rfpath] >
-                           rtlpriv->stats.rx_rssi_percentage[rfpath]) {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   ((rtlpriv->stats.
-                                     rx_rssi_percentage[rfpath] *
-                                     (RX_SMOOTH_FACTOR - 1)) +
-                                    (pstats->rx_mimo_signalstrength[rfpath])) /
-                                   (RX_SMOOTH_FACTOR);
-
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   rtlpriv->stats.rx_rssi_percentage[rfpath] +
-                                   1;
-                       } else {
-                               rtlpriv->stats.rx_rssi_percentage[rfpath] =
-                                   ((rtlpriv->stats.
-                                     rx_rssi_percentage[rfpath] *
-                                     (RX_SMOOTH_FACTOR - 1)) +
-                                    (pstats->rx_mimo_signalstrength[rfpath])) /
-                                   (RX_SMOOTH_FACTOR);
-                       }
-               }
-       }
-}
-
-static void _rtl92c_update_rxsignalstatistics(struct ieee80211_hw *hw,
-                                              struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       int weighting = 0;
-
-       if (rtlpriv->stats.recv_signal_power == 0)
-               rtlpriv->stats.recv_signal_power = pstats->recvsignalpower;
-       if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power)
-               weighting = 5;
-       else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power)
-               weighting = (-5);
-       rtlpriv->stats.recv_signal_power =
-           (rtlpriv->stats.recv_signal_power * 5 +
-            pstats->recvsignalpower + weighting) / 6;
-}
-
-static void _rtl92c_process_pwdb(struct ieee80211_hw *hw,
-               struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
-       long undec_sm_pwdb = 0;
-
-       if (mac->opmode == NL80211_IFTYPE_ADHOC) {
-               return;
-       } else {
-               undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb;
-       }
-       if (pstats->packet_toself || pstats->packet_beacon) {
-               if (undec_sm_pwdb < 0)
-                       undec_sm_pwdb = pstats->rx_pwdb_all;
-               if (pstats->rx_pwdb_all > (u32) undec_sm_pwdb) {
-                       undec_sm_pwdb = (((undec_sm_pwdb) *
-                             (RX_SMOOTH_FACTOR - 1)) +
-                            (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
-                       undec_sm_pwdb += 1;
-               } else {
-                       undec_sm_pwdb = (((undec_sm_pwdb) *
-                             (RX_SMOOTH_FACTOR - 1)) +
-                            (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR);
-               }
-               rtlpriv->dm.undec_sm_pwdb = undec_sm_pwdb;
-               _rtl92c_update_rxsignalstatistics(hw, pstats);
-       }
-}
-
-static void _rtl92c_process_LINK_Q(struct ieee80211_hw *hw,
-                                            struct rtl_stats *pstats)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       u32 last_evm = 0, n_stream, tmpval;
-
-       if (pstats->signalquality != 0) {
-               if (pstats->packet_toself || pstats->packet_beacon) {
-                       if (rtlpriv->stats.LINK_Q.total_num++ >=
-                           PHY_LINKQUALITY_SLID_WIN_MAX) {
-                               rtlpriv->stats.LINK_Q.total_num =
-                                   PHY_LINKQUALITY_SLID_WIN_MAX;
-                               last_evm =
-                                   rtlpriv->stats.LINK_Q.elements
-                                   [rtlpriv->stats.LINK_Q.index];
-                               rtlpriv->stats.LINK_Q.total_val -=
-                                   last_evm;
-                       }
-                       rtlpriv->stats.LINK_Q.total_val +=
-                           pstats->signalquality;
-                       rtlpriv->stats.LINK_Q.elements
-                          [rtlpriv->stats.LINK_Q.index++] =
-                           pstats->signalquality;
-                       if (rtlpriv->stats.LINK_Q.index >=
-                           PHY_LINKQUALITY_SLID_WIN_MAX)
-                               rtlpriv->stats.LINK_Q.index = 0;
-                       tmpval = rtlpriv->stats.LINK_Q.total_val /
-                           rtlpriv->stats.LINK_Q.total_num;
-                       rtlpriv->stats.signal_quality = tmpval;
-                       rtlpriv->stats.last_sigstrength_inpercent = tmpval;
-                       for (n_stream = 0; n_stream < 2;
-                            n_stream++) {
-                               if (pstats->RX_SIGQ[n_stream] != -1) {
-                                       if (!rtlpriv->stats.RX_EVM[n_stream]) {
-                                               rtlpriv->stats.RX_EVM[n_stream]
-                                                = pstats->RX_SIGQ[n_stream];
-                                       }
-                                       rtlpriv->stats.RX_EVM[n_stream] =
-                                           ((rtlpriv->stats.RX_EVM
-                                           [n_stream] *
-                                           (RX_SMOOTH_FACTOR - 1)) +
-                                           (pstats->RX_SIGQ
-                                           [n_stream] * 1)) /
-                                           (RX_SMOOTH_FACTOR);
-                               }
-                       }
-               }
-       } else {
-               ;
-       }
-}
-
-static void _rtl92c_process_phyinfo(struct ieee80211_hw *hw,
-                                    u8 *buffer,
-                                    struct rtl_stats *pcurrent_stats)
-{
-       if (!pcurrent_stats->packet_matchbssid &&
-           !pcurrent_stats->packet_beacon)
-               return;
-       _rtl92c_process_ui_rssi(hw, pcurrent_stats);
-       _rtl92c_process_pwdb(hw, pcurrent_stats);
-       _rtl92c_process_LINK_Q(hw, pcurrent_stats);
-}
-
 void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw,
                                               struct sk_buff *skb,
                                               struct rtl_stats *pstats,
@@ -1123,5 +940,5 @@ void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        _rtl92c_query_rxphystatus(hw, pstats, pdesc, p_drvinfo,
                                   packet_matchbssid, packet_toself,
                                   packet_beacon);
-       _rtl92c_process_phyinfo(hw, tmp_buf, pstats);
+       rtl_process_phyinfo(hw, tmp_buf, pstats);
 }
index 090fd33..11b439d 100644 (file)
 #define RF6052_MAX_REG                 0x3F
 #define RF6052_MAX_PATH                        2
 
-extern void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
-                                           u8 bandwidth);
-extern void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
-                                             u8 *ppowerlevel);
-extern void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
-                                              u8 *ppowerlevel, u8 channel);
+void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth);
+void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+                                      u8 *ppowerlevel);
+void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+                                       u8 *ppowerlevel, u8 channel);
 bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw);
 bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
-                                         enum radio_path rfpath);
+                                          enum radio_path rfpath);
 void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
                                        u8 *ppowerlevel);
 void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
index 2bd5985..9936de7 100644 (file)
@@ -31,6 +31,7 @@
 #include "../core.h"
 #include "../usb.h"
 #include "../efuse.h"
+#include "../base.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -117,7 +118,7 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = {
        .set_bw_mode = rtl92c_phy_set_bw_mode,
        .switch_channel = rtl92c_phy_sw_chnl,
        .dm_watchdog = rtl92c_dm_watchdog,
-       .scan_operation_backup = rtl92c_phy_scan_operation_backup,
+       .scan_operation_backup = rtl_phy_scan_operation_backup,
        .set_rf_power_state = rtl92cu_phy_set_rf_power_state,
        .led_control = rtl92cu_led_control,
        .enable_hw_sec = rtl92cu_enable_hw_security_config,
index 5a060e5..b0c346a 100644 (file)
@@ -349,8 +349,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw,
                                                 p_drvinfo);
        }
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
-       /*rx_status->noise = -stats->noise; */
+       rx_status->signal = stats->recvsignalpower + 10;
        return true;
 }
 
@@ -365,7 +364,6 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb)
        u8 *rxdesc;
        struct rtl_stats stats = {
                .signal = 0,
-               .noise = -98,
                .rate = 0,
        };
        struct rx_fwinfo_92c *p_drvinfo;
index f700f7a..7908e1c 100644 (file)
@@ -840,9 +840,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
        bool internal_pa = false;
        long ele_a = 0, ele_d, temp_cck, val_x, value32;
        long val_y, ele_c = 0;
-       u8 ofdm_index[2];
+       u8 ofdm_index[3];
        s8 cck_index = 0;
-       u8 ofdm_index_old[2] = {0, 0};
+       u8 ofdm_index_old[3] = {0, 0, 0};
        s8 cck_index_old = 0;
        u8 index;
        int i;
@@ -1118,6 +1118,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
                                 val_x, val_y, ele_a, ele_c, ele_d,
                                 val_x, val_y);
 
+                       if (cck_index >= CCK_TABLE_SIZE)
+                               cck_index = CCK_TABLE_SIZE - 1;
+                       if (cck_index < 0)
+                               cck_index = 0;
                        if (rtlhal->current_bandtype == BAND_ON_2_4G) {
                                /* Adjust CCK according to IQK result */
                                if (!rtlpriv->dm.cck_inch14) {
index 7dd8f6d..c4a7db9 100644 (file)
@@ -1194,25 +1194,7 @@ void rtl92d_linked_set_reg(struct ieee80211_hw *hw)
  * mac80211 will send pkt when scan */
 void rtl92de_set_qos(struct ieee80211_hw *hw, int aci)
 {
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
        rtl92d_dm_init_edca_turbo(hw);
-       return;
-       switch (aci) {
-       case AC1_BK:
-               rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f);
-               break;
-       case AC0_BE:
-               break;
-       case AC2_VI:
-               rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322);
-               break;
-       case AC3_VO:
-               rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222);
-               break;
-       default:
-               RT_ASSERT(false, "invalid aci: %d !\n", aci);
-               break;
-       }
 }
 
 void rtl92de_enable_interrupt(struct ieee80211_hw *hw)
index 7c9f7a2..1bc7b1a 100644 (file)
@@ -55,10 +55,9 @@ void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index,
                     u8 *p_macaddr, bool is_group, u8 enc_algo,
                     bool is_wepkey, bool clear_all);
 
-extern void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset,
-                                   u32 value, u8 direct);
-extern u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset,
-                                 u8 direct);
+void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, u32 value,
+                            u8 direct);
+u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, u8 direct);
 void rtl92de_suspend(struct ieee80211_hw *hw);
 void rtl92de_resume(struct ieee80211_hw *hw);
 void rtl92d_linked_set_reg(struct ieee80211_hw *hw);
index 840bac5..13196cc 100644 (file)
@@ -1022,34 +1022,6 @@ void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel)
        rtl92d_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel);
 }
 
-void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       enum io_type iotype;
-
-       if (!is_hal_stop(rtlhal)) {
-               switch (operation) {
-               case SCAN_OPT_BACKUP:
-                       rtlhal->current_bandtypebackup =
-                                                rtlhal->current_bandtype;
-                       iotype = IO_CMD_PAUSE_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-                       break;
-               case SCAN_OPT_RESTORE:
-                       iotype = IO_CMD_RESUME_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-                       break;
-               default:
-                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-                                "Unknown Scan Backup operation\n");
-                       break;
-               }
-       }
-}
-
 void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw,
                            enum nl80211_channel_type ch_type)
 {
index f074952..48d5c68 100644 (file)
@@ -39,9 +39,7 @@
 #define RT_CANNOT_IO(hw)                       false
 #define HIGHPOWER_RADIOA_ARRAYLEN              22
 
-#define IQK_ADDA_REG_NUM                       16
 #define MAX_TOLERANCE                          5
-#define        IQK_DELAY_TIME                          1
 
 #define        APK_BB_REG_NUM                          5
 #define        APK_AFE_REG_NUM                         16
@@ -127,34 +125,32 @@ static inline void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw,
                        *flag);
 }
 
-extern u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw,
-                                  u32 regaddr, u32 bitmask);
-extern void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw,
-                                 u32 regaddr, u32 bitmask, u32 data);
-extern u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw,
-                                  enum radio_path rfpath, u32 regaddr,
-                                  u32 bitmask);
-extern void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw,
-                                 enum radio_path rfpath, u32 regaddr,
-                                 u32 bitmask, u32 data);
-extern bool rtl92d_phy_mac_config(struct ieee80211_hw *hw);
-extern bool rtl92d_phy_bb_config(struct ieee80211_hw *hw);
-extern bool rtl92d_phy_rf_config(struct ieee80211_hw *hw);
-extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw,
-                                                enum radio_path rfpath);
-extern void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
-extern void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
-extern void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw,
-                                            u8 operation);
-extern void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw,
-                                  enum nl80211_channel_type ch_type);
-extern u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw);
+u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw,
+                           u32 regaddr, u32 bitmask);
+void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw,
+                          u32 regaddr, u32 bitmask, u32 data);
+u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw,
+                           enum radio_path rfpath, u32 regaddr,
+                           u32 bitmask);
+void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw,
+                          enum radio_path rfpath, u32 regaddr,
+                          u32 bitmask, u32 data);
+bool rtl92d_phy_mac_config(struct ieee80211_hw *hw);
+bool rtl92d_phy_bb_config(struct ieee80211_hw *hw);
+bool rtl92d_phy_rf_config(struct ieee80211_hw *hw);
+bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw,
+                                         enum radio_path rfpath);
+void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
+void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel);
+void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw,
+                           enum nl80211_channel_type ch_type);
+u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw);
 bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                          enum rf_content content,
                                          enum radio_path rfpath);
 bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
-extern bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw,
-                                         enum rf_pwrstate rfpwr_state);
+bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw,
+                                  enum rf_pwrstate rfpwr_state);
 
 void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw);
 void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw);
@@ -173,6 +169,5 @@ void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw,
                                       unsigned long *flag);
 u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl);
 void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel);
-void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw);
 
 #endif
index 0fe1a48..7303d12 100644 (file)
 #ifndef __RTL92D_RF_H__
 #define __RTL92D_RF_H__
 
-extern void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
-                                           u8 bandwidth);
-extern void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
-                                             u8 *ppowerlevel);
-extern void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
-                                              u8 *ppowerlevel, u8 channel);
-extern bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw);
-extern bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0);
-extern void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw,
-                                           bool bmac0);
+void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth);
+void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+                                      u8 *ppowerlevel);
+void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+                                       u8 *ppowerlevel, u8 channel);
+bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw);
+bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0);
+void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0);
 
 #endif
index c18c04b..edab5a5 100644 (file)
@@ -30,6 +30,7 @@
 #include "../wifi.h"
 #include "../core.h"
 #include "../pci.h"
+#include "../base.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -236,7 +237,7 @@ static struct rtl_hal_ops rtl8192de_hal_ops = {
        .set_bw_mode = rtl92d_phy_set_bw_mode,
        .switch_channel = rtl92d_phy_sw_chnl,
        .dm_watchdog = rtl92d_dm_watchdog,
-       .scan_operation_backup = rtl92d_phy_scan_operation_backup,
+       .scan_operation_backup = rtl_phy_scan_operation_backup,
        .set_rf_power_state = rtl92d_phy_set_rf_power_state,
        .led_control = rtl92de_led_control,
        .set_desc = rtl92de_set_desc,
index b8ec718..0eb0f4a 100644 (file)
@@ -525,8 +525,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
                                                   p_drvinfo);
        }
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
-       /*rx_status->noise = -stats->noise; */
+       rx_status->signal = stats->recvsignalpower + 10;
        return true;
 }
 
index 84d1181..c81c835 100644 (file)
 #define        EXT_IMEM_CODE_DONE                      BIT(2)
 #define        IMEM_CHK_RPT                            BIT(1)
 #define        IMEM_CODE_DONE                          BIT(0)
-#define        IMEM_CODE_DONE                          BIT(0)
-#define        IMEM_CHK_RPT                            BIT(1)
 #define        EMEM_CODE_DONE                          BIT(2)
 #define        EMEM_CHK_RPT                            BIT(3)
-#define        DMEM_CODE_DONE                          BIT(4)
 #define        IMEM_RDY                                BIT(5)
-#define        BASECHG                                 BIT(6)
-#define        FWRDY                                   BIT(7)
 #define        LOAD_FW_READY                           (IMEM_CODE_DONE | \
                                                IMEM_CHK_RPT | \
                                                EMEM_CODE_DONE | \
index 5061f1d..92d38ab 100644 (file)
@@ -265,7 +265,7 @@ static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw,
                                    rtlefuse->pwrgroup_ht40
                                    [RF90_PATH_A][chnl - 1]) {
                                        pwrdiff_limit[i] =
-                                         rtlefuse->pwrgroup_ht20
+                                         rtlefuse->pwrgroup_ht40
                                          [RF90_PATH_A][chnl - 1];
                                }
                        } else {
index c709511..27efbcd 100644 (file)
@@ -329,8 +329,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats,
        }
 
        /*rx_status->qual = stats->signal; */
-       rx_status->signal = stats->rssi + 10;
-       /*rx_status->noise = -stats->noise; */
+       rx_status->signal = stats->recvsignalpower + 10;
 
        return true;
 }
index eafbb18..5d318a8 100644 (file)
@@ -934,35 +934,6 @@ static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
        return pwrout_dbm;
 }
 
-void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
-       enum io_type iotype;
-
-       if (!is_hal_stop(rtlhal)) {
-               switch (operation) {
-               case SCAN_OPT_BACKUP:
-                       iotype = IO_CMD_PAUSE_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                                     HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-
-                       break;
-               case SCAN_OPT_RESTORE:
-                       iotype = IO_CMD_RESUME_DM_BY_SCAN;
-                       rtlpriv->cfg->ops->set_hw_reg(hw,
-                                                     HW_VAR_IO_CMD,
-                                                     (u8 *)&iotype);
-                       break;
-               default:
-                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-                                "Unknown Scan Backup operation.\n");
-                       break;
-               }
-       }
-}
-
 void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
index e7a59eb..007ebdb 100644 (file)
@@ -183,42 +183,40 @@ struct tx_power_struct {
        u32 mcs_original_offset[4][16];
 };
 
-extern u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw,
-                                     u32 regaddr, u32 bitmask);
-extern void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw,
-                                    u32 regaddr, u32 bitmask, u32 data);
-extern u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw,
-                                     enum radio_path rfpath, u32 regaddr,
-                                     u32 bitmask);
-extern void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw,
-                                    enum radio_path rfpath, u32 regaddr,
-                                    u32 bitmask, u32 data);
-extern bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw);
-extern bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw);
-extern bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw);
-extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw,
-                                                enum radio_path rfpath);
-extern void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
-extern void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw,
-                                           long *powerlevel);
-extern void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw,
-                                           u8 channel);
-extern bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw,
-                                            long power_indbm);
-extern void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw,
-                                               u8 operation);
-extern void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
-extern void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw,
-                                     enum nl80211_channel_type ch_type);
-extern void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw);
-extern u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw);
-extern void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery);
+u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw,
+                              u32 regaddr, u32 bitmask);
+void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw,
+                             u32 regaddr, u32 bitmask, u32 data);
+u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw,
+                              enum radio_path rfpath, u32 regaddr,
+                              u32 bitmask);
+void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw,
+                             enum radio_path rfpath, u32 regaddr,
+                             u32 bitmask, u32 data);
+bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw);
+bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw);
+bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw);
+bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw,
+                                         enum radio_path rfpath);
+void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw);
+void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw,
+                                    long *powerlevel);
+void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw,
+                                    u8 channel);
+bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw,
+                                     long power_indbm);
+void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
+void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw,
+                              enum nl80211_channel_type ch_type);
+void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw);
+u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw);
+void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery);
 void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw);
 void rtl8723ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain);
 bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
                                             enum radio_path rfpath);
 bool rtl8723ae_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype);
-extern bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw,
-                                            enum rf_pwrstate rfpwr_state);
+bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw,
+                                     enum rf_pwrstate rfpwr_state);
 
 #endif
index d0f9dd7..57f1933 100644 (file)
 
 #define RF6052_MAX_TX_PWR              0x3F
 
-extern void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw,
-                                           u8 bandwidth);
-extern void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
-                                             u8 *ppowerlevel);
-extern void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
-                                              u8 *ppowerlevel, u8 channel);
-extern bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw);
+void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth);
+void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
+                                         u8 *ppowerlevel);
+void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
+                                          u8 *ppowerlevel, u8 channel);
+bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw);
 
 #endif
index d9ee2ef..62b204f 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "../core.h"
 #include "../pci.h"
+#include "../base.h"
 #include "reg.h"
 #include "def.h"
 #include "phy.h"
@@ -220,7 +221,7 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = {
        .set_bw_mode = rtl8723ae_phy_set_bw_mode,
        .switch_channel = rtl8723ae_phy_sw_chnl,
        .dm_watchdog = rtl8723ae_dm_watchdog,
-       .scan_operation_backup = rtl8723ae_phy_scan_operation_backup,
+       .scan_operation_backup = rtl_phy_scan_operation_backup,
        .set_rf_power_state = rtl8723ae_phy_set_rf_power_state,
        .led_control = rtl8723ae_led_control,
        .set_desc = rtl8723ae_set_desc,
index bcd82a1..50b7be3 100644 (file)
@@ -359,7 +359,6 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw,
 
        /*rx_status->qual = status->signal; */
        rx_status->signal = status->recvsignalpower + 10;
-       /*rx_status->noise = -status->noise; */
 
        return true;
 }
index e56778c..6e2b5c5 100644 (file)
@@ -455,7 +455,6 @@ static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw,
        struct ieee80211_rx_status rx_status = {0};
        struct rtl_stats stats = {
                .signal = 0,
-               .noise = -98,
                .rate = 0,
        };
 
@@ -498,7 +497,6 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
        struct ieee80211_rx_status rx_status = {0};
        struct rtl_stats stats = {
                .signal = 0,
-               .noise = -98,
                .rate = 0,
        };
 
@@ -582,12 +580,15 @@ static void _rtl_rx_work(unsigned long param)
 static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr,
                                        unsigned int len)
 {
+#if NET_IP_ALIGN != 0
        unsigned int padding = 0;
+#endif
 
        /* make function no-op when possible */
        if (NET_IP_ALIGN == 0 || len < sizeof(*hdr))
                return 0;
 
+#if NET_IP_ALIGN != 0
        /* alignment calculation as in lbtf_rx() / carl9170_rx_copy_data() */
        /* TODO: deduplicate common code, define helper function instead? */
 
@@ -608,6 +609,7 @@ static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr,
                padding ^= NET_IP_ALIGN;
 
        return padding;
+#endif
 }
 
 #define __RADIO_TAP_SIZE_RSV   32
index 7032587..0c65386 100644 (file)
 #define RTL_SLOT_TIME_9                                9
 #define RTL_SLOT_TIME_20                       20
 
-/*related with tcp/ip. */
-/*if_ehther.h*/
-#define ETH_P_PAE              0x888E  /*Port Access Entity (IEEE 802.1X) */
-#define ETH_P_IP               0x0800  /*Internet Protocol packet */
-#define ETH_P_ARP              0x0806  /*Address Resolution packet */
+/*related to tcp/ip. */
 #define SNAP_SIZE              6
 #define PROTOC_TYPE_SIZE       2
 
@@ -192,8 +188,6 @@ enum hardware_type {
 (IS_HARDWARE_TYPE_8192DE(rtlhal) || IS_HARDWARE_TYPE_8192DU(rtlhal))
 #define        IS_HARDWARE_TYPE_8723(rtlhal)                   \
 (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal))
-#define IS_HARDWARE_TYPE_8723U(rtlhal)                 \
-       (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U)
 
 #define RX_HAL_IS_CCK_RATE(_pdesc)\
        (_pdesc->rxmcs == DESC92_RATE1M ||              \
index c7dc6fe..1342f81 100644 (file)
@@ -243,7 +243,7 @@ static int wl1251_spi_probe(struct spi_device *spi)
        struct wl1251 *wl;
        int ret;
 
-       pdata = spi->dev.platform_data;
+       pdata = dev_get_platdata(&spi->dev);
        if (!pdata) {
                wl1251_error("no platform data");
                return -ENODEV;
index fd02060..2c3bd1b 100644 (file)
@@ -424,8 +424,8 @@ void wl1251_disable_interrupts(struct wl1251 *wl);
 #define CHIP_ID_1271_PG10                 (0x4030101)
 #define CHIP_ID_1271_PG20                 (0x4030111)
 
-#define WL1251_FW_NAME "wl1251-fw.bin"
-#define WL1251_NVS_NAME "wl1251-nvs.bin"
+#define WL1251_FW_NAME "ti-connectivity/wl1251-fw.bin"
+#define WL1251_NVS_NAME "ti-connectivity/wl1251-nvs.bin"
 
 #define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */
 
index 1c627da..be7129b 100644 (file)
@@ -333,11 +333,11 @@ static struct wlcore_conf wl12xx_conf = {
                .always                        = 0,
        },
        .fwlog = {
-               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mode                         = WL12XX_FWLOG_CONTINUOUS,
                .mem_blocks                   = 2,
                .severity                     = 0,
                .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
-               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .output                       = WL12XX_FWLOG_OUTPUT_DBG_PINS,
                .threshold                    = 0,
        },
        .rate = {
@@ -717,6 +717,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->fw_mem_block_size = 256;
+       wl->fwlog_end = 0x2000000;
+
        /* common settings */
        wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
        wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
@@ -1262,9 +1265,10 @@ static int wl12xx_boot(struct wl1271 *wl)
                BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
-               MAX_TX_RETRY_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID;
 
+       wl->ap_event_mask = MAX_TX_RETRY_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1648,6 +1652,11 @@ static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return true;
 }
 
+static u32 wl12xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       return hwaddr << 5;
+}
+
 static int wl12xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl12xx_ops = {
@@ -1684,6 +1693,7 @@ static struct wlcore_ops wl12xx_ops = {
        .channel_switch         = wl12xx_cmd_channel_switch,
        .pre_pkt_send           = NULL,
        .set_peer_cap           = wl12xx_set_peer_cap,
+       .convert_hwaddr         = wl12xx_convert_hwaddr,
        .lnk_high_prio          = wl12xx_lnk_high_prio,
        .lnk_low_prio           = wl12xx_lnk_low_prio,
 };
@@ -1704,7 +1714,7 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
 static int wl12xx_setup(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
-       struct wlcore_platdev_data *pdev_data = wl->pdev->dev.platform_data;
+       struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev);
        struct wl12xx_platform_data *pdata = pdev_data->pdata;
 
        wl->rtable = wl12xx_rtable;
index 7aa0eb8..ec37b16 100644 (file)
@@ -456,11 +456,11 @@ static struct wlcore_conf wl18xx_conf = {
                .always                        = 0,
        },
        .fwlog = {
-               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mode                         = WL12XX_FWLOG_CONTINUOUS,
                .mem_blocks                   = 2,
                .severity                     = 0,
                .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
-               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .output                       = WL12XX_FWLOG_OUTPUT_DBG_PINS,
                .threshold                    = 0,
        },
        .rate = {
@@ -505,7 +505,7 @@ static struct wlcore_conf wl18xx_conf = {
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
        .ht = {
-               .mode                           = HT_MODE_DEFAULT,
+               .mode                           = HT_MODE_WIDE,
        },
        .phy = {
                .phy_standalone                 = 0x00,
@@ -516,7 +516,7 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
-               .low_band_component_type        = 0x04,
+               .low_band_component_type        = 0x05,
                .high_band_component            = COMPONENT_2_WAY_SWITCH,
                .high_band_component_type       = 0x09,
                .tcxo_ldo_voltage               = 0x00,
@@ -556,15 +556,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
                                                    0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x08,
-               .med_power_val                  = 0x12,
-               .high_power_val                 = 0x18,
-               .low_power_val_2nd              = 0x05,
-               .med_power_val_2nd              = 0x0a,
-               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
                .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .low_power_val                  = 0xff,
+               .med_power_val                  = 0xff,
+               .high_power_val                 = 0xff,
+               .low_power_val_2nd              = 0xff,
+               .med_power_val_2nd              = 0xff,
+               .high_power_val_2nd             = 0xff,
                .tx_rf_margin                   = 1,
        },
 };
@@ -623,6 +623,18 @@ static const int wl18xx_rtable[REG_TABLE_LEN] = {
        [REG_RAW_FW_STATUS_ADDR]        = WL18XX_FW_STATUS_ADDR,
 };
 
+static const struct wl18xx_clk_cfg wl18xx_clk_table_coex[NUM_CLOCK_CONFIGS] = {
+       [CLOCK_CONFIG_16_2_M]   = { 8,  121, 0, 0, false },
+       [CLOCK_CONFIG_16_368_M] = { 8,  120, 0, 0, false },
+       [CLOCK_CONFIG_16_8_M]   = { 8,  117, 0, 0, false },
+       [CLOCK_CONFIG_19_2_M]   = { 10, 128, 0, 0, false },
+       [CLOCK_CONFIG_26_M]     = { 11, 104, 0, 0, false },
+       [CLOCK_CONFIG_32_736_M] = { 8,  120, 0, 0, false },
+       [CLOCK_CONFIG_33_6_M]   = { 8,  117, 0, 0, false },
+       [CLOCK_CONFIG_38_468_M] = { 10, 128, 0, 0, false },
+       [CLOCK_CONFIG_52_M]     = { 11, 104, 0, 0, false },
+};
+
 static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
        [CLOCK_CONFIG_16_2_M]   = { 7,  104,  801, 4,  true },
        [CLOCK_CONFIG_16_368_M] = { 9,  132, 3751, 4,  true },
@@ -674,6 +686,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->fw_mem_block_size = 272;
+       wl->fwlog_end = 0x40000000;
+
        wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
        wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
        wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
@@ -704,6 +719,23 @@ static int wl18xx_set_clk(struct wl1271 *wl)
                     wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q,
                     wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit");
 
+       /* coex PLL configuration */
+       ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_N,
+                                  wl18xx_clk_table_coex[clk_freq].n);
+       if (ret < 0)
+               goto out;
+
+       ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_M,
+                                  wl18xx_clk_table_coex[clk_freq].m);
+       if (ret < 0)
+               goto out;
+
+       /* bypass the swallowing logic */
+       ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_SWALLOW_EN,
+                                  PLLSH_COEX_PLL_SWALLOW_EN_VAL1);
+       if (ret < 0)
+               goto out;
+
        ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N,
                                   wl18xx_clk_table[clk_freq].n);
        if (ret < 0)
@@ -745,6 +777,30 @@ static int wl18xx_set_clk(struct wl1271 *wl)
                                           PLLSH_WCS_PLL_SWALLOW_EN_VAL2);
        }
 
+       /* choose WCS PLL */
+       ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_SEL,
+                                  PLLSH_WL_PLL_SEL_WCS_PLL);
+       if (ret < 0)
+               goto out;
+
+       /* enable both PLLs */
+       ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_EN, PLLSH_WL_PLL_EN_VAL1);
+       if (ret < 0)
+               goto out;
+
+       udelay(1000);
+
+       /* disable coex PLL */
+       ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_EN, PLLSH_WL_PLL_EN_VAL2);
+       if (ret < 0)
+               goto out;
+
+       /* reset the swallowing logic */
+       ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_SWALLOW_EN,
+                                  PLLSH_COEX_PLL_SWALLOW_EN_VAL2);
+       if (ret < 0)
+               goto out;
+
 out:
        return ret;
 }
@@ -935,10 +991,11 @@ static int wl18xx_boot(struct wl1271 *wl)
                BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
-               MAX_TX_FAILURE_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID |
                DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
 
+       wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1175,16 +1232,48 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
        }
 }
 
+static const char *wl18xx_rdl_name(enum wl18xx_rdl_num rdl_num)
+{
+       switch (rdl_num) {
+       case RDL_1_HP:
+               return "183xH";
+       case RDL_2_SP:
+               return "183x or 180x";
+       case RDL_3_HP:
+               return "187xH";
+       case RDL_4_SP:
+               return "187x";
+       case RDL_5_SP:
+               return "RDL11 - Not Supported";
+       case RDL_6_SP:
+               return "180xD";
+       case RDL_7_SP:
+               return "RDL13 - Not Supported (1893Q)";
+       case RDL_8_SP:
+               return "18xxQ";
+       case RDL_NONE:
+               return "UNTRIMMED";
+       default:
+               return "UNKNOWN";
+       }
+}
+
 static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
 {
        u32 fuse;
-       s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0;
+       s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0, package_type = 0;
        int ret;
 
        ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
        if (ret < 0)
                goto out;
 
+       ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_2_3, &fuse);
+       if (ret < 0)
+               goto out;
+
+       package_type = (fuse >> WL18XX_PACKAGE_TYPE_OFFSET) & 1;
+
        ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_1_3, &fuse);
        if (ret < 0)
                goto out;
@@ -1192,7 +1281,7 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
        pg_ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
        rom = (fuse & WL18XX_ROM_VER_MASK) >> WL18XX_ROM_VER_OFFSET;
 
-       if (rom <= 0xE)
+       if ((rom <= 0xE) && (package_type == WL18XX_PACKAGE_TYPE_WSP))
                metal = (fuse & WL18XX_METAL_VER_MASK) >>
                        WL18XX_METAL_VER_OFFSET;
        else
@@ -1204,11 +1293,9 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
                goto out;
 
        rdl_ver = (fuse & WL18XX_RDL_VER_MASK) >> WL18XX_RDL_VER_OFFSET;
-       if (rdl_ver > RDL_MAX)
-               rdl_ver = RDL_NONE;
 
-       wl1271_info("wl18xx HW: RDL %d, %s, PG %x.%x (ROM %x)",
-                   rdl_ver, rdl_names[rdl_ver], pg_ver, metal, rom);
+       wl1271_info("wl18xx HW: %s, PG %d.%d (ROM 0x%x)",
+                   wl18xx_rdl_name(rdl_ver), pg_ver, metal, rom);
 
        if (ver)
                *ver = pg_ver;
@@ -1521,6 +1608,11 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return lnk->allocated_pkts < thold;
 }
 
+static u32 wl18xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       return hwaddr & ~0x80000000;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1558,6 +1650,7 @@ static struct wlcore_ops wl18xx_ops = {
        .pre_pkt_send   = wl18xx_pre_pkt_send,
        .sta_rc_update  = wl18xx_sta_rc_update,
        .set_peer_cap   = wl18xx_set_peer_cap,
+       .convert_hwaddr = wl18xx_convert_hwaddr,
        .lnk_high_prio  = wl18xx_lnk_high_prio,
        .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
index 05dd8ba..a433a75 100644 (file)
 #define PLATFORM_DETECTION             0xA0E3E0
 #define OCS_EN                         0xA02080
 #define PRIMARY_CLK_DETECT             0xA020A6
+#define PLLSH_COEX_PLL_N               0xA02384
+#define PLLSH_COEX_PLL_M               0xA02382
+#define PLLSH_COEX_PLL_SWALLOW_EN      0xA0238E
+#define PLLSH_WL_PLL_SEL               0xA02398
+
 #define PLLSH_WCS_PLL_N                        0xA02362
 #define PLLSH_WCS_PLL_M                        0xA02360
 #define PLLSH_WCS_PLL_Q_FACTOR_CFG_1   0xA02364
 #define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK      0xFFFF
 #define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK      0x000F
 
+#define PLLSH_WL_PLL_EN_VAL1           0x7
+#define PLLSH_WL_PLL_EN_VAL2           0x2
+#define PLLSH_COEX_PLL_SWALLOW_EN_VAL1 0x2
+#define PLLSH_COEX_PLL_SWALLOW_EN_VAL2 0x11
+
 #define PLLSH_WCS_PLL_SWALLOW_EN_VAL1  0x1
 #define PLLSH_WCS_PLL_SWALLOW_EN_VAL2  0x12
 
+#define PLLSH_WL_PLL_SEL_WCS_PLL       0x0
+#define PLLSH_WL_PLL_SEL_COEX_PLL      0x1
+
 #define WL18XX_REG_FUSE_DATA_1_3       0xA0260C
 #define WL18XX_PG_VER_MASK             0x70
 #define WL18XX_PG_VER_OFFSET           4
-#define WL18XX_ROM_VER_MASK            0x3
-#define WL18XX_ROM_VER_OFFSET          0
+#define WL18XX_ROM_VER_MASK            0x3e00
+#define WL18XX_ROM_VER_OFFSET          9
 #define WL18XX_METAL_VER_MASK          0xC
 #define WL18XX_METAL_VER_OFFSET                2
 #define WL18XX_NEW_METAL_VER_MASK      0x180
 #define WL18XX_NEW_METAL_VER_OFFSET    7
 
+#define WL18XX_PACKAGE_TYPE_OFFSET     13
+#define WL18XX_PACKAGE_TYPE_WSP                0
+
 #define WL18XX_REG_FUSE_DATA_2_3       0xA02614
 #define WL18XX_RDL_VER_MASK            0x1f00
 #define WL18XX_RDL_VER_OFFSET          8
@@ -201,24 +217,21 @@ enum {
        NUM_BOARD_TYPES,
 };
 
-enum {
+enum wl18xx_rdl_num {
        RDL_NONE        = 0,
        RDL_1_HP        = 1,
        RDL_2_SP        = 2,
        RDL_3_HP        = 3,
        RDL_4_SP        = 4,
+       RDL_5_SP        = 0x11,
+       RDL_6_SP        = 0x12,
+       RDL_7_SP        = 0x13,
+       RDL_8_SP        = 0x14,
 
        _RDL_LAST,
        RDL_MAX = _RDL_LAST - 1,
 };
 
-static const char * const rdl_names[] = {
-       [RDL_NONE]      = "",
-       [RDL_1_HP]      = "1853 SISO",
-       [RDL_2_SP]      = "1857 MIMO",
-       [RDL_3_HP]      = "1893 SISO",
-       [RDL_4_SP]      = "1897 MIMO",
-};
 
 /* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */
 #define WL18XX_PHY_FPGA_SPARE_1                0x8093CA40
index 7a970cd..ec83675 100644 (file)
@@ -162,7 +162,8 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map,
 
        wl1271_debug(DEBUG_ACX, "acx mem map");
 
-       ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len);
+       ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map,
+                                    sizeof(struct acx_header), len);
        if (ret < 0)
                return ret;
 
@@ -722,6 +723,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, void *stats)
        wl1271_debug(DEBUG_ACX, "acx statistics");
 
        ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats,
+                                    sizeof(struct acx_header),
                                     wl->stats.fw_stats_len);
        if (ret < 0) {
                wl1271_warning("acx statistics failed: %d", ret);
@@ -1470,8 +1472,8 @@ int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        tsf_info->role_id = wlvif->role_id;
 
-       ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO,
-                                    tsf_info, sizeof(*tsf_info));
+       ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info,
+                               sizeof(struct acx_header), sizeof(*tsf_info));
        if (ret < 0) {
                wl1271_warning("acx tsf info interrogate failed");
                goto out;
@@ -1752,7 +1754,7 @@ int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        acx->role_id = wlvif->role_id;
        ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL,
-                                    acx, sizeof(*acx));
+                                    acx, sizeof(*acx), sizeof(*acx));
        if (ret < 0) {
                wl1271_warning("acx roaming statistics failed: %d", ret);
                ret = -ENOMEM;
index c9e0607..34d9dff 100644 (file)
@@ -60,7 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
        u16 status;
        u16 poll_count = 0;
 
-       if (WARN_ON(unlikely(wl->state == WLCORE_STATE_RESTARTING)))
+       if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING &&
+                   id != CMD_STOP_FWLOGGER))
                return -EIO;
 
        cmd = buf;
@@ -845,7 +846,8 @@ EXPORT_SYMBOL_GPL(wl1271_cmd_test);
  * @buf: buffer for the response, including all headers, must work with dma
  * @len: length of buf
  */
-int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
+                          size_t cmd_len, size_t res_len)
 {
        struct acx_header *acx = buf;
        int ret;
@@ -854,10 +856,10 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
 
        acx->id = cpu_to_le16(id);
 
-       /* payload length, does not include any headers */
-       acx->len = cpu_to_le16(len - sizeof(*acx));
+       /* response payload length, does not include any headers */
+       acx->len = cpu_to_le16(res_len - sizeof(*acx));
 
-       ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len);
+       ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len);
        if (ret < 0)
                wl1271_error("INTERROGATE command failed");
 
@@ -1126,6 +1128,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        u16 template_id_2_4 = wl->scan_templ_id_2_4;
        u16 template_id_5 = wl->scan_templ_id_5;
 
+       wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
+
        skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
                                     ie_len);
        if (!skb) {
@@ -1135,8 +1139,6 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        if (ie_len)
                memcpy(skb_put(skb, ie_len), ie, ie_len);
 
-       wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
-
        if (sched_scan &&
            (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
                template_id_2_4 = wl->sched_scan_templ_id_2_4;
@@ -1172,7 +1174,7 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
        if (!skb)
                goto out;
 
-       wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len);
+       wl1271_debug(DEBUG_SCAN, "set ap probe request template");
 
        rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]);
        if (wlvif->band == IEEE80211_BAND_2GHZ)
@@ -1607,33 +1609,43 @@ out:
 
 static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch)
 {
-       int idx = -1;
-
+       /*
+        * map the given band/channel to the respective predefined
+        * bit expected by the fw
+        */
        switch (band) {
-       case IEEE80211_BAND_5GHZ:
-               if (ch >= 8 && ch <= 16)
-                       idx = ((ch-8)/4 + 18);
-               else if (ch >= 34 && ch <= 64)
-                       idx = ((ch-34)/2 + 3 + 18);
-               else if (ch >= 100 && ch <= 140)
-                       idx = ((ch-100)/4 + 15 + 18);
-               else if (ch >= 149 && ch <= 165)
-                       idx = ((ch-149)/4 + 26 + 18);
-               else
-                       idx = -1;
-               break;
        case IEEE80211_BAND_2GHZ:
+               /* channels 1..14 are mapped to 0..13 */
                if (ch >= 1 && ch <= 14)
-                       idx = ch - 1;
-               else
-                       idx = -1;
+                       return ch - 1;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               switch (ch) {
+               case 8 ... 16:
+                       /* channels 8,12,16 are mapped to 18,19,20 */
+                       return 18 + (ch-8)/4;
+               case 34 ... 48:
+                       /* channels 34,36..48 are mapped to 21..28 */
+                       return 21 + (ch-34)/2;
+               case 52 ... 64:
+                       /* channels 52,56..64 are mapped to 29..32 */
+                       return 29 + (ch-52)/4;
+               case 100 ... 140:
+                       /* channels 100,104..140 are mapped to 33..43 */
+                       return 33 + (ch-100)/4;
+               case 149 ... 165:
+                       /* channels 149,153..165 are mapped to 44..48 */
+                       return 44 + (ch-149)/4;
+               default:
+                       break;
+               }
                break;
        default:
-               wl1271_error("get reg conf ch idx - unknown band: %d",
-                            (int)band);
+               break;
        }
 
-       return idx;
+       wl1271_error("%s: unknown band/channel: %d/%d", __func__, band, ch);
+       return -1;
 }
 
 void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
@@ -1646,7 +1658,7 @@ void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
 
        ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel);
 
-       if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS)
+       if (ch_bit_idx >= 0 && ch_bit_idx <= WL1271_MAX_CHANNELS)
                set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending);
 }
 
index fd34123..323d4a8 100644 (file)
@@ -45,7 +45,8 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                     enum ieee80211_band band, int channel);
 int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
-int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
+int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
+                          size_t cmd_len, size_t res_len);
 int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
 int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
                                  size_t len, unsigned long valid_rets);
index 2b96ff8..40995c4 100644 (file)
@@ -1274,6 +1274,9 @@ struct conf_rx_streaming_settings {
        u8 always;
 } __packed;
 
+#define CONF_FWLOG_MIN_MEM_BLOCKS      2
+#define CONF_FWLOG_MAX_MEM_BLOCKS      16
+
 struct conf_fwlog {
        /* Continuous or on-demand */
        u8 mode;
@@ -1281,7 +1284,7 @@ struct conf_fwlog {
        /*
         * Number of memory blocks dedicated for the FW logger
         *
-        * Range: 1-3, or 0 to disable the FW logger
+        * Range: 2-16, or 0 to disable the FW logger
         */
        u8 mem_blocks;
 
index e17630c..89893c7 100644 (file)
@@ -437,6 +437,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        int res = 0;
        ssize_t ret;
        char *buf;
+       struct wl12xx_vif *wlvif;
 
 #define DRIVER_STATE_BUF_LEN 1024
 
@@ -450,12 +451,28 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
                          #x " = " fmt "\n", wl->x))
 
+#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...)   \
+       (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
+                         #x " = " fmt "\n", args))
+
 #define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld")
 #define DRIVER_STATE_PRINT_INT(x)  DRIVER_STATE_PRINT(x, "%d")
 #define DRIVER_STATE_PRINT_STR(x)  DRIVER_STATE_PRINT(x, "%s")
 #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx")
 #define DRIVER_STATE_PRINT_HEX(x)  DRIVER_STATE_PRINT(x, "0x%x")
 
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       continue;
+
+               DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+                                          wlvif->p2p ? "P2P-CL" : "STA");
+       }
+
+       wl12xx_for_each_wlvif_ap(wl, wlvif)
+               DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel,
+                                          wlvif->p2p ? "P2P-GO" : "AP");
+
        DRIVER_STATE_PRINT_INT(tx_blocks_available);
        DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
        DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
@@ -474,7 +491,6 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_INT(tx_blocks_freed);
        DRIVER_STATE_PRINT_INT(rx_counter);
        DRIVER_STATE_PRINT_INT(state);
-       DRIVER_STATE_PRINT_INT(channel);
        DRIVER_STATE_PRINT_INT(band);
        DRIVER_STATE_PRINT_INT(power_level);
        DRIVER_STATE_PRINT_INT(sg_enabled);
index 67f6168..8d3b349 100644 (file)
@@ -266,6 +266,7 @@ int wl1271_event_unmask(struct wl1271 *wl)
 {
        int ret;
 
+       wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask);
        ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
        if (ret < 0)
                return ret;
index 7fd260c..51f8d63 100644 (file)
@@ -222,6 +222,15 @@ wlcore_hw_set_peer_cap(struct wl1271 *wl,
        return 0;
 }
 
+static inline u32
+wlcore_hw_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
+{
+       if (!wl->ops->convert_hwaddr)
+               BUG_ON(1);
+
+       return wl->ops->convert_hwaddr(wl, hwaddr);
+}
+
 static inline bool
 wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
                        struct wl1271_link *lnk)
index 5c6f11e..7699f9d 100644 (file)
@@ -571,6 +571,12 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
                ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                if (ret < 0)
                        return ret;
+
+               /* unmask ap events */
+               wl->event_mask |= wl->ap_event_mask;
+               ret = wl1271_event_unmask(wl);
+               if (ret < 0)
+                       return ret;
        /* first STA, no APs */
        } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
index af7d9f9..07e3d6a 100644 (file)
@@ -165,8 +165,8 @@ static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr,
        int physical;
        int addr;
 
-       /* Addresses are stored internally as addresses to 32 bytes blocks */
-       addr = hwaddr << 5;
+       /* Convert from FW internal address which is chip arch dependent */
+       addr = wl->ops->convert_hwaddr(wl, hwaddr);
 
        physical = wlcore_translate_addr(wl, addr);
 
index 38995f9..0368b9c 100644 (file)
@@ -44,6 +44,7 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
+static int fwlog_mem_blocks = -1;
 static int bug_on_recovery = -1;
 static int no_recovery     = -1;
 
@@ -291,6 +292,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
 
+       /* Firmware Logger params */
+       if (fwlog_mem_blocks != -1) {
+               if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
+                   fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
+                       wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
+               } else {
+                       wl1271_error(
+                               "Illegal fwlog_mem_blocks=%d using default %d",
+                               fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
+               }
+       }
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -780,6 +793,7 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
        if (wl->state == WLCORE_STATE_ON) {
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+               wl1271_ps_elp_wakeup(wl);
                wlcore_disable_interrupts_nosync(wl);
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
        }
@@ -787,19 +801,10 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
 
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
 {
-       size_t len = 0;
-
-       /* The FW log is a length-value list, find where the log end */
-       while (len < maxlen) {
-               if (memblock[len] == 0)
-                       break;
-               if (len + memblock[len] + 1 > maxlen)
-                       break;
-               len += memblock[len] + 1;
-       }
+       size_t len;
 
        /* Make sure we have enough room */
-       len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
+       len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size));
 
        /* Fill the FW log file, consumed by the sysfs fwlog entry */
        memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
@@ -808,10 +813,9 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
        return len;
 }
 
-#define WLCORE_FW_LOG_END 0x2000000
-
 static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 {
+       struct wlcore_partition_set part, old_part;
        u32 addr;
        u32 offset;
        u32 end_of_log;
@@ -824,7 +828,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        wl1271_info("Reading FW panic log");
 
-       block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
+       block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL);
        if (!block)
                return;
 
@@ -850,17 +854,31 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
                offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
-               end_of_log = WLCORE_FW_LOG_END;
+               end_of_log = wl->fwlog_end;
        } else {
                offset = sizeof(addr);
                end_of_log = addr;
        }
 
+       old_part = wl->curr_part;
+       memset(&part, 0, sizeof(part));
+
        /* Traverse the memory blocks linked list */
        do {
-               memset(block, 0, WL12XX_HW_BLOCK_SIZE);
-               ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
-                                        false);
+               part.mem.start = wlcore_hw_convert_hwaddr(wl, addr);
+               part.mem.size  = PAGE_SIZE;
+
+               ret = wlcore_set_partition(wl, &part);
+               if (ret < 0) {
+                       wl1271_error("%s: set_partition start=0x%X size=%d",
+                               __func__, part.mem.start, part.mem.size);
+                       goto out;
+               }
+
+               memset(block, 0, wl->fw_mem_block_size);
+               ret = wlcore_read_hwaddr(wl, addr, block,
+                                       wl->fw_mem_block_size, false);
+
                if (ret < 0)
                        goto out;
 
@@ -871,8 +889,9 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                 * on demand mode and is equal to 0x2000000 in continuous mode.
                 */
                addr = le32_to_cpup((__le32 *)block);
+
                if (!wl12xx_copy_fwlog(wl, block + offset,
-                                      WL12XX_HW_BLOCK_SIZE - offset))
+                                       wl->fw_mem_block_size - offset))
                        break;
        } while (addr && (addr != end_of_log));
 
@@ -880,6 +899,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
 out:
        kfree(block);
+       wlcore_set_partition(wl, &old_part);
 }
 
 static void wlcore_print_recovery(struct wl1271 *wl)
@@ -924,7 +944,8 @@ static void wl1271_recovery_work(struct work_struct *work)
                goto out_unlock;
 
        if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
-               wl12xx_read_fwlog_panic(wl);
+               if (wl->conf.fwlog.output == WL12XX_FWLOG_OUTPUT_HOST)
+                       wl12xx_read_fwlog_panic(wl);
                wlcore_print_recovery(wl);
        }
 
@@ -1062,7 +1083,8 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
        static const char* const PLT_MODE[] = {
                "PLT_OFF",
                "PLT_ON",
-               "PLT_FEM_DETECT"
+               "PLT_FEM_DETECT",
+               "PLT_CHIP_AWAKE"
        };
 
        int ret;
@@ -1088,9 +1110,11 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
                if (ret < 0)
                        goto power_off;
 
-               ret = wl->ops->plt_init(wl);
-               if (ret < 0)
-                       goto power_off;
+               if (plt_mode != PLT_CHIP_AWAKE) {
+                       ret = wl->ops->plt_init(wl);
+                       if (ret < 0)
+                               goto power_off;
+               }
 
                wl->state = WLCORE_STATE_ON;
                wl1271_notice("firmware booted in PLT mode %s (%s)",
@@ -1925,8 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        /*
         * FW channels must be re-calibrated after recovery,
-        * clear the last Reg-Domain channel configuration.
+        * save current Reg-Domain channel configuration and clear it.
         */
+       memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last,
+              sizeof(wl->reg_ch_conf_pending));
        memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
@@ -2008,6 +2034,47 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
+static void wlcore_pending_auth_complete_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct wl12xx_vif *wlvif;
+       unsigned long time_spare;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif,
+                            pending_auth_complete_work);
+       wl = wlvif->wl;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /*
+        * Make sure a second really passed since the last auth reply. Maybe
+        * a second auth reply arrived while we were stuck on the mutex.
+        * Check for a little less than the timeout to protect from scheduler
+        * irregularities.
+        */
+       time_spare = jiffies +
+                       msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50);
+       if (!time_after(time_spare, wlvif->pending_auth_reply_time))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* cancel the ROC if active */
+       wlcore_update_inconn_sta(wl, wlvif, NULL, false);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
 {
        u8 policy = find_first_zero_bit(wl->rate_policies_map,
@@ -2159,6 +2226,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                          wlcore_channel_switch_work);
        INIT_DELAYED_WORK(&wlvif->connection_loss_work,
                          wlcore_connection_loss_work);
+       INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work,
+                         wlcore_pending_auth_complete_work);
        INIT_LIST_HEAD(&wlvif->list);
 
        setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -2376,6 +2445,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
        int ret = 0;
        u8 role_type;
 
+       if (wl->plt) {
+               wl1271_error("Adding Interface not allowed while in PLT mode");
+               return -EBUSY;
+       }
+
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
@@ -2572,6 +2646,12 @@ deinit:
            !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags))
                goto unlock;
 
+       if (wl->ap_count == 0 && is_ap) {
+               /* mask ap events */
+               wl->event_mask &= ~wl->ap_event_mask;
+               wl1271_event_unmask(wl);
+       }
+
        if (wl->ap_count == 0 && is_ap && wl->sta_count) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
                /* Configure for power according to debugfs */
@@ -2590,6 +2670,7 @@ unlock:
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
        cancel_delayed_work_sync(&wlvif->channel_switch_work);
+       cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -2875,6 +2956,25 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        wlvif->rate_set = wlvif->basic_rate_set;
 }
 
+static void wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                  bool idle)
+{
+       bool cur_idle = !test_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+
+       if (idle == cur_idle)
+               return;
+
+       if (idle) {
+               clear_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+       } else {
+               /* The current firmware only supports sched_scan in idle */
+               if (wl->sched_vif == wlvif)
+                       wl->ops->sched_scan_stop(wl, wlvif);
+
+               set_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+       }
+}
+
 static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                             struct ieee80211_conf *conf, u32 changed)
 {
@@ -3969,6 +4069,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                        }
                } else {
                        if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+                               /*
+                                * AP might be in ROC in case we have just
+                                * sent auth reply. handle it.
+                                */
+                               if (test_bit(wlvif->role_id, wl->roc_map))
+                                       wl12xx_croc(wl, wlvif->role_id);
+
                                ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
                                if (ret < 0)
                                        goto out;
@@ -4120,6 +4227,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                do_join = true;
        }
 
+       if (changed & BSS_CHANGED_IDLE && !is_ibss)
+               wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
+
        if (changed & BSS_CHANGED_CQM) {
                bool enable = false;
                if (bss_conf->cqm_rssi_thold)
@@ -4656,29 +4766,49 @@ static void wlcore_roc_if_possible(struct wl1271 *wl,
        wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
 }
 
-static void wlcore_update_inconn_sta(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif,
-                                    struct wl1271_station *wl_sta,
-                                    bool in_connection)
+/*
+ * when wl_sta is NULL, we treat this call as if coming from a
+ * pending auth reply.
+ * wl->mutex must be taken and the FW must be awake when the call
+ * takes place.
+ */
+void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct wl1271_station *wl_sta, bool in_conn)
 {
-       if (in_connection) {
-               if (WARN_ON(wl_sta->in_connection))
+       if (in_conn) {
+               if (WARN_ON(wl_sta && wl_sta->in_connection))
                        return;
-               wl_sta->in_connection = true;
-               if (!wlvif->inconn_count++)
+
+               if (!wlvif->ap_pending_auth_reply &&
+                   !wlvif->inconn_count)
                        wlcore_roc_if_possible(wl, wlvif);
+
+               if (wl_sta) {
+                       wl_sta->in_connection = true;
+                       wlvif->inconn_count++;
+               } else {
+                       wlvif->ap_pending_auth_reply = true;
+               }
        } else {
-               if (!wl_sta->in_connection)
+               if (wl_sta && !wl_sta->in_connection)
                        return;
 
-               wl_sta->in_connection = false;
-               wlvif->inconn_count--;
-               if (WARN_ON(wlvif->inconn_count < 0))
+               if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply))
                        return;
 
-               if (!wlvif->inconn_count)
-                       if (test_bit(wlvif->role_id, wl->roc_map))
-                               wl12xx_croc(wl, wlvif->role_id);
+               if (WARN_ON(wl_sta && !wlvif->inconn_count))
+                       return;
+
+               if (wl_sta) {
+                       wl_sta->in_connection = false;
+                       wlvif->inconn_count--;
+               } else {
+                       wlvif->ap_pending_auth_reply = false;
+               }
+
+               if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply &&
+                   test_bit(wlvif->role_id, wl->roc_map))
+                       wl12xx_croc(wl, wlvif->role_id);
        }
 }
 
@@ -5313,10 +5443,7 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {
 
 /* 5 GHz band channels for WL1273 */
 static struct ieee80211_channel wl1271_channels_5ghz[] = {
-       { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
-       { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
-       { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
@@ -5896,14 +6023,20 @@ static const struct wiphy_wowlan_support wlcore_wowlan_support = {
 };
 #endif
 
+static irqreturn_t wlcore_hardirq(int irq, void *cookie)
+{
+       return IRQ_WAKE_THREAD;
+}
+
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
        struct platform_device *pdev = wl->pdev;
-       struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
+       struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
        struct wl12xx_platform_data *pdata = pdev_data->pdata;
        unsigned long irqflags;
        int ret;
+       irq_handler_t hardirq_fn = NULL;
 
        if (fw) {
                wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
@@ -5932,12 +6065,14 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        wl->platform_quirks = pdata->platform_quirks;
        wl->if_ops = pdev_data->if_ops;
 
-       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) {
                irqflags = IRQF_TRIGGER_RISING;
-       else
+               hardirq_fn = wlcore_hardirq;
+       } else {
                irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+       }
 
-       ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+       ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq,
                                   irqflags, pdev->name, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
@@ -6046,6 +6181,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
+module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
+
 module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
 
index 98066d4..26bfc36 100644 (file)
@@ -83,6 +83,10 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
        struct wl12xx_vif *wlvif;
        u32 timeout;
 
+       /* We do not enter elp sleep in PLT mode */
+       if (wl->plt)
+               return;
+
        if (wl->sleep_auth != WL1271_PSM_ELP)
                return;
 
index f407101..7ed8620 100644 (file)
@@ -92,9 +92,31 @@ out:
 static void wlcore_started_vifs_iter(void *data, u8 *mac,
                                     struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       bool active = false;
        int *count = (int *)data;
 
-       if (!vif->bss_conf.idle)
+       /*
+        * count active interfaces according to interface type.
+        * checking only bss_conf.idle is bad for some cases, e.g.
+        * we don't want to count sta in p2p_find as active interface.
+        */
+       switch (wlvif->bss_type) {
+       case BSS_TYPE_STA_BSS:
+               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       active = true;
+               break;
+
+       case BSS_TYPE_AP_BSS:
+               if (wlvif->wl->active_sta_count > 0)
+                       active = true;
+               break;
+
+       default:
+               break;
+       }
+
+       if (active)
                (*count)++;
 }
 
@@ -174,17 +196,7 @@ wlcore_scan_get_channels(struct wl1271 *wl,
                    /* if radar is set, we ignore the passive flag */
                    (radar ||
                     !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) {
-                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req_channels[i]->band,
-                                    req_channels[i]->center_freq);
-                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req_channels[i]->hw_value,
-                                    req_channels[i]->flags);
-                       wl1271_debug(DEBUG_SCAN, "max_power %d",
-                                    req_channels[i]->max_power);
-                       wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d",
-                                    min_dwell_time_active,
-                                    max_dwell_time_active);
+
 
                        if (flags & IEEE80211_CHAN_RADAR) {
                                channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS;
@@ -222,6 +234,17 @@ wlcore_scan_get_channels(struct wl1271 *wl,
                                             *n_pactive_ch);
                        }
 
+                       wl1271_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s",
+                                    req_channels[i]->center_freq,
+                                    req_channels[i]->hw_value,
+                                    req_channels[i]->flags,
+                                    req_channels[i]->max_power,
+                                    min_dwell_time_active,
+                                    max_dwell_time_active,
+                                    flags & IEEE80211_CHAN_RADAR ?
+                                       ", DFS" : "",
+                                    flags & IEEE80211_CHAN_PASSIVE_SCAN ?
+                                       ", PASSIVE" : "");
                        j++;
                }
        }
@@ -364,7 +387,7 @@ wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
        struct cfg80211_ssid *ssids = req->ssids;
        int ret = 0, type, i, j, n_match_ssids = 0;
 
-       wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list");
+       wl1271_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list");
 
        /* count the match sets that contain SSIDs */
        for (i = 0; i < req->n_match_sets; i++)
@@ -442,8 +465,6 @@ wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
                }
        }
 
-       wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd));
-
        ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd,
                              sizeof(*cmd), 0);
        if (ret < 0) {
index 1b0cd98..b2c018d 100644 (file)
@@ -335,7 +335,7 @@ static int wl1271_probe(struct spi_device *spi)
        if (!pdev_data)
                goto out;
 
-       pdev_data->pdata = spi->dev.platform_data;
+       pdev_data->pdata = dev_get_platdata(&spi->dev);
        if (!pdev_data->pdata) {
                dev_err(&spi->dev, "no platform data\n");
                ret = -ENODEV;
index 527590f..ddad58f 100644 (file)
@@ -179,7 +179,8 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
                goto out_sleep;
        }
 
-       ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd));
+       ret = wl1271_cmd_interrogate(wl, ie_id, cmd,
+                                    sizeof(struct acx_header), sizeof(*cmd));
        if (ret < 0) {
                wl1271_warning("testmode cmd interrogate failed: %d", ret);
                goto out_free;
@@ -297,7 +298,8 @@ static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[])
                ret = wl1271_plt_stop(wl);
                break;
        case PLT_ON:
-               ret = wl1271_plt_start(wl, PLT_ON);
+       case PLT_CHIP_AWAKE:
+               ret = wl1271_plt_start(wl, val);
                break;
        case PLT_FEM_DETECT:
                ret = wl1271_tm_detect_fem(wl, tb);
@@ -361,6 +363,7 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 {
        struct wl1271 *wl = hw->priv;
        struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
+       u32 nla_cmd;
        int err;
 
        err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy);
@@ -370,7 +373,14 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        if (!tb[WL1271_TM_ATTR_CMD_ID])
                return -EINVAL;
 
-       switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) {
+       nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]);
+
+       /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */
+       if (wl->plt_mode == PLT_CHIP_AWAKE &&
+           nla_cmd != WL1271_TM_CMD_SET_PLT_MODE)
+               return -EOPNOTSUPP;
+
+       switch (nla_cmd) {
        case WL1271_TM_CMD_TEST:
                return wl1271_tm_cmd_test(wl, tb);
        case WL1271_TM_CMD_INTERROGATE:
index 7e93fe6..87cd707 100644 (file)
@@ -86,19 +86,34 @@ void wl1271_free_tx_id(struct wl1271 *wl, int id)
 EXPORT_SYMBOL(wl1271_free_tx_id);
 
 static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
+                                                struct wl12xx_vif *wlvif,
                                                 struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr;
 
+       hdr = (struct ieee80211_hdr *)(skb->data +
+                                      sizeof(struct wl1271_tx_hw_descr));
+       if (!ieee80211_is_auth(hdr->frame_control))
+               return;
+
        /*
         * add the station to the known list before transmitting the
         * authentication response. this way it won't get de-authed by FW
         * when transmitting too soon.
         */
-       hdr = (struct ieee80211_hdr *)(skb->data +
-                                      sizeof(struct wl1271_tx_hw_descr));
-       if (ieee80211_is_auth(hdr->frame_control))
-               wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+       wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+
+       /*
+        * ROC for 1 second on the AP channel for completing the connection.
+        * Note the ROC will be continued by the update_sta_state callbacks
+        * once the station reaches the associated state.
+        */
+       wlcore_update_inconn_sta(wl, wlvif, NULL, true);
+       wlvif->pending_auth_reply_time = jiffies;
+       cancel_delayed_work(&wlvif->pending_auth_complete_work);
+       ieee80211_queue_delayed_work(wl->hw,
+                               &wlvif->pending_auth_complete_work,
+                               msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT));
 }
 
 static void wl1271_tx_regulate_link(struct wl1271 *wl,
@@ -386,7 +401,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
                         (cipher == WLAN_CIPHER_SUITE_WEP104);
 
-               if (WARN_ON(is_wep && wlvif->default_key != idx)) {
+               if (WARN_ON(is_wep && wlvif && wlvif->default_key != idx)) {
                        ret = wl1271_set_default_wep_key(wl, wlvif, idx);
                        if (ret < 0)
                                return ret;
@@ -404,7 +419,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid);
 
        if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
-               wl1271_tx_ap_update_inconnection_sta(wl, skb);
+               wl1271_tx_ap_update_inconnection_sta(wl, wlvif, skb);
                wl1271_tx_regulate_link(wl, wlvif, hlid);
        }
 
index 55aa4ac..35489c3 100644 (file)
@@ -56,6 +56,9 @@
 /* Used for management frames and dummy packets */
 #define WL1271_TID_MGMT 7
 
+/* stop a ROC for pending authentication reply after this time (ms) */
+#define WLCORE_PEND_AUTH_ROC_TIMEOUT     1000
+
 struct wl127x_tx_mem {
        /*
         * Number of extra memory blocks to allocate for this packet
index 0034979..06efc12 100644 (file)
@@ -110,6 +110,7 @@ struct wlcore_ops {
                            struct ieee80211_sta_ht_cap *ht_cap,
                            bool allow_ht_operation,
                            u32 rate_set, u8 hlid);
+       u32 (*convert_hwaddr)(struct wl1271 *wl, u32 hwaddr);
        bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
                              struct wl1271_link *lnk);
        bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
@@ -290,6 +291,12 @@ struct wl1271 {
        /* Number of valid bytes in the FW log buffer */
        ssize_t fwlog_size;
 
+       /* FW log end marker */
+       u32 fwlog_end;
+
+       /* FW memory block size */
+       u32 fw_mem_block_size;
+
        /* Sysfs FW log entry readers wait queue */
        wait_queue_head_t fwlog_waitq;
 
@@ -307,6 +314,8 @@ struct wl1271 {
 
        /* The mbox event mask */
        u32 event_mask;
+       /* events to unmask only when ap interface is up */
+       u32 ap_event_mask;
 
        /* Mailbox pointers */
        u32 mbox_size;
@@ -481,6 +490,8 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                   struct ieee80211_sta *sta,
                   struct ieee80211_key_conf *key_conf);
 void wlcore_regdomain_config(struct wl1271 *wl);
+void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct wl1271_station *wl_sta, bool in_conn);
 
 static inline void
 wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
index e5e1464..ce7261c 100644 (file)
@@ -255,6 +255,7 @@ enum wl12xx_vif_flags {
        WLVIF_FLAG_CS_PROGRESS,
        WLVIF_FLAG_AP_PROBE_RESP_SET,
        WLVIF_FLAG_IN_USE,
+       WLVIF_FLAG_ACTIVE,
 };
 
 struct wl12xx_vif;
@@ -307,6 +308,7 @@ enum plt_mode {
        PLT_OFF = 0,
        PLT_ON = 1,
        PLT_FEM_DETECT = 2,
+       PLT_CHIP_AWAKE = 3
 };
 
 struct wl12xx_rx_filter_field {
@@ -456,6 +458,15 @@ struct wl12xx_vif {
         */
        int hw_queue_base;
 
+       /* do we have a pending auth reply? (and ROC) */
+       bool ap_pending_auth_reply;
+
+       /* time when we sent the pending auth reply */
+       unsigned long pending_auth_reply_time;
+
+       /* work for canceling ROC after pending auth reply */
+       struct delayed_work pending_auth_complete_work;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -539,6 +550,4 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
 #define HW_HT_RATES_OFFSET     16
 #define HW_MIMO_RATES_OFFSET   24
 
-#define WL12XX_HW_BLOCK_SIZE   256
-
 #endif /* __WLCORE_I_H__ */
index 5715318..08ae01b 100644 (file)
@@ -87,9 +87,13 @@ struct pending_tx_info {
 struct xenvif_rx_meta {
        int id;
        int size;
+       int gso_type;
        int gso_size;
 };
 
+#define GSO_BIT(type) \
+       (1 << XEN_NETIF_GSO_TYPE_ ## type)
+
 /* Discriminate from any valid pending_idx value. */
 #define INVALID_PENDING_IDX 0xFFFF
 
@@ -150,10 +154,12 @@ struct xenvif {
        u8               fe_dev_addr[6];
 
        /* Frontend feature information. */
+       int gso_mask;
+       int gso_prefix_mask;
+
        u8 can_sg:1;
-       u8 gso:1;
-       u8 gso_prefix:1;
-       u8 csum:1;
+       u8 ip_csum:1;
+       u8 ipv6_csum:1;
 
        /* Internal feature information. */
        u8 can_queue:1;     /* can queue packets for receiver? */
@@ -163,6 +169,7 @@ struct xenvif {
        unsigned long   credit_usec;
        unsigned long   remaining_credit;
        struct timer_list credit_timeout;
+       u64 credit_window_start;
 
        /* Statistics */
        unsigned long rx_gso_checksum_fixup;
index 01bb854..b78ee10 100644 (file)
@@ -214,10 +214,14 @@ static netdev_features_t xenvif_fix_features(struct net_device *dev,
 
        if (!vif->can_sg)
                features &= ~NETIF_F_SG;
-       if (!vif->gso && !vif->gso_prefix)
+       if (~(vif->gso_mask | vif->gso_prefix_mask) & GSO_BIT(TCPV4))
                features &= ~NETIF_F_TSO;
-       if (!vif->csum)
+       if (~(vif->gso_mask | vif->gso_prefix_mask) & GSO_BIT(TCPV6))
+               features &= ~NETIF_F_TSO6;
+       if (!vif->ip_csum)
                features &= ~NETIF_F_IP_CSUM;
+       if (!vif->ipv6_csum)
+               features &= ~NETIF_F_IPV6_CSUM;
 
        return features;
 }
@@ -306,18 +310,19 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        vif->domid  = domid;
        vif->handle = handle;
        vif->can_sg = 1;
-       vif->csum = 1;
+       vif->ip_csum = 1;
        vif->dev = dev;
 
        vif->credit_bytes = vif->remaining_credit = ~0UL;
        vif->credit_usec  = 0UL;
        init_timer(&vif->credit_timeout);
-       /* Initialize 'expires' now: it's used to track the credit window. */
-       vif->credit_timeout.expires = jiffies;
+       vif->credit_window_start = get_jiffies_64();
 
        dev->netdev_ops = &xenvif_netdev_ops;
-       dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
-       dev->features = dev->hw_features;
+       dev->hw_features = NETIF_F_SG |
+               NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+               NETIF_F_TSO | NETIF_F_TSO6;
+       dev->features = dev->hw_features | NETIF_F_RXCSUM;
        SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops);
 
        dev->tx_queue_len = XENVIF_QUEUE_LENGTH;
index f3e591c..919b650 100644 (file)
@@ -109,15 +109,12 @@ static inline unsigned long idx_to_kaddr(struct xenvif *vif,
        return (unsigned long)pfn_to_kaddr(idx_to_pfn(vif, idx));
 }
 
-/*
- * This is the amount of packet we copy rather than map, so that the
- * guest can't fiddle with the contents of the headers while we do
- * packet processing on them (netfilter, routing, etc).
+/* This is a miniumum size for the linear area to avoid lots of
+ * calls to __pskb_pull_tail() as we set up checksum offsets. The
+ * value 128 was chosen as it covers all IPv4 and most likely
+ * IPv6 headers.
  */
-#define PKT_PROT_LEN    (ETH_HLEN + \
-                        VLAN_HLEN + \
-                        sizeof(struct iphdr) + MAX_IPOPTLEN + \
-                        sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE)
+#define PKT_PROT_LEN 128
 
 static u16 frag_get_pending_idx(skb_frag_t *frag)
 {
@@ -145,7 +142,7 @@ static int max_required_rx_slots(struct xenvif *vif)
        int max = DIV_ROUND_UP(vif->dev->mtu, PAGE_SIZE);
 
        /* XXX FIXME: RX path dependent on MAX_SKB_FRAGS */
-       if (vif->can_sg || vif->gso || vif->gso_prefix)
+       if (vif->can_sg || vif->gso_mask || vif->gso_prefix_mask)
                max += MAX_SKB_FRAGS + 1; /* extra_info + frags */
 
        return max;
@@ -317,6 +314,7 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
        req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
 
        meta = npo->meta + npo->meta_prod++;
+       meta->gso_type = XEN_NETIF_GSO_TYPE_NONE;
        meta->gso_size = 0;
        meta->size = 0;
        meta->id = req->id;
@@ -339,6 +337,7 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
        struct gnttab_copy *copy_gop;
        struct xenvif_rx_meta *meta;
        unsigned long bytes;
+       int gso_type;
 
        /* Data must not cross a page boundary. */
        BUG_ON(size + offset > PAGE_SIZE<<compound_order(page));
@@ -397,7 +396,14 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                }
 
                /* Leave a gap for the GSO descriptor. */
-               if (*head && skb_shinfo(skb)->gso_size && !vif->gso_prefix)
+               if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
+                       gso_type = XEN_NETIF_GSO_TYPE_TCPV4;
+               else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
+                       gso_type = XEN_NETIF_GSO_TYPE_TCPV6;
+               else
+                       gso_type = XEN_NETIF_GSO_TYPE_NONE;
+
+               if (*head && ((1 << gso_type) & vif->gso_mask))
                        vif->rx.req_cons++;
 
                *head = 0; /* There must be something in this buffer now. */
@@ -428,14 +434,28 @@ static int xenvif_gop_skb(struct sk_buff *skb,
        unsigned char *data;
        int head = 1;
        int old_meta_prod;
+       int gso_type;
+       int gso_size;
 
        old_meta_prod = npo->meta_prod;
 
+       if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) {
+               gso_type = XEN_NETIF_GSO_TYPE_TCPV4;
+               gso_size = skb_shinfo(skb)->gso_size;
+       } else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) {
+               gso_type = XEN_NETIF_GSO_TYPE_TCPV6;
+               gso_size = skb_shinfo(skb)->gso_size;
+       } else {
+               gso_type = XEN_NETIF_GSO_TYPE_NONE;
+               gso_size = 0;
+       }
+
        /* Set up a GSO prefix descriptor, if necessary */
-       if (skb_shinfo(skb)->gso_size && vif->gso_prefix) {
+       if ((1 << skb_shinfo(skb)->gso_type) & vif->gso_prefix_mask) {
                req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
                meta = npo->meta + npo->meta_prod++;
-               meta->gso_size = skb_shinfo(skb)->gso_size;
+               meta->gso_type = gso_type;
+               meta->gso_size = gso_size;
                meta->size = 0;
                meta->id = req->id;
        }
@@ -443,10 +463,13 @@ static int xenvif_gop_skb(struct sk_buff *skb,
        req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
        meta = npo->meta + npo->meta_prod++;
 
-       if (!vif->gso_prefix)
-               meta->gso_size = skb_shinfo(skb)->gso_size;
-       else
+       if ((1 << gso_type) & vif->gso_mask) {
+               meta->gso_type = gso_type;
+               meta->gso_size = gso_size;
+       } else {
+               meta->gso_type = XEN_NETIF_GSO_TYPE_NONE;
                meta->gso_size = 0;
+       }
 
        meta->size = 0;
        meta->id = req->id;
@@ -592,7 +615,8 @@ void xenvif_rx_action(struct xenvif *vif)
 
                vif = netdev_priv(skb->dev);
 
-               if (vif->meta[npo.meta_cons].gso_size && vif->gso_prefix) {
+               if ((1 << vif->meta[npo.meta_cons].gso_type) &
+                   vif->gso_prefix_mask) {
                        resp = RING_GET_RESPONSE(&vif->rx,
                                                 vif->rx.rsp_prod_pvt++);
 
@@ -629,7 +653,8 @@ void xenvif_rx_action(struct xenvif *vif)
                                        vif->meta[npo.meta_cons].size,
                                        flags);
 
-               if (vif->meta[npo.meta_cons].gso_size && !vif->gso_prefix) {
+               if ((1 << vif->meta[npo.meta_cons].gso_type) &
+                   vif->gso_mask) {
                        struct xen_netif_extra_info *gso =
                                (struct xen_netif_extra_info *)
                                RING_GET_RESPONSE(&vif->rx,
@@ -637,8 +662,8 @@ void xenvif_rx_action(struct xenvif *vif)
 
                        resp->flags |= XEN_NETRXF_extra_info;
 
+                       gso->u.gso.type = vif->meta[npo.meta_cons].gso_type;
                        gso->u.gso.size = vif->meta[npo.meta_cons].gso_size;
-                       gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
                        gso->u.gso.pad = 0;
                        gso->u.gso.features = 0;
 
@@ -1101,15 +1126,20 @@ static int xenvif_set_skb_gso(struct xenvif *vif,
                return -EINVAL;
        }
 
-       /* Currently only TCPv4 S.O. is supported. */
-       if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) {
+       switch (gso->u.gso.type) {
+       case XEN_NETIF_GSO_TYPE_TCPV4:
+               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+               break;
+       case XEN_NETIF_GSO_TYPE_TCPV6:
+               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+               break;
+       default:
                netdev_err(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type);
                xenvif_fatal_tx_err(vif);
                return -EINVAL;
        }
 
        skb_shinfo(skb)->gso_size = gso->u.gso.size;
-       skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
 
        /* Header must be checked, and gso_segs computed. */
        skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
@@ -1118,61 +1148,74 @@ static int xenvif_set_skb_gso(struct xenvif *vif,
        return 0;
 }
 
-static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
+static inline void maybe_pull_tail(struct sk_buff *skb, unsigned int len)
+{
+       if (skb_is_nonlinear(skb) && skb_headlen(skb) < len) {
+               /* If we need to pullup then pullup to the max, so we
+                * won't need to do it again.
+                */
+               int target = min_t(int, skb->len, MAX_TCP_HEADER);
+               __pskb_pull_tail(skb, target - skb_headlen(skb));
+       }
+}
+
+static int checksum_setup_ip(struct xenvif *vif, struct sk_buff *skb,
+                            int recalculate_partial_csum)
 {
-       struct iphdr *iph;
+       struct iphdr *iph = (void *)skb->data;
+       unsigned int header_size;
+       unsigned int off;
        int err = -EPROTO;
-       int recalculate_partial_csum = 0;
 
-       /*
-        * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy
-        * peers can fail to set NETRXF_csum_blank when sending a GSO
-        * frame. In this case force the SKB to CHECKSUM_PARTIAL and
-        * recalculate the partial checksum.
-        */
-       if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
-               vif->rx_gso_checksum_fixup++;
-               skb->ip_summed = CHECKSUM_PARTIAL;
-               recalculate_partial_csum = 1;
-       }
+       off = sizeof(struct iphdr);
 
-       /* A non-CHECKSUM_PARTIAL SKB does not require setup. */
-       if (skb->ip_summed != CHECKSUM_PARTIAL)
-               return 0;
+       header_size = skb->network_header + off + MAX_IPOPTLEN;
+       maybe_pull_tail(skb, header_size);
 
-       if (skb->protocol != htons(ETH_P_IP))
-               goto out;
+       off = iph->ihl * 4;
 
-       iph = (void *)skb->data;
        switch (iph->protocol) {
        case IPPROTO_TCP:
-               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
+               if (!skb_partial_csum_set(skb, off,
                                          offsetof(struct tcphdr, check)))
                        goto out;
 
                if (recalculate_partial_csum) {
                        struct tcphdr *tcph = tcp_hdr(skb);
+
+                       header_size = skb->network_header +
+                               off +
+                               sizeof(struct tcphdr);
+                       maybe_pull_tail(skb, header_size);
+
                        tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                        skb->len - iph->ihl*4,
+                                                        skb->len - off,
                                                         IPPROTO_TCP, 0);
                }
                break;
        case IPPROTO_UDP:
-               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
+               if (!skb_partial_csum_set(skb, off,
                                          offsetof(struct udphdr, check)))
                        goto out;
 
                if (recalculate_partial_csum) {
                        struct udphdr *udph = udp_hdr(skb);
+
+                       header_size = skb->network_header +
+                               off +
+                               sizeof(struct udphdr);
+                       maybe_pull_tail(skb, header_size);
+
                        udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                        skb->len - iph->ihl*4,
+                                                        skb->len - off,
                                                         IPPROTO_UDP, 0);
                }
                break;
        default:
                if (net_ratelimit())
                        netdev_err(vif->dev,
-                                  "Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n",
+                                  "Attempting to checksum a non-TCP/UDP packet, "
+                                  "dropping a protocol %d packet\n",
                                   iph->protocol);
                goto out;
        }
@@ -1183,11 +1226,162 @@ out:
        return err;
 }
 
+static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb,
+                              int recalculate_partial_csum)
+{
+       int err = -EPROTO;
+       struct ipv6hdr *ipv6h = (void *)skb->data;
+       u8 nexthdr;
+       unsigned int header_size;
+       unsigned int off;
+       bool fragment;
+       bool done;
+
+       done = false;
+
+       off = sizeof(struct ipv6hdr);
+
+       header_size = skb->network_header + off;
+       maybe_pull_tail(skb, header_size);
+
+       nexthdr = ipv6h->nexthdr;
+
+       while ((off <= sizeof(struct ipv6hdr) + ntohs(ipv6h->payload_len)) &&
+              !done) {
+               switch (nexthdr) {
+               case IPPROTO_DSTOPTS:
+               case IPPROTO_HOPOPTS:
+               case IPPROTO_ROUTING: {
+                       struct ipv6_opt_hdr *hp = (void *)(skb->data + off);
+
+                       header_size = skb->network_header +
+                               off +
+                               sizeof(struct ipv6_opt_hdr);
+                       maybe_pull_tail(skb, header_size);
+
+                       nexthdr = hp->nexthdr;
+                       off += ipv6_optlen(hp);
+                       break;
+               }
+               case IPPROTO_AH: {
+                       struct ip_auth_hdr *hp = (void *)(skb->data + off);
+
+                       header_size = skb->network_header +
+                               off +
+                               sizeof(struct ip_auth_hdr);
+                       maybe_pull_tail(skb, header_size);
+
+                       nexthdr = hp->nexthdr;
+                       off += (hp->hdrlen+2)<<2;
+                       break;
+               }
+               case IPPROTO_FRAGMENT:
+                       fragment = true;
+                       /* fall through */
+               default:
+                       done = true;
+                       break;
+               }
+       }
+
+       if (!done) {
+               if (net_ratelimit())
+                       netdev_err(vif->dev, "Failed to parse packet header\n");
+               goto out;
+       }
+
+       if (fragment) {
+               if (net_ratelimit())
+                       netdev_err(vif->dev, "Packet is a fragment!\n");
+               goto out;
+       }
+
+       switch (nexthdr) {
+       case IPPROTO_TCP:
+               if (!skb_partial_csum_set(skb, off,
+                                         offsetof(struct tcphdr, check)))
+                       goto out;
+
+               if (recalculate_partial_csum) {
+                       struct tcphdr *tcph = tcp_hdr(skb);
+
+                       header_size = skb->network_header +
+                               off +
+                               sizeof(struct tcphdr);
+                       maybe_pull_tail(skb, header_size);
+
+                       tcph->check = ~csum_ipv6_magic(&ipv6h->saddr,
+                                                      &ipv6h->daddr,
+                                                      skb->len - off,
+                                                      IPPROTO_TCP, 0);
+               }
+               break;
+       case IPPROTO_UDP:
+               if (!skb_partial_csum_set(skb, off,
+                                         offsetof(struct udphdr, check)))
+                       goto out;
+
+               if (recalculate_partial_csum) {
+                       struct udphdr *udph = udp_hdr(skb);
+
+                       header_size = skb->network_header +
+                               off +
+                               sizeof(struct udphdr);
+                       maybe_pull_tail(skb, header_size);
+
+                       udph->check = ~csum_ipv6_magic(&ipv6h->saddr,
+                                                      &ipv6h->daddr,
+                                                      skb->len - off,
+                                                      IPPROTO_UDP, 0);
+               }
+               break;
+       default:
+               if (net_ratelimit())
+                       netdev_err(vif->dev,
+                                  "Attempting to checksum a non-TCP/UDP packet, "
+                                  "dropping a protocol %d packet\n",
+                                  nexthdr);
+               goto out;
+       }
+
+       err = 0;
+
+out:
+       return err;
+}
+
+static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
+{
+       int err = -EPROTO;
+       int recalculate_partial_csum = 0;
+
+       /* A GSO SKB must be CHECKSUM_PARTIAL. However some buggy
+        * peers can fail to set NETRXF_csum_blank when sending a GSO
+        * frame. In this case force the SKB to CHECKSUM_PARTIAL and
+        * recalculate the partial checksum.
+        */
+       if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
+               vif->rx_gso_checksum_fixup++;
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               recalculate_partial_csum = 1;
+       }
+
+       /* A non-CHECKSUM_PARTIAL SKB does not require setup. */
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       if (skb->protocol == htons(ETH_P_IP))
+               err = checksum_setup_ip(vif, skb, recalculate_partial_csum);
+       else if (skb->protocol == htons(ETH_P_IPV6))
+               err = checksum_setup_ipv6(vif, skb, recalculate_partial_csum);
+
+       return err;
+}
+
 static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
 {
-       unsigned long now = jiffies;
-       unsigned long next_credit =
-               vif->credit_timeout.expires +
+       u64 now = get_jiffies_64();
+       u64 next_credit = vif->credit_window_start +
                msecs_to_jiffies(vif->credit_usec / 1000);
 
        /* Timer could already be pending in rare cases. */
@@ -1195,8 +1389,8 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
                return true;
 
        /* Passed the point where we can replenish credit? */
-       if (time_after_eq(now, next_credit)) {
-               vif->credit_timeout.expires = now;
+       if (time_after_eq64(now, next_credit)) {
+               vif->credit_window_start = now;
                tx_add_credit(vif);
        }
 
@@ -1208,6 +1402,7 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
                        tx_credit_callback;
                mod_timer(&vif->credit_timeout,
                          next_credit);
+               vif->credit_window_start = next_credit;
 
                return true;
        }
@@ -1428,12 +1623,7 @@ static int xenvif_tx_submit(struct xenvif *vif, int budget)
 
                xenvif_fill_frags(vif, skb);
 
-               /*
-                * If the initial fragment was < PKT_PROT_LEN then
-                * pull through some bytes from the other fragments to
-                * increase the linear region to PKT_PROT_LEN bytes.
-                */
-               if (skb_headlen(skb) < PKT_PROT_LEN && skb_is_nonlinear(skb)) {
+               if (skb_is_nonlinear(skb) && skb_headlen(skb) < PKT_PROT_LEN) {
                        int target = min_t(int, skb->len, PKT_PROT_LEN);
                        __pskb_pull_tail(skb, target - skb_headlen(skb));
                }
index a53782e..f035899 100644 (file)
 struct backend_info {
        struct xenbus_device *dev;
        struct xenvif *vif;
+
+       /* This is the state that will be reflected in xenstore when any
+        * active hotplug script completes.
+        */
+       enum xenbus_state state;
+
        enum xenbus_state frontend_state;
        struct xenbus_watch hotplug_status_watch;
        u8 have_hotplug_status_watch:1;
@@ -33,11 +39,15 @@ static int connect_rings(struct backend_info *);
 static void connect(struct backend_info *);
 static void backend_create_xenvif(struct backend_info *be);
 static void unregister_hotplug_status_watch(struct backend_info *be);
+static void set_backend_state(struct backend_info *be,
+                             enum xenbus_state state);
 
 static int netback_remove(struct xenbus_device *dev)
 {
        struct backend_info *be = dev_get_drvdata(&dev->dev);
 
+       set_backend_state(be, XenbusStateClosed);
+
        unregister_hotplug_status_watch(be);
        if (be->vif) {
                kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
@@ -95,6 +105,22 @@ static int netback_probe(struct xenbus_device *dev,
                        goto abort_transaction;
                }
 
+               err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv6",
+                                   "%d", sg);
+               if (err) {
+                       message = "writing feature-gso-tcpv6";
+                       goto abort_transaction;
+               }
+
+               /* We support partial checksum setup for IPv6 packets */
+               err = xenbus_printf(xbt, dev->nodename,
+                                   "feature-ipv6-csum-offload",
+                                   "%d", 1);
+               if (err) {
+                       message = "writing feature-ipv6-csum-offload";
+                       goto abort_transaction;
+               }
+
                /* We support rx-copy path. */
                err = xenbus_printf(xbt, dev->nodename,
                                    "feature-rx-copy", "%d", 1);
@@ -136,6 +162,8 @@ static int netback_probe(struct xenbus_device *dev,
        if (err)
                goto fail;
 
+       be->state = XenbusStateInitWait;
+
        /* This kicks hotplug scripts, so do it immediately. */
        backend_create_xenvif(be);
 
@@ -208,24 +236,113 @@ static void backend_create_xenvif(struct backend_info *be)
        kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
 }
 
-
-static void disconnect_backend(struct xenbus_device *dev)
+static void backend_disconnect(struct backend_info *be)
 {
-       struct backend_info *be = dev_get_drvdata(&dev->dev);
-
        if (be->vif)
                xenvif_disconnect(be->vif);
 }
 
-static void destroy_backend(struct xenbus_device *dev)
+static void backend_connect(struct backend_info *be)
 {
-       struct backend_info *be = dev_get_drvdata(&dev->dev);
+       if (be->vif)
+               connect(be);
+}
 
-       if (be->vif) {
-               kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE);
-               xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status");
-               xenvif_free(be->vif);
-               be->vif = NULL;
+static inline void backend_switch_state(struct backend_info *be,
+                                       enum xenbus_state state)
+{
+       struct xenbus_device *dev = be->dev;
+
+       pr_debug("%s -> %s\n", dev->nodename, xenbus_strstate(state));
+       be->state = state;
+
+       /* If we are waiting for a hotplug script then defer the
+        * actual xenbus state change.
+        */
+       if (!be->have_hotplug_status_watch)
+               xenbus_switch_state(dev, state);
+}
+
+/* Handle backend state transitions:
+ *
+ * The backend state starts in InitWait and the following transitions are
+ * allowed.
+ *
+ * InitWait -> Connected
+ *
+ *    ^    \         |
+ *    |     \        |
+ *    |      \       |
+ *    |       \      |
+ *    |        \     |
+ *    |         \    |
+ *    |          V   V
+ *
+ *  Closed  <-> Closing
+ *
+ * The state argument specifies the eventual state of the backend and the
+ * function transitions to that state via the shortest path.
+ */
+static void set_backend_state(struct backend_info *be,
+                             enum xenbus_state state)
+{
+       while (be->state != state) {
+               switch (be->state) {
+               case XenbusStateClosed:
+                       switch (state) {
+                       case XenbusStateInitWait:
+                       case XenbusStateConnected:
+                               pr_info("%s: prepare for reconnect\n",
+                                       be->dev->nodename);
+                               backend_switch_state(be, XenbusStateInitWait);
+                               break;
+                       case XenbusStateClosing:
+                               backend_switch_state(be, XenbusStateClosing);
+                               break;
+                       default:
+                               BUG();
+                       }
+                       break;
+               case XenbusStateInitWait:
+                       switch (state) {
+                       case XenbusStateConnected:
+                               backend_connect(be);
+                               backend_switch_state(be, XenbusStateConnected);
+                               break;
+                       case XenbusStateClosing:
+                       case XenbusStateClosed:
+                               backend_switch_state(be, XenbusStateClosing);
+                               break;
+                       default:
+                               BUG();
+                       }
+                       break;
+               case XenbusStateConnected:
+                       switch (state) {
+                       case XenbusStateInitWait:
+                       case XenbusStateClosing:
+                       case XenbusStateClosed:
+                               backend_disconnect(be);
+                               backend_switch_state(be, XenbusStateClosing);
+                               break;
+                       default:
+                               BUG();
+                       }
+                       break;
+               case XenbusStateClosing:
+                       switch (state) {
+                       case XenbusStateInitWait:
+                       case XenbusStateConnected:
+                       case XenbusStateClosed:
+                               backend_switch_state(be, XenbusStateClosed);
+                               break;
+                       default:
+                               BUG();
+                       }
+                       break;
+               default:
+                       BUG();
+               }
        }
 }
 
@@ -237,40 +354,33 @@ static void frontend_changed(struct xenbus_device *dev,
 {
        struct backend_info *be = dev_get_drvdata(&dev->dev);
 
-       pr_debug("frontend state %s\n", xenbus_strstate(frontend_state));
+       pr_debug("%s -> %s\n", dev->otherend, xenbus_strstate(frontend_state));
 
        be->frontend_state = frontend_state;
 
        switch (frontend_state) {
        case XenbusStateInitialising:
-               if (dev->state == XenbusStateClosed) {
-                       pr_info("%s: prepare for reconnect\n", dev->nodename);
-                       xenbus_switch_state(dev, XenbusStateInitWait);
-               }
+               set_backend_state(be, XenbusStateInitWait);
                break;
 
        case XenbusStateInitialised:
                break;
 
        case XenbusStateConnected:
-               if (dev->state == XenbusStateConnected)
-                       break;
-               if (be->vif)
-                       connect(be);
+               set_backend_state(be, XenbusStateConnected);
                break;
 
        case XenbusStateClosing:
-               disconnect_backend(dev);
-               xenbus_switch_state(dev, XenbusStateClosing);
+               set_backend_state(be, XenbusStateClosing);
                break;
 
        case XenbusStateClosed:
-               xenbus_switch_state(dev, XenbusStateClosed);
+               set_backend_state(be, XenbusStateClosed);
                if (xenbus_dev_is_online(dev))
                        break;
-               destroy_backend(dev);
                /* fall through if not online */
        case XenbusStateUnknown:
+               set_backend_state(be, XenbusStateClosed);
                device_unregister(&dev->dev);
                break;
 
@@ -363,7 +473,9 @@ static void hotplug_status_changed(struct xenbus_watch *watch,
        if (IS_ERR(str))
                return;
        if (len == sizeof("connected")-1 && !memcmp(str, "connected", len)) {
-               xenbus_switch_state(be->dev, XenbusStateConnected);
+               /* Complete any pending state change */
+               xenbus_switch_state(be->dev, be->state);
+
                /* Not interested in this watch anymore. */
                unregister_hotplug_status_watch(be);
        }
@@ -393,12 +505,8 @@ static void connect(struct backend_info *be)
        err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
                                   hotplug_status_changed,
                                   "%s/%s", dev->nodename, "hotplug-status");
-       if (err) {
-               /* Switch now, since we can't do a watch. */
-               xenbus_switch_state(dev, XenbusStateConnected);
-       } else {
+       if (!err)
                be->have_hotplug_status_watch = 1;
-       }
 
        netif_wake_queue(be->vif->dev);
 }
@@ -469,20 +577,50 @@ static int connect_rings(struct backend_info *be)
                val = 0;
        vif->can_sg = !!val;
 
+       vif->gso_mask = 0;
+       vif->gso_prefix_mask = 0;
+
        if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv4",
                         "%d", &val) < 0)
                val = 0;
-       vif->gso = !!val;
+       if (val)
+               vif->gso_mask |= GSO_BIT(TCPV4);
 
        if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv4-prefix",
                         "%d", &val) < 0)
                val = 0;
-       vif->gso_prefix = !!val;
+       if (val)
+               vif->gso_prefix_mask |= GSO_BIT(TCPV4);
+
+       if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv6",
+                        "%d", &val) < 0)
+               val = 0;
+       if (val)
+               vif->gso_mask |= GSO_BIT(TCPV6);
+
+       if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv6-prefix",
+                        "%d", &val) < 0)
+               val = 0;
+       if (val)
+               vif->gso_prefix_mask |= GSO_BIT(TCPV6);
+
+       if (vif->gso_mask & vif->gso_prefix_mask) {
+               xenbus_dev_fatal(dev, err,
+                                "%s: gso and gso prefix flags are not "
+                                "mutually exclusive",
+                                dev->otherend);
+               return -EOPNOTSUPP;
+       }
 
        if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-no-csum-offload",
                         "%d", &val) < 0)
                val = 0;
-       vif->csum = !val;
+       vif->ip_csum = !val;
+
+       if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-ipv6-csum-offload",
+                        "%d", &val) < 0)
+               val = 0;
+       vif->ipv6_csum = !!val;
 
        /* Map the shared frame, irq etc. */
        err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref,
index 36808bf..dd1011e 100644 (file)
@@ -952,7 +952,7 @@ static int handle_incoming_queue(struct net_device *dev,
                u64_stats_update_end(&stats->syncp);
 
                /* Pass it up. */
-               netif_receive_skb(skb);
+               napi_gro_receive(&np->napi, skb);
        }
 
        return packets_dropped;
@@ -1051,6 +1051,8 @@ err:
        if (work_done < budget) {
                int more_to_do = 0;
 
+               napi_gro_flush(napi, false);
+
                local_irq_save(flags);
 
                RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do);
index b0b64cc..c1fb206 100644 (file)
@@ -46,6 +46,16 @@ config NFC_SIM
 
          If unsure, say N.
 
+config NFC_PORT100
+       tristate "Sony NFC Port-100 Series USB device support"
+       depends on USB
+       depends on NFC_DIGITAL
+       help
+         This adds support for Sony Port-100 chip based USB devices such as the
+         RC-S380 dongle.
+
+         If unsure, say N.
+
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 
index be7636a..c715fe8 100644 (file)
@@ -8,5 +8,6 @@ obj-$(CONFIG_NFC_PN533)         += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)      += mei_phy.o
 obj-$(CONFIG_NFC_SIM)          += nfcsim.o
+obj-$(CONFIG_NFC_PORT100)      += port100.o
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index 606bf55..85f9009 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/nfc.h>
@@ -60,13 +62,13 @@ int nfc_mei_phy_enable(void *phy_id)
 
        r = mei_cl_enable_device(phy->device);
        if (r < 0) {
-               pr_err("MEI_PHY: Could not enable device\n");
+               pr_err("Could not enable device\n");
                return r;
        }
 
        r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
        if (r) {
-               pr_err("MEY_PHY: Event cb registration failed\n");
+               pr_err("Event cb registration failed\n");
                mei_cl_disable_device(phy->device);
                phy->powered = 0;
 
index 1010894..696e346 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
@@ -95,12 +97,8 @@ static int check_crc(struct sk_buff *skb)
                crc = crc ^ skb->data[i];
 
        if (crc != skb->data[skb->len-1]) {
-               pr_err(MICROREAD_I2C_DRIVER_NAME
-                      ": CRC error 0x%x != 0x%x\n",
-                      crc, skb->data[skb->len-1]);
-
-               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
-
+               pr_err("CRC error 0x%x != 0x%x\n", crc, skb->data[skb->len-1]);
+               pr_info("%s: BAD CRC\n", __func__);
                return -EPERM;
        }
 
@@ -160,18 +158,15 @@ static int microread_i2c_read(struct microread_i2c_phy *phy,
        u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1];
        struct i2c_client *client = phy->i2c_dev;
 
-       pr_debug("%s\n", __func__);
-
        r = i2c_master_recv(client, &len, 1);
        if (r != 1) {
-               dev_err(&client->dev, "cannot read len byte\n");
+               nfc_err(&client->dev, "cannot read len byte\n");
                return -EREMOTEIO;
        }
 
        if ((len < MICROREAD_I2C_LLC_MIN_SIZE) ||
            (len > MICROREAD_I2C_LLC_MAX_SIZE)) {
-               dev_err(&client->dev, "invalid len byte\n");
-               pr_err("invalid len byte\n");
+               nfc_err(&client->dev, "invalid len byte\n");
                r = -EBADMSG;
                goto flush;
        }
@@ -228,7 +223,6 @@ static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
        }
 
        client = phy->i2c_dev;
-       dev_dbg(&client->dev, "IRQ\n");
 
        if (phy->hard_fault != 0)
                return IRQ_HANDLED;
@@ -263,20 +257,18 @@ static int microread_i2c_probe(struct i2c_client *client,
                dev_get_platdata(&client->dev);
        int r;
 
-       dev_dbg(&client->dev, "client %p", client);
+       dev_dbg(&client->dev, "client %p\n", client);
 
        if (!pdata) {
-               dev_err(&client->dev, "client %p: missing platform data",
+               nfc_err(&client->dev, "client %p: missing platform data\n",
                        client);
                return -EINVAL;
        }
 
        phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
                           GFP_KERNEL);
-       if (!phy) {
-               dev_err(&client->dev, "Can't allocate microread phy");
+       if (!phy)
                return -ENOMEM;
-       }
 
        i2c_set_clientdata(client, phy);
        phy->i2c_dev = client;
@@ -285,7 +277,7 @@ static int microread_i2c_probe(struct i2c_client *client,
                                 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
                                 MICROREAD_I2C_DRIVER_NAME, phy);
        if (r) {
-               dev_err(&client->dev, "Unable to register IRQ handler");
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
                return r;
        }
 
@@ -296,7 +288,7 @@ static int microread_i2c_probe(struct i2c_client *client,
        if (r < 0)
                goto err_irq;
 
-       dev_info(&client->dev, "Probed");
+       nfc_info(&client->dev, "Probed");
 
        return 0;
 
@@ -310,8 +302,6 @@ static int microread_i2c_remove(struct i2c_client *client)
 {
        struct microread_i2c_phy *phy = i2c_get_clientdata(client);
 
-       dev_dbg(&client->dev, "%s\n", __func__);
-
        microread_remove(phy->hdev);
 
        free_irq(client->irq, phy);
index cdf1bc5..72fafec 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/nfc.h>
@@ -59,8 +61,6 @@ static int microread_mei_remove(struct mei_cl_device *device)
 {
        struct nfc_mei_phy *phy = mei_cl_get_drvdata(device);
 
-       pr_info("Removing microread\n");
-
        microread_remove(phy->hdev);
 
        nfc_mei_phy_free(phy);
index cdb9f6d..970ded6 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -546,7 +548,7 @@ exit:
        kfree_skb(skb);
 
        if (r)
-               pr_err("Failed to handle discovered target err=%d", r);
+               pr_err("Failed to handle discovered target err=%d\n", r);
 }
 
 static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate,
@@ -656,7 +658,6 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 
        info = kzalloc(sizeof(struct microread_info), GFP_KERNEL);
        if (!info) {
-               pr_err("Cannot allocate memory for microread_info.\n");
                r = -ENOMEM;
                goto err_info_alloc;
        }
@@ -686,7 +687,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                                             MICROREAD_CMD_TAILROOM,
                                             phy_payload);
        if (!info->hdev) {
-               pr_err("Cannot allocate nfc hdev.\n");
+               pr_err("Cannot allocate nfc hdev\n");
                r = -ENOMEM;
                goto err_alloc_hdev;
        }
index 9a53f13..93111fa 100644 (file)
 #include <linux/nfc.h>
 #include <net/nfc/nfc.h>
 
-#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \
+#define DEV_ERR(_dev, fmt, args...) nfc_err(&_dev->nfc_dev->dev, \
                                                "%s: " fmt, __func__, ## args)
 
-#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \
+#define DEV_DBG(_dev, fmt, args...) dev_dbg(&_dev->nfc_dev->dev, \
                                                "%s: " fmt, __func__, ## args)
 
 #define NFCSIM_VERSION "0.1"
@@ -64,7 +64,7 @@ static struct workqueue_struct *wq;
 
 static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
 {
-       DEV_DBG(dev, "shutdown=%d", shutdown);
+       DEV_DBG(dev, "shutdown=%d\n", shutdown);
 
        mutex_lock(&dev->lock);
 
@@ -84,7 +84,7 @@ static int nfcsim_target_found(struct nfcsim *dev)
 {
        struct nfc_target nfc_tgt;
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        memset(&nfc_tgt, 0, sizeof(struct nfc_target));
 
@@ -98,7 +98,7 @@ static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        mutex_lock(&dev->lock);
 
@@ -113,7 +113,7 @@ static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        mutex_lock(&dev->lock);
 
@@ -143,7 +143,7 @@ static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
 
        remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
        if (!remote_gb) {
-               DEV_ERR(peer, "Can't get remote general bytes");
+               DEV_ERR(peer, "Can't get remote general bytes\n");
 
                mutex_unlock(&peer->lock);
                return -EINVAL;
@@ -155,7 +155,7 @@ static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
 
        rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
        if (rc) {
-               DEV_ERR(dev, "Can't set remote general bytes");
+               DEV_ERR(dev, "Can't set remote general bytes\n");
                mutex_unlock(&dev->lock);
                return rc;
        }
@@ -172,7 +172,7 @@ static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        nfcsim_cleanup_dev(dev, 0);
 
@@ -188,7 +188,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
        mutex_lock(&dev->lock);
 
        if (dev->polling_mode != NFCSIM_POLL_NONE) {
-               DEV_ERR(dev, "Already in polling mode");
+               DEV_ERR(dev, "Already in polling mode\n");
                rc = -EBUSY;
                goto exit;
        }
@@ -200,7 +200,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
                dev->polling_mode |= NFCSIM_POLL_TARGET;
 
        if (dev->polling_mode == NFCSIM_POLL_NONE) {
-               DEV_ERR(dev, "Unsupported polling mode");
+               DEV_ERR(dev, "Unsupported polling mode\n");
                rc = -EINVAL;
                goto exit;
        }
@@ -210,7 +210,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
 
        queue_delayed_work(wq, &dev->poll_work, 0);
 
-       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols,
+       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X\n", im_protocols,
                tm_protocols);
 
        rc = 0;
@@ -224,7 +224,7 @@ static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "Stop poll");
+       DEV_DBG(dev, "Stop poll\n");
 
        mutex_lock(&dev->lock);
 
@@ -240,7 +240,7 @@ static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 
        return -ENOTSUPP;
 }
@@ -250,7 +250,7 @@ static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
 {
        struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
 
-       DEV_DBG(dev, "");
+       DEV_DBG(dev, "\n");
 }
 
 static void nfcsim_wq_recv(struct work_struct *work)
@@ -267,7 +267,7 @@ static void nfcsim_wq_recv(struct work_struct *work)
 
        if (dev->initiator) {
                if (!dev->cb) {
-                       DEV_ERR(dev, "Null recv callback");
+                       DEV_ERR(dev, "Null recv callback\n");
                        dev_kfree_skb(dev->clone_skb);
                        goto exit;
                }
@@ -310,7 +310,7 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
        peer->clone_skb = skb_clone(skb, GFP_KERNEL);
 
        if (!peer->clone_skb) {
-               DEV_ERR(dev, "skb_clone failed");
+               DEV_ERR(dev, "skb_clone failed\n");
                mutex_unlock(&peer->lock);
                err = -ENOMEM;
                goto exit;
@@ -397,13 +397,13 @@ static void nfcsim_wq_poll(struct work_struct *work)
        nfcsim_set_polling_mode(dev);
 
        if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
-               DEV_DBG(dev, "Not polling");
+               DEV_DBG(dev, "Not polling\n");
                goto unlock;
        }
 
        DEV_DBG(dev, "Polling as %s",
                dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
-               "initiator" : "target");
+               "initiator\n" : "target\n");
 
        if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
                goto sched_work;
index 59f95d8..7130864 100644 (file)
@@ -146,13 +146,11 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
        unsigned long comp_ret;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry");
-
        skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
                                        GFP_KERNEL);
        if (!skb) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "no memory for nci_vs_nfcc_info_cmd");
+               nfc_err(&drv->pdev->dev,
+                       "no memory for nci_vs_nfcc_info_cmd\n");
                return -ENOMEM;
        }
 
@@ -170,21 +168,19 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
 
        comp_ret = wait_for_completion_timeout(&drv->completed,
                                msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
-       nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
-                       comp_ret);
+       dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
+               comp_ret);
        if (comp_ret == 0) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "timeout on wait_for_completion_timeout");
+               nfc_err(&drv->pdev->dev,
+                       "timeout on wait_for_completion_timeout\n");
                return -ETIMEDOUT;
        }
 
-       nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d",
-                       drv->nfcc_info.plen,
-                       drv->nfcc_info.status);
+       dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n",
+               drv->nfcc_info.plen, drv->nfcc_info.status);
 
        if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "invalid nci_vs_nfcc_info_rsp");
+               nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n");
                return -EINVAL;
        }
 
@@ -195,7 +191,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
                        drv->nfcc_info.sw_ver_z,
                        drv->nfcc_info.patch_id);
 
-       nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", file_name);
+       nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name);
 
        return 0;
 }
@@ -207,15 +203,13 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
        unsigned long comp_ret;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry");
-
        /* verify valid cmd for the NFC channel */
        if ((len <= sizeof(struct nfcwilink_hdr)) ||
                (len > BTS_FILE_CMD_MAX_LEN) ||
                (hdr->chnl != NFCWILINK_CHNL) ||
                (hdr->opcode != NFCWILINK_OPCODE)) {
-               nfc_dev_err(&drv->pdev->dev,
-                       "ignoring invalid bts cmd, len %d, chnl %d, opcode %d",
+               nfc_err(&drv->pdev->dev,
+                       "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n",
                        len, hdr->chnl, hdr->opcode);
                return 0;
        }
@@ -226,7 +220,7 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
 
        skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
        if (!skb) {
-               nfc_dev_err(&drv->pdev->dev, "no memory for bts cmd");
+               nfc_err(&drv->pdev->dev, "no memory for bts cmd\n");
                return -ENOMEM;
        }
 
@@ -238,11 +232,11 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
 
        comp_ret = wait_for_completion_timeout(&drv->completed,
                                msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
-       nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
-                       comp_ret);
+       dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
+               comp_ret);
        if (comp_ret == 0) {
-               nfc_dev_err(&drv->pdev->dev,
-                               "timeout on wait_for_completion_timeout");
+               nfc_err(&drv->pdev->dev,
+                       "timeout on wait_for_completion_timeout\n");
                return -ETIMEDOUT;
        }
 
@@ -257,8 +251,6 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
        __u8 *ptr;
        int len, rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "download_fw entry");
-
        set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
 
        rc = nfcwilink_get_bts_file_name(drv, file_name);
@@ -267,7 +259,7 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
 
        rc = request_firmware(&fw, file_name, &drv->pdev->dev);
        if (rc) {
-               nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", rc);
+               nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc);
 
                /* if the file is not found, don't exit with failure */
                if (rc == -ENOENT)
@@ -280,14 +272,14 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
        ptr = (__u8 *)fw->data;
 
        if ((len == 0) || (ptr == NULL)) {
-               nfc_dev_dbg(&drv->pdev->dev,
-                               "request_firmware returned size %d", len);
+               dev_dbg(&drv->pdev->dev,
+                       "request_firmware returned size %d\n", len);
                goto release_fw;
        }
 
        if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
                        BTS_FILE_HDR_MAGIC) {
-               nfc_dev_err(&drv->pdev->dev, "wrong bts magic number");
+               nfc_err(&drv->pdev->dev, "wrong bts magic number\n");
                rc = -EINVAL;
                goto release_fw;
        }
@@ -302,8 +294,8 @@ static int nfcwilink_download_fw(struct nfcwilink *drv)
                action_len =
                        __le16_to_cpu(((struct bts_file_action *)ptr)->len);
 
-               nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d",
-                               action_type, action_len);
+               dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n",
+                       action_type, action_len);
 
                switch (action_type) {
                case BTS_FILE_ACTION_TYPE_SEND_CMD:
@@ -333,8 +325,6 @@ static void nfcwilink_register_complete(void *priv_data, char data)
 {
        struct nfcwilink *drv = priv_data;
 
-       nfc_dev_dbg(&drv->pdev->dev, "register_complete entry");
-
        /* store ST registration status */
        drv->st_register_cb_status = data;
 
@@ -356,7 +346,7 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
                return -EFAULT;
        }
 
-       nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
+       dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len);
 
        /* strip the ST header
        (apart for the chnl byte, which is not received in the hdr) */
@@ -370,7 +360,7 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
        /* Forward skb to NCI core layer */
        rc = nci_recv_frame(drv->ndev, skb);
        if (rc < 0) {
-               nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
+               nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc);
                return rc;
        }
 
@@ -396,8 +386,6 @@ static int nfcwilink_open(struct nci_dev *ndev)
        unsigned long comp_ret;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "open entry");
-
        if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
                rc = -EBUSY;
                goto exit;
@@ -415,9 +403,9 @@ static int nfcwilink_open(struct nci_dev *ndev)
                        &drv->completed,
                        msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
 
-                       nfc_dev_dbg(&drv->pdev->dev,
-                       "wait_for_completion_timeout returned %ld",
-                       comp_ret);
+                       dev_dbg(&drv->pdev->dev,
+                               "wait_for_completion_timeout returned %ld\n",
+                               comp_ret);
 
                        if (comp_ret == 0) {
                                /* timeout */
@@ -425,13 +413,12 @@ static int nfcwilink_open(struct nci_dev *ndev)
                                goto clear_exit;
                        } else if (drv->st_register_cb_status != 0) {
                                rc = drv->st_register_cb_status;
-                               nfc_dev_err(&drv->pdev->dev,
-                               "st_register_cb failed %d", rc);
+                               nfc_err(&drv->pdev->dev,
+                                       "st_register_cb failed %d\n", rc);
                                goto clear_exit;
                        }
                } else {
-                       nfc_dev_err(&drv->pdev->dev,
-                               "st_register failed %d", rc);
+                       nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc);
                        goto clear_exit;
                }
        }
@@ -441,8 +428,8 @@ static int nfcwilink_open(struct nci_dev *ndev)
        drv->st_write = nfcwilink_proto.write;
 
        if (nfcwilink_download_fw(drv)) {
-               nfc_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d",
-                               rc);
+               nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n",
+                       rc);
                /* open should succeed, even if the FW download failed */
        }
 
@@ -460,14 +447,12 @@ static int nfcwilink_close(struct nci_dev *ndev)
        struct nfcwilink *drv = nci_get_drvdata(ndev);
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "close entry");
-
        if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
                return 0;
 
        rc = st_unregister(&nfcwilink_proto);
        if (rc)
-               nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc);
+               nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc);
 
        drv->st_write = NULL;
 
@@ -480,7 +465,7 @@ static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
        long len;
 
-       nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len);
+       dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len);
 
        if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) {
                kfree_skb(skb);
@@ -498,7 +483,7 @@ static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
        len = drv->st_write(skb);
        if (len < 0) {
                kfree_skb(skb);
-               nfc_dev_err(&drv->pdev->dev, "st_write failed %ld", len);
+               nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len);
                return -EFAULT;
        }
 
@@ -517,8 +502,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
        int rc;
        __u32 protocols;
 
-       nfc_dev_dbg(&pdev->dev, "probe entry");
-
        drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
        if (!drv) {
                rc = -ENOMEM;
@@ -538,7 +521,7 @@ static int nfcwilink_probe(struct platform_device *pdev)
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
-               nfc_dev_err(&pdev->dev, "nci_allocate_device failed");
+               nfc_err(&pdev->dev, "nci_allocate_device failed\n");
                rc = -ENOMEM;
                goto exit;
        }
@@ -548,7 +531,7 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        rc = nci_register_device(drv->ndev);
        if (rc < 0) {
-               nfc_dev_err(&pdev->dev, "nci_register_device failed %d", rc);
+               nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc);
                goto free_dev_exit;
        }
 
@@ -568,8 +551,6 @@ static int nfcwilink_remove(struct platform_device *pdev)
        struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
        struct nci_dev *ndev;
 
-       nfc_dev_dbg(&pdev->dev, "remove entry");
-
        if (!drv)
                return -EFAULT;
 
@@ -578,8 +559,6 @@ static int nfcwilink_remove(struct platform_device *pdev)
        nci_unregister_device(ndev);
        nci_free_device(ndev);
 
-       dev_set_drvdata(&pdev->dev, NULL);
-
        return 0;
 }
 
index 5df730b..2daf04c 100644 (file)
@@ -150,6 +150,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_TG_INIT_AS_TARGET 0x8c
 #define PN533_CMD_TG_GET_DATA 0x86
 #define PN533_CMD_TG_SET_DATA 0x8e
+#define PN533_CMD_TG_SET_META_DATA 0x94
 #define PN533_CMD_UNDEF 0xff
 
 #define PN533_CMD_RESPONSE(cmd) (cmd + 1)
@@ -373,6 +374,8 @@ struct pn533 {
        struct delayed_work poll_work;
        struct work_struct mi_rx_work;
        struct work_struct mi_tx_work;
+       struct work_struct mi_tm_rx_work;
+       struct work_struct mi_tm_tx_work;
        struct work_struct tg_work;
        struct work_struct rf_work;
 
@@ -387,6 +390,7 @@ struct pn533 {
        struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
        u8 poll_mod_count;
        u8 poll_mod_curr;
+       u8 poll_dep;
        u32 poll_protocols;
        u32 listen_protocols;
        struct timer_list listen_timer;
@@ -722,32 +726,32 @@ static void pn533_recv_response(struct urb *urb)
                break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The urb has been canceled (status %d)",
-                           urb->status);
+               dev_dbg(&dev->interface->dev,
+                       "The urb has been canceled (status %d)\n",
+                       urb->status);
                goto sched_wq;
        case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Urb failure (status %d)", urb->status);
+               nfc_err(&dev->interface->dev,
+                       "Urb failure (status %d)\n", urb->status);
                goto sched_wq;
        }
 
        in_frame = dev->in_urb->transfer_buffer;
 
-       nfc_dev_dbg(&dev->interface->dev, "Received a frame.");
+       dev_dbg(&dev->interface->dev, "Received a frame\n");
        print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
                             dev->ops->rx_frame_size(in_frame), false);
 
        if (!dev->ops->rx_is_frame_valid(in_frame, dev)) {
-               nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
+               nfc_err(&dev->interface->dev, "Received an invalid frame\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
 
        if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) {
-               nfc_dev_err(&dev->interface->dev,
-                           "It it not the response to the last command");
+               nfc_err(&dev->interface->dev,
+                       "It it not the response to the last command\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
@@ -777,29 +781,29 @@ static void pn533_recv_ack(struct urb *urb)
                break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The urb has been stopped (status %d)",
-                           urb->status);
+               dev_dbg(&dev->interface->dev,
+                       "The urb has been stopped (status %d)\n",
+                       urb->status);
                goto sched_wq;
        case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Urb failure (status %d)", urb->status);
+               nfc_err(&dev->interface->dev,
+                       "Urb failure (status %d)\n", urb->status);
                goto sched_wq;
        }
 
        in_frame = dev->in_urb->transfer_buffer;
 
        if (!pn533_std_rx_frame_is_ack(in_frame)) {
-               nfc_dev_err(&dev->interface->dev, "Received an invalid ack");
+               nfc_err(&dev->interface->dev, "Received an invalid ack\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
 
        rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "usb_submit_urb failed with result %d", rc);
+               nfc_err(&dev->interface->dev,
+                       "usb_submit_urb failed with result %d\n", rc);
                cmd->status = rc;
                goto sched_wq;
        }
@@ -823,8 +827,6 @@ static int pn533_send_ack(struct pn533 *dev, gfp_t flags)
        /* spec 7.1.1.3:  Preamble, SoPC (2), ACK Code (2), Postamble */
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        dev->out_urb->transfer_buffer = ack;
        dev->out_urb->transfer_buffer_length = sizeof(ack);
        rc = usb_submit_urb(dev->out_urb, flags);
@@ -927,7 +929,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
        struct pn533_cmd *cmd;
        int rc = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code);
+       dev_dbg(&dev->interface->dev, "Sending command 0x%x\n", cmd_code);
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (!cmd)
@@ -954,8 +956,8 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
                goto unlock;
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __func__,
-                   cmd_code);
+       dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x\n",
+               __func__, cmd_code);
 
        INIT_LIST_HEAD(&cmd->queue);
        list_add_tail(&cmd->queue, &dev->cmd_queue);
@@ -1168,14 +1170,14 @@ static void pn533_send_complete(struct urb *urb)
                break; /* success */
        case -ECONNRESET:
        case -ENOENT:
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The urb has been stopped (status %d)",
-                           urb->status);
+               dev_dbg(&dev->interface->dev,
+                       "The urb has been stopped (status %d)\n",
+                       urb->status);
                break;
        case -ESHUTDOWN:
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Urb failure (status %d)", urb->status);
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
+                       urb->status);
        }
 }
 
@@ -1452,8 +1454,8 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
        struct nfc_target nfc_tgt;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__,
-                   dev->poll_mod_curr);
+       dev_dbg(&dev->interface->dev, "%s: modulation=%d\n",
+               __func__, dev->poll_mod_curr);
 
        if (tg != 1)
                return -EPROTO;
@@ -1475,8 +1477,8 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
                rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len);
                break;
        default:
-               nfc_dev_err(&dev->interface->dev,
-                           "Unknown current poll modulation");
+               nfc_err(&dev->interface->dev,
+                       "Unknown current poll modulation\n");
                return -EPROTO;
        }
 
@@ -1484,14 +1486,14 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata,
                return rc;
 
        if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) {
-               nfc_dev_dbg(&dev->interface->dev,
-                           "The Tg found doesn't have the desired protocol");
+               dev_dbg(&dev->interface->dev,
+                       "The Tg found doesn't have the desired protocol\n");
                return -EAGAIN;
        }
 
-       nfc_dev_dbg(&dev->interface->dev,
-                   "Target found - supported protocols: 0x%x",
-                   nfc_tgt.supported_protocols);
+       dev_dbg(&dev->interface->dev,
+               "Target found - supported protocols: 0x%x\n",
+               nfc_tgt.supported_protocols);
 
        dev->tgt_available_prots = nfc_tgt.supported_protocols;
 
@@ -1548,7 +1550,8 @@ static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp)
        u8 nbtg, tg, *tgdata;
        int rc, tgdata_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       /* Toggle the DEP polling */
+       dev->poll_dep = 1;
 
        nbtg = resp->data[0];
        tg = resp->data[1];
@@ -1624,37 +1627,130 @@ static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev)
 
 #define PN533_CMD_DATAEXCH_HEAD_LEN 1
 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+static void pn533_wq_tm_mi_recv(struct work_struct *work);
+static struct sk_buff *pn533_build_response(struct pn533 *dev);
+
 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
                                      struct sk_buff *resp)
 {
-       u8 status;
+       struct sk_buff *skb;
+       u8 status, ret, mi;
+       int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
-       if (IS_ERR(resp))
+       if (IS_ERR(resp)) {
+               skb_queue_purge(&dev->resp_q);
                return PTR_ERR(resp);
+       }
 
        status = resp->data[0];
+
+       ret = status & PN533_CMD_RET_MASK;
+       mi = status & PN533_CMD_MI_MASK;
+
        skb_pull(resp, sizeof(status));
 
-       if (status != 0) {
-               nfc_tm_deactivated(dev->nfc_dev);
-               dev->tgt_mode = 0;
-               dev_kfree_skb(resp);
-               return 0;
+       if (ret != PN533_CMD_RET_SUCCESS) {
+               rc = -EIO;
+               goto error;
        }
 
-       return nfc_tm_data_received(dev->nfc_dev, resp);
+       skb_queue_tail(&dev->resp_q, resp);
+
+       if (mi) {
+               queue_work(dev->wq, &dev->mi_tm_rx_work);
+               return -EINPROGRESS;
+       }
+
+       skb = pn533_build_response(dev);
+       if (!skb) {
+               rc = -EIO;
+               goto error;
+       }
+
+       return nfc_tm_data_received(dev->nfc_dev, skb);
+
+error:
+       nfc_tm_deactivated(dev->nfc_dev);
+       dev->tgt_mode = 0;
+       skb_queue_purge(&dev->resp_q);
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static void pn533_wq_tm_mi_recv(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tm_rx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       skb = pn533_alloc_skb(dev, 0);
+       if (!skb)
+               return;
+
+       rc = pn533_send_cmd_direct_async(dev,
+                                       PN533_CMD_TG_GET_DATA,
+                                       skb,
+                                       pn533_tm_get_data_complete,
+                                       NULL);
+
+       if (rc < 0)
+               dev_kfree_skb(skb);
+
+       return;
+}
+
+static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
+                                 struct sk_buff *resp);
+static void pn533_wq_tm_mi_send(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       /* Grab the first skb in the queue */
+       skb = skb_dequeue(&dev->fragment_skb);
+       if (skb == NULL) {      /* No more data */
+               /* Reset the queue for future use */
+               skb_queue_head_init(&dev->fragment_skb);
+               goto error;
+       }
+
+       /* last entry - remove MI bit */
+       if (skb_queue_len(&dev->fragment_skb) == 0) {
+               rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA,
+                                       skb, pn533_tm_send_complete, NULL);
+       } else
+               rc = pn533_send_cmd_direct_async(dev,
+                                       PN533_CMD_TG_SET_META_DATA,
+                                       skb, pn533_tm_send_complete, NULL);
+
+       if (rc == 0) /* success */
+               return;
+
+       dev_err(&dev->interface->dev,
+               "Error %d when trying to perform set meta data_exchange", rc);
+
+       dev_kfree_skb(skb);
+
+error:
+       pn533_send_ack(dev, GFP_KERNEL);
+       queue_work(dev->wq, &dev->cmd_work);
 }
 
 static void pn533_wq_tg_get_data(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, tg_work);
-
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, 0);
        if (!skb)
@@ -1676,7 +1772,7 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
        size_t gb_len;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (resp->len < ATR_REQ_GB_OFFSET + 1)
                return -EINVAL;
@@ -1684,8 +1780,8 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
        mode = resp->data[0];
        cmd = &resp->data[1];
 
-       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n",
-                   mode, resp->len);
+       dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n",
+               mode, resp->len);
 
        if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) ==
            PN533_INIT_TARGET_RESP_ACTIVE)
@@ -1700,8 +1796,8 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp)
        rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
                              comm_mode, gb, gb_len);
        if (rc < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error when signaling target activation");
+               nfc_err(&dev->interface->dev,
+                       "Error when signaling target activation\n");
                return rc;
        }
 
@@ -1715,7 +1811,7 @@ static void pn533_listen_mode_timer(unsigned long data)
 {
        struct pn533 *dev = (struct pn533 *)data;
 
-       nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout");
+       dev_dbg(&dev->interface->dev, "Listen mode timeout\n");
 
        dev->cancel_listen = 1;
 
@@ -1730,13 +1826,12 @@ static int pn533_rf_complete(struct pn533 *dev, void *arg,
 {
        int rc = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
 
-               nfc_dev_err(&dev->interface->dev, "%s RF setting error %d",
-                           __func__, rc);
+               nfc_err(&dev->interface->dev, "RF setting error %d", rc);
 
                return rc;
        }
@@ -1754,7 +1849,7 @@ static void pn533_wq_rf(struct work_struct *work)
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, 2);
        if (!skb)
@@ -1767,25 +1862,136 @@ static void pn533_wq_rf(struct work_struct *work)
                                  pn533_rf_complete, NULL);
        if (rc < 0) {
                dev_kfree_skb(skb);
-               nfc_dev_err(&dev->interface->dev, "RF setting error %d", rc);
+               nfc_err(&dev->interface->dev, "RF setting error %d\n", rc);
        }
 
        return;
 }
 
+static int pn533_poll_dep_complete(struct pn533 *dev, void *arg,
+                                  struct sk_buff *resp)
+{
+       struct pn533_cmd_jump_dep_response *rsp;
+       struct nfc_target nfc_target;
+       u8 target_gt_len;
+       int rc;
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rsp = (struct pn533_cmd_jump_dep_response *)resp->data;
+
+       rc = rsp->status & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS) {
+               /* Not target found, turn radio off */
+               queue_work(dev->wq, &dev->rf_work);
+
+               dev_kfree_skb(resp);
+               return 0;
+       }
+
+       dev_dbg(&dev->interface->dev, "Creating new target");
+
+       nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+       nfc_target.nfcid1_len = 10;
+       memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len);
+       rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1);
+       if (rc)
+               goto error;
+
+       dev->tgt_available_prots = 0;
+       dev->tgt_active_prot = NFC_PROTO_NFC_DEP;
+
+       /* ATR_RES general bytes are located at offset 17 */
+       target_gt_len = resp->len - 17;
+       rc = nfc_set_remote_general_bytes(dev->nfc_dev,
+                                         rsp->gt, target_gt_len);
+       if (!rc) {
+               rc = nfc_dep_link_is_up(dev->nfc_dev,
+                                       dev->nfc_dev->targets[0].idx,
+                                       0, NFC_RF_INITIATOR);
+
+               if (!rc)
+                       pn533_poll_reset_mod_list(dev);
+       }
+error:
+       dev_kfree_skb(resp);
+       return rc;
+}
+
+#define PASSIVE_DATA_LEN 5
+static int pn533_poll_dep(struct nfc_dev *nfc_dev)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct sk_buff *skb;
+       int rc, skb_len;
+       u8 *next, nfcid3[NFC_NFCID3_MAXSIZE];
+       u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+
+       dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (!dev->gb) {
+               dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+
+               if (!dev->gb || !dev->gb_len) {
+                       dev->poll_dep = 0;
+                       queue_work(dev->wq, &dev->rf_work);
+               }
+       }
+
+       skb_len = 3 + dev->gb_len; /* ActPass + BR + Next */
+       skb_len += PASSIVE_DATA_LEN;
+
+       /* NFCID3 */
+       skb_len += NFC_NFCID3_MAXSIZE;
+       nfcid3[0] = 0x1;
+       nfcid3[1] = 0xfe;
+       get_random_bytes(nfcid3 + 2, 6);
+
+       skb = pn533_alloc_skb(dev, skb_len);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = 0x01;  /* Active */
+       *skb_put(skb, 1) = 0x02;  /* 424 kbps */
+
+       next = skb_put(skb, 1);  /* Next */
+       *next = 0;
+
+       /* Copy passive data */
+       memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
+       *next |= 1;
+
+       /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
+       memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
+              NFC_NFCID3_MAXSIZE);
+       *next |= 2;
+
+       memcpy(skb_put(skb, dev->gb_len), dev->gb, dev->gb_len);
+       *next |= 4; /* We have some Gi */
+
+       rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
+                                 pn533_poll_dep_complete, NULL);
+
+       if (rc < 0)
+               dev_kfree_skb(skb);
+
+       return rc;
+}
+
 static int pn533_poll_complete(struct pn533 *dev, void *arg,
                               struct sk_buff *resp)
 {
        struct pn533_poll_modulations *cur_mod;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
 
-               nfc_dev_err(&dev->interface->dev, "%s  Poll complete error %d",
-                           __func__, rc);
+               nfc_err(&dev->interface->dev, "%s  Poll complete error %d\n",
+                       __func__, rc);
 
                if (rc == -ENOENT) {
                        if (dev->poll_mod_count != 0)
@@ -1793,8 +1999,8 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg,
                        else
                                goto stop_poll;
                } else if (rc < 0) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Error %d when running poll", rc);
+                       nfc_err(&dev->interface->dev,
+                               "Error %d when running poll\n", rc);
                        goto stop_poll;
                }
        }
@@ -1813,7 +2019,7 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg,
                goto done;
 
        if (!dev->poll_mod_count) {
-               nfc_dev_dbg(&dev->interface->dev, "Polling has been stopped.");
+               dev_dbg(&dev->interface->dev, "Polling has been stopped\n");
                goto done;
        }
 
@@ -1826,7 +2032,7 @@ done:
        return rc;
 
 stop_poll:
-       nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped");
+       nfc_err(&dev->interface->dev, "Polling operation has been stopped\n");
 
        pn533_poll_reset_mod_list(dev);
        dev->poll_protocols = 0;
@@ -1856,8 +2062,13 @@ static int pn533_send_poll_frame(struct pn533 *dev)
 
        mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n",
-                   __func__, mod->len);
+       dev_dbg(&dev->interface->dev, "%s mod len %d\n",
+               __func__, mod->len);
+
+       if (dev->poll_dep)  {
+               dev->poll_dep = 0;
+               return pn533_poll_dep(dev->nfc_dev);
+       }
 
        if (mod->len == 0) {  /* Listen mode */
                cmd_code = PN533_CMD_TG_INIT_AS_TARGET;
@@ -1868,7 +2079,7 @@ static int pn533_send_poll_frame(struct pn533 *dev)
        }
 
        if (!skb) {
-               nfc_dev_err(&dev->interface->dev, "Failed to allocate skb.");
+               nfc_err(&dev->interface->dev, "Failed to allocate skb\n");
                return -ENOMEM;
        }
 
@@ -1876,7 +2087,7 @@ static int pn533_send_poll_frame(struct pn533 *dev)
                                  NULL);
        if (rc < 0) {
                dev_kfree_skb(skb);
-               nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc);
+               nfc_err(&dev->interface->dev, "Polling loop error %d\n", rc);
        }
 
        return rc;
@@ -1890,9 +2101,9 @@ static void pn533_wq_poll(struct work_struct *work)
 
        cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
 
-       nfc_dev_dbg(&dev->interface->dev,
-                   "%s cancel_listen %d modulation len %d",
-                   __func__, dev->cancel_listen, cur_mod->len);
+       dev_dbg(&dev->interface->dev,
+               "%s cancel_listen %d modulation len %d\n",
+               __func__, dev->cancel_listen, cur_mod->len);
 
        if (dev->cancel_listen == 1) {
                dev->cancel_listen = 0;
@@ -1913,21 +2124,23 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
                            u32 im_protocols, u32 tm_protocols)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct pn533_poll_modulations *cur_mod;
        u8 rand_mod;
+       int rc;
 
-       nfc_dev_dbg(&dev->interface->dev,
-                   "%s: im protocols 0x%x tm protocols 0x%x",
-                   __func__, im_protocols, tm_protocols);
+       dev_dbg(&dev->interface->dev,
+               "%s: im protocols 0x%x tm protocols 0x%x\n",
+               __func__, im_protocols, tm_protocols);
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot poll with a target already activated");
+               nfc_err(&dev->interface->dev,
+                       "Cannot poll with a target already activated\n");
                return -EBUSY;
        }
 
        if (dev->tgt_mode) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot poll while already being activated");
+               nfc_err(&dev->interface->dev,
+                       "Cannot poll while already being activated\n");
                return -EBUSY;
        }
 
@@ -1946,20 +2159,26 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
        rand_mod %= dev->poll_mod_count;
        dev->poll_mod_curr = rand_mod;
 
-       return pn533_send_poll_frame(dev);
+       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       rc = pn533_send_poll_frame(dev);
+
+       /* Start listen timer */
+       if (!rc && cur_mod->len == 0 && dev->poll_mod_count > 1)
+               mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ);
+
+       return rc;
 }
 
 static void pn533_stop_poll(struct nfc_dev *nfc_dev)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
-
        del_timer(&dev->listen_timer);
 
        if (!dev->poll_mod_count) {
-               nfc_dev_dbg(&dev->interface->dev,
-                           "Polling operation was not running");
+               dev_dbg(&dev->interface->dev,
+                       "Polling operation was not running\n");
                return;
        }
 
@@ -1973,11 +2192,10 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev)
        struct pn533_cmd_activate_response *rsp;
        u16 gt_len;
        int rc;
-
        struct sk_buff *skb;
        struct sk_buff *resp;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/
        if (!skb)
@@ -1993,8 +2211,8 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev)
        rsp = (struct pn533_cmd_activate_response *)resp->data;
        rc = rsp->status & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Target activation failed (error 0x%x)", rc);
+               nfc_err(&dev->interface->dev,
+                       "Target activation failed (error 0x%x)\n", rc);
                dev_kfree_skb(resp);
                return -EIO;
        }
@@ -2013,39 +2231,38 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev,
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__,
-                   protocol);
+       dev_dbg(&dev->interface->dev, "%s: protocol=%u\n", __func__, protocol);
 
        if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot activate while polling");
+               nfc_err(&dev->interface->dev,
+                       "Cannot activate while polling\n");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "There is already an active target");
+               nfc_err(&dev->interface->dev,
+                       "There is already an active target\n");
                return -EBUSY;
        }
 
        if (!dev->tgt_available_prots) {
-               nfc_dev_err(&dev->interface->dev,
-                           "There is no available target to activate");
+               nfc_err(&dev->interface->dev,
+                       "There is no available target to activate\n");
                return -EINVAL;
        }
 
        if (!(dev->tgt_available_prots & (1 << protocol))) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Target doesn't support requested proto %u",
-                           protocol);
+               nfc_err(&dev->interface->dev,
+                       "Target doesn't support requested proto %u\n",
+                       protocol);
                return -EINVAL;
        }
 
        if (protocol == NFC_PROTO_NFC_DEP) {
                rc = pn533_activate_target_nfcdep(dev);
                if (rc) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Activating target with DEP failed %d", rc);
+                       nfc_err(&dev->interface->dev,
+                               "Activating target with DEP failed %d\n", rc);
                        return rc;
                }
        }
@@ -2060,16 +2277,14 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
                                    struct nfc_target *target)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-
        struct sk_buff *skb;
        struct sk_buff *resp;
-
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (!dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "There is no active target");
+               nfc_err(&dev->interface->dev, "There is no active target\n");
                return;
        }
 
@@ -2088,8 +2303,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
 
        rc = resp->data[0] & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS)
-               nfc_dev_err(&dev->interface->dev,
-                           "Error 0x%x when releasing the target", rc);
+               nfc_err(&dev->interface->dev,
+                       "Error 0x%x when releasing the target\n", rc);
 
        dev_kfree_skb(resp);
        return;
@@ -2111,8 +2326,8 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
 
        if (dev->tgt_available_prots &&
            !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) {
-               nfc_dev_err(&dev->interface->dev,
-                           "The target does not support DEP");
+               nfc_err(&dev->interface->dev,
+                       "The target does not support DEP\n");
                rc =  -EINVAL;
                goto error;
        }
@@ -2121,15 +2336,15 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
 
        rc = rsp->status & PN533_CMD_RET_MASK;
        if (rc != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Bringing DEP link up failed (error 0x%x)", rc);
+               nfc_err(&dev->interface->dev,
+                       "Bringing DEP link up failed (error 0x%x)\n", rc);
                goto error;
        }
 
        if (!dev->tgt_available_prots) {
                struct nfc_target nfc_target;
 
-               nfc_dev_dbg(&dev->interface->dev, "Creating new target");
+               dev_dbg(&dev->interface->dev, "Creating new target\n");
 
                nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
                nfc_target.nfcid1_len = 10;
@@ -2158,7 +2373,6 @@ error:
 }
 
 static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf);
-#define PASSIVE_DATA_LEN 5
 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                             u8 comm_mode, u8 *gb, size_t gb_len)
 {
@@ -2166,20 +2380,19 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
        struct sk_buff *skb;
        int rc, skb_len;
        u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE];
-
        u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Cannot bring the DEP link up while polling");
+               nfc_err(&dev->interface->dev,
+                       "Cannot bring the DEP link up while polling\n");
                return -EBUSY;
        }
 
        if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "There is already an active target");
+               nfc_err(&dev->interface->dev,
+                       "There is already an active target\n");
                return -EBUSY;
        }
 
@@ -2249,7 +2462,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        pn533_poll_reset_mod_list(dev);
 
@@ -2274,7 +2487,7 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev)
        struct sk_buff *skb, *tmp, *t;
        unsigned int skb_len = 0, tmp_len = 0;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (skb_queue_empty(&dev->resp_q))
                return NULL;
@@ -2287,8 +2500,8 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev)
        skb_queue_walk_safe(&dev->resp_q, tmp, t)
                skb_len += tmp->len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s total length %d\n",
-                   __func__, skb_len);
+       dev_dbg(&dev->interface->dev, "%s total length %d\n",
+               __func__, skb_len);
 
        skb = alloc_skb(skb_len, GFP_KERNEL);
        if (skb == NULL)
@@ -2315,7 +2528,7 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        int rc = 0;
        u8 status, ret, mi;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
@@ -2329,8 +2542,8 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        skb_pull(resp, sizeof(status));
 
        if (ret != PN533_CMD_RET_SUCCESS) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Exchanging data failed (error 0x%x)", ret);
+               nfc_err(&dev->interface->dev,
+                       "Exchanging data failed (error 0x%x)\n", ret);
                rc = -EIO;
                goto error;
        }
@@ -2388,14 +2601,17 @@ static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
                        break;
                }
 
-               /* Reserve the TG/MI byte */
-               skb_reserve(frag, 1);
+               if (!dev->tgt_mode) {
+                       /* Reserve the TG/MI byte */
+                       skb_reserve(frag, 1);
 
-               /* MI + TG */
-               if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
-                       *skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1);
-               else
-                       *skb_push(frag, sizeof(u8)) =  1; /* TG */
+                       /* MI + TG */
+                       if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
+                               *skb_push(frag, sizeof(u8)) =
+                                                       (PN533_CMD_MI_MASK | 1);
+                       else
+                               *skb_push(frag, sizeof(u8)) =  1; /* TG */
+               }
 
                memcpy(skb_put(frag, frag_size), skb->data, frag_size);
 
@@ -2420,11 +2636,11 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
        struct pn533_data_exchange_arg *arg = NULL;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (!dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Can't exchange data if there is no active target");
+               nfc_err(&dev->interface->dev,
+                       "Can't exchange data if there is no active target\n");
                rc = -EINVAL;
                goto error;
        }
@@ -2487,13 +2703,18 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
 {
        u8 status;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        if (IS_ERR(resp))
                return PTR_ERR(resp);
 
        status = resp->data[0];
 
+       /* Prepare for the next round */
+       if (skb_queue_len(&dev->fragment_skb) > 0) {
+               queue_work(dev->wq, &dev->mi_tm_tx_work);
+               return -EINPROGRESS;
+       }
        dev_kfree_skb(resp);
 
        if (status != 0) {
@@ -2514,19 +2735,34 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
+       /* let's split in multiple chunks if size's too big */
        if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Data length greater than the max allowed: %d",
-                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
-               return -ENOSYS;
+               rc = pn533_fill_fragment_skbs(dev, skb);
+               if (rc <= 0)
+                       goto error;
+
+               /* get the first skb */
+               skb = skb_dequeue(&dev->fragment_skb);
+               if (!skb) {
+                       rc = -EIO;
+                       goto error;
+               }
+
+               rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb,
+                                               pn533_tm_send_complete, NULL);
+       } else {
+               /* Send th skb */
+               rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
+                                               pn533_tm_send_complete, NULL);
        }
 
-       rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb,
-                                  pn533_tm_send_complete, NULL);
-       if (rc < 0)
+error:
+       if (rc < 0) {
                dev_kfree_skb(skb);
+               skb_queue_purge(&dev->fragment_skb);
+       }
 
        return rc;
 }
@@ -2534,11 +2770,10 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 static void pn533_wq_mi_recv(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
-
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN);
        if (!skb)
@@ -2570,8 +2805,8 @@ static void pn533_wq_mi_recv(struct work_struct *work)
        if (rc == 0) /* success */
                return;
 
-       nfc_dev_err(&dev->interface->dev,
-                   "Error %d when trying to perform data_exchange", rc);
+       nfc_err(&dev->interface->dev,
+               "Error %d when trying to perform data_exchange\n", rc);
 
        dev_kfree_skb(skb);
        kfree(dev->cmd_complete_mi_arg);
@@ -2587,7 +2822,7 @@ static void pn533_wq_mi_send(struct work_struct *work)
        struct sk_buff *skb;
        int rc;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        /* Grab the first skb in the queue */
        skb = skb_dequeue(&dev->fragment_skb);
@@ -2625,8 +2860,8 @@ static void pn533_wq_mi_send(struct work_struct *work)
        if (rc == 0) /* success */
                return;
 
-       nfc_dev_err(&dev->interface->dev,
-                   "Error %d when trying to perform data_exchange", rc);
+       nfc_err(&dev->interface->dev,
+               "Error %d when trying to perform data_exchange\n", rc);
 
        dev_kfree_skb(skb);
        kfree(dev->cmd_complete_dep_arg);
@@ -2641,10 +2876,9 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
 {
        struct sk_buff *skb;
        struct sk_buff *resp;
-
        int skb_len;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */
 
@@ -2691,7 +2925,7 @@ static int pn533_pasori_fw_reset(struct pn533 *dev)
        struct sk_buff *skb;
        struct sk_buff *resp;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        skb = pn533_alloc_skb(dev, sizeof(u8));
        if (!skb)
@@ -2717,7 +2951,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
 {
        struct pn533_acr122_poweron_rdr_arg *arg = urb->context;
 
-       nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
+       dev_dbg(&urb->dev->dev, "%s\n", __func__);
 
        print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
                       urb->transfer_buffer, urb->transfer_buffer_length,
@@ -2737,7 +2971,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
        void *cntx;
        struct pn533_acr122_poweron_rdr_arg arg;
 
-       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
 
        init_completion(&arg.done);
        cntx = dev->in_urb->context;  /* backup context */
@@ -2755,16 +2989,15 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
 
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Reader power on cmd error %d", rc);
+               nfc_err(&dev->interface->dev,
+                       "Reader power on cmd error %d\n", rc);
                return rc;
        }
 
        rc =  usb_submit_urb(dev->in_urb, GFP_KERNEL);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Can't submit for reader power on cmd response %d",
-                           rc);
+               nfc_err(&dev->interface->dev,
+                       "Can't submit reader poweron cmd response %d\n", rc);
                return rc;
        }
 
@@ -2785,20 +3018,19 @@ static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf)
        rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
                                     (u8 *)&rf_field, 1);
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error on setting RF field");
+               nfc_err(&dev->interface->dev, "Error on setting RF field\n");
                return rc;
        }
 
        return rc;
 }
 
-int pn533_dev_up(struct nfc_dev *nfc_dev)
+static int pn533_dev_up(struct nfc_dev *nfc_dev)
 {
        return pn533_rf_field(nfc_dev, 1);
 }
 
-int pn533_dev_down(struct nfc_dev *nfc_dev)
+static int pn533_dev_down(struct nfc_dev *nfc_dev)
 {
        return pn533_rf_field(nfc_dev, 0);
 }
@@ -2839,16 +3071,16 @@ static int pn533_setup(struct pn533 *dev)
                break;
 
        default:
-               nfc_dev_err(&dev->interface->dev, "Unknown device type %d\n",
-                           dev->device_type);
+               nfc_err(&dev->interface->dev, "Unknown device type %d\n",
+                       dev->device_type);
                return -EINVAL;
        }
 
        rc = pn533_set_configuration(dev, PN533_CFGITEM_MAX_RETRIES,
                                     (u8 *)&max_retries, sizeof(max_retries));
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error on setting MAX_RETRIES config");
+               nfc_err(&dev->interface->dev,
+                       "Error on setting MAX_RETRIES config\n");
                return rc;
        }
 
@@ -2856,8 +3088,7 @@ static int pn533_setup(struct pn533 *dev)
        rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING,
                                     (u8 *)&timing, sizeof(timing));
        if (rc) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Error on setting RF timings");
+               nfc_err(&dev->interface->dev, "Error on setting RF timings\n");
                return rc;
        }
 
@@ -2871,8 +3102,8 @@ static int pn533_setup(struct pn533 *dev)
                rc = pn533_set_configuration(dev, PN533_CFGITEM_PASORI,
                                             pasori_cfg, 3);
                if (rc) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Error while settings PASORI config");
+                       nfc_err(&dev->interface->dev,
+                               "Error while settings PASORI config\n");
                        return rc;
                }
 
@@ -2917,8 +3148,8 @@ static int pn533_probe(struct usb_interface *interface,
        }
 
        if (!in_endpoint || !out_endpoint) {
-               nfc_dev_err(&interface->dev,
-                           "Could not find bulk-in or bulk-out endpoint");
+               nfc_err(&interface->dev,
+                       "Could not find bulk-in or bulk-out endpoint\n");
                rc = -ENODEV;
                goto error;
        }
@@ -2941,6 +3172,8 @@ static int pn533_probe(struct usb_interface *interface,
        INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv);
        INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
        INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
+       INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv);
+       INIT_WORK(&dev->mi_tm_tx_work, pn533_wq_tm_mi_send);
        INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
        INIT_WORK(&dev->rf_work, pn533_wq_rf);
        dev->wq = alloc_ordered_workqueue("pn533", 0);
@@ -2978,16 +3211,15 @@ static int pn533_probe(struct usb_interface *interface,
 
                rc = pn533_acr122_poweron_rdr(dev);
                if (rc < 0) {
-                       nfc_dev_err(&dev->interface->dev,
-                                   "Couldn't poweron the reader (error %d)",
-                                   rc);
+                       nfc_err(&dev->interface->dev,
+                               "Couldn't poweron the reader (error %d)\n", rc);
                        goto destroy_wq;
                }
                break;
 
        default:
-               nfc_dev_err(&dev->interface->dev, "Unknown device type %d\n",
-                           dev->device_type);
+               nfc_err(&dev->interface->dev, "Unknown device type %d\n",
+                       dev->device_type);
                rc = -EINVAL;
                goto destroy_wq;
        }
@@ -2997,9 +3229,9 @@ static int pn533_probe(struct usb_interface *interface,
        if (rc < 0)
                goto destroy_wq;
 
-       nfc_dev_info(&dev->interface->dev,
-                    "NXP PN5%02X firmware ver %d.%d now attached",
-                    fw_ver.ic, fw_ver.ver, fw_ver.rev);
+       nfc_info(&dev->interface->dev,
+                "NXP PN5%02X firmware ver %d.%d now attached\n",
+                fw_ver.ic, fw_ver.ver, fw_ver.rev);
 
 
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
@@ -3070,7 +3302,7 @@ static void pn533_disconnect(struct usb_interface *interface)
        usb_free_urb(dev->out_urb);
        kfree(dev);
 
-       nfc_dev_info(&interface->dev, "NXP PN533 NFC device disconnected");
+       nfc_info(&interface->dev, "NXP PN533 NFC device disconnected\n");
 }
 
 static struct usb_driver pn533_driver = {
index 01e27d4..b158ee1 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/crc-ccitt.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
@@ -151,8 +153,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
        char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 };
        int count = sizeof(rset_cmd);
 
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-       dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
+       nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
 
        /* Disable fw download */
        gpio_set_value(phy->gpio_fw, 0);
@@ -173,7 +174,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
                        dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n");
                        ret = i2c_master_send(phy->i2c_dev, rset_cmd, count);
                        if (ret == count) {
-                               dev_info(&phy->i2c_dev->dev,
+                               nfc_info(&phy->i2c_dev->dev,
                                         "nfc_en polarity : active %s\n",
                                         (polarity == 0 ? "low" : "high"));
                                goto out;
@@ -181,7 +182,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
                }
        }
 
-       dev_err(&phy->i2c_dev->dev,
+       nfc_err(&phy->i2c_dev->dev,
                "Could not detect nfc_en polarity, fallback to active high\n");
 
 out:
@@ -201,7 +202,7 @@ static int pn544_hci_i2c_enable(void *phy_id)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
-       pr_info(DRIVER_DESC ": %s\n", __func__);
+       pr_info("%s\n", __func__);
 
        pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE);
 
@@ -214,8 +215,6 @@ static void pn544_hci_i2c_disable(void *phy_id)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-
        gpio_set_value(phy->gpio_fw, 0);
        gpio_set_value(phy->gpio_en, !phy->en_polarity);
        usleep_range(10000, 15000);
@@ -298,11 +297,9 @@ static int check_crc(u8 *buf, int buflen)
        crc = ~crc;
 
        if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) {
-               pr_err(PN544_HCI_I2C_DRIVER_NAME
-                      ": CRC error 0x%x != 0x%x 0x%x\n",
+               pr_err("CRC error 0x%x != 0x%x 0x%x\n",
                       crc, buf[len - 1], buf[len - 2]);
-
-               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+               pr_info("%s: BAD CRC\n", __func__);
                print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
                               16, 2, buf, buflen, false);
                return -EPERM;
@@ -328,13 +325,13 @@ static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb)
 
        r = i2c_master_recv(client, &len, 1);
        if (r != 1) {
-               dev_err(&client->dev, "cannot read len byte\n");
+               nfc_err(&client->dev, "cannot read len byte\n");
                return -EREMOTEIO;
        }
 
        if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) ||
            (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) {
-               dev_err(&client->dev, "invalid len byte\n");
+               nfc_err(&client->dev, "invalid len byte\n");
                r = -EBADMSG;
                goto flush;
        }
@@ -386,7 +383,7 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
 
        r = i2c_master_recv(client, (char *) &response, sizeof(response));
        if (r != sizeof(response)) {
-               dev_err(&client->dev, "cannot read fw status\n");
+               nfc_err(&client->dev, "cannot read fw status\n");
                return -EIO;
        }
 
@@ -478,8 +475,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
-       pr_info(DRIVER_DESC ": Starting Firmware Download (%s)\n",
-               firmware_name);
+       pr_info("Starting Firmware Download (%s)\n", firmware_name);
 
        strcpy(phy->firmware_name, firmware_name);
 
@@ -493,7 +489,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
 static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy,
                                           int result)
 {
-       pr_info(DRIVER_DESC ": Firmware Download Complete, result=%d\n", result);
+       pr_info("Firmware Download Complete, result=%d\n", result);
 
        pn544_hci_i2c_disable(phy);
 
@@ -694,14 +690,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
        dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-               dev_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
                return -ENODEV;
        }
 
        phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
                           GFP_KERNEL);
        if (!phy) {
-               dev_err(&client->dev,
+               nfc_err(&client->dev,
                        "Cannot allocate memory for pn544 i2c phy.\n");
                return -ENOMEM;
        }
@@ -714,18 +710,18 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
 
        pdata = client->dev.platform_data;
        if (pdata == NULL) {
-               dev_err(&client->dev, "No platform data\n");
+               nfc_err(&client->dev, "No platform data\n");
                return -EINVAL;
        }
 
        if (pdata->request_resources == NULL) {
-               dev_err(&client->dev, "request_resources() missing\n");
+               nfc_err(&client->dev, "request_resources() missing\n");
                return -EINVAL;
        }
 
        r = pdata->request_resources(client);
        if (r) {
-               dev_err(&client->dev, "Cannot get platform resources\n");
+               nfc_err(&client->dev, "Cannot get platform resources\n");
                return r;
        }
 
@@ -739,7 +735,7 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
                                 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
                                 PN544_HCI_I2C_DRIVER_NAME, phy);
        if (r < 0) {
-               dev_err(&client->dev, "Unable to register IRQ handler\n");
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
                goto err_rti;
        }
 
index 078e62f..74cfa0a 100644 (file)
@@ -18,6 +18,8 @@
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -41,6 +43,7 @@ enum pn544_state {
 
 /* Proprietary commands */
 #define PN544_WRITE            0x3f
+#define PN544_TEST_SWP         0x21
 
 /* Proprietary gates, events, commands and registers */
 
@@ -81,14 +84,17 @@ enum pn544_state {
 #define PN544_PL_NFCT_DEACTIVATED              0x09
 
 #define PN544_SWP_MGMT_GATE                    0xA0
+#define PN544_SWP_DEFAULT_MODE                 0x01
 
 #define PN544_NFC_WI_MGMT_GATE                 0xA1
+#define PN544_NFC_ESE_DEFAULT_MODE             0x01
 
 #define PN544_HCI_EVT_SND_DATA                 0x01
 #define PN544_HCI_EVT_ACTIVATED                        0x02
 #define PN544_HCI_EVT_DEACTIVATED              0x03
 #define PN544_HCI_EVT_RCV_DATA                 0x04
 #define PN544_HCI_EVT_CONTINUE_MI              0x05
+#define PN544_HCI_EVT_SWITCH_MODE              0x03
 
 #define PN544_HCI_CMD_ATTREQUEST               0x12
 #define PN544_HCI_CMD_CONTINUE_ACTIVATION      0x13
@@ -187,13 +193,6 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev)
 
                {{0x9e, 0xb4}, 0x00},
 
-               {{0x9e, 0xd9}, 0xff},
-               {{0x9e, 0xda}, 0xff},
-               {{0x9e, 0xdb}, 0x23},
-               {{0x9e, 0xdc}, 0x21},
-               {{0x9e, 0xdd}, 0x22},
-               {{0x9e, 0xde}, 0x24},
-
                {{0x9c, 0x01}, 0x08},
 
                {{0x9e, 0xaa}, 0x01},
@@ -394,7 +393,7 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
        if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
                hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
                                                        &hdev->gb_len);
-               pr_debug("generate local bytes %p", hdev->gb);
+               pr_debug("generate local bytes %p\n", hdev->gb);
                if (hdev->gb == NULL || hdev->gb_len == 0) {
                        im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
                        tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
@@ -696,7 +695,7 @@ static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
                                   struct nfc_target *target)
 {
-       pr_debug("supported protocol %d", target->supported_protocols);
+       pr_debug("supported protocol %d\b", target->supported_protocols);
        if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK |
                                        NFC_PROTO_ISO14443_B_MASK)) {
                return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
@@ -733,7 +732,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
        struct sk_buff *rgb_skb = NULL;
        int r;
 
-       pr_debug("hci event %d", event);
+       pr_debug("hci event %d\n", event);
        switch (event) {
        case PN544_HCI_EVT_ACTIVATED:
                if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) {
@@ -764,7 +763,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                }
 
                if (skb->data[0] != 0) {
-                       pr_debug("data0 %d", skb->data[0]);
+                       pr_debug("data0 %d\n", skb->data[0]);
                        r = -EPROTO;
                        goto exit;
                }
@@ -792,6 +791,108 @@ static int pn544_hci_fw_download(struct nfc_hci_dev *hdev,
        return info->fw_download(info->phy_id, firmware_name);
 }
 
+static int pn544_hci_discover_se(struct nfc_hci_dev *hdev)
+{
+       u32 se_idx = 0;
+       u8 ese_mode = 0x01; /* Default mode */
+       struct sk_buff *res_skb;
+       int r;
+
+       r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_TEST_SWP,
+                            NULL, 0, &res_skb);
+
+       if (r == 0) {
+               if (res_skb->len == 2 && res_skb->data[0] == 0x00)
+                       nfc_add_se(hdev->ndev, se_idx++, NFC_SE_UICC);
+
+               kfree_skb(res_skb);
+       }
+
+       r = nfc_hci_send_event(hdev, PN544_NFC_WI_MGMT_GATE,
+                               PN544_HCI_EVT_SWITCH_MODE,
+                               &ese_mode, 1);
+       if (r == 0)
+               nfc_add_se(hdev->ndev, se_idx++, NFC_SE_EMBEDDED);
+
+       return !se_idx;
+}
+
+#define PN544_SE_MODE_OFF      0x00
+#define PN544_SE_MODE_ON       0x01
+static int pn544_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+       struct nfc_se *se;
+       u8 enable = PN544_SE_MODE_ON;
+       static struct uicc_gatelist {
+               u8 head;
+               u8 adr[2];
+               u8 value;
+       } uicc_gatelist[] = {
+               {0x00, {0x9e, 0xd9}, 0x23},
+               {0x00, {0x9e, 0xda}, 0x21},
+               {0x00, {0x9e, 0xdb}, 0x22},
+               {0x00, {0x9e, 0xdc}, 0x24},
+       };
+       struct uicc_gatelist *p = uicc_gatelist;
+       int count = ARRAY_SIZE(uicc_gatelist);
+       struct sk_buff *res_skb;
+       int r;
+
+       se = nfc_find_se(hdev->ndev, se_idx);
+
+       switch (se->type) {
+       case NFC_SE_UICC:
+               while (count--) {
+                       r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE,
+                                       PN544_WRITE, (u8 *)p, 4, &res_skb);
+                       if (r < 0)
+                               return r;
+
+                       if (res_skb->len != 1) {
+                               kfree_skb(res_skb);
+                               return -EPROTO;
+                       }
+
+                       if (res_skb->data[0] != p->value) {
+                               kfree_skb(res_skb);
+                               return -EIO;
+                       }
+
+                       kfree_skb(res_skb);
+
+                       p++;
+               }
+
+               return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE,
+                             PN544_SWP_DEFAULT_MODE, &enable, 1);
+       case NFC_SE_EMBEDDED:
+               return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE,
+                             PN544_NFC_ESE_DEFAULT_MODE, &enable, 1);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int pn544_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx)
+{
+       struct nfc_se *se;
+       u8 disable = PN544_SE_MODE_OFF;
+
+       se = nfc_find_se(hdev->ndev, se_idx);
+
+       switch (se->type) {
+       case NFC_SE_UICC:
+               return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE,
+                             PN544_SWP_DEFAULT_MODE, &disable, 1);
+       case NFC_SE_EMBEDDED:
+               return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE,
+                             PN544_NFC_ESE_DEFAULT_MODE, &disable, 1);
+       default:
+               return -EINVAL;
+       }
+}
+
 static struct nfc_hci_ops pn544_hci_ops = {
        .open = pn544_hci_open,
        .close = pn544_hci_close,
@@ -807,6 +908,9 @@ static struct nfc_hci_ops pn544_hci_ops = {
        .check_presence = pn544_hci_check_presence,
        .event_received = pn544_hci_event_received,
        .fw_download = pn544_hci_fw_download,
+       .discover_se = pn544_hci_discover_se,
+       .enable_se = pn544_hci_enable_se,
+       .disable_se = pn544_hci_disable_se,
 };
 
 int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
@@ -820,7 +924,6 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 
        info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
        if (!info) {
-               pr_err("Cannot allocate memory for pn544_hci_info.\n");
                r = -ENOMEM;
                goto err_info_alloc;
        }
@@ -853,7 +956,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
-               pr_err("Cannot allocate nfc hdev.\n");
+               pr_err("Cannot allocate nfc hdev\n");
                r = -ENOMEM;
                goto err_alloc_hdev;
        }
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
new file mode 100644 (file)
index 0000000..8a0571e
--- /dev/null
@@ -0,0 +1,1529 @@
+/*
+ * Sony NFC Port-100 Series driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * Partly based/Inspired by Stephen Tiedemann's nfcpy
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <net/nfc/digital.h>
+
+#define VERSION "0.1"
+
+#define SONY_VENDOR_ID    0x054c
+#define RCS380_PRODUCT_ID 0x06c1
+
+#define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK    | \
+                          NFC_PROTO_MIFARE_MASK   | \
+                          NFC_PROTO_FELICA_MASK   | \
+                          NFC_PROTO_NFC_DEP_MASK)
+
+#define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
+                             NFC_DIGITAL_DRV_CAPS_TG_CRC)
+
+/* Standard port100 frame definitions */
+#define PORT100_FRAME_HEADER_LEN (sizeof(struct port100_frame) \
+                                 + 2) /* data[0] CC, data[1] SCC */
+#define PORT100_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+
+#define PORT100_COMM_RF_HEAD_MAX_LEN (sizeof(struct port100_tg_comm_rf_cmd))
+
+/*
+ * Max extended frame payload len, excluding CC and SCC
+ * which are already in PORT100_FRAME_HEADER_LEN.
+ */
+#define PORT100_FRAME_MAX_PAYLOAD_LEN 1001
+
+#define PORT100_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2),
+                                   Postamble (1) */
+static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = {
+       0x00, 0x00, 0xff, 0x00, 0xff, 0x00
+};
+
+#define PORT100_FRAME_CHECKSUM(f) (f->data[le16_to_cpu(f->datalen)])
+#define PORT100_FRAME_POSTAMBLE(f) (f->data[le16_to_cpu(f->datalen) + 1])
+
+/* start of frame */
+#define PORT100_FRAME_SOF      0x00FF
+#define PORT100_FRAME_EXT      0xFFFF
+#define PORT100_FRAME_ACK      0x00FF
+
+/* Port-100 command: in or out */
+#define PORT100_FRAME_DIRECTION(f) (f->data[0]) /* CC */
+#define PORT100_FRAME_DIR_OUT 0xD6
+#define PORT100_FRAME_DIR_IN  0xD7
+
+/* Port-100 sub-command */
+#define PORT100_FRAME_CMD(f) (f->data[1]) /* SCC */
+
+#define PORT100_CMD_GET_FIRMWARE_VERSION 0x20
+#define PORT100_CMD_GET_COMMAND_TYPE     0x28
+#define PORT100_CMD_SET_COMMAND_TYPE     0x2A
+
+#define PORT100_CMD_IN_SET_RF       0x00
+#define PORT100_CMD_IN_SET_PROTOCOL 0x02
+#define PORT100_CMD_IN_COMM_RF      0x04
+
+#define PORT100_CMD_TG_SET_RF       0x40
+#define PORT100_CMD_TG_SET_PROTOCOL 0x42
+#define PORT100_CMD_TG_SET_RF_OFF   0x46
+#define PORT100_CMD_TG_COMM_RF      0x48
+
+#define PORT100_CMD_SWITCH_RF       0x06
+
+#define PORT100_CMD_RESPONSE(cmd) (cmd + 1)
+
+#define PORT100_CMD_TYPE_IS_SUPPORTED(mask, cmd_type) \
+       ((mask) & (0x01 << (cmd_type)))
+#define PORT100_CMD_TYPE_0     0
+#define PORT100_CMD_TYPE_1     1
+
+#define PORT100_CMD_STATUS_OK      0x00
+#define PORT100_CMD_STATUS_TIMEOUT 0x80
+
+#define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01
+#define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK      0x02
+
+struct port100;
+
+typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg,
+                                             struct sk_buff *resp);
+
+/**
+ * Setting sets structure for in_set_rf command
+ *
+ * @in_*_set_number: Represent the entry indexes in the port-100 RF Base Table.
+ *              This table contains multiple RF setting sets required for RF
+ *              communication.
+ *
+ * @in_*_comm_type: Theses fields set the communication type to be used.
+ */
+struct port100_in_rf_setting {
+       u8 in_send_set_number;
+       u8 in_send_comm_type;
+       u8 in_recv_set_number;
+       u8 in_recv_comm_type;
+} __packed;
+
+#define PORT100_COMM_TYPE_IN_212F 0x01
+#define PORT100_COMM_TYPE_IN_424F 0x02
+#define PORT100_COMM_TYPE_IN_106A 0x03
+
+static const struct port100_in_rf_setting in_rf_settings[] = {
+       [NFC_DIGITAL_RF_TECH_212F] = {
+               .in_send_set_number = 1,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_212F,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_212F,
+       },
+       [NFC_DIGITAL_RF_TECH_424F] = {
+               .in_send_set_number = 1,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_424F,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_424F,
+       },
+       [NFC_DIGITAL_RF_TECH_106A] = {
+               .in_send_set_number = 2,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_106A,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_106A,
+       },
+};
+
+/**
+ * Setting sets structure for tg_set_rf command
+ *
+ * @tg_set_number: Represents the entry index in the port-100 RF Base Table.
+ *                 This table contains multiple RF setting sets required for RF
+ *                 communication. this field is used for both send and receive
+ *                 settings.
+ *
+ * @tg_comm_type: Sets the communication type to be used to send and receive
+ *                data.
+ */
+struct port100_tg_rf_setting {
+       u8 tg_set_number;
+       u8 tg_comm_type;
+} __packed;
+
+#define PORT100_COMM_TYPE_TG_106A 0x0B
+#define PORT100_COMM_TYPE_TG_212F 0x0C
+#define PORT100_COMM_TYPE_TG_424F 0x0D
+
+static const struct port100_tg_rf_setting tg_rf_settings[] = {
+       [NFC_DIGITAL_RF_TECH_106A] = {
+               .tg_set_number = 8,
+               .tg_comm_type = PORT100_COMM_TYPE_TG_106A,
+       },
+       [NFC_DIGITAL_RF_TECH_212F] = {
+               .tg_set_number = 8,
+               .tg_comm_type = PORT100_COMM_TYPE_TG_212F,
+       },
+       [NFC_DIGITAL_RF_TECH_424F] = {
+               .tg_set_number = 8,
+               .tg_comm_type = PORT100_COMM_TYPE_TG_424F,
+       },
+};
+
+#define PORT100_IN_PROT_INITIAL_GUARD_TIME      0x00
+#define PORT100_IN_PROT_ADD_CRC                 0x01
+#define PORT100_IN_PROT_CHECK_CRC               0x02
+#define PORT100_IN_PROT_MULTI_CARD              0x03
+#define PORT100_IN_PROT_ADD_PARITY              0x04
+#define PORT100_IN_PROT_CHECK_PARITY            0x05
+#define PORT100_IN_PROT_BITWISE_AC_RECV_MODE    0x06
+#define PORT100_IN_PROT_VALID_BIT_NUMBER        0x07
+#define PORT100_IN_PROT_CRYPTO1                 0x08
+#define PORT100_IN_PROT_ADD_SOF                 0x09
+#define PORT100_IN_PROT_CHECK_SOF               0x0A
+#define PORT100_IN_PROT_ADD_EOF                 0x0B
+#define PORT100_IN_PROT_CHECK_EOF               0x0C
+#define PORT100_IN_PROT_DEAF_TIME               0x0E
+#define PORT100_IN_PROT_CRM                     0x0F
+#define PORT100_IN_PROT_CRM_MIN_LEN             0x10
+#define PORT100_IN_PROT_T1_TAG_FRAME            0x11
+#define PORT100_IN_PROT_RFCA                    0x12
+#define PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR 0x13
+#define PORT100_IN_PROT_END                     0x14
+
+#define PORT100_IN_MAX_NUM_PROTOCOLS            19
+
+#define PORT100_TG_PROT_TU           0x00
+#define PORT100_TG_PROT_RF_OFF       0x01
+#define PORT100_TG_PROT_CRM          0x02
+#define PORT100_TG_PROT_END          0x03
+
+#define PORT100_TG_MAX_NUM_PROTOCOLS 3
+
+struct port100_protocol {
+       u8 number;
+       u8 value;
+} __packed;
+
+static struct port100_protocol
+in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
+       [NFC_DIGITAL_FRAMING_NFCA_SHORT] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,      6 },
+               { PORT100_IN_PROT_ADD_CRC,                 0 },
+               { PORT100_IN_PROT_CHECK_CRC,               0 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              0 },
+               { PORT100_IN_PROT_CHECK_PARITY,            1 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        7 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,      6 },
+               { PORT100_IN_PROT_ADD_CRC,                 0 },
+               { PORT100_IN_PROT_CHECK_CRC,               0 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              1 },
+               { PORT100_IN_PROT_CHECK_PARITY,            1 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,      6 },
+               { PORT100_IN_PROT_ADD_CRC,                 1 },
+               { PORT100_IN_PROT_CHECK_CRC,               1 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              1 },
+               { PORT100_IN_PROT_CHECK_PARITY,            1 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T1T] = {
+               /* nfc_digital_framing_nfca_short */
+               { PORT100_IN_PROT_ADD_CRC,          2 },
+               { PORT100_IN_PROT_CHECK_CRC,        2 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,     2 },
+               { PORT100_IN_PROT_END,              0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T2T] = {
+               /* nfc_digital_framing_nfca_standard */
+               { PORT100_IN_PROT_ADD_CRC,   1 },
+               { PORT100_IN_PROT_CHECK_CRC, 0 },
+               { PORT100_IN_PROT_END,       0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
+               /* nfc_digital_framing_nfca_standard */
+               { PORT100_IN_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,     18 },
+               { PORT100_IN_PROT_ADD_CRC,                 1 },
+               { PORT100_IN_PROT_CHECK_CRC,               1 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              0 },
+               { PORT100_IN_PROT_CHECK_PARITY,            0 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_T3T] = {
+               /* nfc_digital_framing_nfcf */
+               { PORT100_IN_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
+               /* nfc_digital_framing_nfcf */
+               { PORT100_IN_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
+               { PORT100_IN_PROT_END, 0 },
+       },
+};
+
+static struct port100_protocol
+tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = {
+       [NFC_DIGITAL_FRAMING_NFCA_SHORT] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T1T] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_T2T] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = {
+               { PORT100_TG_PROT_TU,     1 },
+               { PORT100_TG_PROT_RF_OFF, 0 },
+               { PORT100_TG_PROT_CRM,    7 },
+               { PORT100_TG_PROT_END,    0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_T3T] = {
+               { PORT100_TG_PROT_END, 0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
+               { PORT100_TG_PROT_TU,     1 },
+               { PORT100_TG_PROT_RF_OFF, 0 },
+               { PORT100_TG_PROT_CRM,    7 },
+               { PORT100_TG_PROT_END,    0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
+               { PORT100_TG_PROT_RF_OFF, 1 },
+               { PORT100_TG_PROT_END,    0 },
+       },
+};
+
+struct port100 {
+       struct nfc_digital_dev *nfc_digital_dev;
+
+       int skb_headroom;
+       int skb_tailroom;
+
+       struct usb_device *udev;
+       struct usb_interface *interface;
+
+       struct urb *out_urb;
+       struct urb *in_urb;
+
+       struct work_struct cmd_complete_work;
+
+       u8 cmd_type;
+
+       /* The digital stack serializes commands to be sent. There is no need
+        * for any queuing/locking mechanism at driver level.
+        */
+       struct port100_cmd *cmd;
+};
+
+struct port100_cmd {
+       u8 code;
+       int status;
+       struct sk_buff *req;
+       struct sk_buff *resp;
+       int resp_len;
+       port100_send_async_complete_t  complete_cb;
+       void *complete_cb_context;
+};
+
+struct port100_frame {
+       u8 preamble;
+       __be16 start_frame;
+       __be16 extended_frame;
+       __le16 datalen;
+       u8 datalen_checksum;
+       u8 data[];
+} __packed;
+
+struct port100_ack_frame {
+       u8 preamble;
+       __be16 start_frame;
+       __be16 ack_frame;
+       u8 postambule;
+} __packed;
+
+struct port100_cb_arg {
+       nfc_digital_cmd_complete_t complete_cb;
+       void *complete_arg;
+       u8 mdaa;
+};
+
+struct port100_tg_comm_rf_cmd {
+       __le16 guard_time;
+       __le16 send_timeout;
+       u8 mdaa;
+       u8 nfca_param[6];
+       u8 nfcf_param[18];
+       u8 mf_halted;
+       u8 arae_flag;
+       __le16 recv_timeout;
+       u8 data[];
+} __packed;
+
+struct port100_tg_comm_rf_res {
+       u8 comm_type;
+       u8 ar_status;
+       u8 target_activated;
+       __le32 status;
+       u8 data[];
+} __packed;
+
+/* The rule: value + checksum = 0 */
+static inline u8 port100_checksum(u16 value)
+{
+       return ~(((u8 *)&value)[0] + ((u8 *)&value)[1]) + 1;
+}
+
+/* The rule: sum(data elements) + checksum = 0 */
+static u8 port100_data_checksum(u8 *data, int datalen)
+{
+       u8 sum = 0;
+       int i;
+
+       for (i = 0; i < datalen; i++)
+               sum += data[i];
+
+       return port100_checksum(sum);
+}
+
+static void port100_tx_frame_init(void *_frame, u8 cmd_code)
+{
+       struct port100_frame *frame = _frame;
+
+       frame->preamble = 0;
+       frame->start_frame = cpu_to_be16(PORT100_FRAME_SOF);
+       frame->extended_frame = cpu_to_be16(PORT100_FRAME_EXT);
+       PORT100_FRAME_DIRECTION(frame) = PORT100_FRAME_DIR_OUT;
+       PORT100_FRAME_CMD(frame) = cmd_code;
+       frame->datalen = cpu_to_le16(2);
+}
+
+static void port100_tx_frame_finish(void *_frame)
+{
+       struct port100_frame *frame = _frame;
+
+       frame->datalen_checksum = port100_checksum(le16_to_cpu(frame->datalen));
+
+       PORT100_FRAME_CHECKSUM(frame) =
+               port100_data_checksum(frame->data, le16_to_cpu(frame->datalen));
+
+       PORT100_FRAME_POSTAMBLE(frame) = 0;
+}
+
+static void port100_tx_update_payload_len(void *_frame, int len)
+{
+       struct port100_frame *frame = _frame;
+
+       frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len);
+}
+
+static bool port100_rx_frame_is_valid(void *_frame)
+{
+       u8 checksum;
+       struct port100_frame *frame = _frame;
+
+       if (frame->start_frame != cpu_to_be16(PORT100_FRAME_SOF) ||
+           frame->extended_frame != cpu_to_be16(PORT100_FRAME_EXT))
+               return false;
+
+       checksum = port100_checksum(le16_to_cpu(frame->datalen));
+       if (checksum != frame->datalen_checksum)
+               return false;
+
+       checksum = port100_data_checksum(frame->data,
+                                        le16_to_cpu(frame->datalen));
+       if (checksum != PORT100_FRAME_CHECKSUM(frame))
+               return false;
+
+       return true;
+}
+
+static bool port100_rx_frame_is_ack(struct port100_ack_frame *frame)
+{
+       return (frame->start_frame == cpu_to_be16(PORT100_FRAME_SOF) &&
+               frame->ack_frame == cpu_to_be16(PORT100_FRAME_ACK));
+}
+
+static inline int port100_rx_frame_size(void *frame)
+{
+       struct port100_frame *f = frame;
+
+       return sizeof(struct port100_frame) + le16_to_cpu(f->datalen) +
+              PORT100_FRAME_TAIL_LEN;
+}
+
+static bool port100_rx_frame_is_cmd_response(struct port100 *dev, void *frame)
+{
+       struct port100_frame *f = frame;
+
+       return (PORT100_FRAME_CMD(f) == PORT100_CMD_RESPONSE(dev->cmd->code));
+}
+
+static void port100_recv_response(struct urb *urb)
+{
+       struct port100 *dev = urb->context;
+       struct port100_cmd *cmd = dev->cmd;
+       u8 *in_frame;
+
+       cmd->status = urb->status;
+
+       switch (urb->status) {
+       case 0:
+               break; /* success */
+       case -ECONNRESET:
+       case -ENOENT:
+               nfc_err(&dev->interface->dev,
+                       "The urb has been canceled (status %d)", urb->status);
+               goto sched_wq;
+       case -ESHUTDOWN:
+       default:
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+                       urb->status);
+               goto sched_wq;
+       }
+
+       in_frame = dev->in_urb->transfer_buffer;
+
+       if (!port100_rx_frame_is_valid(in_frame)) {
+               nfc_err(&dev->interface->dev, "Received an invalid frame");
+               cmd->status = -EIO;
+               goto sched_wq;
+       }
+
+       print_hex_dump_debug("PORT100 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
+                            port100_rx_frame_size(in_frame), false);
+
+       if (!port100_rx_frame_is_cmd_response(dev, in_frame)) {
+               nfc_err(&dev->interface->dev,
+                       "It's not the response to the last command");
+               cmd->status = -EIO;
+               goto sched_wq;
+       }
+
+sched_wq:
+       schedule_work(&dev->cmd_complete_work);
+}
+
+static int port100_submit_urb_for_response(struct port100 *dev, gfp_t flags)
+{
+       dev->in_urb->complete = port100_recv_response;
+
+       return usb_submit_urb(dev->in_urb, flags);
+}
+
+static void port100_recv_ack(struct urb *urb)
+{
+       struct port100 *dev = urb->context;
+       struct port100_cmd *cmd = dev->cmd;
+       struct port100_ack_frame *in_frame;
+       int rc;
+
+       cmd->status = urb->status;
+
+       switch (urb->status) {
+       case 0:
+               break; /* success */
+       case -ECONNRESET:
+       case -ENOENT:
+               nfc_err(&dev->interface->dev,
+                       "The urb has been stopped (status %d)", urb->status);
+               goto sched_wq;
+       case -ESHUTDOWN:
+       default:
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+                       urb->status);
+               goto sched_wq;
+       }
+
+       in_frame = dev->in_urb->transfer_buffer;
+
+       if (!port100_rx_frame_is_ack(in_frame)) {
+               nfc_err(&dev->interface->dev, "Received an invalid ack");
+               cmd->status = -EIO;
+               goto sched_wq;
+       }
+
+       rc = port100_submit_urb_for_response(dev, GFP_ATOMIC);
+       if (rc) {
+               nfc_err(&dev->interface->dev,
+                       "usb_submit_urb failed with result %d", rc);
+               cmd->status = rc;
+               goto sched_wq;
+       }
+
+       return;
+
+sched_wq:
+       schedule_work(&dev->cmd_complete_work);
+}
+
+static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags)
+{
+       dev->in_urb->complete = port100_recv_ack;
+
+       return usb_submit_urb(dev->in_urb, flags);
+}
+
+static int port100_send_ack(struct port100 *dev)
+{
+       int rc;
+
+       dev->out_urb->transfer_buffer = ack_frame;
+       dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
+       rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+
+       return rc;
+}
+
+static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
+                                   struct sk_buff *in, int in_len)
+{
+       int rc;
+
+       dev->out_urb->transfer_buffer = out->data;
+       dev->out_urb->transfer_buffer_length = out->len;
+
+       dev->in_urb->transfer_buffer = in->data;
+       dev->in_urb->transfer_buffer_length = in_len;
+
+       print_hex_dump_debug("PORT100 TX: ", DUMP_PREFIX_NONE, 16, 1,
+                            out->data, out->len, false);
+
+       rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
+       if (rc)
+               return rc;
+
+       rc = port100_submit_urb_for_ack(dev, GFP_KERNEL);
+       if (rc)
+               goto error;
+
+       return 0;
+
+error:
+       usb_unlink_urb(dev->out_urb);
+       return rc;
+}
+
+static void port100_build_cmd_frame(struct port100 *dev, u8 cmd_code,
+                                   struct sk_buff *skb)
+{
+       /* payload is already there, just update datalen */
+       int payload_len = skb->len;
+
+       skb_push(skb, PORT100_FRAME_HEADER_LEN);
+       skb_put(skb, PORT100_FRAME_TAIL_LEN);
+
+       port100_tx_frame_init(skb->data, cmd_code);
+       port100_tx_update_payload_len(skb->data, payload_len);
+       port100_tx_frame_finish(skb->data);
+}
+
+static void port100_send_async_complete(struct port100 *dev)
+{
+       struct port100_cmd *cmd = dev->cmd;
+       int status = cmd->status;
+
+       struct sk_buff *req = cmd->req;
+       struct sk_buff *resp = cmd->resp;
+
+       dev_kfree_skb(req);
+
+       dev->cmd = NULL;
+
+       if (status < 0) {
+               cmd->complete_cb(dev, cmd->complete_cb_context,
+                                ERR_PTR(status));
+               dev_kfree_skb(resp);
+               goto done;
+       }
+
+       skb_put(resp, port100_rx_frame_size(resp->data));
+       skb_pull(resp, PORT100_FRAME_HEADER_LEN);
+       skb_trim(resp, resp->len - PORT100_FRAME_TAIL_LEN);
+
+       cmd->complete_cb(dev, cmd->complete_cb_context, resp);
+
+done:
+       kfree(cmd);
+}
+
+static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code,
+                               struct sk_buff *req,
+                               port100_send_async_complete_t complete_cb,
+                               void *complete_cb_context)
+{
+       struct port100_cmd *cmd;
+       struct sk_buff *resp;
+       int rc;
+       int  resp_len = PORT100_FRAME_HEADER_LEN +
+                       PORT100_FRAME_MAX_PAYLOAD_LEN +
+                       PORT100_FRAME_TAIL_LEN;
+
+       resp = alloc_skb(resp_len, GFP_KERNEL);
+       if (!resp)
+               return -ENOMEM;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               dev_kfree_skb(resp);
+               return -ENOMEM;
+       }
+
+       cmd->code = cmd_code;
+       cmd->req = req;
+       cmd->resp = resp;
+       cmd->resp_len = resp_len;
+       cmd->complete_cb = complete_cb;
+       cmd->complete_cb_context = complete_cb_context;
+
+       port100_build_cmd_frame(dev, cmd_code, req);
+
+       dev->cmd = cmd;
+
+       rc = port100_send_frame_async(dev, req, resp, resp_len);
+       if (rc) {
+               kfree(cmd);
+               dev_kfree_skb(resp);
+               dev->cmd = NULL;
+       }
+
+       return rc;
+}
+
+struct port100_sync_cmd_response {
+       struct sk_buff *resp;
+       struct completion done;
+};
+
+static void port100_wq_cmd_complete(struct work_struct *work)
+{
+       struct port100 *dev = container_of(work, struct port100,
+                                          cmd_complete_work);
+
+       port100_send_async_complete(dev);
+}
+
+static void port100_send_sync_complete(struct port100 *dev, void *_arg,
+                                     struct sk_buff *resp)
+{
+       struct port100_sync_cmd_response *arg = _arg;
+
+       arg->resp = resp;
+       complete(&arg->done);
+}
+
+static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code,
+                                            struct sk_buff *req)
+{
+       int rc;
+       struct port100_sync_cmd_response arg;
+
+       init_completion(&arg.done);
+
+       rc = port100_send_cmd_async(dev, cmd_code, req,
+                                   port100_send_sync_complete, &arg);
+       if (rc) {
+               dev_kfree_skb(req);
+               return ERR_PTR(rc);
+       }
+
+       wait_for_completion(&arg.done);
+
+       return arg.resp;
+}
+
+static void port100_send_complete(struct urb *urb)
+{
+       struct port100 *dev = urb->context;
+
+       switch (urb->status) {
+       case 0:
+               break; /* success */
+       case -ECONNRESET:
+       case -ENOENT:
+               nfc_err(&dev->interface->dev,
+                       "The urb has been stopped (status %d)", urb->status);
+               break;
+       case -ESHUTDOWN:
+       default:
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+                       urb->status);
+       }
+}
+
+static void port100_abort_cmd(struct nfc_digital_dev *ddev)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+
+       /* An ack will cancel the last issued command */
+       port100_send_ack(dev);
+
+       /* cancel the urb request */
+       usb_kill_urb(dev->in_urb);
+}
+
+static struct sk_buff *port100_alloc_skb(struct port100 *dev, unsigned int size)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(dev->skb_headroom + dev->skb_tailroom + size,
+                       GFP_KERNEL);
+       if (skb)
+               skb_reserve(skb, dev->skb_headroom);
+
+       return skb;
+}
+
+static int port100_set_command_type(struct port100 *dev, u8 command_type)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+
+       skb = port100_alloc_skb(dev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, sizeof(u8)) = command_type;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_SET_COMMAND_TYPE, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static u64 port100_get_command_type_mask(struct port100 *dev)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       u64 mask;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb)
+               return -ENOMEM;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_COMMAND_TYPE, skb);
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       if (resp->len < 8)
+               mask = 0;
+       else
+               mask = be64_to_cpu(*(__be64 *)resp->data);
+
+       dev_kfree_skb(resp);
+
+       return mask;
+}
+
+static u16 port100_get_firmware_version(struct port100 *dev)
+{
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       u16 fw_ver;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb)
+               return 0;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_FIRMWARE_VERSION,
+                                    skb);
+       if (IS_ERR(resp))
+               return 0;
+
+       fw_ver = le16_to_cpu(*(__le16 *)resp->data);
+
+       dev_kfree_skb(resp);
+
+       return fw_ver;
+}
+
+static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb, *resp;
+
+       skb = port100_alloc_skb(dev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = on ? 1 : 0;
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       dev_kfree_skb(resp);
+
+       return 0;
+}
+
+static int port100_in_set_rf(struct nfc_digital_dev *ddev, u8 rf)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+
+       if (rf >= NFC_DIGITAL_RF_TECH_LAST)
+               return -EINVAL;
+
+       skb = port100_alloc_skb(dev, sizeof(struct port100_in_rf_setting));
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, sizeof(struct port100_in_rf_setting)),
+              &in_rf_settings[rf],
+              sizeof(struct port100_in_rf_setting));
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_RF, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_in_set_framing(struct nfc_digital_dev *ddev, int param)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_protocol *protocols;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int num_protocols;
+       size_t size;
+       int rc;
+
+       if (param >= NFC_DIGITAL_FRAMING_LAST)
+               return -EINVAL;
+
+       protocols = in_protocols[param];
+
+       num_protocols = 0;
+       while (protocols[num_protocols].number != PORT100_IN_PROT_END)
+               num_protocols++;
+
+       if (!num_protocols)
+               return 0;
+
+       size = sizeof(struct port100_protocol) * num_protocols;
+
+       skb = port100_alloc_skb(dev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, size), protocols, size);
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_PROTOCOL, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_in_configure_hw(struct nfc_digital_dev *ddev, int type,
+                                  int param)
+{
+       if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+               return port100_in_set_rf(ddev, param);
+
+       if (type == NFC_DIGITAL_CONFIG_FRAMING)
+               return port100_in_set_framing(ddev, param);
+
+       return -EINVAL;
+}
+
+static void port100_in_comm_rf_complete(struct port100 *dev, void *arg,
+                                      struct sk_buff *resp)
+{
+       struct port100_cb_arg *cb_arg = arg;
+       nfc_digital_cmd_complete_t cb = cb_arg->complete_cb;
+       u32 status;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc =  PTR_ERR(resp);
+               goto exit;
+       }
+
+       if (resp->len < 4) {
+               nfc_err(&dev->interface->dev,
+                       "Invalid packet length received.\n");
+               rc = -EIO;
+               goto error;
+       }
+
+       status = le32_to_cpu(*(__le32 *)resp->data);
+
+       skb_pull(resp, sizeof(u32));
+
+       if (status == PORT100_CMD_STATUS_TIMEOUT) {
+               rc = -ETIMEDOUT;
+               goto error;
+       }
+
+       if (status != PORT100_CMD_STATUS_OK) {
+               nfc_err(&dev->interface->dev,
+                       "in_comm_rf failed with status 0x%08x\n", status);
+               rc = -EIO;
+               goto error;
+       }
+
+       /* Remove collision bits byte */
+       skb_pull(resp, 1);
+
+       goto exit;
+
+error:
+       kfree_skb(resp);
+       resp = ERR_PTR(rc);
+
+exit:
+       cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp);
+
+       kfree(cb_arg);
+}
+
+static int port100_in_send_cmd(struct nfc_digital_dev *ddev,
+                              struct sk_buff *skb, u16 _timeout,
+                              nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_cb_arg *cb_arg;
+       __le16 timeout;
+
+       cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+       if (!cb_arg)
+               return -ENOMEM;
+
+       cb_arg->complete_cb = cb;
+       cb_arg->complete_arg = arg;
+
+       timeout = cpu_to_le16(_timeout * 10);
+
+       memcpy(skb_push(skb, sizeof(__le16)), &timeout, sizeof(__le16));
+
+       return port100_send_cmd_async(dev, PORT100_CMD_IN_COMM_RF, skb,
+                                     port100_in_comm_rf_complete, cb_arg);
+}
+
+static int port100_tg_set_rf(struct nfc_digital_dev *ddev, u8 rf)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+
+       if (rf >= NFC_DIGITAL_RF_TECH_LAST)
+               return -EINVAL;
+
+       skb = port100_alloc_skb(dev, sizeof(struct port100_tg_rf_setting));
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, sizeof(struct port100_tg_rf_setting)),
+              &tg_rf_settings[rf],
+              sizeof(struct port100_tg_rf_setting));
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_RF, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_tg_set_framing(struct nfc_digital_dev *ddev, int param)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_protocol *protocols;
+       struct sk_buff *skb;
+       struct sk_buff *resp;
+       int rc;
+       int num_protocols;
+       size_t size;
+
+       if (param >= NFC_DIGITAL_FRAMING_LAST)
+               return -EINVAL;
+
+       protocols = tg_protocols[param];
+
+       num_protocols = 0;
+       while (protocols[num_protocols].number != PORT100_TG_PROT_END)
+               num_protocols++;
+
+       if (!num_protocols)
+               return 0;
+
+       size = sizeof(struct port100_protocol) * num_protocols;
+
+       skb = port100_alloc_skb(dev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, size), protocols, size);
+
+       resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_PROTOCOL, skb);
+
+       if (IS_ERR(resp))
+               return PTR_ERR(resp);
+
+       rc = resp->data[0];
+
+       dev_kfree_skb(resp);
+
+       return rc;
+}
+
+static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type,
+                                  int param)
+{
+       if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+               return port100_tg_set_rf(ddev, param);
+
+       if (type == NFC_DIGITAL_CONFIG_FRAMING)
+               return port100_tg_set_framing(ddev, param);
+
+       return -EINVAL;
+}
+
+static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated)
+{
+       u8 mask;
+
+       switch (dev->cmd_type) {
+       case PORT100_CMD_TYPE_0:
+               mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK;
+               break;
+       case PORT100_CMD_TYPE_1:
+               mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK |
+                      PORT100_MDAA_TGT_WAS_ACTIVATED_MASK;
+               break;
+       default:
+               nfc_err(&dev->interface->dev, "Unknonwn command type.\n");
+               return false;
+       }
+
+       return ((tgt_activated & mask) == mask);
+}
+
+static void port100_tg_comm_rf_complete(struct port100 *dev, void *arg,
+                                       struct sk_buff *resp)
+{
+       u32 status;
+       struct port100_cb_arg *cb_arg = arg;
+       nfc_digital_cmd_complete_t cb = cb_arg->complete_cb;
+       struct port100_tg_comm_rf_res *hdr;
+
+       if (IS_ERR(resp))
+               goto exit;
+
+       hdr = (struct port100_tg_comm_rf_res *)resp->data;
+
+       status = le32_to_cpu(hdr->status);
+
+       if (cb_arg->mdaa &&
+           !port100_tg_target_activated(dev, hdr->target_activated)) {
+               kfree_skb(resp);
+               resp = ERR_PTR(-ETIMEDOUT);
+
+               goto exit;
+       }
+
+       skb_pull(resp, sizeof(struct port100_tg_comm_rf_res));
+
+       if (status != PORT100_CMD_STATUS_OK) {
+               kfree_skb(resp);
+
+               if (status == PORT100_CMD_STATUS_TIMEOUT)
+                       resp = ERR_PTR(-ETIMEDOUT);
+               else
+                       resp = ERR_PTR(-EIO);
+       }
+
+exit:
+       cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp);
+
+       kfree(cb_arg);
+}
+
+static int port100_tg_send_cmd(struct nfc_digital_dev *ddev,
+                              struct sk_buff *skb, u16 timeout,
+                              nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_tg_comm_rf_cmd *hdr;
+       struct port100_cb_arg *cb_arg;
+
+       cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+       if (!cb_arg)
+               return -ENOMEM;
+
+       cb_arg->complete_cb = cb;
+       cb_arg->complete_arg = arg;
+
+       skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd));
+
+       hdr = (struct port100_tg_comm_rf_cmd *)skb->data;
+
+       memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd));
+       hdr->guard_time = cpu_to_le16(500);
+       hdr->send_timeout = cpu_to_le16(0xFFFF);
+       hdr->recv_timeout = cpu_to_le16(timeout);
+
+       return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb,
+                                     port100_tg_comm_rf_complete, cb_arg);
+}
+
+static int port100_listen_mdaa(struct nfc_digital_dev *ddev,
+                              struct digital_tg_mdaa_params *params,
+                              u16 timeout,
+                              nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct port100_tg_comm_rf_cmd *hdr;
+       struct port100_cb_arg *cb_arg;
+       struct sk_buff *skb;
+       int rc;
+
+       rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+                                    NFC_DIGITAL_RF_TECH_106A);
+       if (rc)
+               return rc;
+
+       rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_NFC_DEP);
+       if (rc)
+               return rc;
+
+       cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL);
+       if (!cb_arg)
+               return -ENOMEM;
+
+       cb_arg->complete_cb = cb;
+       cb_arg->complete_arg = arg;
+       cb_arg->mdaa = 1;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb) {
+               kfree(cb_arg);
+               return -ENOMEM;
+       }
+
+       skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd));
+       hdr = (struct port100_tg_comm_rf_cmd *)skb->data;
+
+       memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd));
+
+       hdr->guard_time = 0;
+       hdr->send_timeout = cpu_to_le16(0xFFFF);
+       hdr->mdaa = 1;
+       hdr->nfca_param[0] = (params->sens_res >> 8) & 0xFF;
+       hdr->nfca_param[1] = params->sens_res & 0xFF;
+       memcpy(hdr->nfca_param + 2, params->nfcid1, 3);
+       hdr->nfca_param[5] = params->sel_res;
+       memcpy(hdr->nfcf_param, params->nfcid2, 8);
+       hdr->nfcf_param[16] = (params->sc >> 8) & 0xFF;
+       hdr->nfcf_param[17] = params->sc & 0xFF;
+       hdr->recv_timeout = cpu_to_le16(timeout);
+
+       return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb,
+                                     port100_tg_comm_rf_complete, cb_arg);
+}
+
+static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout,
+                         nfc_digital_cmd_complete_t cb, void *arg)
+{
+       struct port100 *dev = nfc_digital_get_drvdata(ddev);
+       struct sk_buff *skb;
+
+       skb = port100_alloc_skb(dev, 0);
+       if (!skb)
+               return -ENOMEM;
+
+       return port100_tg_send_cmd(ddev, skb, timeout, cb, arg);
+}
+
+static struct nfc_digital_ops port100_digital_ops = {
+       .in_configure_hw = port100_in_configure_hw,
+       .in_send_cmd = port100_in_send_cmd,
+
+       .tg_listen_mdaa = port100_listen_mdaa,
+       .tg_listen = port100_listen,
+       .tg_configure_hw = port100_tg_configure_hw,
+       .tg_send_cmd = port100_tg_send_cmd,
+
+       .switch_rf = port100_switch_rf,
+       .abort_cmd = port100_abort_cmd,
+};
+
+static const struct usb_device_id port100_table[] = {
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE,
+         .idVendor             = SONY_VENDOR_ID,
+         .idProduct            = RCS380_PRODUCT_ID,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, port100_table);
+
+static int port100_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id)
+{
+       struct port100 *dev;
+       int rc;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int in_endpoint;
+       int out_endpoint;
+       u16 fw_version;
+       u64 cmd_type_mask;
+       int i;
+
+       dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->udev = usb_get_dev(interface_to_usbdev(interface));
+       dev->interface = interface;
+       usb_set_intfdata(interface, dev);
+
+       in_endpoint = out_endpoint = 0;
+       iface_desc = interface->cur_altsetting;
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint))
+                       in_endpoint = endpoint->bEndpointAddress;
+
+               if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint))
+                       out_endpoint = endpoint->bEndpointAddress;
+       }
+
+       if (!in_endpoint || !out_endpoint) {
+               nfc_err(&interface->dev,
+                       "Could not find bulk-in or bulk-out endpoint\n");
+               rc = -ENODEV;
+               goto error;
+       }
+
+       dev->in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       dev->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!dev->in_urb || !dev->out_urb) {
+               nfc_err(&interface->dev, "Could not allocate USB URBs\n");
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       usb_fill_bulk_urb(dev->in_urb, dev->udev,
+                         usb_rcvbulkpipe(dev->udev, in_endpoint),
+                         NULL, 0, NULL, dev);
+       usb_fill_bulk_urb(dev->out_urb, dev->udev,
+                         usb_sndbulkpipe(dev->udev, out_endpoint),
+                         NULL, 0, port100_send_complete, dev);
+
+       dev->skb_headroom = PORT100_FRAME_HEADER_LEN +
+                           PORT100_COMM_RF_HEAD_MAX_LEN;
+       dev->skb_tailroom = PORT100_FRAME_TAIL_LEN;
+
+       INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete);
+
+       /* The first thing to do with the Port-100 is to set the command type
+        * to be used. If supported we use command type 1. 0 otherwise.
+        */
+       cmd_type_mask = port100_get_command_type_mask(dev);
+       if (!cmd_type_mask) {
+               nfc_err(&interface->dev,
+                       "Could not get supported command types.\n");
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1))
+               dev->cmd_type = PORT100_CMD_TYPE_1;
+       else
+               dev->cmd_type = PORT100_CMD_TYPE_0;
+
+       rc = port100_set_command_type(dev, dev->cmd_type);
+       if (rc) {
+               nfc_err(&interface->dev,
+                       "The device does not support command type %u.\n",
+                       dev->cmd_type);
+               goto error;
+       }
+
+       fw_version = port100_get_firmware_version(dev);
+       if (!fw_version)
+               nfc_err(&interface->dev,
+                       "Could not get device firmware version.\n");
+
+       nfc_info(&interface->dev,
+                "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
+                (fw_version & 0xFF00) >> 8, fw_version & 0xFF);
+
+       dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops,
+                                                          PORT100_PROTOCOLS,
+                                                          PORT100_CAPABILITIES,
+                                                          dev->skb_headroom,
+                                                          dev->skb_tailroom);
+       if (!dev->nfc_digital_dev) {
+               nfc_err(&interface->dev,
+                       "Could not allocate nfc_digital_dev.\n");
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev);
+       nfc_digital_set_drvdata(dev->nfc_digital_dev, dev);
+
+       rc = nfc_digital_register_device(dev->nfc_digital_dev);
+       if (rc) {
+               nfc_err(&interface->dev,
+                       "Could not register digital device.\n");
+               goto free_nfc_dev;
+       }
+
+       return 0;
+
+free_nfc_dev:
+       nfc_digital_free_device(dev->nfc_digital_dev);
+
+error:
+       usb_free_urb(dev->in_urb);
+       usb_free_urb(dev->out_urb);
+       usb_put_dev(dev->udev);
+
+       return rc;
+}
+
+static void port100_disconnect(struct usb_interface *interface)
+{
+       struct port100 *dev;
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       nfc_digital_unregister_device(dev->nfc_digital_dev);
+       nfc_digital_free_device(dev->nfc_digital_dev);
+
+       usb_kill_urb(dev->in_urb);
+       usb_kill_urb(dev->out_urb);
+
+       usb_free_urb(dev->in_urb);
+       usb_free_urb(dev->out_urb);
+
+       kfree(dev->cmd);
+
+       nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected");
+}
+
+static struct usb_driver port100_driver = {
+       .name =         "port100",
+       .probe =        port100_probe,
+       .disconnect =   port100_disconnect,
+       .id_table =     port100_table,
+};
+
+module_usb_driver(port100_driver);
+
+MODULE_DESCRIPTION("NFC Port-100 series usb driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
index 9d2009a..78cc760 100644 (file)
@@ -74,10 +74,4 @@ config OF_MTD
        depends on MTD
        def_bool y
 
-config OF_RESERVED_MEM
-       depends on OF_FLATTREE && (DMA_CMA || (HAVE_GENERIC_DMA_COHERENT && HAVE_MEMBLOCK))
-       def_bool y
-       help
-         Initialization code for DMA reserved memory
-
 endmenu # OF
index ed9660a..efd0510 100644 (file)
@@ -9,4 +9,3 @@ obj-$(CONFIG_OF_MDIO)   += of_mdio.o
 obj-$(CONFIG_OF_PCI)   += of_pci.o
 obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
 obj-$(CONFIG_OF_MTD)   += of_mtd.o
-obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
index 865d3f6..7d4c70f 100644 (file)
@@ -303,10 +303,8 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
        struct device_node *cpun, *cpus;
 
        cpus = of_find_node_by_path("/cpus");
-       if (!cpus) {
-               pr_warn("Missing cpus node, bailing out\n");
+       if (!cpus)
                return NULL;
-       }
 
        for_each_child_of_node(cpus, cpun) {
                if (of_node_cmp(cpun->type, "cpu"))
index 229dd9d..a4fa9ad 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-#include <linux/random.h>
 
 #include <asm/setup.h>  /* for COMMAND_LINE_SIZE */
 #ifdef CONFIG_PPC
@@ -803,14 +802,3 @@ void __init unflatten_device_tree(void)
 }
 
 #endif /* CONFIG_OF_EARLY_FLATTREE */
-
-/* Feed entire flattened device tree into the random pool */
-static int __init add_fdt_randomness(void)
-{
-       if (initial_boot_params)
-               add_device_randomness(initial_boot_params,
-                               be32_to_cpu(initial_boot_params->totalsize));
-
-       return 0;
-}
-core_initcall(add_fdt_randomness);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
deleted file mode 100644 (file)
index 0fe40c7..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Device tree based initialization code for reserved memory.
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *             http://www.samsung.com
- * Author: Marek Szyprowski <m.szyprowski@samsung.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 optional) any later version of the license.
- */
-
-#include <linux/memblock.h>
-#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_fdt.h>
-#include <linux/of_platform.h>
-#include <linux/mm.h>
-#include <linux/sizes.h>
-#include <linux/mm_types.h>
-#include <linux/dma-contiguous.h>
-#include <linux/dma-mapping.h>
-#include <linux/of_reserved_mem.h>
-
-#define MAX_RESERVED_REGIONS   16
-struct reserved_mem {
-       phys_addr_t             base;
-       unsigned long           size;
-       struct cma              *cma;
-       char                    name[32];
-};
-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
-static int reserved_mem_count;
-
-static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname,
-                                       int depth, void *data)
-{
-       struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
-       phys_addr_t base, size;
-       int is_cma, is_reserved;
-       unsigned long len;
-       const char *status;
-       __be32 *prop;
-
-       is_cma = IS_ENABLED(CONFIG_DMA_CMA) &&
-              of_flat_dt_is_compatible(node, "linux,contiguous-memory-region");
-       is_reserved = of_flat_dt_is_compatible(node, "reserved-memory-region");
-
-       if (!is_reserved && !is_cma) {
-               /* ignore node and scan next one */
-               return 0;
-       }
-
-       status = of_get_flat_dt_prop(node, "status", &len);
-       if (status && strcmp(status, "okay") != 0) {
-               /* ignore disabled node nad scan next one */
-               return 0;
-       }
-
-       prop = of_get_flat_dt_prop(node, "reg", &len);
-       if (!prop || (len < (dt_root_size_cells + dt_root_addr_cells) *
-                            sizeof(__be32))) {
-               pr_err("Reserved mem: node %s, incorrect \"reg\" property\n",
-                      uname);
-               /* ignore node and scan next one */
-               return 0;
-       }
-       base = dt_mem_next_cell(dt_root_addr_cells, &prop);
-       size = dt_mem_next_cell(dt_root_size_cells, &prop);
-
-       if (!size) {
-               /* ignore node and scan next one */
-               return 0;
-       }
-
-       pr_info("Reserved mem: found %s, memory base %lx, size %ld MiB\n",
-               uname, (unsigned long)base, (unsigned long)size / SZ_1M);
-
-       if (reserved_mem_count == ARRAY_SIZE(reserved_mem))
-               return -ENOSPC;
-
-       rmem->base = base;
-       rmem->size = size;
-       strlcpy(rmem->name, uname, sizeof(rmem->name));
-
-       if (is_cma) {
-               struct cma *cma;
-               if (dma_contiguous_reserve_area(size, base, 0, &cma) == 0) {
-                       rmem->cma = cma;
-                       reserved_mem_count++;
-                       if (of_get_flat_dt_prop(node,
-                                               "linux,default-contiguous-region",
-                                               NULL))
-                               dma_contiguous_set_default(cma);
-               }
-       } else if (is_reserved) {
-               if (memblock_remove(base, size) == 0)
-                       reserved_mem_count++;
-               else
-                       pr_err("Failed to reserve memory for %s\n", uname);
-       }
-
-       return 0;
-}
-
-static struct reserved_mem *get_dma_memory_region(struct device *dev)
-{
-       struct device_node *node;
-       const char *name;
-       int i;
-
-       node = of_parse_phandle(dev->of_node, "memory-region", 0);
-       if (!node)
-               return NULL;
-
-       name = kbasename(node->full_name);
-       for (i = 0; i < reserved_mem_count; i++)
-               if (strcmp(name, reserved_mem[i].name) == 0)
-                       return &reserved_mem[i];
-       return NULL;
-}
-
-/**
- * of_reserved_mem_device_init() - assign reserved memory region to given device
- *
- * This function assign memory region pointed by "memory-region" device tree
- * property to the given device.
- */
-void of_reserved_mem_device_init(struct device *dev)
-{
-       struct reserved_mem *region = get_dma_memory_region(dev);
-       if (!region)
-               return;
-
-       if (region->cma) {
-               dev_set_cma_area(dev, region->cma);
-               pr_info("Assigned CMA %s to %s device\n", region->name,
-                       dev_name(dev));
-       } else {
-               if (dma_declare_coherent_memory(dev, region->base, region->base,
-                   region->size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) != 0)
-                       pr_info("Declared reserved memory %s to %s device\n",
-                               region->name, dev_name(dev));
-       }
-}
-
-/**
- * of_reserved_mem_device_release() - release reserved memory device structures
- *
- * This function releases structures allocated for memory region handling for
- * the given device.
- */
-void of_reserved_mem_device_release(struct device *dev)
-{
-       struct reserved_mem *region = get_dma_memory_region(dev);
-       if (!region && !region->cma)
-               dma_release_declared_memory(dev);
-}
-
-/**
- * early_init_dt_scan_reserved_mem() - create reserved memory regions
- *
- * This function grabs memory from early allocator for device exclusive use
- * defined in device tree structures. It should be called by arch specific code
- * once the early allocator (memblock) has been activated and all other
- * subsystems have already allocated/reserved memory.
- */
-void __init early_init_dt_scan_reserved_mem(void)
-{
-       of_scan_flat_dt_by_path("/memory/reserved-memory",
-                               fdt_scan_reserved_mem, NULL);
-}
index 9b439ac..f6dcde2 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
-#include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
 
 const struct of_device_id of_default_bus_match_table[] = {
@@ -219,8 +218,6 @@ static struct platform_device *of_platform_device_create_pdata(
        dev->dev.bus = &platform_bus_type;
        dev->dev.platform_data = platform_data;
 
-       of_reserved_mem_device_init(&dev->dev);
-
        /* We do not fill the DMA ops for platform devices by default.
         * This is currently the responsibility of the platform code
         * to do such, possibly using a device notifier
@@ -228,7 +225,6 @@ static struct platform_device *of_platform_device_create_pdata(
 
        if (of_device_add(dev) != 0) {
                platform_device_put(dev);
-               of_reserved_mem_device_release(&dev->dev);
                return NULL;
        }
 
index 0b7d23b..1ea7523 100644 (file)
@@ -552,9 +552,8 @@ static void __ref enable_slot(struct acpiphp_slot *slot)
        struct acpiphp_func *func;
        int max, pass;
        LIST_HEAD(add_list);
-       int nr_found;
 
-       nr_found = acpiphp_rescan_slot(slot);
+       acpiphp_rescan_slot(slot);
        max = acpiphp_max_busnr(bus);
        for (pass = 0; pass < 2; pass++) {
                list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -574,9 +573,6 @@ static void __ref enable_slot(struct acpiphp_slot *slot)
                }
        }
        __pci_bus_assign_resources(bus, &add_list, NULL);
-       /* Nothing more to do here if there are no new devices on this bus. */
-       if (!nr_found && (slot->flags & SLOT_ENABLED))
-               return;
 
        acpiphp_sanitize_bus(bus);
        acpiphp_set_hpp_values(bus);
@@ -994,14 +990,16 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
 
                /*
                 * This bridge should have been registered as a hotplug function
-                * under its parent, so the context has to be there.  If not, we
-                * are in deep goo.
+                * under its parent, so the context should be there, unless the
+                * parent is going to be handled by pciehp, in which case this
+                * bridge is not interesting to us either.
                 */
                mutex_lock(&acpiphp_context_lock);
                context = acpiphp_get_context(handle);
-               if (WARN_ON(!context)) {
+               if (!context) {
                        mutex_unlock(&acpiphp_context_lock);
                        put_device(&bus->dev);
+                       pci_dev_put(bridge->pci_dev);
                        kfree(bridge);
                        return;
                }
index 7c29ee4..b0299e6 100644 (file)
@@ -47,6 +47,9 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
        if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
                return;
 
+       if (pci_dev->pme_poll)
+               pci_dev->pme_poll = false;
+
        if (pci_dev->current_state == PCI_D3cold) {
                pci_wakeup_event(pci_dev);
                pm_runtime_resume(&pci_dev->dev);
@@ -57,9 +60,6 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
        if (pci_dev->pme_support)
                pci_check_pme_status(pci_dev);
 
-       if (pci_dev->pme_poll)
-               pci_dev->pme_poll = false;
-
        pci_wakeup_event(pci_dev);
        pm_runtime_resume(&pci_dev->dev);
 
index e8ccf6c..bdd64b1 100644 (file)
@@ -1155,8 +1155,14 @@ static void pci_enable_bridge(struct pci_dev *dev)
 
        pci_enable_bridge(dev->bus->self);
 
-       if (pci_is_enabled(dev))
+       if (pci_is_enabled(dev)) {
+               if (!dev->is_busmaster) {
+                       dev_warn(&dev->dev, "driver skip pci_set_master, fix it!\n");
+                       pci_set_master(dev);
+               }
                return;
+       }
+
        retval = pci_enable_device(dev);
        if (retval)
                dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n",
index a138965..b8fcc38 100644 (file)
@@ -490,7 +490,7 @@ exit:
  * <devicename> <state> <pinname> are values that should match the pinctrl-maps
  * <newvalue> reflects the new config and is driver dependant
  */
-static int pinconf_dbg_config_write(struct file *file,
+static ssize_t pinconf_dbg_config_write(struct file *file,
        const char __user *user_buf, size_t count, loff_t *ppos)
 {
        struct pinctrl_maps *maps_node;
@@ -508,7 +508,7 @@ static int pinconf_dbg_config_write(struct file *file,
        int i;
 
        /* Get userspace string and assure termination */
-       buf_size = min(count, (size_t)(sizeof(buf)-1));
+       buf_size = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, user_buf, buf_size))
                return -EFAULT;
        buf[buf_size] = 0;
index 2689f8d..155b1b3 100644 (file)
@@ -663,18 +663,18 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
 /* pin banks of s5pv210 pin-controller */
 static struct samsung_pin_bank s5pv210_pin_bank[] = {
        EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00),
-       EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04),
+       EXYNOS_PIN_BANK_EINTG(4, 0x020, "gpa1", 0x04),
        EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpb", 0x08),
        EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpc0", 0x0c),
        EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpc1", 0x10),
        EXYNOS_PIN_BANK_EINTG(4, 0x0a0, "gpd0", 0x14),
-       EXYNOS_PIN_BANK_EINTG(4, 0x0c0, "gpd1", 0x18),
-       EXYNOS_PIN_BANK_EINTG(5, 0x0e0, "gpe0", 0x1c),
-       EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpe1", 0x20),
-       EXYNOS_PIN_BANK_EINTG(6, 0x120, "gpf0", 0x24),
+       EXYNOS_PIN_BANK_EINTG(6, 0x0c0, "gpd1", 0x18),
+       EXYNOS_PIN_BANK_EINTG(8, 0x0e0, "gpe0", 0x1c),
+       EXYNOS_PIN_BANK_EINTG(5, 0x100, "gpe1", 0x20),
+       EXYNOS_PIN_BANK_EINTG(8, 0x120, "gpf0", 0x24),
        EXYNOS_PIN_BANK_EINTG(8, 0x140, "gpf1", 0x28),
        EXYNOS_PIN_BANK_EINTG(8, 0x160, "gpf2", 0x2c),
-       EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpf3", 0x30),
+       EXYNOS_PIN_BANK_EINTG(6, 0x180, "gpf3", 0x30),
        EXYNOS_PIN_BANK_EINTG(7, 0x1a0, "gpg0", 0x34),
        EXYNOS_PIN_BANK_EINTG(7, 0x1c0, "gpg1", 0x38),
        EXYNOS_PIN_BANK_EINTG(7, 0x1e0, "gpg2", 0x3c),
index 82638fa..30c4d35 100644 (file)
@@ -891,9 +891,10 @@ static int palmas_pinconf_set(struct pinctrl_dev *pctldev,
                param = pinconf_to_config_param(configs[i]);
                param_val = pinconf_to_config_argument(configs[i]);
 
+               if (param == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT)
+                       continue;
+
                switch (param) {
-               case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
-                       return 0;
                case PIN_CONFIG_BIAS_DISABLE:
                case PIN_CONFIG_BIAS_PULL_UP:
                case PIN_CONFIG_BIAS_PULL_DOWN:
index 622c485..93c9e38 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
  *
- * Arthur:  Pritesh Raithatha <praithatha@nvidia.com>
+ * Author:  Pritesh Raithatha <praithatha@nvidia.com>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -2763,7 +2763,6 @@ static struct platform_driver tegra114_pinctrl_driver = {
 };
 module_platform_driver(tegra114_pinctrl_driver);
 
-MODULE_ALIAS("platform:tegra114-pinctrl");
 MODULE_AUTHOR("Pritesh Raithatha <praithatha@nvidia.com>");
-MODULE_DESCRIPTION("NVIDIA Tegra114 pincontrol driver");
+MODULE_DESCRIPTION("NVIDIA Tegra114 pinctrl driver");
 MODULE_LICENSE("GPL v2");
index 96d6b2e..b51a746 100644 (file)
@@ -504,6 +504,7 @@ config ASUS_WMI
        depends on BACKLIGHT_CLASS_DEVICE
        depends on RFKILL || RFKILL = n
        depends on HOTPLUG_PCI
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        select LEDS_CLASS
        select NEW_LEDS
index d3fd520..13ec195 100644 (file)
@@ -127,18 +127,17 @@ MODULE_PARM_DESC(minor,
                 "default is -1 (automatic)");
 #endif
 
-static int kbd_backlight = 1;
+static int kbd_backlight = -1;
 module_param(kbd_backlight, int, 0444);
 MODULE_PARM_DESC(kbd_backlight,
                 "set this to 0 to disable keyboard backlight, "
-                "1 to enable it (default: 0)");
+                "1 to enable it (default: no change from current value)");
 
-static int kbd_backlight_timeout;      /* = 0 */
+static int kbd_backlight_timeout = -1;
 module_param(kbd_backlight_timeout, int, 0444);
 MODULE_PARM_DESC(kbd_backlight_timeout,
-                "set this to 0 to set the default 10 seconds timeout, "
-                "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
-                "(default: 0)");
+                "meaningful values vary from 0 to 3 and their meaning depends "
+                "on the model (default: no change from current value)");
 
 #ifdef CONFIG_PM_SLEEP
 static void sony_nc_kbd_backlight_resume(void);
@@ -1844,6 +1843,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
        if (!kbdbl_ctl)
                return -ENOMEM;
 
+       kbdbl_ctl->mode = kbd_backlight;
+       kbdbl_ctl->timeout = kbd_backlight_timeout;
        kbdbl_ctl->handle = handle;
        if (handle == 0x0137)
                kbdbl_ctl->base = 0x0C00;
@@ -1870,8 +1871,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
        if (ret)
                goto outmode;
 
-       __sony_nc_kbd_backlight_mode_set(kbd_backlight);
-       __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout);
+       __sony_nc_kbd_backlight_mode_set(kbdbl_ctl->mode);
+       __sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);
 
        return 0;
 
@@ -1886,17 +1887,8 @@ outkzalloc:
 static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
 {
        if (kbdbl_ctl) {
-               int result;
-
                device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
                device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
-
-               /* restore the default hw behaviour */
-               sony_call_snc_handle(kbdbl_ctl->handle,
-                               kbdbl_ctl->base | 0x10000, &result);
-               sony_call_snc_handle(kbdbl_ctl->handle,
-                               kbdbl_ctl->base + 0x200, &result);
-
                kfree(kbdbl_ctl);
                kbdbl_ctl = NULL;
        }
index 1a78163..b9f2653 100644 (file)
@@ -709,7 +709,7 @@ static struct da9063_regulators_pdata *da9063_parse_regulators_dt(
                struct of_regulator_match **da9063_reg_matches)
 {
        da9063_reg_matches = NULL;
-       return PTR_ERR(-ENODEV);
+       return ERR_PTR(-ENODEV);
 }
 #endif
 
index 488dfe7..7e2b165 100644 (file)
@@ -201,13 +201,7 @@ static unsigned int palmas_smps_ramp_delay[4] = {0, 10000, 5000, 2500};
 #define SMPS_CTRL_MODE_ECO             0x02
 #define SMPS_CTRL_MODE_PWM             0x03
 
-/* These values are derived from the data sheet. And are the number of steps
- * where there is a voltage change, the ranges at beginning and end of register
- * max/min values where there are no change are ommitted.
- *
- * So they are basically (maxV-minV)/stepV
- */
-#define PALMAS_SMPS_NUM_VOLTAGES       117
+#define PALMAS_SMPS_NUM_VOLTAGES       122
 #define PALMAS_SMPS10_NUM_VOLTAGES     2
 #define PALMAS_LDO_NUM_VOLTAGES                50
 
@@ -979,6 +973,7 @@ static int palmas_regulators_probe(struct platform_device *pdev)
                        pmic->desc[id].min_uV = 900000;
                        pmic->desc[id].uV_step = 50000;
                        pmic->desc[id].linear_min_sel = 1;
+                       pmic->desc[id].enable_time = 500;
                        pmic->desc[id].vsel_reg =
                                        PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
                                                palmas_regs_info[id].vsel_addr);
@@ -997,6 +992,11 @@ static int palmas_regulators_probe(struct platform_device *pdev)
                                pmic->desc[id].min_uV = 450000;
                                pmic->desc[id].uV_step = 25000;
                        }
+
+                       /* LOD6 in vibrator mode will have enable time 2000us */
+                       if (pdata && pdata->ldo6_vibrator &&
+                               (id == PALMAS_REG_LDO6))
+                               pmic->desc[id].enable_time = 2000;
                } else {
                        pmic->desc[id].n_voltages = 1;
                        pmic->desc[id].ops = &palmas_ops_extreg;
index d8e3e12..20c271d 100644 (file)
@@ -279,8 +279,12 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb,
        ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg,
                   abb->base);
 
-       /* program LDO VBB vset override if needed */
-       if (abb->ldo_base)
+       /*
+        * program LDO VBB vset override if needed for !bypass mode
+        * XXX: Do not switch sequence - for !bypass, LDO override reset *must*
+        * be performed *before* switch to bias mode else VBB glitches.
+        */
+       if (abb->ldo_base && info->opp_sel != TI_ABB_NOMINAL_OPP)
                ti_abb_program_ldovbb(dev, abb, info);
 
        /* Initiate ABB ldo change */
@@ -295,6 +299,14 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb,
        if (ret)
                goto out;
 
+       /*
+        * Reset LDO VBB vset override bypass mode
+        * XXX: Do not switch sequence - for bypass, LDO override reset *must*
+        * be performed *after* switch to bypass else VBB glitches.
+        */
+       if (abb->ldo_base && info->opp_sel == TI_ABB_NOMINAL_OPP)
+               ti_abb_program_ldovbb(dev, abb, info);
+
 out:
        return ret;
 }
index 1432b26..2205fbc 100644 (file)
@@ -63,7 +63,7 @@ static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data)
  */
 
 static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = {
-       { .min_uV =  900000, .max_uV = 1650000, .min_sel =  0, .max_sel = 14,
+       { .min_uV =  900000, .max_uV = 1600000, .min_sel =  0, .max_sel = 14,
          .uV_step =  50000 },
        { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31,
          .uV_step = 100000 },
@@ -332,7 +332,7 @@ static struct platform_driver wm831x_gp_ldo_driver = {
  */
 
 static const struct regulator_linear_range wm831x_aldo_ranges[] = {
-       { .min_uV = 1000000, .max_uV = 1650000, .min_sel =  0, .max_sel = 12,
+       { .min_uV = 1000000, .max_uV = 1600000, .min_sel =  0, .max_sel = 12,
          .uV_step =  50000 },
        { .min_uV = 1700000, .max_uV = 3500000, .min_sel = 13, .max_sel = 31,
          .uV_step = 100000 },
index 835b5f0..61ca929 100644 (file)
@@ -543,7 +543,7 @@ static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev,
 }
 
 static const struct regulator_linear_range wm8350_ldo_ranges[] = {
-       { .min_uV =  900000, .max_uV = 1750000, .min_sel =  0, .max_sel = 15,
+       { .min_uV =  900000, .max_uV = 1650000, .min_sel =  0, .max_sel = 15,
          .uV_step =  50000 },
        { .min_uV = 1800000, .max_uV = 3300000, .min_sel = 16, .max_sel = 31,
          .uV_step = 100000 },
index 5adb204..cee7e27 100644 (file)
@@ -2077,6 +2077,7 @@ dasd_eckd_build_format(struct dasd_device *base,
        int intensity = 0;
        int r0_perm;
        int nr_tracks;
+       int use_prefix;
 
        startdev = dasd_alias_get_start_dev(base);
        if (!startdev)
@@ -2106,28 +2107,46 @@ dasd_eckd_build_format(struct dasd_device *base,
                intensity = fdata->intensity;
        }
 
+       use_prefix = base_priv->features.feature[8] & 0x01;
+
        switch (intensity) {
        case 0x00:      /* Normal format */
        case 0x08:      /* Normal format, use cdl. */
                cplength = 2 + (rpt*nr_tracks);
-               datasize = sizeof(struct PFX_eckd_data) +
-                       sizeof(struct LO_eckd_data) +
-                       rpt * nr_tracks * sizeof(struct eckd_count);
+               if (use_prefix)
+                       datasize = sizeof(struct PFX_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
+               else
+                       datasize = sizeof(struct DE_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
                break;
        case 0x01:      /* Write record zero and format track. */
        case 0x09:      /* Write record zero and format track, use cdl. */
                cplength = 2 + rpt * nr_tracks;
-               datasize = sizeof(struct PFX_eckd_data) +
-                       sizeof(struct LO_eckd_data) +
-                       sizeof(struct eckd_count) +
-                       rpt * nr_tracks * sizeof(struct eckd_count);
+               if (use_prefix)
+                       datasize = sizeof(struct PFX_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
+               else
+                       datasize = sizeof(struct DE_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count) +
+                               rpt * nr_tracks * sizeof(struct eckd_count);
                break;
        case 0x04:      /* Invalidate track. */
        case 0x0c:      /* Invalidate track, use cdl. */
                cplength = 3;
-               datasize = sizeof(struct PFX_eckd_data) +
-                       sizeof(struct LO_eckd_data) +
-                       sizeof(struct eckd_count);
+               if (use_prefix)
+                       datasize = sizeof(struct PFX_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count);
+               else
+                       datasize = sizeof(struct DE_eckd_data) +
+                               sizeof(struct LO_eckd_data) +
+                               sizeof(struct eckd_count);
                break;
        default:
                dev_warn(&startdev->cdev->dev,
@@ -2147,14 +2166,25 @@ dasd_eckd_build_format(struct dasd_device *base,
 
        switch (intensity & ~0x08) {
        case 0x00: /* Normal format. */
-               prefix(ccw++, (struct PFX_eckd_data *) data,
-                      fdata->start_unit, fdata->stop_unit,
-                      DASD_ECKD_CCW_WRITE_CKD, base, startdev);
-               /* grant subsystem permission to format R0 */
-               if (r0_perm)
-                       ((struct PFX_eckd_data *)data)
-                               ->define_extent.ga_extended |= 0x04;
-               data += sizeof(struct PFX_eckd_data);
+               if (use_prefix) {
+                       prefix(ccw++, (struct PFX_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_CKD, base, startdev);
+                       /* grant subsystem permission to format R0 */
+                       if (r0_perm)
+                               ((struct PFX_eckd_data *)data)
+                                       ->define_extent.ga_extended |= 0x04;
+                       data += sizeof(struct PFX_eckd_data);
+               } else {
+                       define_extent(ccw++, (struct DE_eckd_data *) data,
+                                     fdata->start_unit, fdata->stop_unit,
+                                     DASD_ECKD_CCW_WRITE_CKD, startdev);
+                       /* grant subsystem permission to format R0 */
+                       if (r0_perm)
+                               ((struct DE_eckd_data *) data)
+                                       ->ga_extended |= 0x04;
+                       data += sizeof(struct DE_eckd_data);
+               }
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, rpt*nr_tracks,
@@ -2163,11 +2193,18 @@ dasd_eckd_build_format(struct dasd_device *base,
                data += sizeof(struct LO_eckd_data);
                break;
        case 0x01: /* Write record zero + format track. */
-               prefix(ccw++, (struct PFX_eckd_data *) data,
-                      fdata->start_unit, fdata->stop_unit,
-                      DASD_ECKD_CCW_WRITE_RECORD_ZERO,
-                      base, startdev);
-               data += sizeof(struct PFX_eckd_data);
+               if (use_prefix) {
+                       prefix(ccw++, (struct PFX_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_RECORD_ZERO,
+                              base, startdev);
+                       data += sizeof(struct PFX_eckd_data);
+               } else {
+                       define_extent(ccw++, (struct DE_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_RECORD_ZERO, startdev);
+                       data += sizeof(struct DE_eckd_data);
+               }
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, rpt * nr_tracks + 1,
@@ -2176,10 +2213,17 @@ dasd_eckd_build_format(struct dasd_device *base,
                data += sizeof(struct LO_eckd_data);
                break;
        case 0x04: /* Invalidate track. */
-               prefix(ccw++, (struct PFX_eckd_data *) data,
-                      fdata->start_unit, fdata->stop_unit,
-                      DASD_ECKD_CCW_WRITE_CKD, base, startdev);
-               data += sizeof(struct PFX_eckd_data);
+               if (use_prefix) {
+                       prefix(ccw++, (struct PFX_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_CKD, base, startdev);
+                       data += sizeof(struct PFX_eckd_data);
+               } else {
+                       define_extent(ccw++, (struct DE_eckd_data *) data,
+                              fdata->start_unit, fdata->stop_unit,
+                              DASD_ECKD_CCW_WRITE_CKD, startdev);
+                       data += sizeof(struct DE_eckd_data);
+               }
                ccw[-1].flags |= CCW_FLAG_CC;
                locate_record(ccw++, (struct LO_eckd_data *) data,
                              fdata->start_unit, 0, 1,
index a3aa374..1fe2643 100644 (file)
@@ -486,7 +486,7 @@ sclp_sync_wait(void)
        timeout = 0;
        if (timer_pending(&sclp_request_timer)) {
                /* Get timeout TOD value */
-               timeout = get_tod_clock() +
+               timeout = get_tod_clock_fast() +
                          sclp_tod_from_jiffies(sclp_request_timer.expires -
                                                jiffies);
        }
@@ -508,7 +508,7 @@ sclp_sync_wait(void)
        while (sclp_running_state != sclp_running_state_idle) {
                /* Check for expired request timer */
                if (timer_pending(&sclp_request_timer) &&
-                   get_tod_clock() > timeout &&
+                   get_tod_clock_fast() > timeout &&
                    del_timer(&sclp_request_timer))
                        sclp_request_timer.function(sclp_request_timer.data);
                cpu_relax();
index 8cd34bf..77df9cb 100644 (file)
@@ -145,9 +145,11 @@ bool __init sclp_has_linemode(void)
 
        if (sccb->header.response_code != 0x20)
                return 0;
-       if (sccb->sclp_send_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))
-               return 1;
-       return 0;
+       if (!(sccb->sclp_send_mask & (EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK)))
+               return 0;
+       if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
+               return 0;
+       return 1;
 }
 
 bool __init sclp_has_vt220(void)
index a0f47c8..3f4ca4e 100644 (file)
@@ -810,7 +810,7 @@ static void tty3270_resize_work(struct work_struct *work)
        struct winsize ws;
 
        screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols);
-       if (!screen)
+       if (IS_ERR(screen))
                return;
        /* Switch to new output size */
        spin_lock_bh(&tp->view.lock);
index 9b3a24e..cf31d33 100644 (file)
@@ -313,7 +313,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
        int ret;
 
        dev_num = iminor(inode);
-       if (dev_num > MAXMINOR)
+       if (dev_num >= MAXMINOR)
                return -ENODEV;
        logptr = &sys_ser[dev_num];
 
index d7da67a..88e35d8 100644 (file)
@@ -878,9 +878,9 @@ static void css_reset(void)
                        atomic_inc(&chpid_reset_count);
        }
        /* Wait for machine check for all channel paths. */
-       timeout = get_tod_clock() + (RCHP_TIMEOUT << 12);
+       timeout = get_tod_clock_fast() + (RCHP_TIMEOUT << 12);
        while (atomic_read(&chpid_reset_count) != 0) {
-               if (get_tod_clock() > timeout)
+               if (get_tod_clock_fast() > timeout)
                        break;
                cpu_relax();
        }
index 8ed52aa..bbd3e51 100644 (file)
@@ -338,10 +338,10 @@ again:
                retries++;
 
                if (!start_time) {
-                       start_time = get_tod_clock();
+                       start_time = get_tod_clock_fast();
                        goto again;
                }
-               if ((get_tod_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE)
+               if (get_tod_clock_fast() - start_time < QDIO_BUSY_BIT_PATIENCE)
                        goto again;
        }
        if (retries) {
@@ -504,7 +504,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
        int count, stop;
        unsigned char state = 0;
 
-       q->timestamp = get_tod_clock();
+       q->timestamp = get_tod_clock_fast();
 
        /*
         * Don't check 128 buffers, as otherwise qdio_inbound_q_moved
@@ -595,7 +595,7 @@ static inline int qdio_inbound_q_done(struct qdio_q *q)
         * At this point we know, that inbound first_to_check
         * has (probably) not moved (see qdio_inbound_processing).
         */
-       if (get_tod_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
+       if (get_tod_clock_fast() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x",
                              q->first_to_check);
                return 1;
@@ -728,7 +728,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
        int count, stop;
        unsigned char state = 0;
 
-       q->timestamp = get_tod_clock();
+       q->timestamp = get_tod_clock_fast();
 
        if (need_siga_sync(q))
                if (((queue_type(q) != QDIO_IQDIO_QFMT) &&
index 0a328d0..bd8c09e 100644 (file)
@@ -4451,7 +4451,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)
        struct qeth_cmd_buffer *iob;
        struct qeth_ipa_cmd *cmd;
        struct qeth_snmp_ureq *ureq;
-       int req_len;
+       unsigned int req_len;
        struct qeth_arp_query_info qinfo = {0, };
        int rc = 0;
 
@@ -4467,6 +4467,10 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)
        /* skip 4 bytes (data_len struct member) to get req_len */
        if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))
                return -EFAULT;
+       if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE -
+                      sizeof(struct qeth_ipacmd_hdr) -
+                      sizeof(struct qeth_ipacmd_setadpparms_hdr)))
+               return -EINVAL;
        ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr));
        if (IS_ERR(ureq)) {
                QETH_CARD_TEXT(card, 2, "snmpnome");
index feab3a5..757eb07 100644 (file)
@@ -696,7 +696,7 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
        while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC,
                                        PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
                                        pci_device)) != NULL) {
-               struct blogic_adapter *adapter = adapter;
+               struct blogic_adapter *host_adapter = adapter;
                struct blogic_adapter_info adapter_info;
                enum blogic_isa_ioport mod_ioaddr_req;
                unsigned char bus;
@@ -744,9 +744,9 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
                   known and enabled, note that the particular Standard ISA I/O
                   Address should not be probed.
                 */
-               adapter->io_addr = io_addr;
-               blogic_intreset(adapter);
-               if (blogic_cmd(adapter, BLOGIC_INQ_PCI_INFO, NULL, 0,
+               host_adapter->io_addr = io_addr;
+               blogic_intreset(host_adapter);
+               if (blogic_cmd(host_adapter, BLOGIC_INQ_PCI_INFO, NULL, 0,
                                &adapter_info, sizeof(adapter_info)) ==
                                sizeof(adapter_info)) {
                        if (adapter_info.isa_port < 6)
@@ -762,7 +762,7 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
                   I/O Address assigned at system initialization.
                 */
                mod_ioaddr_req = BLOGIC_IO_DISABLE;
-               blogic_cmd(adapter, BLOGIC_MOD_IOADDR, &mod_ioaddr_req,
+               blogic_cmd(host_adapter, BLOGIC_MOD_IOADDR, &mod_ioaddr_req,
                                sizeof(mod_ioaddr_req), NULL, 0);
                /*
                   For the first MultiMaster Host Adapter enumerated,
@@ -779,12 +779,12 @@ static int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter)
 
                        fetch_localram.offset = BLOGIC_AUTOSCSI_BASE + 45;
                        fetch_localram.count = sizeof(autoscsi_byte45);
-                       blogic_cmd(adapter, BLOGIC_FETCH_LOCALRAM,
+                       blogic_cmd(host_adapter, BLOGIC_FETCH_LOCALRAM,
                                        &fetch_localram, sizeof(fetch_localram),
                                        &autoscsi_byte45,
                                        sizeof(autoscsi_byte45));
-                       blogic_cmd(adapter, BLOGIC_GET_BOARD_ID, NULL, 0, &id,
-                                       sizeof(id));
+                       blogic_cmd(host_adapter, BLOGIC_GET_BOARD_ID, NULL, 0,
+                                       &id, sizeof(id));
                        if (id.fw_ver_digit1 == '5')
                                force_scan_order =
                                        autoscsi_byte45.force_scan_order;
index 408a42e..f0d432c 100644 (file)
@@ -771,6 +771,8 @@ static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long
 static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
 {
        struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
        return aac_compat_do_ioctl(dev, cmd, (unsigned long)arg);
 }
 
index 2ef497e..ee5c183 100644 (file)
@@ -20,7 +20,7 @@
  * | Device Discovery             |       0x2095       | 0x2020-0x2022, |
  * |                              |                    | 0x2011-0x2012, |
  * |                              |                    | 0x2016         |
- * | Queue Command and IO tracing |       0x3058       | 0x3006-0x300b  |
+ * | Queue Command and IO tracing |       0x3059       | 0x3006-0x300b  |
  * |                              |                    | 0x3027-0x3028  |
  * |                              |                    | 0x303d-0x3041  |
  * |                              |                    | 0x302d,0x3033  |
index df1b30b..ff9c86b 100644 (file)
@@ -1957,6 +1957,15 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
        que = MSW(sts->handle);
        req = ha->req_q_map[que];
 
+       /* Check for invalid queue pointer */
+       if (req == NULL ||
+           que >= find_first_zero_bit(ha->req_qid_map, ha->max_req_queues)) {
+               ql_dbg(ql_dbg_io, vha, 0x3059,
+                   "Invalid status handle (0x%x): Bad req pointer. req=%p, "
+                   "que=%u.\n", sts->handle, req, que);
+               return;
+       }
+
        /* Validate handle. */
        if (handle < req->num_outstanding_cmds)
                sp = req->outstanding_cmds[handle];
index e62d17d..5693f6d 100644 (file)
@@ -2854,6 +2854,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
                gd->events |= DISK_EVENT_MEDIA_CHANGE;
        }
 
+       blk_pm_runtime_init(sdp->request_queue, dev);
        add_disk(gd);
        if (sdkp->capacity)
                sd_dif_config_host(sdkp);
@@ -2862,7 +2863,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
 
        sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
                  sdp->removable ? "removable " : "");
-       blk_pm_runtime_init(sdp->request_queue, dev);
        scsi_autopm_put_device(sdp);
        put_device(&sdkp->dev);
 }
index 5cbc4bb..df5e961 100644 (file)
@@ -105,8 +105,11 @@ static int scatter_elem_sz_prev = SG_SCATTER_SZ;
 static int sg_add(struct device *, struct class_interface *);
 static void sg_remove(struct device *, struct class_interface *);
 
+static DEFINE_SPINLOCK(sg_open_exclusive_lock);
+
 static DEFINE_IDR(sg_index_idr);
-static DEFINE_RWLOCK(sg_index_lock);
+static DEFINE_RWLOCK(sg_index_lock);   /* Also used to lock
+                                                          file descriptor list for device */
 
 static struct class_interface sg_interface = {
        .add_dev        = sg_add,
@@ -143,7 +146,8 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
 } Sg_request;
 
 typedef struct sg_fd {         /* holds the state of a file descriptor */
-       struct list_head sfd_siblings; /* protected by sfd_lock of device */
+       /* sfd_siblings is protected by sg_index_lock */
+       struct list_head sfd_siblings;
        struct sg_device *parentdp;     /* owning device */
        wait_queue_head_t read_wait;    /* queue read until command done */
        rwlock_t rq_list_lock;  /* protect access to list in req_arr */
@@ -166,12 +170,13 @@ typedef struct sg_fd {            /* holds the state of a file descriptor */
 
 typedef struct sg_device { /* holds the state of each scsi generic device */
        struct scsi_device *device;
+       wait_queue_head_t o_excl_wait;  /* queue open() when O_EXCL in use */
        int sg_tablesize;       /* adapter's max scatter-gather table size */
        u32 index;              /* device index number */
-       spinlock_t sfd_lock;    /* protect file descriptor list for device */
+       /* sfds is protected by sg_index_lock */
        struct list_head sfds;
-       struct rw_semaphore o_sem;      /* exclude open should hold this rwsem */
        volatile char detached; /* 0->attached, 1->detached pending removal */
+       /* exclude protected by sg_open_exclusive_lock */
        char exclude;           /* opened for exclusive access */
        char sgdebug;           /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
        struct gendisk *disk;
@@ -220,14 +225,35 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd)
        return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE);
 }
 
+static int get_exclude(Sg_device *sdp)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&sg_open_exclusive_lock, flags);
+       ret = sdp->exclude;
+       spin_unlock_irqrestore(&sg_open_exclusive_lock, flags);
+       return ret;
+}
+
+static int set_exclude(Sg_device *sdp, char val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&sg_open_exclusive_lock, flags);
+       sdp->exclude = val;
+       spin_unlock_irqrestore(&sg_open_exclusive_lock, flags);
+       return val;
+}
+
 static int sfds_list_empty(Sg_device *sdp)
 {
        unsigned long flags;
        int ret;
 
-       spin_lock_irqsave(&sdp->sfd_lock, flags);
+       read_lock_irqsave(&sg_index_lock, flags);
        ret = list_empty(&sdp->sfds);
-       spin_unlock_irqrestore(&sdp->sfd_lock, flags);
+       read_unlock_irqrestore(&sg_index_lock, flags);
        return ret;
 }
 
@@ -239,6 +265,7 @@ sg_open(struct inode *inode, struct file *filp)
        struct request_queue *q;
        Sg_device *sdp;
        Sg_fd *sfp;
+       int res;
        int retval;
 
        nonseekable_open(inode, filp);
@@ -267,52 +294,54 @@ sg_open(struct inode *inode, struct file *filp)
                goto error_out;
        }
 
-       if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) {
-               retval = -EPERM; /* Can't lock it with read only access */
-               goto error_out;
-       }
-       if (flags & O_NONBLOCK) {
-               if (flags & O_EXCL) {
-                       if (!down_write_trylock(&sdp->o_sem)) {
-                               retval = -EBUSY;
-                               goto error_out;
-                       }
-               } else {
-                       if (!down_read_trylock(&sdp->o_sem)) {
-                               retval = -EBUSY;
-                               goto error_out;
-                       }
+       if (flags & O_EXCL) {
+               if (O_RDONLY == (flags & O_ACCMODE)) {
+                       retval = -EPERM; /* Can't lock it with read only access */
+                       goto error_out;
+               }
+               if (!sfds_list_empty(sdp) && (flags & O_NONBLOCK)) {
+                       retval = -EBUSY;
+                       goto error_out;
+               }
+               res = wait_event_interruptible(sdp->o_excl_wait,
+                                          ((!sfds_list_empty(sdp) || get_exclude(sdp)) ? 0 : set_exclude(sdp, 1)));
+               if (res) {
+                       retval = res;   /* -ERESTARTSYS because signal hit process */
+                       goto error_out;
+               }
+       } else if (get_exclude(sdp)) {  /* some other fd has an exclusive lock on dev */
+               if (flags & O_NONBLOCK) {
+                       retval = -EBUSY;
+                       goto error_out;
+               }
+               res = wait_event_interruptible(sdp->o_excl_wait, !get_exclude(sdp));
+               if (res) {
+                       retval = res;   /* -ERESTARTSYS because signal hit process */
+                       goto error_out;
                }
-       } else {
-               if (flags & O_EXCL)
-                       down_write(&sdp->o_sem);
-               else
-                       down_read(&sdp->o_sem);
        }
-       /* Since write lock is held, no need to check sfd_list */
-       if (flags & O_EXCL)
-               sdp->exclude = 1;       /* used by release lock */
-
+       if (sdp->detached) {
+               retval = -ENODEV;
+               goto error_out;
+       }
        if (sfds_list_empty(sdp)) {     /* no existing opens on this device */
                sdp->sgdebug = 0;
                q = sdp->device->request_queue;
                sdp->sg_tablesize = queue_max_segments(q);
        }
-       sfp = sg_add_sfp(sdp, dev);
-       if (!IS_ERR(sfp))
+       if ((sfp = sg_add_sfp(sdp, dev)))
                filp->private_data = sfp;
-               /* retval is already provably zero at this point because of the
-                * check after retval = scsi_autopm_get_device(sdp->device))
-                */
        else {
-               retval = PTR_ERR(sfp);
-
                if (flags & O_EXCL) {
-                       sdp->exclude = 0;       /* undo if error */
-                       up_write(&sdp->o_sem);
-               } else
-                       up_read(&sdp->o_sem);
+                       set_exclude(sdp, 0);    /* undo if error */
+                       wake_up_interruptible(&sdp->o_excl_wait);
+               }
+               retval = -ENOMEM;
+               goto error_out;
+       }
+       retval = 0;
 error_out:
+       if (retval) {
                scsi_autopm_put_device(sdp->device);
 sdp_put:
                scsi_device_put(sdp->device);
@@ -329,18 +358,13 @@ sg_release(struct inode *inode, struct file *filp)
 {
        Sg_device *sdp;
        Sg_fd *sfp;
-       int excl;
 
        if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
                return -ENXIO;
        SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name));
 
-       excl = sdp->exclude;
-       sdp->exclude = 0;
-       if (excl)
-               up_write(&sdp->o_sem);
-       else
-               up_read(&sdp->o_sem);
+       set_exclude(sdp, 0);
+       wake_up_interruptible(&sdp->o_excl_wait);
 
        scsi_autopm_put_device(sdp->device);
        kref_put(&sfp->f_ref, sg_remove_sfp);
@@ -1391,9 +1415,8 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
        disk->first_minor = k;
        sdp->disk = disk;
        sdp->device = scsidp;
-       spin_lock_init(&sdp->sfd_lock);
        INIT_LIST_HEAD(&sdp->sfds);
-       init_rwsem(&sdp->o_sem);
+       init_waitqueue_head(&sdp->o_excl_wait);
        sdp->sg_tablesize = queue_max_segments(q);
        sdp->index = k;
        kref_init(&sdp->d_ref);
@@ -1526,13 +1549,11 @@ static void sg_remove(struct device *cl_dev, struct class_interface *cl_intf)
 
        /* Need a write lock to set sdp->detached. */
        write_lock_irqsave(&sg_index_lock, iflags);
-       spin_lock(&sdp->sfd_lock);
        sdp->detached = 1;
        list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) {
                wake_up_interruptible(&sfp->read_wait);
                kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP);
        }
-       spin_unlock(&sdp->sfd_lock);
        write_unlock_irqrestore(&sg_index_lock, iflags);
 
        sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
@@ -2043,7 +2064,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
 
        sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN);
        if (!sfp)
-               return ERR_PTR(-ENOMEM);
+               return NULL;
 
        init_waitqueue_head(&sfp->read_wait);
        rwlock_init(&sfp->rq_list_lock);
@@ -2057,13 +2078,9 @@ sg_add_sfp(Sg_device * sdp, int dev)
        sfp->cmd_q = SG_DEF_COMMAND_Q;
        sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
        sfp->parentdp = sdp;
-       spin_lock_irqsave(&sdp->sfd_lock, iflags);
-       if (sdp->detached) {
-               spin_unlock_irqrestore(&sdp->sfd_lock, iflags);
-               return ERR_PTR(-ENODEV);
-       }
+       write_lock_irqsave(&sg_index_lock, iflags);
        list_add_tail(&sfp->sfd_siblings, &sdp->sfds);
-       spin_unlock_irqrestore(&sdp->sfd_lock, iflags);
+       write_unlock_irqrestore(&sg_index_lock, iflags);
        SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp));
        if (unlikely(sg_big_buff != def_reserved_size))
                sg_big_buff = def_reserved_size;
@@ -2113,9 +2130,10 @@ static void sg_remove_sfp(struct kref *kref)
        struct sg_device *sdp = sfp->parentdp;
        unsigned long iflags;
 
-       spin_lock_irqsave(&sdp->sfd_lock, iflags);
+       write_lock_irqsave(&sg_index_lock, iflags);
        list_del(&sfp->sfd_siblings);
-       spin_unlock_irqrestore(&sdp->sfd_lock, iflags);
+       write_unlock_irqrestore(&sg_index_lock, iflags);
+       wake_up_interruptible(&sdp->o_excl_wait);
 
        INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext);
        schedule_work(&sfp->ew.work);
@@ -2502,7 +2520,7 @@ static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v)
        return 0;
 }
 
-/* must be called while holding sg_index_lock and sfd_lock */
+/* must be called while holding sg_index_lock */
 static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
 {
        int k, m, new_interface, blen, usg;
@@ -2587,26 +2605,22 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v)
 
        read_lock_irqsave(&sg_index_lock, iflags);
        sdp = it ? sg_lookup_dev(it->index) : NULL;
-       if (sdp) {
-               spin_lock(&sdp->sfd_lock);
-               if (!list_empty(&sdp->sfds)) {
-                       struct scsi_device *scsidp = sdp->device;
+       if (sdp && !list_empty(&sdp->sfds)) {
+               struct scsi_device *scsidp = sdp->device;
 
-                       seq_printf(s, " >>> device=%s ", sdp->disk->disk_name);
-                       if (sdp->detached)
-                               seq_printf(s, "detached pending close ");
-                       else
-                               seq_printf
-                                   (s, "scsi%d chan=%d id=%d lun=%d   em=%d",
-                                    scsidp->host->host_no,
-                                    scsidp->channel, scsidp->id,
-                                    scsidp->lun,
-                                    scsidp->host->hostt->emulated);
-                       seq_printf(s, " sg_tablesize=%d excl=%d\n",
-                                  sdp->sg_tablesize, sdp->exclude);
-                       sg_proc_debug_helper(s, sdp);
-               }
-               spin_unlock(&sdp->sfd_lock);
+               seq_printf(s, " >>> device=%s ", sdp->disk->disk_name);
+               if (sdp->detached)
+                       seq_printf(s, "detached pending close ");
+               else
+                       seq_printf
+                           (s, "scsi%d chan=%d id=%d lun=%d   em=%d",
+                            scsidp->host->host_no,
+                            scsidp->channel, scsidp->id,
+                            scsidp->lun,
+                            scsidp->host->hostt->emulated);
+               seq_printf(s, " sg_tablesize=%d excl=%d\n",
+                          sdp->sg_tablesize, get_exclude(sdp));
+               sg_proc_debug_helper(s, sdp);
        }
        read_unlock_irqrestore(&sg_index_lock, iflags);
        return 0;
index fd7cc56..d4ac60b 100644 (file)
@@ -1583,7 +1583,7 @@ static int atmel_spi_probe(struct platform_device *pdev)
        /* Initialize the hardware */
        ret = clk_prepare_enable(clk);
        if (ret)
-               goto out_unmap_regs;
+               goto out_free_irq;
        spi_writel(as, CR, SPI_BIT(SWRST));
        spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
        if (as->caps.has_wdrbt) {
@@ -1614,6 +1614,7 @@ out_free_dma:
        spi_writel(as, CR, SPI_BIT(SWRST));
        spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
        clk_disable_unprepare(clk);
+out_free_irq:
        free_irq(irq, master);
 out_unmap_regs:
        iounmap(as->regs);
index 5655acf..6416798 100644 (file)
@@ -226,7 +226,6 @@ static int spi_clps711x_probe(struct platform_device *pdev)
                               dev_name(&pdev->dev), hw);
        if (ret) {
                dev_err(&pdev->dev, "Can't request IRQ\n");
-               clk_put(hw->spi_clk);
                goto clk_out;
        }
 
@@ -247,7 +246,6 @@ err_out:
                        gpio_free(hw->chipselect[i]);
 
        spi_master_put(master);
-       kfree(master);
 
        return ret;
 }
@@ -263,7 +261,6 @@ static int spi_clps711x_remove(struct platform_device *pdev)
                        gpio_free(hw->chipselect[i]);
 
        spi_unregister_master(master);
-       kfree(master);
 
        return 0;
 }
index 6cd07d1..4e44575 100644 (file)
@@ -476,15 +476,9 @@ static int dspi_probe(struct platform_device *pdev)
        master->bus_num = bus_num;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "can't get platform resource\n");
-               ret = -EINVAL;
-               goto out_master_put;
-       }
-
        dspi->base = devm_ioremap_resource(&pdev->dev, res);
-       if (!dspi->base) {
-               ret = -EINVAL;
+       if (IS_ERR(dspi->base)) {
+               ret = PTR_ERR(dspi->base);
                goto out_master_put;
        }
 
index dbc5e99..6adf4e3 100644 (file)
@@ -522,8 +522,10 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
        psc_num = master->bus_num;
        snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num);
        clk = devm_clk_get(dev, clk_name);
-       if (IS_ERR(clk))
+       if (IS_ERR(clk)) {
+               ret = PTR_ERR(clk);
                goto free_irq;
+       }
        ret = clk_prepare_enable(clk);
        if (ret)
                goto free_irq;
index 2eb06ee..c1a5067 100644 (file)
@@ -546,8 +546,17 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
        if (pm_runtime_suspended(&drv_data->pdev->dev))
                return IRQ_NONE;
 
-       sccr1_reg = read_SSCR1(reg);
+       /*
+        * If the device is not yet in RPM suspended state and we get an
+        * interrupt that is meant for another device, check if status bits
+        * are all set to one. That means that the device is already
+        * powered off.
+        */
        status = read_SSSR(reg);
+       if (status == ~0)
+               return IRQ_NONE;
+
+       sccr1_reg = read_SSCR1(reg);
 
        /* Ignore possible writes if we don't need to write */
        if (!(sccr1_reg & SSCR1_TIE))
index 512b889..a80376d 100644 (file)
@@ -1428,6 +1428,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
               S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
               sdd->regs + S3C64XX_SPI_INT_EN);
 
+       pm_runtime_enable(&pdev->dev);
+
        if (spi_register_master(master)) {
                dev_err(&pdev->dev, "cannot register SPI master\n");
                ret = -EBUSY;
@@ -1440,8 +1442,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
                                        mem_res,
                                        sdd->rx_dma.dmach, sdd->tx_dma.dmach);
 
-       pm_runtime_enable(&pdev->dev);
-
        return 0;
 
 err3:
index 0b68cb5..e488a90 100644 (file)
@@ -296,6 +296,8 @@ static int hspi_probe(struct platform_device *pdev)
                goto error1;
        }
 
+       pm_runtime_enable(&pdev->dev);
+
        master->num_chipselect  = 1;
        master->bus_num         = pdev->id;
        master->setup           = hspi_setup;
@@ -309,8 +311,6 @@ static int hspi_probe(struct platform_device *pdev)
                goto error1;
        }
 
-       pm_runtime_enable(&pdev->dev);
-
        return 0;
 
  error1:
index f91bc1f..639ba96 100644 (file)
@@ -1960,6 +1960,7 @@ cntrlEnd:
 
                BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL, "Called IOCTL_BCM_GET_DEVICE_DRIVER_INFO\n");
 
+               memset(&DevInfo, 0, sizeof(DevInfo));
                DevInfo.MaxRDMBufferSize = BUFFER_4K;
                DevInfo.u32DSDStartOffset = EEPROM_CALPARAM_START;
                DevInfo.u32RxAlignmentCorrection = 0;
index a84aab4..f73287e 100644 (file)
@@ -96,6 +96,15 @@ config COMEDI_SKEL
          To compile this driver as a module, choose M here: the module will be
          called skel.
 
+config COMEDI_SSV_DNP
+       tristate "SSV Embedded Systems DIL/Net-PC support"
+       depends on X86_32 || COMPILE_TEST
+       ---help---
+         Enable support for SSV Embedded Systems DIL/Net-PC
+
+         To compile this driver as a module, choose M here: the module will be
+         called ssv_dnp.
+
 endif # COMEDI_MISC_DRIVERS
 
 menuconfig COMEDI_ISA_DRIVERS
@@ -386,6 +395,14 @@ config COMEDI_DMM32AT
          To compile this driver as a module, choose M here: the module will be
          called dmm32at.
 
+config COMEDI_UNIOXX5
+       tristate "Fastwel UNIOxx-5 analog and digital io board support"
+       ---help---
+         Enable support for Fastwel UNIOxx-5 (analog and digital i/o) boards
+
+         To compile this driver as a module, choose M here: the module will be
+         called unioxx5.
+
 config COMEDI_FL512
        tristate "FL512 ISA card support"
        ---help---
@@ -855,14 +872,6 @@ config COMEDI_DYNA_PCI10XX
          To compile this driver as a module, choose M here: the module will be
          called dyna_pci10xx.
 
-config COMEDI_UNIOXX5
-       tristate "Fastwel UNIOxx-5 analog and digital io board support"
-       ---help---
-         Enable support for Fastwel UNIOxx-5 (analog and digital i/o) boards
-
-         To compile this driver as a module, choose M here: the module will be
-         called unioxx5.
-
 config COMEDI_GSC_HPDI
        tristate "General Standards PCI-HPDI32 / PMC-HPDI32 support"
        select COMEDI_FC
@@ -1085,14 +1094,6 @@ config COMEDI_S626
          To compile this driver as a module, choose M here: the module will be
          called s626.
 
-config COMEDI_SSV_DNP
-       tristate "SSV Embedded Systems DIL/Net-PC support"
-       ---help---
-         Enable support for SSV Embedded Systems DIL/Net-PC
-
-         To compile this driver as a module, choose M here: the module will be
-         called ssv_dnp.
-
 config COMEDI_MITE
        depends on HAS_DMA
        tristate
index 3ba4c57..853f62b 100644 (file)
@@ -369,28 +369,23 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev,
 {
        const struct ni_65xx_board *board = comedi_board(dev);
        struct ni_65xx_private *devpriv = dev->private;
-       unsigned base_bitfield_channel;
-       const unsigned max_ports_per_bitfield = 5;
+       int base_bitfield_channel;
        unsigned read_bits = 0;
-       unsigned j;
+       int last_port_offset = ni_65xx_port_by_channel(s->n_chan - 1);
+       int port_offset;
 
        base_bitfield_channel = CR_CHAN(insn->chanspec);
-       for (j = 0; j < max_ports_per_bitfield; ++j) {
-               const unsigned port_offset =
-                       ni_65xx_port_by_channel(base_bitfield_channel) + j;
-               const unsigned port =
-                       sprivate(s)->base_port + port_offset;
-               unsigned base_port_channel;
+       for (port_offset = ni_65xx_port_by_channel(base_bitfield_channel);
+            port_offset <= last_port_offset; port_offset++) {
+               unsigned port = sprivate(s)->base_port + port_offset;
+               int base_port_channel = port_offset * ni_65xx_channels_per_port;
                unsigned port_mask, port_data, port_read_bits;
-               int bitshift;
-               if (port >= ni_65xx_total_num_ports(board))
+               int bitshift = base_port_channel - base_bitfield_channel;
+
+               if (bitshift >= 32)
                        break;
-               base_port_channel = port_offset * ni_65xx_channels_per_port;
                port_mask = data[0];
                port_data = data[1];
-               bitshift = base_port_channel - base_bitfield_channel;
-               if (bitshift >= 32 || bitshift <= -32)
-                       break;
                if (bitshift > 0) {
                        port_mask >>= bitshift;
                        port_data >>= bitshift;
index 724a685..40ef785 100644 (file)
@@ -474,7 +474,7 @@ static void dgap_cleanup_board(struct board_t *brd)
 
                 DGAP_LOCK(dgap_global_lock, flags);
                 brd->msgbuf = NULL;
-                printk(brd->msgbuf_head);
+                printk("%s", brd->msgbuf_head);
                 kfree(brd->msgbuf_head);
                 brd->msgbuf_head = NULL;
                 DGAP_UNLOCK(dgap_global_lock, flags);
@@ -628,7 +628,7 @@ static int dgap_found_board(struct pci_dev *pdev, int id)
        DPR_INIT(("dgap_scan(%d) - printing out the msgbuf\n", i));
        DGAP_LOCK(dgap_global_lock, flags);
        brd->msgbuf = NULL;
-       printk(brd->msgbuf_head);
+       printk("%s", brd->msgbuf_head);
        kfree(brd->msgbuf_head);
        brd->msgbuf_head = NULL;
        DGAP_UNLOCK(dgap_global_lock, flags);
@@ -955,25 +955,28 @@ static void dgap_mbuf(struct board_t *brd, const char *fmt, ...) {
        char            buf[1024];
        int             i;
        unsigned long   flags;
+       size_t          length;
 
        DGAP_LOCK(dgap_global_lock, flags);
 
        /* Format buf using fmt and arguments contained in ap. */
        va_start(ap, fmt);
-       i = vsprintf(buf, fmt,  ap);
+       i = vsnprintf(buf, sizeof(buf), fmt,  ap);
        va_end(ap);
 
        DPR((buf));
 
        if (!brd || !brd->msgbuf) {
-               printk(buf);
+               printk("%s", buf);
                DGAP_UNLOCK(dgap_global_lock, flags);
                return;
        }
 
-       memcpy(brd->msgbuf, buf, strlen(buf));
-       brd->msgbuf += strlen(buf);
-       *brd->msgbuf = 0;
+       length = strlen(buf) + 1;
+       if (brd->msgbuf - brd->msgbuf_head < length)
+               length = brd->msgbuf - brd->msgbuf_head;
+       memcpy(brd->msgbuf, buf, length);
+       brd->msgbuf += length;
 
        DGAP_UNLOCK(dgap_global_lock, flags);
 }
index f8c1e22..71d2b83 100644 (file)
@@ -454,7 +454,7 @@ static void dgnc_cleanup_board(struct board_t *brd)
 
                DGNC_LOCK(dgnc_global_lock, flags);
                brd->msgbuf = NULL;
-               printk(brd->msgbuf_head);
+               printk("%s", brd->msgbuf_head);
                kfree(brd->msgbuf_head);
                brd->msgbuf_head = NULL;
                DGNC_UNLOCK(dgnc_global_lock, flags);
@@ -710,7 +710,7 @@ static int dgnc_found_board(struct pci_dev *pdev, int id)
        DPR_INIT(("dgnc_scan(%d) - printing out the msgbuf\n", i));
        DGNC_LOCK(dgnc_global_lock, flags);
        brd->msgbuf = NULL;
-       printk(brd->msgbuf_head);
+       printk("%s", brd->msgbuf_head);
        kfree(brd->msgbuf_head);
        brd->msgbuf_head = NULL;
        DGNC_UNLOCK(dgnc_global_lock, flags);
index db4d6dc..b36feb0 100644 (file)
@@ -37,7 +37,7 @@ config IIO_SIMPLE_DUMMY_EVENTS
 
 config IIO_SIMPLE_DUMMY_BUFFER
        boolean "Buffered capture support"
-       depends on IIO_KFIFO_BUF
+       select IIO_KFIFO_BUF
        help
          Add buffered data capture to the simple dummy driver.
 
index 351936c..e4998e4 100644 (file)
@@ -563,6 +563,7 @@ static int isl29018_probe(struct i2c_client *client,
        mutex_init(&chip->lock);
 
        chip->lux_scale = 1;
+       chip->lux_uscale = 0;
        chip->range = 1000;
        chip->adc_bit = 16;
        chip->suspended = false;
index d2748c3..c3f3f53 100644 (file)
@@ -229,7 +229,7 @@ static int hmc5843_read_measurement(struct iio_dev *indio_dev,
        if (result < 0)
                return -EINVAL;
 
-       *val = result;
+       *val = sign_extend32(result, 15);
        return IIO_VAL_INT;
 }
 
index a802cf2..4c6d204 100644 (file)
@@ -299,7 +299,7 @@ static int ade7854_spi_probe(struct spi_device *spi)
        if (ret)
                iio_device_free(indio_dev);
 
-       return 0;
+       return ret;
 }
 
 static int ade7854_spi_remove(struct spi_device *spi)
index 47c5888..a2e52a0 100644 (file)
@@ -41,7 +41,6 @@ struct imx_drm_device {
        struct list_head                        encoder_list;
        struct list_head                        connector_list;
        struct mutex                            mutex;
-       int                                     references;
        int                                     pipes;
        struct drm_fbdev_cma                    *fbhelper;
 };
@@ -241,8 +240,6 @@ struct drm_device *imx_drm_device_get(void)
                }
        }
 
-       imxdrm->references++;
-
        return imxdrm->drm;
 
 unwind_crtc:
@@ -280,8 +277,6 @@ void imx_drm_device_put(void)
        list_for_each_entry(enc, &imxdrm->encoder_list, list)
                module_put(enc->owner);
 
-       imxdrm->references--;
-
        mutex_unlock(&imxdrm->mutex);
 }
 EXPORT_SYMBOL_GPL(imx_drm_device_put);
@@ -485,7 +480,7 @@ int imx_drm_add_crtc(struct drm_crtc *crtc,
 
        mutex_lock(&imxdrm->mutex);
 
-       if (imxdrm->references) {
+       if (imxdrm->drm->open_count) {
                ret = -EBUSY;
                goto err_busy;
        }
@@ -564,7 +559,7 @@ int imx_drm_add_encoder(struct drm_encoder *encoder,
 
        mutex_lock(&imxdrm->mutex);
 
-       if (imxdrm->references) {
+       if (imxdrm->drm->open_count) {
                ret = -EBUSY;
                goto err_busy;
        }
@@ -709,7 +704,7 @@ int imx_drm_add_connector(struct drm_connector *connector,
 
        mutex_lock(&imxdrm->mutex);
 
-       if (imxdrm->references) {
+       if (imxdrm->drm->open_count) {
                ret = -EBUSY;
                goto err_busy;
        }
index 2f44d56..776d363 100644 (file)
@@ -244,13 +244,17 @@ static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
        struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
        struct usb_line6_toneport *toneport =
            (struct usb_line6_toneport *)line6pcm->line6;
+       unsigned int source;
 
-       if (ucontrol->value.enumerated.item[0] == toneport->source)
+       source = ucontrol->value.enumerated.item[0];
+       if (source >= ARRAY_SIZE(toneport_source_info))
+               return -EINVAL;
+       if (source == toneport->source)
                return 0;
 
-       toneport->source = ucontrol->value.enumerated.item[0];
+       toneport->source = source;
        toneport_send_cmd(toneport->line6.usbdev,
-                         toneport_source_info[toneport->source].code, 0x0000);
+                         toneport_source_info[source].code, 0x0000);
        return 1;
 }
 
index 086ca3d..26b49a2 100644 (file)
@@ -1802,7 +1802,7 @@ kiblnd_recv (lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg, int delayed,
 int
 kiblnd_thread_start(int (*fn)(void *arg), void *arg, char *name)
 {
-       struct task_struct *task = kthread_run(fn, arg, name);
+       struct task_struct *task = kthread_run(fn, arg, "%s", name);
 
        if (IS_ERR(task))
                return PTR_ERR(task);
index 2c581b7..68a4f52 100644 (file)
@@ -1005,7 +1005,7 @@ ksocknal_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg)
 int
 ksocknal_thread_start(int (*fn)(void *arg), void *arg, char *name)
 {
-       struct task_struct *task = kthread_run(fn, arg, name);
+       struct task_struct *task = kthread_run(fn, arg, "%s", name);
 
        if (IS_ERR(task))
                return PTR_ERR(task);
index 4e898e4..2156a44 100644 (file)
@@ -1,6 +1,6 @@
 config LUSTRE_FS
        tristate "Lustre file system client support"
-       depends on INET && m
+       depends on INET && m && !MIPS && !XTENSA && !SUPERH
        select LNET
        select CRYPTO
        select CRYPTO_CRC32
@@ -52,7 +52,7 @@ config LUSTRE_DEBUG_EXPENSIVE_CHECK
 config LUSTRE_TRANSLATE_ERRNOS
        bool
        depends on LUSTRE_FS && !X86
-       default true
+       default y
 
 config LUSTRE_LLITE_LLOOP
        bool "Lustre virtual block device"
index 3916bda..a100a0b 100644 (file)
@@ -800,9 +800,9 @@ static int ldlm_bl_thread_start(struct ldlm_bl_pool *blp)
 
        init_completion(&bltd.bltd_comp);
        bltd.bltd_num = atomic_read(&blp->blp_num_threads);
-       snprintf(bltd.bltd_name, sizeof(bltd.bltd_name) - 1,
+       snprintf(bltd.bltd_name, sizeof(bltd.bltd_name),
                "ldlm_bl_%02d", bltd.bltd_num);
-       task = kthread_run(ldlm_bl_thread_main, &bltd, bltd.bltd_name);
+       task = kthread_run(ldlm_bl_thread_main, &bltd, "%s", bltd.bltd_name);
        if (IS_ERR(task)) {
                CERROR("cannot start LDLM thread ldlm_bl_%02d: rc %ld\n",
                       atomic_read(&blp->blp_num_threads), PTR_ERR(task));
index 462172d..1a55c81 100644 (file)
@@ -397,7 +397,7 @@ cfs_wi_sched_create(char *name, struct cfs_cpt_table *cptab,
                                 sched->ws_name, sched->ws_nthreads);
                }
 
-               task = kthread_run(cfs_wi_scheduler, sched, name);
+               task = kthread_run(cfs_wi_scheduler, sched, "%s", name);
                if (!IS_ERR(task)) {
                        nthrs--;
                        continue;
index 2644edf..c8b4344 100644 (file)
@@ -1387,7 +1387,7 @@ echo_copyout_lsm (struct lov_stripe_md *lsm, void *_ulsm, int ulsm_nob)
        if (nob > ulsm_nob)
                return (-EINVAL);
 
-       if (copy_to_user (ulsm, lsm, sizeof(ulsm)))
+       if (copy_to_user (ulsm, lsm, sizeof(*ulsm)))
                return (-EFAULT);
 
        for (i = 0; i < lsm->lsm_stripe_count; i++) {
index 227a0ae..5dec771 100644 (file)
@@ -383,8 +383,8 @@ int ptlrpc_start_pinger(void)
 
        /* CLONE_VM and CLONE_FILES just avoid a needless copy, because we
         * just drop the VM and FILES in cfs_daemonize_ctxt() right away. */
-       rc = PTR_ERR(kthread_run(ptlrpc_pinger_main,
-                                &pinger_thread, pinger_thread.t_name));
+       rc = PTR_ERR(kthread_run(ptlrpc_pinger_main, &pinger_thread,
+                                "%s", pinger_thread.t_name));
        if (IS_ERR_VALUE(rc)) {
                CERROR("cannot start thread: %d\n", rc);
                return rc;
index fbdeff6..89c9be9 100644 (file)
@@ -615,7 +615,7 @@ int ptlrpcd_start(int index, int max, const char *name, struct ptlrpcd_ctl *pc)
        init_completion(&pc->pc_starting);
        init_completion(&pc->pc_finishing);
        spin_lock_init(&pc->pc_lock);
-       strncpy(pc->pc_name, name, sizeof(pc->pc_name) - 1);
+       strlcpy(pc->pc_name, name, sizeof(pc->pc_name));
        pc->pc_set = ptlrpc_prep_set();
        if (pc->pc_set == NULL)
                GOTO(out, rc = -ENOMEM);
@@ -638,7 +638,7 @@ int ptlrpcd_start(int index, int max, const char *name, struct ptlrpcd_ctl *pc)
                                GOTO(out, rc);
                }
 
-               task = kthread_run(ptlrpcd, pc, pc->pc_name);
+               task = kthread_run(ptlrpcd, pc, "%s", pc->pc_name);
                if (IS_ERR(task))
                        GOTO(out, rc = PTR_ERR(task));
 
@@ -745,7 +745,7 @@ static int ptlrpcd_init(void)
        if (ptlrpcds == NULL)
                GOTO(out, rc = -ENOMEM);
 
-       snprintf(name, 15, "ptlrpcd_rcv");
+       snprintf(name, sizeof(name), "ptlrpcd_rcv");
        set_bit(LIOD_RECOVERY, &ptlrpcds->pd_thread_rcv.pc_flags);
        rc = ptlrpcd_start(-1, nthreads, name, &ptlrpcds->pd_thread_rcv);
        if (rc < 0)
@@ -764,7 +764,7 @@ static int ptlrpcd_init(void)
         *      unnecessary dependency. But how to distribute async RPCs load
         *      among all the ptlrpc daemons becomes another trouble. */
        for (i = 0; i < nthreads; i++) {
-               snprintf(name, 15, "ptlrpcd_%d", i);
+               snprintf(name, sizeof(name), "ptlrpcd_%d", i);
                rc = ptlrpcd_start(i, nthreads, name, &ptlrpcds->pd_threads[i]);
                if (rc < 0)
                        GOTO(out, rc);
index e90c8fb..6547f46 100644 (file)
@@ -59,8 +59,8 @@
  ****************************************/
 
 
-#define PTRS_PER_PAGE   (PAGE_CACHE_SIZE / sizeof(void *))
-#define PAGES_PER_POOL  (PTRS_PER_PAGE)
+#define POINTERS_PER_PAGE      (PAGE_CACHE_SIZE / sizeof(void *))
+#define PAGES_PER_POOL         (POINTERS_PER_PAGE)
 
 #define IDLE_IDX_MAX       (100)
 #define IDLE_IDX_WEIGHT         (3)
index ac8b5fd..acf75f3 100644 (file)
@@ -2718,15 +2718,15 @@ int ptlrpc_start_thread(struct ptlrpc_service_part *svcpt, int wait)
        spin_unlock(&svcpt->scp_lock);
 
        if (svcpt->scp_cpt >= 0) {
-               snprintf(thread->t_name, PTLRPC_THR_NAME_LEN, "%s%02d_%03d",
+               snprintf(thread->t_name, sizeof(thread->t_name), "%s%02d_%03d",
                         svc->srv_thread_name, svcpt->scp_cpt, thread->t_id);
        } else {
-               snprintf(thread->t_name, PTLRPC_THR_NAME_LEN, "%s_%04d",
+               snprintf(thread->t_name, sizeof(thread->t_name), "%s_%04d",
                         svc->srv_thread_name, thread->t_id);
        }
 
        CDEBUG(D_RPCTRACE, "starting thread '%s'\n", thread->t_name);
-       rc = PTR_ERR(kthread_run(ptlrpc_main, thread, thread->t_name));
+       rc = PTR_ERR(kthread_run(ptlrpc_main, thread, "%s", thread->t_name));
        if (IS_ERR_VALUE(rc)) {
                CERROR("cannot start thread '%s': rc %d\n",
                       thread->t_name, rc);
index b94a95a..76d5bbd 100644 (file)
@@ -1,3 +1,4 @@
 config USB_MSI3101
        tristate "Mirics MSi3101 SDR Dongle"
        depends on USB && VIDEO_DEV && VIDEO_V4L2
+        select VIDEOBUF2_VMALLOC
index 24c7b70..4c3bf77 100644 (file)
@@ -1131,7 +1131,13 @@ static int msi3101_queue_setup(struct vb2_queue *vq,
        /* Absolute min and max number of buffers available for mmap() */
        *nbuffers = 32;
        *nplanes = 1;
-       sizes[0] = PAGE_ALIGN(3 * 3072); /* 3 * 768 * 4 */
+       /*
+        *   3, wMaxPacketSize 3x 1024 bytes
+        * 504, max IQ sample pairs per 1024 frame
+        *   2, two samples, I and Q
+        *   4, 32-bit float
+        */
+       sizes[0] = PAGE_ALIGN(3 * 504 * 2 * 4); /* = 12096 */
        dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
                        __func__, *nbuffers, sizes[0]);
        return 0;
@@ -1657,7 +1663,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
                        f->frequency * 625UL / 10UL);
 }
 
-const struct v4l2_ioctl_ops msi3101_ioctl_ops = {
+static const struct v4l2_ioctl_ops msi3101_ioctl_ops = {
        .vidioc_querycap          = msi3101_querycap,
 
        .vidioc_enum_input        = msi3101_enum_input,
index d7b3c82..45dfe94 100644 (file)
@@ -604,7 +604,7 @@ int cvmx_usb_initialize(struct cvmx_usb_state *state, int usb_port_number,
                        }
        }
 
-       memset(usb, 0, sizeof(usb));
+       memset(usb, 0, sizeof(*usb));
        usb->init_flags = flags;
 
        /* Initialize the USB state structure */
index 6ccb64f..6ce0af9 100644 (file)
@@ -155,6 +155,9 @@ static ssize_t oz_cdev_write(struct file *filp, const char __user *buf,
        struct oz_app_hdr *app_hdr;
        struct oz_serial_ctx *ctx;
 
+       if (count > sizeof(ei->data) - sizeof(*elt) - sizeof(*app_hdr))
+               return -EINVAL;
+
        spin_lock_bh(&g_cdev.lock);
        pd = g_cdev.active_pd;
        if (pd)
index 3605c5d..6fc7742 100644 (file)
@@ -157,8 +157,8 @@ _func_enter_;
 
        *frlen = *frlen + (len + 2);
 
-       return pbuf + len + 2;
 _func_exit_;
+       return pbuf + len + 2;
 }
 
 inline u8 *rtw_set_ie_ch_switch (u8 *buf, u32 *buf_len, u8 ch_switch_mode,
index 8b2ba26..4b2eb8e 100644 (file)
@@ -1827,13 +1827,13 @@ unsigned int OnAction_back(struct adapter *padapter, union recv_frame *precv_fra
 
 #ifdef CONFIG_88EU_P2P
 
-static int get_reg_classes_full_count(struct p2p_channels channel_list)
+static int get_reg_classes_full_count(struct p2p_channels *channel_list)
 {
        int cnt = 0;
        int i;
 
-       for (i = 0; i < channel_list.reg_classes; i++) {
-               cnt += channel_list.reg_class[i].channels;
+       for (i = 0; i < channel_list->reg_classes; i++) {
+               cnt += channel_list->reg_class[i].channels;
        }
 
        return cnt;
@@ -2065,7 +2065,7 @@ void issue_p2p_GO_request(struct adapter *padapter, u8 *raddr)
        /*  + number of channels in all classes */
        len_channellist_attr = 3
           + (1 + 1) * (u16)(pmlmeext->channel_list.reg_classes)
-          + get_reg_classes_full_count(pmlmeext->channel_list);
+          + get_reg_classes_full_count(&pmlmeext->channel_list);
 
        *(__le16 *)(p2pie + p2pielen) = cpu_to_le16(len_channellist_attr);
        p2pielen += 2;
@@ -2437,7 +2437,7 @@ static void issue_p2p_GO_response(struct adapter *padapter, u8 *raddr, u8 *frame
        /*  + number of channels in all classes */
        len_channellist_attr = 3
           + (1 + 1) * (u16)pmlmeext->channel_list.reg_classes
-          + get_reg_classes_full_count(pmlmeext->channel_list);
+          + get_reg_classes_full_count(&pmlmeext->channel_list);
 
        *(__le16 *)(p2pie + p2pielen) = cpu_to_le16(len_channellist_attr);
 
@@ -2859,7 +2859,7 @@ void issue_p2p_invitation_request(struct adapter *padapter, u8 *raddr)
        /*  + number of channels in all classes */
        len_channellist_attr = 3
           + (1 + 1) * (u16)pmlmeext->channel_list.reg_classes
-          + get_reg_classes_full_count(pmlmeext->channel_list);
+          + get_reg_classes_full_count(&pmlmeext->channel_list);
 
        *(__le16 *)(p2pie + p2pielen) = cpu_to_le16(len_channellist_attr);
 
@@ -3120,7 +3120,7 @@ void issue_p2p_invitation_response(struct adapter *padapter, u8 *raddr, u8 dialo
                /*  + number of channels in all classes */
                len_channellist_attr = 3
                        + (1 + 1) * (u16)pmlmeext->channel_list.reg_classes
-                       + get_reg_classes_full_count(pmlmeext->channel_list);
+                       + get_reg_classes_full_count(&pmlmeext->channel_list);
 
                *(__le16 *)(p2pie + p2pielen) = cpu_to_le16(len_channellist_attr);
                p2pielen += 2;
index c7ff2e4..9832dcb 100644 (file)
@@ -907,7 +907,7 @@ u32 mp_query_psd(struct adapter *pAdapter, u8 *data)
                sscanf(data, "pts =%d, start =%d, stop =%d", &psd_pts, &psd_start, &psd_stop);
        }
 
-       _rtw_memset(data, '\0', sizeof(data));
+       _rtw_memset(data, '\0', sizeof(*data));
 
        i = psd_start;
        while (i < psd_stop) {
index 013ea48..8018edd 100644 (file)
@@ -631,7 +631,7 @@ void WMMOnAssocRsp(struct adapter *padapter)
        inx[0] = 0; inx[1] = 1; inx[2] = 2; inx[3] = 3;
 
        if (pregpriv->wifi_spec == 1) {
-               u32     j, tmp, change_inx;
+               u32     j, tmp, change_inx = false;
 
                /* entry indx: 0->vo, 1->vi, 2->be, 3->bk. */
                for (i = 0; i < 4; i++) {
index 9c2e7a2..ec0028d 100644 (file)
@@ -57,7 +57,7 @@ static void Init_ODM_ComInfo_88E(struct adapter *Adapter)
        u8 cut_ver, fab_ver;
 
        /*  Init Value */
-       _rtw_memset(dm_odm, 0, sizeof(dm_odm));
+       _rtw_memset(dm_odm, 0, sizeof(*dm_odm));
 
        dm_odm->Adapter = Adapter;
 
index 2bfe728..4787bac 100644 (file)
@@ -1010,7 +1010,7 @@ enum dm_dig_op {
 #define                DM_false_ALARM_THRESH_LOW       400
 #define                DM_false_ALARM_THRESH_HIGH      1000
 
-#define                DM_DIG_MAX_NIC                  0x3e
+#define                DM_DIG_MAX_NIC                  0x4e
 #define                DM_DIG_MIN_NIC                  0x1e /* 0x22/0x1c */
 
 #define                DM_DIG_MAX_AP                   0x32
index 52b2801..555c801 100644 (file)
@@ -188,7 +188,7 @@ enum ChannelPlan {
 
 struct txpowerinfo24g {
        u8 IndexCCK_Base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
-       u8 IndexBW40_Base[MAX_RF_PATH][MAX_CHNL_GROUP_24G-1];
+       u8 IndexBW40_Base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
        /* If only one tx, only BW20 and OFDM are used. */
        s8 CCK_Diff[MAX_RF_PATH][MAX_TX_COUNT];
        s8 OFDM_Diff[MAX_RF_PATH][MAX_TX_COUNT];
index a96b018..853ab80 100644 (file)
@@ -870,6 +870,7 @@ static struct fwevent wlanevents[] = {
        {0, NULL},
        {0, NULL},
        {0, &rtw_cpwm_event_callback},
+       {0, NULL},
 };
 
 #endif/* _RTL_MLME_EXT_C_ */
index cd4100f..95953eb 100644 (file)
@@ -6973,7 +6973,7 @@ static int rtw_mp_ctx(struct net_device *dev,
        stop = strncmp(extra, "stop", 4);
        sscanf(extra, "count =%d, pkt", &count);
 
-       _rtw_memset(extra, '\0', sizeof(extra));
+       _rtw_memset(extra, '\0', sizeof(*extra));
 
        if (stop == 0) {
                bStartTest = 0; /*  To set Stop */
index d3078d2..9ca3180 100644 (file)
@@ -54,6 +54,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = {
        /*=== Customer ID ===*/
        /****** 8188EUS ********/
        {USB_DEVICE(0x8179, 0x07B8)}, /* Abocom - Abocom */
+       {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
        {}      /* Terminating entry */
 };
 
index 5bc361b..5614401 100644 (file)
@@ -37,6 +37,8 @@ rt_status SendTxCommandPacket(struct net_device *dev, void *pData, u32 DataLen)
        /* Get TCB and local buffer from common pool.
           (It is shared by CmdQ, MgntQ, and USB coalesce DataQ) */
        skb  = dev_alloc_skb(USB_HWDESC_HEADER_LEN + DataLen + 4);
+       if (!skb)
+               return RT_STATUS_FAILURE;
        memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
        tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
        tcb_desc->queue_index = TXCMD_QUEUE;
index 23db32f..a10cdb1 100644 (file)
@@ -1063,7 +1063,7 @@ static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg)
 
 static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt)
 {
-       struct serial_icounter_struct icount;
+       struct serial_icounter_struct icount = {};
        struct sb_uart_icount cnow;
        struct sb_uart_port *port = state->port;
 
index dbf11ec..19d3cf4 100644 (file)
@@ -172,8 +172,8 @@ static u16 swGetOFDMControlRate(struct vnt_private *pDevice, u16 wRateIdx)
        if (!CARDbIsOFDMinBasicRate(pDevice)) {
                DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO
                        "swGetOFDMControlRate:(NO OFDM) %d\n", wRateIdx);
-       if (wRateIdx > RATE_24M)
-               wRateIdx = RATE_24M;
+               if (wRateIdx > RATE_24M)
+                       wRateIdx = RATE_24M;
                return wRateIdx;
        }
 
index d0cf7d8..8872e0f 100644 (file)
@@ -1634,6 +1634,9 @@ int iwctl_siwencodeext(struct net_device *dev, struct iw_request_info *info,
        if (pMgmt == NULL)
                return -EFAULT;
 
+       if (!(pDevice->flags & DEVICE_FLAGS_OPENED))
+               return -ENODEV;
+
        buf = kzalloc(sizeof(struct viawget_wpa_param), GFP_KERNEL);
        if (buf == NULL)
                return -ENOMEM;
index 5369717..6f9d281 100644 (file)
@@ -1098,6 +1098,8 @@ static int device_close(struct net_device *dev)
     memset(pMgmt->abyCurrBSSID, 0, 6);
     pMgmt->eCurrState = WMAC_STATE_IDLE;
 
+       pDevice->flags &= ~DEVICE_FLAGS_OPENED;
+
     device_free_tx_bufs(pDevice);
     device_free_rx_bufs(pDevice);
     device_free_int_bufs(pDevice);
@@ -1109,7 +1111,6 @@ static int device_close(struct net_device *dev)
     usb_free_urb(pDevice->pInterruptURB);
 
     BSSvClearNodeDBTable(pDevice, 0);
-    pDevice->flags &=(~DEVICE_FLAGS_OPENED);
 
     DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "device_close2 \n");
 
index fb743a8..14f3e85 100644 (file)
@@ -148,6 +148,8 @@ static void *s_vGetFreeContext(struct vnt_private *pDevice)
     DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"GetFreeContext()\n");
 
     for (ii = 0; ii < pDevice->cbTD; ii++) {
+       if (!pDevice->apTD[ii])
+               return NULL;
         pContext = pDevice->apTD[ii];
         if (pContext->bBoolInUse == false) {
             pContext->bBoolInUse = true;
index c97e0e1..7e10dcd 100644 (file)
@@ -570,6 +570,7 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
        ltv_t                   *pLtv;
        bool_t                  ltvAllocated = FALSE;
        ENCSTRCT                sEncryption;
+       size_t                  len;
 
 #ifdef USE_WDS
        hcf_16                  hcfPort  = HCF_PORT_0;
@@ -686,7 +687,8 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
                                        break;
                                case CFG_CNF_OWN_NAME:
                                        memset(lp->StationName, 0, sizeof(lp->StationName));
-                                       memcpy((void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);
+                                       len = min_t(size_t, pLtv->u.u16[0], sizeof(lp->StationName));
+                                       strlcpy(lp->StationName, &pLtv->u.u8[2], len);
                                        pLtv->u.u16[0] = CNV_INT_TO_LITTLE(pLtv->u.u16[0]);
                                        break;
                                case CFG_CNF_LOAD_BALANCING:
@@ -1783,6 +1785,7 @@ int wvlan_set_station_nickname(struct net_device *dev,
 {
        struct wl_private *lp = wl_priv(dev);
        unsigned long flags;
+       size_t len;
        int         ret = 0;
        /*------------------------------------------------------------------------*/
 
@@ -1793,8 +1796,8 @@ int wvlan_set_station_nickname(struct net_device *dev,
        wl_lock(lp, &flags);
 
        memset(lp->StationName, 0, sizeof(lp->StationName));
-
-       memcpy(lp->StationName, extra, wrqu->data.length);
+       len = min_t(size_t, wrqu->data.length, sizeof(lp->StationName));
+       strlcpy(lp->StationName, extra, len);
 
        /* Commit the adapter parameters */
        wl_apply(lp);
index efc5698..7db6f03 100644 (file)
@@ -2054,7 +2054,7 @@ static int xillybus_init_chrdev(struct xilly_endpoint *endpoint,
                                       NULL,
                                       MKDEV(major, i),
                                       NULL,
-                                      devname);
+                                      "%s", devname);
 
                if (IS_ERR(device)) {
                        pr_warn("xillybus: Failed to create %s "
index 91d94b5..2c4ed52 100644 (file)
@@ -981,4 +981,3 @@ MODULE_PARM_DESC(num_devices, "Number of zram devices");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>");
 MODULE_DESCRIPTION("Compressed RAM Block Device");
-MODULE_ALIAS("devname:zram");
index 35b61f7..38e44b9 100644 (file)
@@ -753,7 +753,8 @@ static void iscsit_unmap_iovec(struct iscsi_cmd *cmd)
 
 static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn)
 {
-       struct iscsi_cmd *cmd;
+       LIST_HEAD(ack_list);
+       struct iscsi_cmd *cmd, *cmd_p;
 
        conn->exp_statsn = exp_statsn;
 
@@ -761,19 +762,23 @@ static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn)
                return;
 
        spin_lock_bh(&conn->cmd_lock);
-       list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
+       list_for_each_entry_safe(cmd, cmd_p, &conn->conn_cmd_list, i_conn_node) {
                spin_lock(&cmd->istate_lock);
                if ((cmd->i_state == ISTATE_SENT_STATUS) &&
                    iscsi_sna_lt(cmd->stat_sn, exp_statsn)) {
                        cmd->i_state = ISTATE_REMOVE;
                        spin_unlock(&cmd->istate_lock);
-                       iscsit_add_cmd_to_immediate_queue(cmd, conn,
-                                               cmd->i_state);
+                       list_move_tail(&cmd->i_conn_node, &ack_list);
                        continue;
                }
                spin_unlock(&cmd->istate_lock);
        }
        spin_unlock_bh(&conn->cmd_lock);
+
+       list_for_each_entry_safe(cmd, cmd_p, &ack_list, i_conn_node) {
+               list_del(&cmd->i_conn_node);
+               iscsit_free_cmd(cmd, false);
+       }
 }
 
 static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd)
index 14d1aed..ef6d836 100644 (file)
@@ -1192,7 +1192,7 @@ get_target:
         */
 alloc_tags:
        tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth);
-       tag_num += ISCSIT_EXTRA_TAGS;
+       tag_num += (tag_num / 2) + ISCSIT_EXTRA_TAGS;
        tag_size = sizeof(struct iscsi_cmd) + conn->conn_transport->priv_size;
 
        ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size);
index f2de28e..b0cac0c 100644 (file)
@@ -736,7 +736,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown)
                 * Fallthrough
                 */
        case ISCSI_OP_SCSI_TMFUNC:
-               rc = transport_generic_free_cmd(&cmd->se_cmd, 1);
+               rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
                if (!rc && shutdown && se_cmd && se_cmd->se_sess) {
                        __iscsit_free_cmd(cmd, true, shutdown);
                        target_put_sess_cmd(se_cmd->se_sess, se_cmd);
@@ -752,7 +752,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown)
                        se_cmd = &cmd->se_cmd;
                        __iscsit_free_cmd(cmd, true, shutdown);
 
-                       rc = transport_generic_free_cmd(&cmd->se_cmd, 1);
+                       rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
                        if (!rc && shutdown && se_cmd->se_sess) {
                                __iscsit_free_cmd(cmd, true, shutdown);
                                target_put_sess_cmd(se_cmd->se_sess, se_cmd);
index 551c96c..0f199f6 100644 (file)
@@ -134,10 +134,10 @@ static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag)
         * pSCSI Host ID and enable for phba mode
         */
        sh = scsi_host_lookup(phv->phv_host_id);
-       if (IS_ERR(sh)) {
+       if (!sh) {
                pr_err("pSCSI: Unable to locate SCSI Host for"
                        " phv_host_id: %d\n", phv->phv_host_id);
-               return PTR_ERR(sh);
+               return -EINVAL;
        }
 
        phv->phv_lld_host = sh;
@@ -515,10 +515,10 @@ static int pscsi_configure_device(struct se_device *dev)
                        sh = phv->phv_lld_host;
                } else {
                        sh = scsi_host_lookup(pdv->pdv_host_id);
-                       if (IS_ERR(sh)) {
+                       if (!sh) {
                                pr_err("pSCSI: Unable to locate"
                                        " pdv_host_id: %d\n", pdv->pdv_host_id);
-                               return PTR_ERR(sh);
+                               return -EINVAL;
                        }
                }
        } else {
index 6c17295..d9b92b2 100644 (file)
@@ -263,6 +263,11 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
                        sectors, cmd->se_dev->dev_attrib.max_write_same_len);
                return TCM_INVALID_CDB_FIELD;
        }
+       /* We always have ANC_SUP == 0 so setting ANCHOR is always an error */
+       if (flags[0] & 0x10) {
+               pr_warn("WRITE SAME with ANCHOR not supported\n");
+               return TCM_INVALID_CDB_FIELD;
+       }
        /*
         * Special case for WRITE_SAME w/ UNMAP=1 that ends up getting
         * translated into block discard requests within backend code.
@@ -349,7 +354,16 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
 
-       cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
+       /*
+        * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through
+        * within target_complete_ok_work() if the command was successfully
+        * sent to the backend driver.
+        */
+       spin_lock_irq(&cmd->t_state_lock);
+       if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status)
+               cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
+       spin_unlock_irq(&cmd->t_state_lock);
+
        /*
         * Unlock ->caw_sem originally obtained during sbc_compare_and_write()
         * before the original READ I/O submission.
@@ -363,7 +377,7 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
        struct scatterlist *write_sg = NULL, *sg;
-       unsigned char *buf, *addr;
+       unsigned char *buf = NULL, *addr;
        struct sg_mapping_iter m;
        unsigned int offset = 0, len;
        unsigned int nlbas = cmd->t_task_nolb;
@@ -378,6 +392,15 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
         */
        if (!cmd->t_data_sg || !cmd->t_bidi_data_sg)
                return TCM_NO_SENSE;
+       /*
+        * Immediately exit + release dev->caw_sem if command has already
+        * been failed with a non-zero SCSI status.
+        */
+       if (cmd->scsi_status) {
+               pr_err("compare_and_write_callback: non zero scsi_status:"
+                       " 0x%02x\n", cmd->scsi_status);
+               goto out;
+       }
 
        buf = kzalloc(cmd->data_length, GFP_KERNEL);
        if (!buf) {
@@ -508,6 +531,12 @@ sbc_compare_and_write(struct se_cmd *cmd)
                cmd->transport_complete_callback = NULL;
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
        }
+       /*
+        * Reset cmd->data_length to individual block_size in order to not
+        * confuse backend drivers that depend on this value matching the
+        * size of the I/O being submitted.
+        */
+       cmd->data_length = cmd->t_task_nolb * dev->dev_attrib.block_size;
 
        ret = cmd->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents,
                              DMA_FROM_DEVICE);
index 84747cc..81e945e 100644 (file)
@@ -236,17 +236,24 @@ int transport_alloc_session_tags(struct se_session *se_sess,
 {
        int rc;
 
-       se_sess->sess_cmd_map = kzalloc(tag_num * tag_size, GFP_KERNEL);
+       se_sess->sess_cmd_map = kzalloc(tag_num * tag_size,
+                                       GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
        if (!se_sess->sess_cmd_map) {
-               pr_err("Unable to allocate se_sess->sess_cmd_map\n");
-               return -ENOMEM;
+               se_sess->sess_cmd_map = vzalloc(tag_num * tag_size);
+               if (!se_sess->sess_cmd_map) {
+                       pr_err("Unable to allocate se_sess->sess_cmd_map\n");
+                       return -ENOMEM;
+               }
        }
 
        rc = percpu_ida_init(&se_sess->sess_tag_pool, tag_num);
        if (rc < 0) {
                pr_err("Unable to init se_sess->sess_tag_pool,"
                        " tag_num: %u\n", tag_num);
-               kfree(se_sess->sess_cmd_map);
+               if (is_vmalloc_addr(se_sess->sess_cmd_map))
+                       vfree(se_sess->sess_cmd_map);
+               else
+                       kfree(se_sess->sess_cmd_map);
                se_sess->sess_cmd_map = NULL;
                return -ENOMEM;
        }
@@ -412,7 +419,10 @@ void transport_free_session(struct se_session *se_sess)
 {
        if (se_sess->sess_cmd_map) {
                percpu_ida_destroy(&se_sess->sess_tag_pool);
-               kfree(se_sess->sess_cmd_map);
+               if (is_vmalloc_addr(se_sess->sess_cmd_map))
+                       vfree(se_sess->sess_cmd_map);
+               else
+                       kfree(se_sess->sess_cmd_map);
        }
        kmem_cache_free(se_sess_cache, se_sess);
 }
index 4d22e7d..474cd44 100644 (file)
@@ -82,6 +82,9 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
        mutex_lock(&g_device_mutex);
        list_for_each_entry(se_dev, &g_device_list, g_dev_node) {
 
+               if (!se_dev->dev_attrib.emulate_3pc)
+                       continue;
+
                memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
                target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
 
@@ -298,8 +301,8 @@ static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op
                (unsigned long long)xop->dst_lba);
 
        if (dc != 0) {
-               xop->dbl = (desc[29] << 16) & 0xff;
-               xop->dbl |= (desc[30] << 8) & 0xff;
+               xop->dbl = (desc[29] & 0xff) << 16;
+               xop->dbl |= (desc[30] & 0xff) << 8;
                xop->dbl |= desc[31] & 0xff;
 
                pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl);
@@ -357,6 +360,7 @@ struct xcopy_pt_cmd {
        struct se_cmd se_cmd;
        struct xcopy_op *xcopy_op;
        struct completion xpt_passthrough_sem;
+       unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
 };
 
 static struct se_port xcopy_pt_port;
@@ -675,7 +679,8 @@ static int target_xcopy_issue_pt_cmd(struct xcopy_pt_cmd *xpt_cmd)
 
        pr_debug("target_xcopy_issue_pt_cmd(): SCSI status: 0x%02x\n",
                        se_cmd->scsi_status);
-       return 0;
+
+       return (se_cmd->scsi_status) ? -EINVAL : 0;
 }
 
 static int target_xcopy_read_source(
@@ -708,7 +713,7 @@ static int target_xcopy_read_source(
                (unsigned long long)src_lba, src_sectors, length);
 
        transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
-                               DMA_FROM_DEVICE, 0, NULL);
+                             DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
        xop->src_pt_cmd = xpt_cmd;
 
        rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
@@ -768,7 +773,7 @@ static int target_xcopy_write_destination(
                (unsigned long long)dst_lba, dst_sectors, length);
 
        transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
-                               DMA_TO_DEVICE, 0, NULL);
+                             DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
        xop->dst_pt_cmd = xpt_cmd;
 
        rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, dst_dev, &cdb[0],
@@ -884,30 +889,42 @@ out:
 
 sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
 {
+       struct se_device *dev = se_cmd->se_dev;
        struct xcopy_op *xop = NULL;
        unsigned char *p = NULL, *seg_desc;
        unsigned int list_id, list_id_usage, sdll, inline_dl, sa;
+       sense_reason_t ret = TCM_INVALID_PARAMETER_LIST;
        int rc;
        unsigned short tdll;
 
+       if (!dev->dev_attrib.emulate_3pc) {
+               pr_err("EXTENDED_COPY operation explicitly disabled\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
        sa = se_cmd->t_task_cdb[1] & 0x1f;
        if (sa != 0x00) {
                pr_err("EXTENDED_COPY(LID4) not supported\n");
                return TCM_UNSUPPORTED_SCSI_OPCODE;
        }
 
+       xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
+       if (!xop) {
+               pr_err("Unable to allocate xcopy_op\n");
+               return TCM_OUT_OF_RESOURCES;
+       }
+       xop->xop_se_cmd = se_cmd;
+
        p = transport_kmap_data_sg(se_cmd);
        if (!p) {
                pr_err("transport_kmap_data_sg() failed in target_do_xcopy\n");
+               kfree(xop);
                return TCM_OUT_OF_RESOURCES;
        }
 
        list_id = p[0];
-       if (list_id != 0x00) {
-               pr_err("XCOPY with non zero list_id: 0x%02x\n", list_id);
-               goto out;
-       }
-       list_id_usage = (p[1] & 0x18);
+       list_id_usage = (p[1] & 0x18) >> 3;
+
        /*
         * Determine TARGET DESCRIPTOR LIST LENGTH + SEGMENT DESCRIPTOR LIST LENGTH
         */
@@ -920,13 +937,6 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
                goto out;
        }
 
-       xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
-       if (!xop) {
-               pr_err("Unable to allocate xcopy_op\n");
-               goto out;
-       }
-       xop->xop_se_cmd = se_cmd;
-
        pr_debug("Processing XCOPY with list_id: 0x%02x list_id_usage: 0x%02x"
                " tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage,
                tdll, sdll, inline_dl);
@@ -935,6 +945,17 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
        if (rc <= 0)
                goto out;
 
+       if (xop->src_dev->dev_attrib.block_size !=
+           xop->dst_dev->dev_attrib.block_size) {
+               pr_err("XCOPY: Non matching src_dev block_size: %u + dst_dev"
+                      " block_size: %u currently unsupported\n",
+                       xop->src_dev->dev_attrib.block_size,
+                       xop->dst_dev->dev_attrib.block_size);
+               xcopy_pt_undepend_remotedev(xop);
+               ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+               goto out;
+       }
+
        pr_debug("XCOPY: Processed %d target descriptors, length: %u\n", rc,
                                rc * XCOPY_TARGET_DESC_LEN);
        seg_desc = &p[16];
@@ -957,7 +978,7 @@ out:
        if (p)
                transport_kunmap_data_sg(se_cmd);
        kfree(xop);
-       return TCM_INVALID_CDB_FIELD;
+       return ret;
 }
 
 static sense_reason_t target_rcr_operating_parameters(struct se_cmd *se_cmd)
index f10a6ad..c2301da 100644 (file)
@@ -310,8 +310,6 @@ void exynos_report_trigger(struct thermal_sensor_conf *conf)
        }
 
        th_zone = conf->pzone_data;
-       if (th_zone->therm_dev)
-               return;
 
        if (th_zone->bind == false) {
                for (i = 0; i < th_zone->cool_dev_size; i++) {
index b43afda..32f38b9 100644 (file)
@@ -317,6 +317,9 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
 
        con = readl(data->base + reg->tmu_ctrl);
 
+       if (pdata->test_mux)
+               con |= (pdata->test_mux << reg->test_mux_addr_shift);
+
        if (pdata->reference_voltage) {
                con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift);
                con |= pdata->reference_voltage << reg->buf_vref_sel_shift;
@@ -488,7 +491,7 @@ static const struct of_device_id exynos_tmu_match[] = {
        },
        {
                .compatible = "samsung,exynos4412-tmu",
-               .data = (void *)EXYNOS5250_TMU_DRV_DATA,
+               .data = (void *)EXYNOS4412_TMU_DRV_DATA,
        },
        {
                .compatible = "samsung,exynos5250-tmu",
@@ -629,9 +632,10 @@ static int exynos_tmu_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       if (pdata->type == SOC_ARCH_EXYNOS ||
-               pdata->type == SOC_ARCH_EXYNOS4210 ||
-                               pdata->type == SOC_ARCH_EXYNOS5440)
+       if (pdata->type == SOC_ARCH_EXYNOS4210 ||
+           pdata->type == SOC_ARCH_EXYNOS4412 ||
+           pdata->type == SOC_ARCH_EXYNOS5250 ||
+           pdata->type == SOC_ARCH_EXYNOS5440)
                data->soc = pdata->type;
        else {
                ret = -EINVAL;
index b364c9e..3fb6554 100644 (file)
@@ -41,7 +41,8 @@ enum calibration_mode {
 
 enum soc_type {
        SOC_ARCH_EXYNOS4210 = 1,
-       SOC_ARCH_EXYNOS,
+       SOC_ARCH_EXYNOS4412,
+       SOC_ARCH_EXYNOS5250,
        SOC_ARCH_EXYNOS5440,
 };
 
@@ -84,6 +85,7 @@ enum soc_type {
  * @triminfo_reload_shift: shift of triminfo reload enable bit in triminfo_ctrl
        reg.
  * @tmu_ctrl: TMU main controller register.
+ * @test_mux_addr_shift: shift bits of test mux address.
  * @buf_vref_sel_shift: shift bits of reference voltage in tmu_ctrl register.
  * @buf_vref_sel_mask: mask bits of reference voltage in tmu_ctrl register.
  * @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register.
@@ -150,6 +152,7 @@ struct exynos_tmu_registers {
        u32     triminfo_reload_shift;
 
        u32     tmu_ctrl;
+       u32     test_mux_addr_shift;
        u32     buf_vref_sel_shift;
        u32     buf_vref_sel_mask;
        u32     therm_trip_mode_shift;
@@ -257,6 +260,7 @@ struct exynos_tmu_registers {
  * @first_point_trim: temp value of the first point trimming
  * @second_point_trim: temp value of the second point trimming
  * @default_temp_offset: default temperature offset in case of no trimming
+ * @test_mux; information if SoC supports test MUX
  * @cal_type: calibration type for temperature
  * @cal_mode: calibration mode for temperature
  * @freq_clip_table: Table representing frequency reduction percentage.
@@ -286,6 +290,7 @@ struct exynos_tmu_platform_data {
        u8 first_point_trim;
        u8 second_point_trim;
        u8 default_temp_offset;
+       u8 test_mux;
 
        enum calibration_type cal_type;
        enum calibration_mode cal_mode;
index 9002499..073c292 100644 (file)
@@ -90,14 +90,15 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
 };
 #endif
 
-#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
-static const struct exynos_tmu_registers exynos5250_tmu_registers = {
+#if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
+static const struct exynos_tmu_registers exynos4412_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
        .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
        .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
        .triminfo_ctrl = EXYNOS_TMU_TRIMINFO_CON,
        .triminfo_reload_shift = EXYNOS_TRIMINFO_RELOAD_SHIFT,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
+       .test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
        .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
        .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
@@ -128,7 +129,7 @@ static const struct exynos_tmu_registers exynos5250_tmu_registers = {
        .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
 };
 
-#define EXYNOS5250_TMU_DATA \
+#define EXYNOS4412_TMU_DATA \
        .threshold_falling = 10, \
        .trigger_levels[0] = 85, \
        .trigger_levels[1] = 103, \
@@ -162,15 +163,32 @@ static const struct exynos_tmu_registers exynos5250_tmu_registers = {
                .temp_level = 103, \
        }, \
        .freq_tab_count = 2, \
-       .type = SOC_ARCH_EXYNOS, \
-       .registers = &exynos5250_tmu_registers, \
+       .registers = &exynos4412_tmu_registers, \
        .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
                        TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
                        TMU_SUPPORT_EMUL_TIME)
+#endif
 
+#if defined(CONFIG_SOC_EXYNOS4412)
+struct exynos_tmu_init_data const exynos4412_default_tmu_data = {
+       .tmu_data = {
+               {
+                       EXYNOS4412_TMU_DATA,
+                       .type = SOC_ARCH_EXYNOS4412,
+                       .test_mux = EXYNOS4412_MUX_ADDR_VALUE,
+               },
+       },
+       .tmu_count = 1,
+};
+#endif
+
+#if defined(CONFIG_SOC_EXYNOS5250)
 struct exynos_tmu_init_data const exynos5250_default_tmu_data = {
        .tmu_data = {
-               { EXYNOS5250_TMU_DATA },
+               {
+                       EXYNOS4412_TMU_DATA,
+                       .type = SOC_ARCH_EXYNOS5250,
+               },
        },
        .tmu_count = 1,
 };
index dc7feb5..a1ea19d 100644 (file)
 
 #define EXYNOS_MAX_TRIGGER_PER_REG     4
 
+/* Exynos4412 specific */
+#define EXYNOS4412_MUX_ADDR_VALUE          6
+#define EXYNOS4412_MUX_ADDR_SHIFT          20
+
 /*exynos5440 specific registers*/
 #define EXYNOS5440_TMU_S0_7_TRIM               0x000
 #define EXYNOS5440_TMU_S0_7_CTRL               0x020
@@ -138,7 +142,14 @@ extern struct exynos_tmu_init_data const exynos4210_default_tmu_data;
 #define EXYNOS4210_TMU_DRV_DATA (NULL)
 #endif
 
-#if (defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412))
+#if defined(CONFIG_SOC_EXYNOS4412)
+extern struct exynos_tmu_init_data const exynos4412_default_tmu_data;
+#define EXYNOS4412_TMU_DRV_DATA (&exynos4412_default_tmu_data)
+#else
+#define EXYNOS4412_TMU_DRV_DATA (NULL)
+#endif
+
+#if defined(CONFIG_SOC_EXYNOS5250)
 extern struct exynos_tmu_init_data const exynos5250_default_tmu_data;
 #define EXYNOS5250_TMU_DRV_DATA (&exynos5250_default_tmu_data)
 #else
index eeef0e2..fdb0719 100644 (file)
@@ -159,7 +159,7 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
 
        INIT_LIST_HEAD(&hwmon->tz_list);
        strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
-       hwmon->device = hwmon_device_register(&tz->device);
+       hwmon->device = hwmon_device_register(NULL);
        if (IS_ERR(hwmon->device)) {
                result = PTR_ERR(hwmon->device);
                goto free_mem;
index 4f8b9af..5a47cc8 100644 (file)
@@ -110,6 +110,7 @@ static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
                } else {
                        dev_err(bgp->dev,
                                "Failed to read PCB state. Using defaults\n");
+                       ret = 0;
                }
        }
        *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
index f36950e..7722cb9 100644 (file)
@@ -316,18 +316,19 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
        int phy_id = topology_physical_package_id(cpu);
        struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu);
        bool notify = false;
+       unsigned long flags;
 
        if (!phdev)
                return;
 
-       spin_lock(&pkg_work_lock);
+       spin_lock_irqsave(&pkg_work_lock, flags);
        ++pkg_work_cnt;
        if (unlikely(phy_id > max_phy_id)) {
-               spin_unlock(&pkg_work_lock);
+               spin_unlock_irqrestore(&pkg_work_lock, flags);
                return;
        }
        pkg_work_scheduled[phy_id] = 0;
-       spin_unlock(&pkg_work_lock);
+       spin_unlock_irqrestore(&pkg_work_lock, flags);
 
        enable_pkg_thres_interrupt();
        rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
@@ -397,6 +398,7 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
        int thres_count;
        u32 eax, ebx, ecx, edx;
        u8 *temp;
+       unsigned long flags;
 
        cpuid(6, &eax, &ebx, &ecx, &edx);
        thres_count = ebx & 0x07;
@@ -420,19 +422,19 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
                goto err_ret_unlock;
        }
 
-       spin_lock(&pkg_work_lock);
+       spin_lock_irqsave(&pkg_work_lock, flags);
        if (topology_physical_package_id(cpu) > max_phy_id)
                max_phy_id = topology_physical_package_id(cpu);
        temp = krealloc(pkg_work_scheduled,
                        (max_phy_id+1) * sizeof(u8), GFP_ATOMIC);
        if (!temp) {
-               spin_unlock(&pkg_work_lock);
+               spin_unlock_irqrestore(&pkg_work_lock, flags);
                err = -ENOMEM;
                goto err_ret_free;
        }
        pkg_work_scheduled = temp;
        pkg_work_scheduled[topology_physical_package_id(cpu)] = 0;
-       spin_unlock(&pkg_work_lock);
+       spin_unlock_irqrestore(&pkg_work_lock, flags);
 
        phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu);
        phy_dev_entry->first_cpu = cpu;
index e61c36c..c193af6 100644 (file)
@@ -636,6 +636,7 @@ struct console xenboot_console = {
        .name           = "xenboot",
        .write          = xenboot_write_console,
        .flags          = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
+       .index          = -1,
 };
 #endif /* CONFIG_EARLY_PRINTK */
 
index c9a9ddd..7a744b6 100644 (file)
@@ -1758,8 +1758,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
                canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
        if (canon_change) {
                bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
-               ldata->line_start = 0;
-               ldata->canon_head = ldata->read_tail;
+               ldata->line_start = ldata->canon_head = ldata->read_tail;
                ldata->erasing = 0;
                ldata->lnext = 0;
        }
@@ -2184,28 +2183,34 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 
                if (!input_available_p(tty, 0)) {
                        if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
-                               retval = -EIO;
-                               break;
-                       }
-                       if (tty_hung_up_p(file))
-                               break;
-                       if (!timeout)
-                               break;
-                       if (file->f_flags & O_NONBLOCK) {
-                               retval = -EAGAIN;
-                               break;
-                       }
-                       if (signal_pending(current)) {
-                               retval = -ERESTARTSYS;
-                               break;
-                       }
-                       n_tty_set_room(tty);
-                       up_read(&tty->termios_rwsem);
+                               up_read(&tty->termios_rwsem);
+                               tty_flush_to_ldisc(tty);
+                               down_read(&tty->termios_rwsem);
+                               if (!input_available_p(tty, 0)) {
+                                       retval = -EIO;
+                                       break;
+                               }
+                       } else {
+                               if (tty_hung_up_p(file))
+                                       break;
+                               if (!timeout)
+                                       break;
+                               if (file->f_flags & O_NONBLOCK) {
+                                       retval = -EAGAIN;
+                                       break;
+                               }
+                               if (signal_pending(current)) {
+                                       retval = -ERESTARTSYS;
+                                       break;
+                               }
+                               n_tty_set_room(tty);
+                               up_read(&tty->termios_rwsem);
 
-                       timeout = schedule_timeout(timeout);
+                               timeout = schedule_timeout(timeout);
 
-                       down_read(&tty->termios_rwsem);
-                       continue;
+                               down_read(&tty->termios_rwsem);
+                               continue;
+                       }
                }
                __set_current_state(TASK_RUNNING);
 
index d067285..6b0f75e 100644 (file)
@@ -1499,7 +1499,7 @@ static void atmel_set_ops(struct uart_port *port)
 /*
  * Get ip name usart or uart
  */
-static int atmel_get_ip_name(struct uart_port *port)
+static void atmel_get_ip_name(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
        int name = UART_GET_IP_NAME(port);
@@ -1518,10 +1518,7 @@ static int atmel_get_ip_name(struct uart_port *port)
                atmel_port->is_usart = false;
        } else {
                dev_err(port->dev, "Not supported ip name, set to uart\n");
-               return -EINVAL;
        }
-
-       return 0;
 }
 
 /*
@@ -2405,9 +2402,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
        /*
         * Get port name of usart or uart
         */
-       ret = atmel_get_ip_name(&port->uart);
-       if (ret < 0)
-               goto err_add_port;
+       atmel_get_ip_name(&port->uart);
 
        return 0;
 
index a0ebbc9..042aa07 100644 (file)
@@ -1912,9 +1912,6 @@ static int serial_imx_probe_dt(struct imx_port *sport,
 
        sport->devdata = of_id->data;
 
-       if (of_device_is_stdout_path(np))
-               add_preferred_console(imx_reg.cons->name, sport->port.line, 0);
-
        return 0;
 }
 #else
index 52379e5..44077c0 100644 (file)
@@ -667,30 +667,21 @@ static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf)
 
 static int dma_push_rx(struct eg20t_port *priv, int size)
 {
-       struct tty_struct *tty;
        int room;
        struct uart_port *port = &priv->port;
        struct tty_port *tport = &port->state->port;
 
-       port = &priv->port;
-       tty = tty_port_tty_get(tport);
-       if (!tty) {
-               dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
-               return 0;
-       }
-
        room = tty_buffer_request_room(tport, size);
 
        if (room < size)
                dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
                         size - room);
        if (!room)
-               return room;
+               return 0;
 
        tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size);
 
        port->icount.rx += room;
-       tty_kref_put(tty);
 
        return room;
 }
@@ -1098,6 +1089,8 @@ static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr)
        if (tty == NULL) {
                for (i = 0; error_msg[i] != NULL; i++)
                        dev_err(&priv->pdev->dev, error_msg[i]);
+       } else {
+               tty_kref_put(tty);
        }
 }
 
index d0d972f..0489a2b 100644 (file)
@@ -732,7 +732,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
 static void tegra_uart_stop_rx(struct uart_port *u)
 {
        struct tegra_uart_port *tup = to_tegra_uport(u);
-       struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+       struct tty_struct *tty;
        struct tty_port *port = &u->state->port;
        struct dma_tx_state state;
        unsigned long ier;
@@ -744,6 +744,8 @@ static void tegra_uart_stop_rx(struct uart_port *u)
        if (!tup->rx_in_progress)
                return;
 
+       tty = tty_port_tty_get(&tup->uport.state->port);
+
        tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */
 
        ier = tup->ier_shadow;
index 93b697a..15ad6fc 100644 (file)
@@ -561,12 +561,13 @@ static int vt8500_serial_probe(struct platform_device *pdev)
        if (!mmres || !irqres)
                return -ENODEV;
 
-       if (np)
+       if (np) {
                port = of_alias_get_id(np, "serial");
                if (port >= VT8500_MAX_PORTS)
                        port = -1;
-       else
+       } else {
                port = -1;
+       }
 
        if (port < 0) {
                /* calculate the port id */
index 03ba081..6fd60fe 100644 (file)
@@ -1201,6 +1201,9 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
                }
                return 0;
        case TCFLSH:
+               retval = tty_check_change(tty);
+               if (retval)
+                       return retval;
                return __tty_perform_flush(tty, arg);
        default:
                /* Try the mode commands */
index ba47563..0e808cf 100644 (file)
@@ -642,16 +642,29 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
 {
        struct uio_device *idev = vma->vm_private_data;
        int mi = uio_find_mem_index(vma);
+       struct uio_mem *mem;
        if (mi < 0)
                return -EINVAL;
+       mem = idev->info->mem + mi;
 
-       vma->vm_ops = &uio_physical_vm_ops;
+       if (vma->vm_end - vma->vm_start > mem->size)
+               return -EINVAL;
 
+       vma->vm_ops = &uio_physical_vm_ops;
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
+       /*
+        * We cannot use the vm_iomap_memory() helper here,
+        * because vma->vm_pgoff is the map index we looked
+        * up above in uio_find_mem_index(), rather than an
+        * actual page offset into the mmap.
+        *
+        * So we just do the physical mmap without a page
+        * offset.
+        */
        return remap_pfn_range(vma,
                               vma->vm_start,
-                              idev->info->mem[mi].addr >> PAGE_SHIFT,
+                              mem->addr >> PAGE_SHIFT,
                               vma->vm_end - vma->vm_start,
                               vma->vm_page_prot);
 }
index 4a851e1..77b47d8 100644 (file)
@@ -1,6 +1,6 @@
 config USB_CHIPIDEA
        tristate "ChipIdea Highspeed Dual Role Controller"
-       depends on (USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)
+       depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
        help
          Say Y here if your system has a dual role high speed USB
          controller based on ChipIdea silicon IP. Currently, only the
index 74d998d..be822a2 100644 (file)
@@ -131,7 +131,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n",
                                        ret);
-                       goto err_clk;
+                       goto err_phy;
                }
        }
 
@@ -143,7 +143,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                dev_err(&pdev->dev,
                        "Can't register ci_hdrc platform device, err=%d\n",
                        ret);
-               goto err_clk;
+               goto err_phy;
        }
 
        if (data->usbmisc_data) {
@@ -164,6 +164,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
 
 disable_device:
        ci_hdrc_remove_device(data->ci_pdev);
+err_phy:
+       if (data->phy)
+               usb_phy_shutdown(data->phy);
 err_clk:
        clk_disable_unprepare(data->clk);
        return ret;
index 042320a..d514332 100644 (file)
@@ -129,7 +129,12 @@ static DEFINE_PCI_DEVICE_TABLE(ci_hdrc_pci_id_table) = {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829),
                .driver_data = (kernel_ulong_t)&penwell_pci_platdata,
        },
-       { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
+       {
+               /* Intel Clovertrail */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006),
+               .driver_data = (kernel_ulong_t)&penwell_pci_platdata,
+       },
+       { 0 } /* end: all zeroes */
 };
 MODULE_DEVICE_TABLE(pci, ci_hdrc_pci_id_table);
 
index 9462640..23763dc 100644 (file)
@@ -605,6 +605,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
        dbg_remove_files(ci);
        free_irq(ci->irq, ci);
        ci_role_destroy(ci);
+       kfree(ci->hw_bank.regmap);
 
        return 0;
 }
index 6f96795..64d7a6d 100644 (file)
@@ -100,8 +100,10 @@ static void host_stop(struct ci_hdrc *ci)
 {
        struct usb_hcd *hcd = ci->hcd;
 
-       usb_remove_hcd(hcd);
-       usb_put_hcd(hcd);
+       if (hcd) {
+               usb_remove_hcd(hcd);
+               usb_put_hcd(hcd);
+       }
        if (ci->platdata->reg_vbus)
                regulator_disable(ci->platdata->reg_vbus);
 }
index 6b4c2f2..9333083 100644 (file)
@@ -1600,6 +1600,8 @@ static void destroy_eps(struct ci_hdrc *ci)
        for (i = 0; i < ci->hw_ep_max; i++) {
                struct ci_hw_ep *hwep = &ci->ci_hw_ep[i];
 
+               if (hwep->pending_td)
+                       free_pending_td(hwep);
                dma_pool_free(ci->qh_pool, hwep->qh.ptr, hwep->qh.dma);
        }
 }
@@ -1667,13 +1669,13 @@ static int ci_udc_stop(struct usb_gadget *gadget,
                if (ci->platdata->notify_event)
                        ci->platdata->notify_event(ci,
                        CI_HDRC_CONTROLLER_STOPPED_EVENT);
-               ci->driver = NULL;
                spin_unlock_irqrestore(&ci->lock, flags);
                _gadget_stop_activity(&ci->gadget);
                spin_lock_irqsave(&ci->lock, flags);
                pm_runtime_put(&ci->gadget.dev);
        }
 
+       ci->driver = NULL;
        spin_unlock_irqrestore(&ci->lock, flags);
 
        return 0;
index 737e3c1..71dc5d7 100644 (file)
@@ -742,6 +742,22 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
                if ((index & ~USB_DIR_IN) == 0)
                        return 0;
                ret = findintfep(ps->dev, index);
+               if (ret < 0) {
+                       /*
+                        * Some not fully compliant Win apps seem to get
+                        * index wrong and have the endpoint number here
+                        * rather than the endpoint address (with the
+                        * correct direction). Win does let this through,
+                        * so we'll not reject it here but leave it to
+                        * the device to not break KVM. But we warn.
+                        */
+                       ret = findintfep(ps->dev, index ^ 0x80);
+                       if (ret >= 0)
+                               dev_info(&ps->dev->dev,
+                                       "%s: process %i (%s) requesting ep %02x but needs %02x\n",
+                                       __func__, task_pid_nr(current),
+                                       current->comm, index, index ^ 0x80);
+               }
                if (ret >= 0)
                        ret = checkintf(ps, ret);
                break;
index dde4c83..e6b682c 100644 (file)
@@ -3426,6 +3426,9 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
        unsigned long long u2_pel;
        int ret;
 
+       if (udev->state != USB_STATE_CONFIGURED)
+               return 0;
+
        /* Convert SEL and PEL stored in ns to us */
        u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
        u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
index 5b44cd4..01fe362 100644 (file)
@@ -97,6 +97,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Alcor Micro Corp. Hub */
        { USB_DEVICE(0x058f, 0x9254), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* MicroTouch Systems touchscreen */
+       { USB_DEVICE(0x0596, 0x051e), .driver_info = USB_QUIRK_RESET_RESUME },
+
        /* appletouch */
        { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
 
@@ -130,6 +133,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Broadcom BCM92035DGROM BT dongle */
        { USB_DEVICE(0x0a5c, 0x2021), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* MAYA44USB sound device */
+       { USB_DEVICE(0x0a92, 0x0091), .driver_info = USB_QUIRK_RESET_RESUME },
+
        /* Action Semiconductor flash disk */
        { USB_DEVICE(0x10d6, 0x2200), .driver_info =
                        USB_QUIRK_STRING_FETCH_255 },
index b870872..70fc430 100644 (file)
@@ -1,7 +1,6 @@
 config USB_DWC3
        tristate "DesignWare USB3 DRD Core Support"
        depends on (USB || USB_GADGET) && HAS_DMA
-       depends on EXTCON
        select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
        help
          Say Y or M here if your system has a Dual Role SuperSpeed
index 9b13812..2e252aa 100644 (file)
@@ -28,6 +28,8 @@
 /* FIXME define these in <linux/pci_ids.h> */
 #define PCI_VENDOR_ID_SYNOPSYS         0x16c3
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3        0xabcd
+#define PCI_DEVICE_ID_INTEL_BYT                0x0f37
+#define PCI_DEVICE_ID_INTEL_MRFLD      0x119e
 
 struct dwc3_pci {
        struct device           *dev;
@@ -187,6 +189,8 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
                PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
                                PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
        },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
        {  }    /* Terminating Entry */
 };
 MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
index f168eae..5452c0f 100644 (file)
@@ -2611,15 +2611,13 @@ int dwc3_gadget_init(struct dwc3 *dwc)
        ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
        if (ret) {
                dev_err(dwc->dev, "failed to register udc\n");
-               goto err5;
+               goto err4;
        }
 
        return 0;
 
-err5:
-       dwc3_gadget_free_endpoints(dwc);
-
 err4:
+       dwc3_gadget_free_endpoints(dwc);
        dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
                        dwc->ep0_bounce, dwc->ep0_bounce_addr);
 
index 5a5acf2..e126b6b 100644 (file)
@@ -113,12 +113,6 @@ static int __init cdc_do_config(struct usb_configuration *c)
                c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
-       fi_ecm = usb_get_function_instance("ecm");
-       if (IS_ERR(fi_ecm)) {
-               status = PTR_ERR(fi_ecm);
-               goto err_func_ecm;
-       }
-
        f_ecm = usb_get_function(fi_ecm);
        if (IS_ERR(f_ecm)) {
                status = PTR_ERR(f_ecm);
@@ -129,35 +123,24 @@ static int __init cdc_do_config(struct usb_configuration *c)
        if (status)
                goto err_add_ecm;
 
-       fi_serial = usb_get_function_instance("acm");
-       if (IS_ERR(fi_serial)) {
-               status = PTR_ERR(fi_serial);
-               goto err_get_acm;
-       }
-
        f_acm = usb_get_function(fi_serial);
        if (IS_ERR(f_acm)) {
                status = PTR_ERR(f_acm);
-               goto err_func_acm;
+               goto err_get_acm;
        }
 
        status = usb_add_function(c, f_acm);
        if (status)
                goto err_add_acm;
-
        return 0;
 
 err_add_acm:
        usb_put_function(f_acm);
-err_func_acm:
-       usb_put_function_instance(fi_serial);
 err_get_acm:
        usb_remove_function(c, f_ecm);
 err_add_ecm:
        usb_put_function(f_ecm);
 err_get_ecm:
-       usb_put_function_instance(fi_ecm);
-err_func_ecm:
        return status;
 }
 
index 06ecd08..b8a2376 100644 (file)
@@ -923,8 +923,9 @@ static int dummy_udc_stop(struct usb_gadget *g,
        struct dummy_hcd        *dum_hcd = gadget_to_dummy_hcd(g);
        struct dummy            *dum = dum_hcd->dum;
 
-       dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
-                       driver->driver.name);
+       if (driver)
+               dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
+                               driver->driver.name);
 
        dum->driver = NULL;
 
@@ -1000,8 +1001,8 @@ static int dummy_udc_remove(struct platform_device *pdev)
 {
        struct dummy    *dum = platform_get_drvdata(pdev);
 
-       usb_del_gadget_udc(&dum->gadget);
        device_remove_file(&dum->gadget.dev, &dev_attr_function);
+       usb_del_gadget_udc(&dum->gadget);
        return 0;
 }
 
index edab45d..8d9e6f7 100644 (file)
@@ -995,7 +995,7 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
        usb_ep_free_request(ecm->notify, ecm->notify_req);
 }
 
-struct usb_function *ecm_alloc(struct usb_function_instance *fi)
+static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
 {
        struct f_ecm    *ecm;
        struct f_ecm_opts *opts;
index d00392d..d61c11d 100644 (file)
@@ -624,7 +624,7 @@ static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
        usb_free_all_descriptors(f);
 }
 
-struct usb_function *eem_alloc(struct usb_function_instance *fi)
+static struct usb_function *eem_alloc(struct usb_function_instance *fi)
 {
        struct f_eem    *eem;
        struct f_eem_opts *opts;
index 1a66c5b..44cf775 100644 (file)
@@ -1034,37 +1034,19 @@ struct ffs_sb_fill_data {
        struct ffs_file_perms perms;
        umode_t root_mode;
        const char *dev_name;
-       union {
-               /* set by ffs_fs_mount(), read by ffs_sb_fill() */
-               void *private_data;
-               /* set by ffs_sb_fill(), read by ffs_fs_mount */
-               struct ffs_data *ffs_data;
-       };
+       struct ffs_data *ffs_data;
 };
 
 static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
 {
        struct ffs_sb_fill_data *data = _data;
        struct inode    *inode;
-       struct ffs_data *ffs;
+       struct ffs_data *ffs = data->ffs_data;
 
        ENTER();
 
-       /* Initialise data */
-       ffs = ffs_data_new();
-       if (unlikely(!ffs))
-               goto Enomem;
-
        ffs->sb              = sb;
-       ffs->dev_name        = kstrdup(data->dev_name, GFP_KERNEL);
-       if (unlikely(!ffs->dev_name))
-               goto Enomem;
-       ffs->file_perms      = data->perms;
-       ffs->private_data    = data->private_data;
-
-       /* used by the caller of this function */
-       data->ffs_data       = ffs;
-
+       data->ffs_data       = NULL;
        sb->s_fs_info        = ffs;
        sb->s_blocksize      = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -1080,17 +1062,14 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
                                  &data->perms);
        sb->s_root = d_make_root(inode);
        if (unlikely(!sb->s_root))
-               goto Enomem;
+               return -ENOMEM;
 
        /* EP0 file */
        if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
                                         &ffs_ep0_operations, NULL)))
-               goto Enomem;
+               return -ENOMEM;
 
        return 0;
-
-Enomem:
-       return -ENOMEM;
 }
 
 static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
@@ -1193,6 +1172,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
        struct dentry *rv;
        int ret;
        void *ffs_dev;
+       struct ffs_data *ffs;
 
        ENTER();
 
@@ -1200,18 +1180,30 @@ ffs_fs_mount(struct file_system_type *t, int flags,
        if (unlikely(ret < 0))
                return ERR_PTR(ret);
 
+       ffs = ffs_data_new();
+       if (unlikely(!ffs))
+               return ERR_PTR(-ENOMEM);
+       ffs->file_perms = data.perms;
+
+       ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
+       if (unlikely(!ffs->dev_name)) {
+               ffs_data_put(ffs);
+               return ERR_PTR(-ENOMEM);
+       }
+
        ffs_dev = functionfs_acquire_dev_callback(dev_name);
-       if (IS_ERR(ffs_dev))
-               return ffs_dev;
+       if (IS_ERR(ffs_dev)) {
+               ffs_data_put(ffs);
+               return ERR_CAST(ffs_dev);
+       }
+       ffs->private_data = ffs_dev;
+       data.ffs_data = ffs;
 
-       data.dev_name = dev_name;
-       data.private_data = ffs_dev;
        rv = mount_nodev(t, flags, &data, ffs_sb_fill);
-
-       /* data.ffs_data is set by ffs_sb_fill */
-       if (IS_ERR(rv))
+       if (IS_ERR(rv) && data.ffs_data) {
                functionfs_release_dev_callback(data.ffs_data);
-
+               ffs_data_put(data.ffs_data);
+       }
        return rv;
 }
 
@@ -2264,6 +2256,8 @@ static int ffs_func_bind(struct usb_configuration *c,
                                   data->raw_descs + ret,
                                   (sizeof data->raw_descs) - ret,
                                   __ffs_func_bind_do_descs, func);
+               if (unlikely(ret < 0))
+                       goto error;
        }
 
        /*
index 313b835..a01d7d3 100644 (file)
@@ -2260,10 +2260,12 @@ reset:
                /* Disable the endpoints */
                if (fsg->bulk_in_enabled) {
                        usb_ep_disable(fsg->bulk_in);
+                       fsg->bulk_in->driver_data = NULL;
                        fsg->bulk_in_enabled = 0;
                }
                if (fsg->bulk_out_enabled) {
                        usb_ep_disable(fsg->bulk_out);
+                       fsg->bulk_out->driver_data = NULL;
                        fsg->bulk_out_enabled = 0;
                }
 
index 32db2ee..bbbfd19 100644 (file)
@@ -1214,6 +1214,6 @@ static struct platform_driver fotg210_driver = {
 
 module_platform_driver(fotg210_driver);
 
-MODULE_AUTHOR("Yuan-Hsin Chen <yhchen@faraday-tech.com>");
+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION(DRIVER_DESC);
index f1dd6da..b278abe 100644 (file)
@@ -22,7 +22,7 @@
 
 MODULE_DESCRIPTION("FUSB300  USB gadget driver");
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Yuan Hsin Chen <yhchen@faraday-tech.com>");
+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
 MODULE_ALIAS("platform:fusb300_udc");
 
 #define DRIVER_VERSION "20 October 2010"
index 2a1ebef..2339325 100644 (file)
@@ -179,7 +179,7 @@ err_conf:
        return ret;
 }
 
-static int rndis_config_register(struct usb_composite_dev *cdev)
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
 {
        static struct usb_configuration config = {
                .bConfigurationValue    = MULTI_RNDIS_CONFIG_NUM,
@@ -194,7 +194,7 @@ static int rndis_config_register(struct usb_composite_dev *cdev)
 
 #else
 
-static int rndis_config_register(struct usb_composite_dev *cdev)
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
 {
        return 0;
 }
@@ -241,7 +241,7 @@ err_conf:
        return ret;
 }
 
-static int cdc_config_register(struct usb_composite_dev *cdev)
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
 {
        static struct usb_configuration config = {
                .bConfigurationValue    = MULTI_CDC_CONFIG_NUM,
@@ -256,7 +256,7 @@ static int cdc_config_register(struct usb_composite_dev *cdev)
 
 #else
 
-static int cdc_config_register(struct usb_composite_dev *cdev)
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
 {
        return 0;
 }
index bbb6e98..561b30e 100644 (file)
@@ -645,6 +645,7 @@ static int  mv_u3d_ep_disable(struct usb_ep *_ep)
        struct mv_u3d_ep *ep;
        struct mv_u3d_ep_context *ep_context;
        u32 epxcr, direction;
+       unsigned long flags;
 
        if (!_ep)
                return -EINVAL;
@@ -661,7 +662,9 @@ static int  mv_u3d_ep_disable(struct usb_ep *_ep)
        direction = mv_u3d_ep_dir(ep);
 
        /* nuke all pending requests (does flush) */
+       spin_lock_irqsave(&u3d->lock, flags);
        mv_u3d_nuke(ep, -ESHUTDOWN);
+       spin_unlock_irqrestore(&u3d->lock, flags);
 
        /* Disable the endpoint for Rx or Tx and reset the endpoint type */
        if (direction == MV_U3D_EP_DIR_OUT) {
index cc92074..0ac6064 100644 (file)
@@ -2054,7 +2054,7 @@ static struct pxa25x_udc memory = {
 /*
  *     probe - binds to the platform device
  */
-static int __init pxa25x_udc_probe(struct platform_device *pdev)
+static int pxa25x_udc_probe(struct platform_device *pdev)
 {
        struct pxa25x_udc *dev = &memory;
        int retval, irq;
@@ -2203,7 +2203,7 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev)
        pullup_off();
 }
 
-static int __exit pxa25x_udc_remove(struct platform_device *pdev)
+static int pxa25x_udc_remove(struct platform_device *pdev)
 {
        struct pxa25x_udc *dev = platform_get_drvdata(pdev);
 
@@ -2294,7 +2294,8 @@ static int pxa25x_udc_resume(struct platform_device *dev)
 
 static struct platform_driver udc_driver = {
        .shutdown       = pxa25x_udc_shutdown,
-       .remove         = __exit_p(pxa25x_udc_remove),
+       .probe          = pxa25x_udc_probe,
+       .remove         = pxa25x_udc_remove,
        .suspend        = pxa25x_udc_suspend,
        .resume         = pxa25x_udc_resume,
        .driver         = {
@@ -2303,7 +2304,7 @@ static struct platform_driver udc_driver = {
        },
 };
 
-module_platform_driver_probe(udc_driver, pxa25x_udc_probe);
+module_platform_driver(udc_driver);
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");
index d69b36a..a8a99e4 100644 (file)
@@ -543,7 +543,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
         * FIFO, requests of >512 cause the endpoint to get stuck with a
         * fragment of the end of the transfer in it.
         */
-       if (can_write > 512)
+       if (can_write > 512 && !periodic)
                can_write = 512;
 
        /*
@@ -2475,8 +2475,6 @@ irq_retry:
        if (gintsts & GINTSTS_ErlySusp) {
                dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
                writel(GINTSTS_ErlySusp, hsotg->regs + GINTSTS);
-
-               s3c_hsotg_disconnect(hsotg);
        }
 
        /*
@@ -2962,9 +2960,6 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
        if (!hsotg)
                return -ENODEV;
 
-       if (!driver || driver != hsotg->driver || !driver->unbind)
-               return -EINVAL;
-
        /* all endpoints should be shutdown */
        for (ep = 0; ep < hsotg->num_of_eps; ep++)
                s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
@@ -2972,15 +2967,15 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
        spin_lock_irqsave(&hsotg->lock, flags);
 
        s3c_hsotg_phy_disable(hsotg);
-       regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 
-       hsotg->driver = NULL;
+       if (!driver)
+               hsotg->driver = NULL;
+
        hsotg->gadget.speed = USB_SPEED_UNKNOWN;
 
        spin_unlock_irqrestore(&hsotg->lock, flags);
 
-       dev_info(hsotg->dev, "unregistered gadget driver '%s'\n",
-                driver->driver.name);
+       regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 
        return 0;
 }
index 947b009..f2407b2 100644 (file)
@@ -130,7 +130,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
        }
 
        /* Enable USB controller, 83xx or 8536 */
-       if (pdata->have_sysif_regs)
+       if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6)
                setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
 
        /* Don't need to set host mode here. It will be done by tdi_reset() */
@@ -232,15 +232,9 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
        case FSL_USB2_PHY_ULPI:
                if (pdata->have_sysif_regs && pdata->controller_ver) {
                        /* controller version 1.6 or above */
+                       clrbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
                        setbits32(non_ehci + FSL_SOC_USB_CTRL,
-                                       ULPI_PHY_CLK_SEL);
-                       /*
-                        * Due to controller issue of PHY_CLK_VALID in ULPI
-                        * mode, we set USB_CTRL_USB_EN before checking
-                        * PHY_CLK_VALID, otherwise PHY_CLK_VALID doesn't work.
-                        */
-                       clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL,
-                                       UTMI_PHY_EN, USB_CTRL_USB_EN);
+                               ULPI_PHY_CLK_SEL | USB_CTRL_USB_EN);
                }
                portsc |= PORT_PTS_ULPI;
                break;
@@ -270,8 +264,9 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
        if (pdata->have_sysif_regs && pdata->controller_ver &&
            (phy_mode == FSL_USB2_PHY_ULPI)) {
                /* check PHY_CLK_VALID to get phy clk valid */
-               if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) &
-                               PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) {
+               if (!(spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) &
+                               PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0) ||
+                               in_be32(non_ehci + FSL_SOC_USB_PRICTRL))) {
                        printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n");
                        return -EINVAL;
                }
@@ -669,7 +664,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
+       .flags = HCD_USB2 | HCD_MEMORY,
 
        /*
         * basic lifecycle operations
index b52a66c..83ab51a 100644 (file)
@@ -43,7 +43,7 @@ static const struct hc_driver ehci_grlib_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index 5d6022f..86ab9fd 100644 (file)
@@ -1158,7 +1158,7 @@ static const struct hc_driver ehci_hc_driver = {
         * generic hardware linkage
         */
        .irq =                  ehci_irq,
-       .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags =                HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index 417c10d..35cdbd8 100644 (file)
@@ -96,7 +96,7 @@ static const struct hc_driver mv_ehci_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags = HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index ab0397e..45cc001 100644 (file)
@@ -51,7 +51,7 @@ static const struct hc_driver ehci_octeon_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index 6bd299e..854c2ec 100644 (file)
@@ -361,7 +361,7 @@ static struct pci_driver ehci_pci_driver = {
        .remove =       usb_hcd_pci_remove,
        .shutdown =     usb_hcd_pci_shutdown,
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        .driver =       {
                .pm =   &usb_hcd_pci_pm_ops
        },
index 893b707..601e208 100644 (file)
@@ -286,7 +286,7 @@ static const struct hc_driver ehci_msp_hc_driver = {
 #else
        .irq =                  ehci_irq,
 #endif
-       .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags =                HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index 6cc5567..932293f 100644 (file)
@@ -28,7 +28,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index 8188542..fd98377 100644 (file)
@@ -71,7 +71,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
        .product_desc           = "PS3 EHCI Host Controller",
        .hcd_priv_size          = sizeof(struct ehci_hcd),
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
        .reset                  = ps3_ehci_hc_reset,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
index e321804..a7f776a 100644 (file)
@@ -247,6 +247,8 @@ static int qtd_copy_status (
 
 static void
 ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
+__releases(ehci->lock)
+__acquires(ehci->lock)
 {
        if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
                /* ... update hc-wide periodic stats */
@@ -272,8 +274,11 @@ ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
                urb->actual_length, urb->transfer_buffer_length);
 #endif
 
+       /* complete() can reenter this HCD */
        usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+       spin_unlock (&ehci->lock);
        usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
+       spin_lock (&ehci->lock);
 }
 
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
index 8a73449..b2de52d 100644 (file)
@@ -55,7 +55,7 @@ const struct hc_driver ehci_sead3_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index dc899eb..93e59a1 100644 (file)
@@ -36,7 +36,7 @@ static const struct hc_driver ehci_sh_hc_driver = {
         * generic hardware linkage
         */
        .irq                            = ehci_irq,
-       .flags                          = HCD_USB2 | HCD_MEMORY | HCD_BH,
+       .flags                          = HCD_USB2 | HCD_MEMORY,
 
        /*
         * basic lifecycle operations
index 67026ff..cca4be9 100644 (file)
@@ -61,7 +61,7 @@ static const struct hc_driver ehci_tilegx_hc_driver = {
         * Generic hardware linkage.
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
 
        /*
         * Basic lifecycle operations.
index 1c370df..59e0e24 100644 (file)
@@ -108,7 +108,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_USB2|HCD_MEMORY|HCD_BH,
+       .flags = HCD_USB2|HCD_MEMORY,
 
        /*
         * basic lifecycle operations
index 95979f9..eba962e 100644 (file)
@@ -79,7 +79,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
+       .flags                  = HCD_MEMORY | HCD_USB2,
 
        /*
         * basic lifecycle operations
index 9e0020d..abd5050 100644 (file)
@@ -24,7 +24,7 @@ struct fsl_usb2_dev_data {
        enum fsl_usb2_operating_modes op_mode;  /* operating mode */
 };
 
-struct fsl_usb2_dev_data dr_mode_data[] = {
+static struct fsl_usb2_dev_data dr_mode_data[] = {
        {
                .dr_mode = "host",
                .drivers = { "fsl-ehci", NULL, NULL, },
@@ -42,7 +42,7 @@ struct fsl_usb2_dev_data dr_mode_data[] = {
        },
 };
 
-struct fsl_usb2_dev_data *get_dr_mode_data(struct device_node *np)
+static struct fsl_usb2_dev_data *get_dr_mode_data(struct device_node *np)
 {
        const unsigned char *prop;
        int i;
@@ -75,7 +75,7 @@ static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type)
        return FSL_USB2_PHY_NONE;
 }
 
-struct platform_device *fsl_usb2_device_register(
+static struct platform_device *fsl_usb2_device_register(
                                        struct platform_device *ofdev,
                                        struct fsl_usb2_platform_data *pdata,
                                        const char *name, int id)
index 60a5de5..adb01d9 100644 (file)
@@ -824,13 +824,13 @@ static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
                        i = DIV_ROUND_UP(wrap_frame(
                                        cur_frame - urb->start_frame),
                                        urb->interval);
-                       if (urb->transfer_flags & URB_ISO_ASAP) {
+
+                       /* Treat underruns as if URB_ISO_ASAP was set */
+                       if ((urb->transfer_flags & URB_ISO_ASAP) ||
+                                       i >= urb->number_of_packets) {
                                urb->start_frame = wrap_frame(urb->start_frame
                                                + i * urb->interval);
                                i = 0;
-                       } else if (i >= urb->number_of_packets) {
-                               ret = -EXDEV;
-                               goto alloc_dmem_failed;
                        }
                }
        }
index 8f6b695..604cad1 100644 (file)
@@ -216,31 +216,26 @@ static int ohci_urb_enqueue (
                        frame &= ~(ed->interval - 1);
                        frame |= ed->branch;
                        urb->start_frame = frame;
+                       ed->last_iso = frame + ed->interval * (size - 1);
                }
        } else if (ed->type == PIPE_ISOCHRONOUS) {
                u16     next = ohci_frame_no(ohci) + 1;
                u16     frame = ed->last_iso + ed->interval;
+               u16     length = ed->interval * (size - 1);
 
                /* Behind the scheduling threshold? */
                if (unlikely(tick_before(frame, next))) {
 
-                       /* USB_ISO_ASAP: Round up to the first available slot */
+                       /* URB_ISO_ASAP: Round up to the first available slot */
                        if (urb->transfer_flags & URB_ISO_ASAP) {
                                frame += (next - frame + ed->interval - 1) &
                                                -ed->interval;
 
                        /*
-                        * Not ASAP: Use the next slot in the stream.  If
-                        * the entire URB falls before the threshold, fail.
+                        * Not ASAP: Use the next slot in the stream,
+                        * no matter what.
                         */
                        } else {
-                               if (tick_before(frame + ed->interval *
-                                       (urb->number_of_packets - 1), next)) {
-                                       retval = -EXDEV;
-                                       usb_hcd_unlink_urb_from_ep(hcd, urb);
-                                       goto fail;
-                               }
-
                                /*
                                 * Some OHCI hardware doesn't handle late TDs
                                 * correctly.  After retiring them it proceeds
@@ -251,9 +246,16 @@ static int ohci_urb_enqueue (
                                urb_priv->td_cnt = DIV_ROUND_UP(
                                                (u16) (next - frame),
                                                ed->interval);
+                               if (urb_priv->td_cnt >= urb_priv->length) {
+                                       ++urb_priv->td_cnt;     /* Mark it */
+                                       ohci_dbg(ohci, "iso underrun %p (%u+%u < %u)\n",
+                                                       urb, frame, length,
+                                                       next);
+                               }
                        }
                }
                urb->start_frame = frame;
+               ed->last_iso = frame + length;
        }
 
        /* fill the TDs and link them to the ed; and
index df4a670..e7f577e 100644 (file)
@@ -41,9 +41,13 @@ finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status)
 __releases(ohci->lock)
 __acquires(ohci->lock)
 {
-        struct device *dev = ohci_to_hcd(ohci)->self.controller;
+       struct device *dev = ohci_to_hcd(ohci)->self.controller;
+       struct usb_host_endpoint *ep = urb->ep;
+       struct urb_priv *urb_priv;
+
        // ASSERT (urb->hcpriv != 0);
 
+ restart:
        urb_free_priv (ohci, urb->hcpriv);
        urb->hcpriv = NULL;
        if (likely(status == -EINPROGRESS))
@@ -80,6 +84,21 @@ __acquires(ohci->lock)
                ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);
                ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
        }
+
+       /*
+        * An isochronous URB that is sumitted too late won't have any TDs
+        * (marked by the fact that the td_cnt value is larger than the
+        * actual number of TDs).  If the next URB on this endpoint is like
+        * that, give it back now.
+        */
+       if (!list_empty(&ep->urb_list)) {
+               urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+               urb_priv = urb->hcpriv;
+               if (urb_priv->td_cnt > urb_priv->length) {
+                       status = 0;
+                       goto restart;
+               }
+       }
 }
 
 
@@ -546,7 +565,6 @@ td_fill (struct ohci_hcd *ohci, u32 info,
                td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
                *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
                                                (data & 0x0FFF) | 0xE000);
-               td->ed->last_iso = info & 0xffff;
        } else {
                td->hwCBP = cpu_to_hc32 (ohci, data);
        }
@@ -996,7 +1014,7 @@ rescan_this:
                        urb_priv->td_cnt++;
 
                        /* if URB is done, clean up */
-                       if (urb_priv->td_cnt == urb_priv->length) {
+                       if (urb_priv->td_cnt >= urb_priv->length) {
                                modified = completed = 1;
                                finish_urb(ohci, urb, 0);
                        }
@@ -1086,7 +1104,7 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td)
        urb_priv->td_cnt++;
 
        /* If all this urb's TDs are done, call complete() */
-       if (urb_priv->td_cnt == urb_priv->length)
+       if (urb_priv->td_cnt >= urb_priv->length)
                finish_urb(ohci, urb, status);
 
        /* clean schedule:  unlink EDs that are no longer busy */
index 2c76ef1..08ef282 100644 (file)
@@ -799,7 +799,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
         * switchable ports.
         */
        pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
-                       cpu_to_le32(ports_available));
+                       ports_available);
 
        pci_read_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
                        &ports_available);
@@ -821,7 +821,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
         * host.
         */
        pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
-                       cpu_to_le32(ports_available));
+                       ports_available);
 
        pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
                        &ports_available);
index c300bd2..0f228c4 100644 (file)
@@ -293,7 +293,7 @@ static struct pci_driver uhci_pci_driver = {
        .remove =       usb_hcd_pci_remove,
        .shutdown =     uhci_shutdown,
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        .driver =       {
                .pm =   &usb_hcd_pci_pm_ops
        },
index 041c6dd..da6f56d 100644 (file)
@@ -1303,7 +1303,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                }
 
                /* Fell behind? */
-               if (uhci_frame_before_eq(frame, next)) {
+               if (!uhci_frame_before_eq(next, frame)) {
 
                        /* USB_ISO_ASAP: Round up to the first available slot */
                        if (urb->transfer_flags & URB_ISO_ASAP)
@@ -1311,13 +1311,17 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                                                -qh->period;
 
                        /*
-                        * Not ASAP: Use the next slot in the stream.  If
-                        * the entire URB falls before the threshold, fail.
+                        * Not ASAP: Use the next slot in the stream,
+                        * no matter what.
                         */
                        else if (!uhci_frame_before_eq(next,
                                        frame + (urb->number_of_packets - 1) *
                                                qh->period))
-                               return -EXDEV;
+                               dev_dbg(uhci_dev(uhci), "iso underrun %p (%u+%u < %u)\n",
+                                               urb, frame,
+                                               (urb->number_of_packets - 1) *
+                                                       qh->period,
+                                               next);
                }
        }
 
index fae697e..e8b4c56 100644 (file)
@@ -287,7 +287,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
                if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue)
                        xhci_queue_stop_endpoint(xhci, slot_id, i, suspend);
        }
-       cmd->command_trb = xhci->cmd_ring->enqueue;
+       cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
        xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend);
        xhci_ring_cmd_db(xhci);
@@ -552,11 +552,15 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
  *  - Mark a port as being done with device resume,
  *    and ring the endpoint doorbells.
  *  - Stop the Synopsys redriver Compliance Mode polling.
+ *  - Drop and reacquire the xHCI lock, in order to wait for port resume.
  */
 static u32 xhci_get_port_status(struct usb_hcd *hcd,
                struct xhci_bus_state *bus_state,
                __le32 __iomem **port_array,
-               u16 wIndex, u32 raw_port_status)
+               u16 wIndex, u32 raw_port_status,
+               unsigned long flags)
+       __releases(&xhci->lock)
+       __acquires(&xhci->lock)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        u32 status = 0;
@@ -591,21 +595,42 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
                        return 0xffffffff;
                if (time_after_eq(jiffies,
                                        bus_state->resume_done[wIndex])) {
+                       int time_left;
+
                        xhci_dbg(xhci, "Resume USB2 port %d\n",
                                        wIndex + 1);
                        bus_state->resume_done[wIndex] = 0;
                        clear_bit(wIndex, &bus_state->resuming_ports);
+
+                       set_bit(wIndex, &bus_state->rexit_ports);
                        xhci_set_link_state(xhci, port_array, wIndex,
                                        XDEV_U0);
-                       xhci_dbg(xhci, "set port %d resume\n",
-                                       wIndex + 1);
-                       slot_id = xhci_find_slot_id_by_port(hcd, xhci,
-                                       wIndex + 1);
-                       if (!slot_id) {
-                               xhci_dbg(xhci, "slot_id is zero\n");
-                               return 0xffffffff;
+
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       time_left = wait_for_completion_timeout(
+                                       &bus_state->rexit_done[wIndex],
+                                       msecs_to_jiffies(
+                                               XHCI_MAX_REXIT_TIMEOUT));
+                       spin_lock_irqsave(&xhci->lock, flags);
+
+                       if (time_left) {
+                               slot_id = xhci_find_slot_id_by_port(hcd,
+                                               xhci, wIndex + 1);
+                               if (!slot_id) {
+                                       xhci_dbg(xhci, "slot_id is zero\n");
+                                       return 0xffffffff;
+                               }
+                               xhci_ring_device(xhci, slot_id);
+                       } else {
+                               int port_status = xhci_readl(xhci,
+                                               port_array[wIndex]);
+                               xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n",
+                                               XHCI_MAX_REXIT_TIMEOUT,
+                                               port_status);
+                               status |= USB_PORT_STAT_SUSPEND;
+                               clear_bit(wIndex, &bus_state->rexit_ports);
                        }
-                       xhci_ring_device(xhci, slot_id);
+
                        bus_state->port_c_suspend |= 1 << wIndex;
                        bus_state->suspended_ports &= ~(1 << wIndex);
                } else {
@@ -728,7 +753,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        break;
                }
                status = xhci_get_port_status(hcd, bus_state, port_array,
-                               wIndex, temp);
+                               wIndex, temp, flags);
                if (status == 0xffffffff)
                        goto error;
 
@@ -1132,18 +1157,6 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                t1 = xhci_port_state_to_neutral(t1);
                if (t1 != t2)
                        xhci_writel(xhci, t2, port_array[port_index]);
-
-               if (hcd->speed != HCD_USB3) {
-                       /* enable remote wake up for USB 2.0 */
-                       __le32 __iomem *addr;
-                       u32 tmp;
-
-                       /* Get the port power control register address. */
-                       addr = port_array[port_index] + PORTPMSC;
-                       tmp = xhci_readl(xhci, addr);
-                       tmp |= PORT_RWE;
-                       xhci_writel(xhci, tmp, addr);
-               }
        }
        hcd->state = HC_STATE_SUSPENDED;
        bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -1222,20 +1235,6 @@ int xhci_bus_resume(struct usb_hcd *hcd)
                                xhci_ring_device(xhci, slot_id);
                } else
                        xhci_writel(xhci, temp, port_array[port_index]);
-
-               if (hcd->speed != HCD_USB3) {
-                       /* disable remote wake up for USB 2.0 */
-                       __le32 __iomem *addr;
-                       u32 tmp;
-
-                       /* Add one to the port status register address to get
-                        * the port power control register address.
-                        */
-                       addr = port_array[port_index] + PORTPMSC;
-                       tmp = xhci_readl(xhci, addr);
-                       tmp &= ~PORT_RWE;
-                       xhci_writel(xhci, tmp, addr);
-               }
        }
 
        (void) xhci_readl(xhci, &xhci->op_regs->command);
index 53b972c..83bcd13 100644 (file)
@@ -2428,6 +2428,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        for (i = 0; i < USB_MAXCHILDREN; ++i) {
                xhci->bus_state[0].resume_done[i] = 0;
                xhci->bus_state[1].resume_done[i] = 0;
+               /* Only the USB 2.0 completions will ever be used. */
+               init_completion(&xhci->bus_state[1].rexit_done[i]);
        }
 
        if (scratchpad_alloc(xhci, flags))
index c2d4950..b8dffd5 100644 (file)
@@ -35,6 +35,9 @@
 #define PCI_VENDOR_ID_ETRON            0x1b6f
 #define PCI_DEVICE_ID_ASROCK_P67       0x7023
 
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI     0x8c31
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
+
 static const char hcd_name[] = "xhci_hcd";
 
 /* called after powerup, by probe or system-pm "wakeup" */
@@ -69,6 +72,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                                "QUIRK: Fresco Logic xHC needs configure"
                                " endpoint cmd after reset endpoint");
                }
+               if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
+                               pdev->revision == 0x4) {
+                       xhci->quirks |= XHCI_SLOW_SUSPEND;
+                       xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+                               "QUIRK: Fresco Logic xHC revision %u"
+                               "must be suspended extra slowly",
+                               pdev->revision);
+               }
                /* Fresco Logic confirms: all revisions of this chip do not
                 * support MSI, even though some of them claim to in their PCI
                 * capabilities.
@@ -110,6 +121,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                xhci->quirks |= XHCI_SPURIOUS_REBOOT;
                xhci->quirks |= XHCI_AVOID_BEI;
        }
+       if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+           (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI ||
+            pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI)) {
+               /* Workaround for occasional spurious wakeups from S5 (or
+                * any other sleep) on Haswell machines with LPT and LPT-LP
+                * with the new Intel BIOS
+                */
+               xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
+       }
        if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
                        pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
                xhci->quirks |= XHCI_RESET_ON_RESUME;
@@ -217,6 +237,11 @@ static void xhci_pci_remove(struct pci_dev *dev)
                usb_put_hcd(xhci->shared_hcd);
        }
        usb_hcd_pci_remove(dev);
+
+       /* Workaround for spurious wakeups at shutdown with HSW */
+       if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+               pci_set_power_state(dev, PCI_D3hot);
+
        kfree(xhci);
 }
 
@@ -351,7 +376,7 @@ static struct pci_driver xhci_pci_driver = {
        /* suspend and resume implemented later */
 
        .shutdown =     usb_hcd_pci_shutdown,
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        .driver = {
                .pm = &usb_hcd_pci_pm_ops
        },
index 411da1f..6bfbd80 100644 (file)
@@ -123,6 +123,16 @@ static int enqueue_is_link_trb(struct xhci_ring *ring)
        return TRB_TYPE_LINK_LE32(link->control);
 }
 
+union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring)
+{
+       /* Enqueue pointer can be left pointing to the link TRB,
+        * we must handle that
+        */
+       if (TRB_TYPE_LINK_LE32(ring->enqueue->link.control))
+               return ring->enq_seg->next->trbs;
+       return ring->enqueue;
+}
+
 /* Updates trb to point to the next TRB in the ring, and updates seg if the next
  * TRB is in a new segment.  This does not skip over link TRBs, and it does not
  * effect the ring dequeue or enqueue pointers.
@@ -859,8 +869,12 @@ remove_finished_td:
                /* Otherwise ring the doorbell(s) to restart queued transfers */
                ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
        }
-       ep->stopped_td = NULL;
-       ep->stopped_trb = NULL;
+
+       /* Clear stopped_td and stopped_trb if endpoint is not halted */
+       if (!(ep->ep_state & EP_HALTED)) {
+               ep->stopped_td = NULL;
+               ep->stopped_trb = NULL;
+       }
 
        /*
         * Drop the lock and complete the URBs in the cancelled TD list.
@@ -1414,6 +1428,12 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                        inc_deq(xhci, xhci->cmd_ring);
                        return;
                }
+               /* There is no command to handle if we get a stop event when the
+                * command ring is empty, event->cmd_trb points to the next
+                * unset command
+                */
+               if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
+                       return;
        }
 
        switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
@@ -1743,6 +1763,19 @@ static void handle_port_status(struct xhci_hcd *xhci,
                }
        }
 
+       /*
+        * Check to see if xhci-hub.c is waiting on RExit to U0 transition (or
+        * RExit to a disconnect state).  If so, let the the driver know it's
+        * out of the RExit state.
+        */
+       if (!DEV_SUPERSPEED(temp) &&
+                       test_and_clear_bit(faked_port_index,
+                               &bus_state->rexit_ports)) {
+               complete(&bus_state->rexit_done[faked_port_index]);
+               bogus_port_status = true;
+               goto cleanup;
+       }
+
        if (hcd->speed != HCD_USB3)
                xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
                                        PORT_PLC);
index 49b6edb..6e0d886 100644 (file)
@@ -730,6 +730,9 @@ void xhci_shutdown(struct usb_hcd *hcd)
 
        spin_lock_irq(&xhci->lock);
        xhci_halt(xhci);
+       /* Workaround for spurious wakeups at shutdown with HSW */
+       if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+               xhci_reset(xhci);
        spin_unlock_irq(&xhci->lock);
 
        xhci_cleanup_msix(xhci);
@@ -737,6 +740,10 @@ void xhci_shutdown(struct usb_hcd *hcd)
        xhci_dbg_trace(xhci, trace_xhci_dbg_init,
                        "xhci_shutdown completed - status = %x",
                        xhci_readl(xhci, &xhci->op_regs->status));
+
+       /* Yet another workaround for spurious wakeups at shutdown with HSW */
+       if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+               pci_set_power_state(to_pci_dev(hcd->self.controller), PCI_D3hot);
 }
 
 #ifdef CONFIG_PM
@@ -839,6 +846,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
 int xhci_suspend(struct xhci_hcd *xhci)
 {
        int                     rc = 0;
+       unsigned int            delay = XHCI_MAX_HALT_USEC;
        struct usb_hcd          *hcd = xhci_to_hcd(xhci);
        u32                     command;
 
@@ -861,8 +869,12 @@ int xhci_suspend(struct xhci_hcd *xhci)
        command = xhci_readl(xhci, &xhci->op_regs->command);
        command &= ~CMD_RUN;
        xhci_writel(xhci, command, &xhci->op_regs->command);
+
+       /* Some chips from Fresco Logic need an extraordinary delay */
+       delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1;
+
        if (xhci_handshake(xhci, &xhci->op_regs->status,
-                     STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC)) {
+                     STS_HALT, STS_HALT, delay)) {
                xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
                spin_unlock_irq(&xhci->lock);
                return -ETIMEDOUT;
@@ -2598,15 +2610,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        if (command) {
                cmd_completion = command->completion;
                cmd_status = &command->status;
-               command->command_trb = xhci->cmd_ring->enqueue;
-
-               /* Enqueue pointer can be left pointing to the link TRB,
-                * we must handle that
-                */
-               if (TRB_TYPE_LINK_LE32(command->command_trb->link.control))
-                       command->command_trb =
-                               xhci->cmd_ring->enq_seg->next->trbs;
-
+               command->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
                list_add_tail(&command->cmd_list, &virt_dev->cmd_list);
        } else {
                cmd_completion = &virt_dev->cmd_completion;
@@ -2614,7 +2618,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        }
        init_completion(cmd_completion);
 
-       cmd_trb = xhci->cmd_ring->dequeue;
+       cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        if (!ctx_change)
                ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
                                udev->slot_id, must_succeed);
@@ -3439,14 +3443,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
 
        /* Attempt to submit the Reset Device command to the command ring */
        spin_lock_irqsave(&xhci->lock, flags);
-       reset_device_cmd->command_trb = xhci->cmd_ring->enqueue;
-
-       /* Enqueue pointer can be left pointing to the link TRB,
-        * we must handle that
-        */
-       if (TRB_TYPE_LINK_LE32(reset_device_cmd->command_trb->link.control))
-               reset_device_cmd->command_trb =
-                       xhci->cmd_ring->enq_seg->next->trbs;
+       reset_device_cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
 
        list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list);
        ret = xhci_queue_reset_device(xhci, slot_id);
@@ -3650,7 +3647,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
        union xhci_trb *cmd_trb;
 
        spin_lock_irqsave(&xhci->lock, flags);
-       cmd_trb = xhci->cmd_ring->dequeue;
+       cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
        if (ret) {
                spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3785,7 +3782,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
                                slot_ctx->dev_info >> 27);
 
        spin_lock_irqsave(&xhci->lock, flags);
-       cmd_trb = xhci->cmd_ring->dequeue;
+       cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring);
        ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
                                        udev->slot_id);
        if (ret) {
index 46aa148..941d5f5 100644 (file)
@@ -1412,8 +1412,18 @@ struct xhci_bus_state {
        unsigned long           resume_done[USB_MAXCHILDREN];
        /* which ports have started to resume */
        unsigned long           resuming_ports;
+       /* Which ports are waiting on RExit to U0 transition. */
+       unsigned long           rexit_ports;
+       struct completion       rexit_done[USB_MAXCHILDREN];
 };
 
+
+/*
+ * It can take up to 20 ms to transition from RExit to U0 on the
+ * Intel Lynx Point LP xHCI host.
+ */
+#define        XHCI_MAX_REXIT_TIMEOUT  (20 * 1000)
+
 static inline unsigned int hcd_index(struct usb_hcd *hcd)
 {
        if (hcd->speed == HCD_USB3)
@@ -1538,6 +1548,8 @@ struct xhci_hcd {
 #define XHCI_COMP_MODE_QUIRK   (1 << 14)
 #define XHCI_AVOID_BEI         (1 << 15)
 #define XHCI_PLAT              (1 << 16)
+#define XHCI_SLOW_SUSPEND      (1 << 17)
+#define XHCI_SPURIOUS_WAKEUP   (1 << 18)
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
        /* There are two roothubs to keep track of bus suspend info for */
@@ -1840,6 +1852,7 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
                union xhci_trb *cmd_trb);
 void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
                unsigned int ep_index, unsigned int stream_id);
+union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring);
 
 /* xHCI roothub code */
 void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
index e2b21c1..ba5f70f 100644 (file)
@@ -246,6 +246,6 @@ config USB_EZUSB_FX2
 config USB_HSIC_USB3503
        tristate "USB3503 HSIC to USB20 Driver"
        depends on I2C
-       select REGMAP
+       select REGMAP_I2C
        help
          This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
index 18e877f..cd70cc8 100644 (file)
@@ -921,6 +921,52 @@ static void musb_generic_disable(struct musb *musb)
 
 }
 
+/*
+ * Program the HDRC to start (enable interrupts, dma, etc.).
+ */
+void musb_start(struct musb *musb)
+{
+       void __iomem    *regs = musb->mregs;
+       u8              devctl = musb_readb(regs, MUSB_DEVCTL);
+
+       dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
+
+       /*  Set INT enable registers, enable interrupts */
+       musb->intrtxe = musb->epmask;
+       musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
+       musb->intrrxe = musb->epmask & 0xfffe;
+       musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
+       musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
+
+       musb_writeb(regs, MUSB_TESTMODE, 0);
+
+       /* put into basic highspeed mode and start session */
+       musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
+                       | MUSB_POWER_HSENAB
+                       /* ENSUSPEND wedges tusb */
+                       /* | MUSB_POWER_ENSUSPEND */
+                  );
+
+       musb->is_active = 0;
+       devctl = musb_readb(regs, MUSB_DEVCTL);
+       devctl &= ~MUSB_DEVCTL_SESSION;
+
+       /* session started after:
+        * (a) ID-grounded irq, host mode;
+        * (b) vbus present/connect IRQ, peripheral mode;
+        * (c) peripheral initiates, using SRP
+        */
+       if (musb->port_mode != MUSB_PORT_MODE_HOST &&
+                       (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
+               musb->is_active = 1;
+       } else {
+               devctl |= MUSB_DEVCTL_SESSION;
+       }
+
+       musb_platform_enable(musb);
+       musb_writeb(regs, MUSB_DEVCTL, devctl);
+}
+
 /*
  * Make the HDRC stop (disable interrupts, etc.);
  * reversible by musb_start
index 65f3917..1c5bf75 100644 (file)
@@ -503,6 +503,7 @@ static inline void musb_configure_ep0(struct musb *musb)
 extern const char musb_driver_name[];
 
 extern void musb_stop(struct musb *musb);
+extern void musb_start(struct musb *musb);
 
 extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
 extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
index 4047cbb..bd4138d 100644 (file)
@@ -535,6 +535,9 @@ static int dsps_probe(struct platform_device *pdev)
        struct dsps_glue *glue;
        int ret;
 
+       if (!strcmp(pdev->name, "musb-hdrc"))
+               return -ENODEV;
+
        match = of_match_node(musb_dsps_of_match, pdev->dev.of_node);
        if (!match) {
                dev_err(&pdev->dev, "fail to get matching of_match struct\n");
index 9a08679..3671898 100644 (file)
@@ -1790,6 +1790,10 @@ int musb_gadget_setup(struct musb *musb)
        musb->g.max_speed = USB_SPEED_HIGH;
        musb->g.speed = USB_SPEED_UNKNOWN;
 
+       MUSB_DEV_MODE(musb);
+       musb->xceiv->otg->default_a = 0;
+       musb->xceiv->state = OTG_STATE_B_IDLE;
+
        /* this "gadget" abstracts/virtualizes the controller */
        musb->g.name = musb_driver_name;
        musb->g.is_otg = 1;
@@ -1855,6 +1859,8 @@ static int musb_gadget_start(struct usb_gadget *g,
        musb->xceiv->state = OTG_STATE_B_IDLE;
        spin_unlock_irqrestore(&musb->lock, flags);
 
+       musb_start(musb);
+
        /* REVISIT:  funcall to other code, which also
         * handles power budgeting ... this way also
         * ensures HdrcStart is indirectly called.
index a523950..d1d6b83 100644 (file)
 
 #include "musb_core.h"
 
-/*
-* Program the HDRC to start (enable interrupts, dma, etc.).
-*/
-static void musb_start(struct musb *musb)
-{
-       void __iomem    *regs = musb->mregs;
-       u8              devctl = musb_readb(regs, MUSB_DEVCTL);
-
-       dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
-
-       /*  Set INT enable registers, enable interrupts */
-       musb->intrtxe = musb->epmask;
-       musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
-       musb->intrrxe = musb->epmask & 0xfffe;
-       musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
-       musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
-
-       musb_writeb(regs, MUSB_TESTMODE, 0);
-
-       /* put into basic highspeed mode and start session */
-       musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
-                                               | MUSB_POWER_HSENAB
-                                               /* ENSUSPEND wedges tusb */
-                                               /* | MUSB_POWER_ENSUSPEND */
-                                               );
-
-       musb->is_active = 0;
-       devctl = musb_readb(regs, MUSB_DEVCTL);
-       devctl &= ~MUSB_DEVCTL_SESSION;
-
-       /* session started after:
-        * (a) ID-grounded irq, host mode;
-        * (b) vbus present/connect IRQ, peripheral mode;
-        * (c) peripheral initiates, using SRP
-        */
-       if (musb->port_mode != MUSB_PORT_MODE_HOST &&
-           (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
-               musb->is_active = 1;
-       } else {
-               devctl |= MUSB_DEVCTL_SESSION;
-       }
-
-       musb_platform_enable(musb);
-       musb_writeb(regs, MUSB_DEVCTL, devctl);
-}
-
 static void musb_port_suspend(struct musb *musb, bool do_suspend)
 {
        struct usb_otg  *otg = musb->xceiv->otg;
index b2f29c9..02799a5 100644 (file)
@@ -241,7 +241,7 @@ static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend)
 
 /* platform driver interface */
 
-static int __init gpio_vbus_probe(struct platform_device *pdev)
+static int gpio_vbus_probe(struct platform_device *pdev)
 {
        struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
        struct gpio_vbus_data *gpio_vbus;
@@ -349,7 +349,7 @@ err_gpio:
        return err;
 }
 
-static int __exit gpio_vbus_remove(struct platform_device *pdev)
+static int gpio_vbus_remove(struct platform_device *pdev)
 {
        struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
        struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
@@ -398,8 +398,6 @@ static const struct dev_pm_ops gpio_vbus_dev_pm_ops = {
 };
 #endif
 
-/* NOTE:  the gpio-vbus device may *NOT* be hotplugged */
-
 MODULE_ALIAS("platform:gpio-vbus");
 
 static struct platform_driver gpio_vbus_driver = {
@@ -410,10 +408,11 @@ static struct platform_driver gpio_vbus_driver = {
                .pm = &gpio_vbus_dev_pm_ops,
 #endif
        },
-       .remove  = __exit_p(gpio_vbus_remove),
+       .probe          = gpio_vbus_probe,
+       .remove         = gpio_vbus_remove,
 };
 
-module_platform_driver_probe(gpio_vbus_driver, gpio_vbus_probe);
+module_platform_driver(gpio_vbus_driver);
 
 MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver");
 MODULE_AUTHOR("Philipp Zabel");
index fc15694..4e8a040 100644 (file)
@@ -79,7 +79,7 @@ static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate)
                        return &dpll_map[i].params;
        }
 
-       return 0;
+       return NULL;
 }
 
 static int omap_usb3_suspend(struct usb_phy *x, int suspend)
index c454bfa..ddb9c51 100644 (file)
@@ -60,7 +60,7 @@ config USB_SERIAL_SIMPLE
                - Suunto ANT+ USB device.
                - Fundamental Software dongle.
                - HP4x calculators
-               - a number of Motoroloa phones
+               - a number of Motorola phones
                - Siemens USB/MPI adapter.
                - ViVOtech ViVOpay USB device.
                - Infineon Modem Flashloader USB interface
index c45f9c0..b21d553 100644 (file)
@@ -904,6 +904,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
        /* Crucible Devices */
        { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_Z3X_PID) },
        { }                                     /* Terminating entry */
 };
 
index 1b8af46..a7019d1 100644 (file)
  * Manufacturer: Crucible Technologies
  */
 #define FTDI_CT_COMET_PID      0x8e08
+
+/*
+ * Product: Z3X Box
+ * Manufacturer: Smart GSM Team
+ */
+#define FTDI_Z3X_PID           0x0011
index 1cf6f12..acaee06 100644 (file)
@@ -81,6 +81,7 @@ static void option_instat_callback(struct urb *urb);
 
 #define HUAWEI_VENDOR_ID                       0x12D1
 #define HUAWEI_PRODUCT_E173                    0x140C
+#define HUAWEI_PRODUCT_E1750                   0x1406
 #define HUAWEI_PRODUCT_K4505                   0x1464
 #define HUAWEI_PRODUCT_K3765                   0x1465
 #define HUAWEI_PRODUCT_K4605                   0x14C6
@@ -450,6 +451,10 @@ static void option_instat_callback(struct urb *urb);
 #define CHANGHONG_VENDOR_ID                    0x2077
 #define CHANGHONG_PRODUCT_CH690                        0x7001
 
+/* Inovia */
+#define INOVIA_VENDOR_ID                       0x20a6
+#define INOVIA_SEW858                          0x1105
+
 /* some devices interfaces need special handling due to a number of reasons */
 enum option_blacklist_reason {
                OPTION_BLACKLIST_NONE = 0,
@@ -567,6 +572,8 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff),
                .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+       { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff),
+               .driver_info = (kernel_ulong_t) &net_intf2_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
@@ -686,6 +693,222 @@ static const struct usb_device_id option_ids[] = {
        { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7A) },
        { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7B) },
        { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x02) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x03) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x04) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x05) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x06) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x0F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x10) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x12) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x13) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x14) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x15) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x17) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x18) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x19) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x1C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x31) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x32) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x33) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x34) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x35) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x36) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x3F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x48) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x49) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x4C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x61) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x62) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x63) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x64) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x65) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x66) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x6F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x78) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x79) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x03, 0x7C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x02) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x03) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x04) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x05) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x06) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x0F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x10) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x12) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x13) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x14) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x15) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x17) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x18) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x19) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x1C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x31) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x32) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x33) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x34) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x35) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x36) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x3F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x48) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x49) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x4C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x61) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x62) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x63) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x64) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x65) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x66) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x6F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x78) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x79) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x04, 0x7C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x02) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x03) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x04) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x05) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x06) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x0F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x10) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x12) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x13) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x14) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x15) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x17) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x18) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x19) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x1C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x31) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x32) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x33) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x34) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x35) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x36) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x3F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x48) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x49) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x4C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x61) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x62) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x63) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x64) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x65) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x66) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x6F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x78) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x79) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x05, 0x7C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x02) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x03) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x04) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x05) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x06) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x0F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x10) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x12) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x13) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x14) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x15) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x17) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x18) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x19) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x1C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x31) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x32) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x33) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x34) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x35) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x36) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x3F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x48) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x49) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x4C) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x61) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x62) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x63) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x64) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x65) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x66) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6D) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6E) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x6F) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x78) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x79) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7A) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7B) },
+       { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7C) },
 
 
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
@@ -1254,7 +1477,9 @@ static const struct usb_device_id option_ids[] = {
 
        { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) },
        { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) },
-       { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200) },
+       { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200),
+               .driver_info = (kernel_ulong_t)&net_intf6_blacklist
+       },
        { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
        { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/
        { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM600) },
@@ -1342,6 +1567,7 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
        { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
        { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
+       { USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) },
        { } /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, option_ids);
index e7a84f0..1e6de4c 100644 (file)
@@ -4,11 +4,6 @@
  * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
  * Copyright (C) 2003 IBM Corp.
  *
- * Copyright (C) 2009, 2013 Frank Schäfer <fschaefer.oss@googlemail.com>
- *  - fixes, improvements and documentation for the baud rate encoding methods
- * Copyright (C) 2013 Reinhard Max <max@suse.de>
- *  - fixes and improvements for the divisor based baud rate encoding method
- *
  * Original driver for 2.2.x by anonymous
  *
  *     This program is free software; you can redistribute it and/or
@@ -134,17 +129,10 @@ MODULE_DEVICE_TABLE(usb, id_table);
 
 
 enum pl2303_type {
-       type_0,         /* H version ? */
-       type_1,         /* H version ? */
-       HX_TA,          /* HX(A) / X(A) / TA version  */ /* TODO: improve */
-       HXD_EA_RA_SA,   /* HXD / EA / RA / SA version */ /* TODO: improve */
-       TB,             /* TB version */
+       type_0,         /* don't know the difference between type 0 and */
+       type_1,         /* type 1, until someone from prolific tells us... */
+       HX,             /* HX version of the pl2303 chip */
 };
-/*
- * NOTE: don't know the difference between type 0 and type 1,
- * until someone from Prolific tells us...
- * TODO: distinguish between X/HX, TA and HXD, EA, RA, SA variants
- */
 
 struct pl2303_serial_private {
        enum pl2303_type type;
@@ -184,7 +172,6 @@ static int pl2303_startup(struct usb_serial *serial)
 {
        struct pl2303_serial_private *spriv;
        enum pl2303_type type = type_0;
-       char *type_str = "unknown (treating as type_0)";
        unsigned char *buf;
 
        spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
@@ -197,38 +184,15 @@ static int pl2303_startup(struct usb_serial *serial)
                return -ENOMEM;
        }
 
-       if (serial->dev->descriptor.bDeviceClass == 0x02) {
+       if (serial->dev->descriptor.bDeviceClass == 0x02)
                type = type_0;
-               type_str = "type_0";
-       } else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) {
-               /*
-                * NOTE: The bcdDevice version is the only difference between
-                * the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB
-                */
-               if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) {
-                       type = HX_TA;
-                       type_str = "X/HX/TA";
-               } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
-                                                                    == 0x400) {
-                       type = HXD_EA_RA_SA;
-                       type_str = "HXD/EA/RA/SA";
-               } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
-                                                                    == 0x500) {
-                       type = TB;
-                       type_str = "TB";
-               } else {
-                       dev_info(&serial->interface->dev,
-                                          "unknown/unsupported device type\n");
-                       kfree(spriv);
-                       kfree(buf);
-                       return -ENODEV;
-               }
-       } else if (serial->dev->descriptor.bDeviceClass == 0x00
-                  || serial->dev->descriptor.bDeviceClass == 0xFF) {
+       else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
+               type = HX;
+       else if (serial->dev->descriptor.bDeviceClass == 0x00)
                type = type_1;
-               type_str = "type_1";
-       }
-       dev_dbg(&serial->interface->dev, "device type: %s\n", type_str);
+       else if (serial->dev->descriptor.bDeviceClass == 0xFF)
+               type = type_1;
+       dev_dbg(&serial->interface->dev, "device type: %d\n", type);
 
        spriv->type = type;
        usb_set_serial_data(serial, spriv);
@@ -243,10 +207,10 @@ static int pl2303_startup(struct usb_serial *serial)
        pl2303_vendor_read(0x8383, 0, serial, buf);
        pl2303_vendor_write(0, 1, serial);
        pl2303_vendor_write(1, 0, serial);
-       if (type == type_0 || type == type_1)
-               pl2303_vendor_write(2, 0x24, serial);
-       else
+       if (type == HX)
                pl2303_vendor_write(2, 0x44, serial);
+       else
+               pl2303_vendor_write(2, 0x24, serial);
 
        kfree(buf);
        return 0;
@@ -300,170 +264,65 @@ static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
        return retval;
 }
 
-static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
-                                                                     u8 buf[4])
+static void pl2303_encode_baudrate(struct tty_struct *tty,
+                                       struct usb_serial_port *port,
+                                       u8 buf[4])
 {
-       /*
-        * NOTE: Only the values defined in baud_sup are supported !
-        *       => if unsupported values are set, the PL2303 seems to
-        *          use 9600 baud (at least my PL2303X always does)
-        */
        const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600,
-                                4800, 7200, 9600, 14400, 19200, 28800, 38400,
-                                57600, 115200, 230400, 460800, 614400, 921600,
-                                1228800, 2457600, 3000000, 6000000, 12000000 };
+                                4800, 7200, 9600, 14400, 19200, 28800, 38400,
+                                57600, 115200, 230400, 460800, 500000, 614400,
+                                921600, 1228800, 2457600, 3000000, 6000000 };
+
+       struct usb_serial *serial = port->serial;
+       struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+       int baud;
+       int i;
+
        /*
-        * NOTE: With the exception of type_0/1 devices, the following
-        * additional baud rates are supported (tested with HX rev. 3A only):
-        * 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800,
-        * 403200, 806400.      (*: not HX)
-        *
-        * Maximum values: HXD, TB: 12000000; HX, TA: 6000000;
-        *                 type_0+1: 1228800; RA: 921600; SA: 115200
-        *
-        * As long as we are not using this encoding method for anything else
-        * than the type_0+1 and HX chips, there is no point in complicating
-        * the code to support them.
+        * NOTE: Only the values defined in baud_sup are supported!
+        *       => if unsupported values are set, the PL2303 seems to use
+        *          9600 baud (at least my PL2303X always does)
         */
-       int i;
+       baud = tty_get_baud_rate(tty);
+       dev_dbg(&port->dev, "baud requested = %d\n", baud);
+       if (!baud)
+               return;
 
        /* Set baudrate to nearest supported value */
        for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {
                if (baud_sup[i] > baud)
                        break;
        }
+
        if (i == ARRAY_SIZE(baud_sup))
                baud = baud_sup[i - 1];
        else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))
                baud = baud_sup[i - 1];
        else
                baud = baud_sup[i];
-       /* Respect the chip type specific baud rate limits */
-       /*
-        * FIXME: as long as we don't know how to distinguish between the
-        * HXD, EA, RA, and SA chip variants, allow the max. value of 12M.
-        */
-       if (type == HX_TA)
-               baud = min_t(int, baud, 6000000);
-       else if (type == type_0 || type == type_1)
-               baud = min_t(int, baud, 1228800);
-       /* Direct (standard) baud rate encoding method */
-       put_unaligned_le32(baud, buf);
 
-       return baud;
-}
-
-static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type,
-                                                                     u8 buf[4])
-{
-       /*
-        * Divisor based baud rate encoding method
-        *
-        * NOTE: it's not clear if the type_0/1 chips support this method
-        *
-        * divisor = 12MHz * 32 / baudrate = 2^A * B
-        *
-        * with
-        *
-        * A = buf[1] & 0x0e
-        * B = buf[0]  +  (buf[1] & 0x01) << 8
-        *
-        * Special cases:
-        * => 8 < B < 16: device seems to work not properly
-        * => B <= 8: device uses the max. value B = 512 instead
-        */
-       unsigned int A, B;
+       /* type_0, type_1 only support up to 1228800 baud */
+       if (spriv->type != HX)
+               baud = min_t(int, baud, 1228800);
 
-       /*
-        * NOTE: The Windows driver allows maximum baud rates of 110% of the
-        * specified maximium value.
-        * Quick tests with early (2004) HX (rev. A) chips suggest, that even
-        * higher baud rates (up to the maximum of 24M baud !) are working fine,
-        * but that should really be tested carefully in "real life" scenarios
-        * before removing the upper limit completely.
-        * Baud rates smaller than the specified 75 baud are definitely working
-        * fine.
-        */
-       if (type == type_0 || type == type_1)
-               baud = min_t(int, baud, 1228800 * 1.1);
-       else if (type == HX_TA)
-               baud = min_t(int, baud, 6000000 * 1.1);
-       else if (type == HXD_EA_RA_SA)
-               /* HXD, EA: 12Mbps; RA: 1Mbps; SA: 115200 bps */
-               /*
-                * FIXME: as long as we don't know how to distinguish between
-                * these chip variants, allow the max. of these values
-                */
-               baud = min_t(int, baud, 12000000 * 1.1);
-       else if (type == TB)
-               baud = min_t(int, baud, 12000000 * 1.1);
-       /* Determine factors A and B */
-       A = 0;
-       B = 12000000 * 32 / baud;  /* 12MHz */
-       B <<= 1; /* Add one bit for rounding */
-       while (B > (512 << 1) && A <= 14) {
-               A += 2;
-               B >>= 2;
-       }
-       if (A > 14) { /* max. divisor = min. baudrate reached */
-               A = 14;
-               B = 512;
-               /* => ~45.78 baud */
+       if (baud <= 115200) {
+               put_unaligned_le32(baud, buf);
        } else {
-               B = (B + 1) >> 1; /* Round the last bit */
-       }
-       /* Handle special cases */
-       if (B == 512)
-               B = 0; /* also: 1 to 8 */
-       else if (B < 16)
                /*
-                * NOTE: With the current algorithm this happens
-                * only for A=0 and means that the min. divisor
-                * (respectively: the max. baudrate) is reached.
+                * Apparently the formula for higher speeds is:
+                * baudrate = 12M * 32 / (2^buf[1]) / buf[0]
                 */
-               B = 16;         /* => 24 MBaud */
-       /* Encode the baud rate */
-       buf[3] = 0x80;     /* Select divisor encoding method */
-       buf[2] = 0;
-       buf[1] = (A & 0x0e);            /* A */
-       buf[1] |= ((B & 0x100) >> 8);   /* MSB of B */
-       buf[0] = B & 0xff;              /* 8 LSBs of B */
-       /* Calculate the actual/resulting baud rate */
-       if (B <= 8)
-               B = 512;
-       baud = 12000000 * 32 / ((1 << A) * B);
-
-       return baud;
-}
-
-static void pl2303_encode_baudrate(struct tty_struct *tty,
-                                       struct usb_serial_port *port,
-                                       enum pl2303_type type,
-                                       u8 buf[4])
-{
-       int baud;
+               unsigned tmp = 12000000 * 32 / baud;
+               buf[3] = 0x80;
+               buf[2] = 0;
+               buf[1] = (tmp >= 256);
+               while (tmp >= 256) {
+                       tmp >>= 2;
+                       buf[1] <<= 1;
+               }
+               buf[0] = tmp;
+       }
 
-       baud = tty_get_baud_rate(tty);
-       dev_dbg(&port->dev, "baud requested = %d\n", baud);
-       if (!baud)
-               return;
-       /*
-        * There are two methods for setting/encoding the baud rate
-        * 1) Direct method: encodes the baud rate value directly
-        *    => supported by all chip types
-        * 2) Divisor based method: encodes a divisor to a base value (12MHz*32)
-        *    => supported by HX chips (and likely not by type_0/1 chips)
-        *
-        * NOTE: Although the divisor based baud rate encoding method is much
-        * more flexible, some of the standard baud rate values can not be
-        * realized exactly. But the difference is very small (max. 0.2%) and
-        * the device likely uses the same baud rate generator for both methods
-        * so that there is likley no difference.
-        */
-       if (type == type_0 || type == type_1)
-               baud = pl2303_baudrate_encode_direct(baud, type, buf);
-       else
-               baud = pl2303_baudrate_encode_divisor(baud, type, buf);
        /* Save resulting baud rate */
        tty_encode_baud_rate(tty, baud, baud);
        dev_dbg(&port->dev, "baud set = %d\n", baud);
@@ -520,8 +379,8 @@ static void pl2303_set_termios(struct tty_struct *tty,
                dev_dbg(&port->dev, "data bits = %d\n", buf[6]);
        }
 
-       /* For reference:   buf[0]:buf[3] baud rate value */
-       pl2303_encode_baudrate(tty, port, spriv->type, buf);
+       /* For reference buf[0]:buf[3] baud rate value */
+       pl2303_encode_baudrate(tty, port, &buf[0]);
 
        /* For reference buf[4]=0 is 1 stop bits */
        /* For reference buf[4]=1 is 1.5 stop bits */
@@ -598,10 +457,10 @@ static void pl2303_set_termios(struct tty_struct *tty,
        dev_dbg(&port->dev, "0xa1:0x21:0:0  %d - %7ph\n", i, buf);
 
        if (C_CRTSCTS(tty)) {
-               if (spriv->type == type_0 || spriv->type == type_1)
-                       pl2303_vendor_write(0x0, 0x41, serial);
-               else
+               if (spriv->type == HX)
                        pl2303_vendor_write(0x0, 0x61, serial);
+               else
+                       pl2303_vendor_write(0x0, 0x41, serial);
        } else {
                pl2303_vendor_write(0x0, 0x0, serial);
        }
@@ -638,7 +497,7 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
        struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
        int result;
 
-       if (spriv->type == type_0 || spriv->type == type_1) {
+       if (spriv->type != HX) {
                usb_clear_halt(serial->dev, port->write_urb->pipe);
                usb_clear_halt(serial->dev, port->read_urb->pipe);
        } else {
index 760b785..c9a3569 100644 (file)
@@ -190,6 +190,7 @@ static struct usb_device_id ti_id_table_combined[] = {
        { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
        { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
        { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
+       { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
        { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
        { }     /* terminator */
 };
index 94d75ed..18509e6 100644 (file)
@@ -211,8 +211,11 @@ static int slave_configure(struct scsi_device *sdev)
                /*
                 * Many devices do not respond properly to READ_CAPACITY_16.
                 * Tell the SCSI layer to try READ_CAPACITY_10 first.
+                * However some USB 3.0 drive enclosures return capacity
+                * modulo 2TB. Those must use READ_CAPACITY_16
                 */
-               sdev->try_rc_10_first = 1;
+               if (!(us->fflags & US_FL_NEEDS_CAP16))
+                       sdev->try_rc_10_first = 1;
 
                /* assume SPC3 or latter devices support sense size > 18 */
                if (sdev->scsi_level > SCSI_SPC_2)
index c015f2c..de32cfa 100644 (file)
@@ -1925,6 +1925,13 @@ UNUSUAL_DEV(  0x1652, 0x6600, 0x0201, 0x0201,
                USB_SC_DEVICE, USB_PR_DEVICE, NULL,
                US_FL_IGNORE_RESIDUE ),
 
+/* Reported by Oliver Neukum <oneukum@suse.com> */
+UNUSUAL_DEV(  0x174c, 0x55aa, 0x0100, 0x0100,
+               "ASMedia",
+               "AS2105",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_NEEDS_CAP16),
+
 /* Reported by Jesse Feddema <jdfeddema@gmail.com> */
 UNUSUAL_DEV(  0x177f, 0x0400, 0x0000, 0x0000,
                "Yarvik",
index a9807de..4fb7a8f 100644 (file)
@@ -545,6 +545,8 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
        long npage;
        int ret = 0, prot = 0;
        uint64_t mask;
+       struct vfio_dma *dma = NULL;
+       unsigned long pfn;
 
        end = map->iova + map->size;
 
@@ -587,8 +589,6 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
        }
 
        for (iova = map->iova; iova < end; iova += size, vaddr += size) {
-               struct vfio_dma *dma = NULL;
-               unsigned long pfn;
                long i;
 
                /* Pin a contiguous chunk of memory */
@@ -597,16 +597,15 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
                if (npage <= 0) {
                        WARN_ON(!npage);
                        ret = (int)npage;
-                       break;
+                       goto out;
                }
 
                /* Verify pages are not already mapped */
                for (i = 0; i < npage; i++) {
                        if (iommu_iova_to_phys(iommu->domain,
                                               iova + (i << PAGE_SHIFT))) {
-                               vfio_unpin_pages(pfn, npage, prot, true);
                                ret = -EBUSY;
-                               break;
+                               goto out_unpin;
                        }
                }
 
@@ -616,8 +615,7 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
                if (ret) {
                        if (ret != -EBUSY ||
                            map_try_harder(iommu, iova, pfn, npage, prot)) {
-                               vfio_unpin_pages(pfn, npage, prot, true);
-                               break;
+                               goto out_unpin;
                        }
                }
 
@@ -672,9 +670,8 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
                        dma = kzalloc(sizeof(*dma), GFP_KERNEL);
                        if (!dma) {
                                iommu_unmap(iommu->domain, iova, size);
-                               vfio_unpin_pages(pfn, npage, prot, true);
                                ret = -ENOMEM;
-                               break;
+                               goto out_unpin;
                        }
 
                        dma->size = size;
@@ -685,16 +682,21 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
                }
        }
 
-       if (ret) {
-               struct vfio_dma *tmp;
-               iova = map->iova;
-               size = map->size;
-               while ((tmp = vfio_find_dma(iommu, iova, size))) {
-                       int r = vfio_remove_dma_overlap(iommu, iova,
-                                                       &size, tmp);
-                       if (WARN_ON(r || !size))
-                               break;
-               }
+       WARN_ON(ret);
+       mutex_unlock(&iommu->lock);
+       return ret;
+
+out_unpin:
+       vfio_unpin_pages(pfn, npage, prot, true);
+
+out:
+       iova = map->iova;
+       size = map->size;
+       while ((dma = vfio_find_dma(iommu, iova, size))) {
+               int r = vfio_remove_dma_overlap(iommu, iova,
+                                               &size, dma);
+               if (WARN_ON(r || !size))
+                       break;
        }
 
        mutex_unlock(&iommu->lock);
index 4b79a1f..e663921 100644 (file)
@@ -461,7 +461,7 @@ static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
                u32 i;
                for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
                        put_page(sg_page(&tv_cmd->tvc_sgl[i]));
-        }
+       }
 
        tcm_vhost_put_inflight(tv_cmd->inflight);
        percpu_ida_free(&se_sess->sess_tag_pool, se_cmd->map_tag);
@@ -728,7 +728,12 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq,
        }
        se_sess = tv_nexus->tvn_se_sess;
 
-       tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_KERNEL);
+       tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_ATOMIC);
+       if (tag < 0) {
+               pr_err("Unable to obtain tag for tcm_vhost_cmd\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
        cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag];
        sg = cmd->tvc_sgl;
        pages = cmd->tvc_upages;
@@ -1051,7 +1056,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                if (data_direction != DMA_NONE) {
                        ret = vhost_scsi_map_iov_to_sgl(cmd,
                                        &vq->iov[data_first], data_num,
-                                       data_direction == DMA_TO_DEVICE);
+                                       data_direction == DMA_FROM_DEVICE);
                        if (unlikely(ret)) {
                                vq_err(vq, "Failed to map iov to sgl\n");
                                goto err_free;
@@ -1373,21 +1378,30 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
        return 0;
 }
 
+static void vhost_scsi_free(struct vhost_scsi *vs)
+{
+       if (is_vmalloc_addr(vs))
+               vfree(vs);
+       else
+               kfree(vs);
+}
+
 static int vhost_scsi_open(struct inode *inode, struct file *f)
 {
        struct vhost_scsi *vs;
        struct vhost_virtqueue **vqs;
-       int r, i;
+       int r = -ENOMEM, i;
 
-       vs = kzalloc(sizeof(*vs), GFP_KERNEL);
-       if (!vs)
-               return -ENOMEM;
+       vs = kzalloc(sizeof(*vs), GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+       if (!vs) {
+               vs = vzalloc(sizeof(*vs));
+               if (!vs)
+                       goto err_vs;
+       }
 
        vqs = kmalloc(VHOST_SCSI_MAX_VQ * sizeof(*vqs), GFP_KERNEL);
-       if (!vqs) {
-               kfree(vs);
-               return -ENOMEM;
-       }
+       if (!vqs)
+               goto err_vqs;
 
        vhost_work_init(&vs->vs_completion_work, vhost_scsi_complete_cmd_work);
        vhost_work_init(&vs->vs_event_work, tcm_vhost_evt_work);
@@ -1407,14 +1421,18 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
 
        tcm_vhost_init_inflight(vs, NULL);
 
-       if (r < 0) {
-               kfree(vqs);
-               kfree(vs);
-               return r;
-       }
+       if (r < 0)
+               goto err_init;
 
        f->private_data = vs;
        return 0;
+
+err_init:
+       kfree(vqs);
+err_vqs:
+       vhost_scsi_free(vs);
+err_vs:
+       return r;
 }
 
 static int vhost_scsi_release(struct inode *inode, struct file *f)
@@ -1431,7 +1449,7 @@ static int vhost_scsi_release(struct inode *inode, struct file *f)
        /* Jobs can re-queue themselves in evt kick handler. Do extra flush. */
        vhost_scsi_flush(vs);
        kfree(vs->dev.vqs);
-       kfree(vs);
+       vhost_scsi_free(vs);
        return 0;
 }
 
index 9a9502a..69068e0 100644 (file)
@@ -161,9 +161,11 @@ void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work)
        if (list_empty(&work->node)) {
                list_add_tail(&work->node, &dev->work_list);
                work->queue_seq++;
+               spin_unlock_irqrestore(&dev->work_lock, flags);
                wake_up_process(dev->worker);
+       } else {
+               spin_unlock_irqrestore(&dev->work_lock, flags);
        }
-       spin_unlock_irqrestore(&dev->work_lock, flags);
 }
 EXPORT_SYMBOL_GPL(vhost_work_queue);
 
index a54ccdc..22ad852 100644 (file)
@@ -361,37 +361,13 @@ void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
 int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 {
        struct au1100fb_device *fbdev;
-       unsigned int len;
-       unsigned long start=0, off;
 
        fbdev = to_au1100fb_device(fbi);
 
-       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
-               return -EINVAL;
-       }
-
-       start = fbdev->fb_phys & PAGE_MASK;
-       len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
-       off = vma->vm_pgoff << PAGE_SHIFT;
-
-       if ((vma->vm_end - vma->vm_start + off) > len) {
-               return -EINVAL;
-       }
-
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
 
-       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-                               vma->vm_end - vma->vm_start,
-                               vma->vm_page_prot)) {
-               return -EAGAIN;
-       }
-
-       return 0;
+       return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
 }
 
 static struct fb_ops au1100fb_ops =
index 301224e..1d02897 100644 (file)
@@ -1233,34 +1233,13 @@ static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi)
  * method mainly to allow the use of the TLB streaming flag (CCA=6)
  */
 static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
-
 {
-       unsigned int len;
-       unsigned long start=0, off;
        struct au1200fb_device *fbdev = info->par;
 
-       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
-               return -EINVAL;
-       }
-
-       start = fbdev->fb_phys & PAGE_MASK;
-       len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
-       off = vma->vm_pgoff << PAGE_SHIFT;
-
-       if ((vma->vm_end - vma->vm_start + off) > len) {
-               return -EINVAL;
-       }
-
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */
 
-       return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-                                 vma->vm_end - vma->vm_start,
-                                 vma->vm_page_prot);
+       return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
 }
 
 static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata)
index 75dca19..6ac7552 100644 (file)
@@ -514,7 +514,7 @@ static int mmphw_probe(struct platform_device *pdev)
        if (IS_ERR(ctrl->clk)) {
                dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
                ret = -ENOENT;
-               goto failed_get_clk;
+               goto failed;
        }
        clk_prepare_enable(ctrl->clk);
 
@@ -551,21 +551,8 @@ failed_path_init:
                path_deinit(path_plat);
        }
 
-       if (ctrl->clk) {
-               devm_clk_put(ctrl->dev, ctrl->clk);
-               clk_disable_unprepare(ctrl->clk);
-       }
-failed_get_clk:
-       devm_free_irq(ctrl->dev, ctrl->irq, ctrl);
+       clk_disable_unprepare(ctrl->clk);
 failed:
-       if (ctrl) {
-               if (ctrl->reg_base)
-                       devm_iounmap(ctrl->dev, ctrl->reg_base);
-               devm_release_mem_region(ctrl->dev, res->start,
-                               resource_size(res));
-               devm_kfree(ctrl->dev, ctrl);
-       }
-
        dev_err(&pdev->dev, "device init failed\n");
 
        return ret;
index d250ed0..27197a8 100644 (file)
@@ -620,6 +620,7 @@ static int mxsfb_restore_mode(struct mxsfb_info *host)
                break;
        case 3:
                bits_per_pixel = 32;
+               break;
        case 1:
        default:
                return -EINVAL;
index 7ef079c..c172a52 100644 (file)
@@ -2075,6 +2075,7 @@ static int neofb_probe(struct pci_dev *dev, const struct pci_device_id *id)
        if (!fb_find_mode(&info->var, info, mode_option, NULL, 0,
                        info->monspecs.modedb, 16)) {
                printk(KERN_ERR "neofb: Unable to find usable video mode.\n");
+               err = -EINVAL;
                goto err_map_video;
        }
 
@@ -2097,7 +2098,8 @@ static int neofb_probe(struct pci_dev *dev, const struct pci_device_id *id)
               info->fix.smem_len >> 10, info->var.xres,
               info->var.yres, h_sync / 1000, h_sync % 1000, v_sync);
 
-       if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
+       err = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (err < 0)
                goto err_map_video;
 
        err = register_framebuffer(info);
index 171821d..ba5b40f 100644 (file)
@@ -120,7 +120,7 @@ int of_get_display_timing(struct device_node *np, const char *name,
                return -EINVAL;
        }
 
-       timing_np = of_find_node_by_name(np, name);
+       timing_np = of_get_child_by_name(np, name);
        if (!timing_np) {
                pr_err("%s: could not find node '%s'\n",
                        of_node_full_name(np), name);
@@ -143,11 +143,11 @@ struct display_timings *of_get_display_timings(struct device_node *np)
        struct display_timings *disp;
 
        if (!np) {
-               pr_err("%s: no devicenode given\n", of_node_full_name(np));
+               pr_err("%s: no device node given\n", of_node_full_name(np));
                return NULL;
        }
 
-       timings_np = of_find_node_by_name(np, "display-timings");
+       timings_np = of_get_child_by_name(np, "display-timings");
        if (!timings_np) {
                pr_err("%s: could not find display-timings node\n",
                        of_node_full_name(np));
index 6c90885..10b25e7 100644 (file)
@@ -35,6 +35,7 @@ config DISPLAY_PANEL_DPI
 
 config DISPLAY_PANEL_DSI_CM
        tristate "Generic DSI Command Mode Panel"
+       depends on BACKLIGHT_CLASS_DEVICE
        help
          Driver for generic DSI command mode panels.
 
index 1b60698..ccd9073 100644 (file)
@@ -191,7 +191,7 @@ static int tvc_probe_pdata(struct platform_device *pdev)
        in = omap_dss_find_output(pdata->source);
        if (in == NULL) {
                dev_err(&pdev->dev, "Failed to find video source\n");
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
 
        ddata->in = in;
index bc5f8ce..63d88ee 100644 (file)
@@ -263,7 +263,7 @@ static int dvic_probe_pdata(struct platform_device *pdev)
        in = omap_dss_find_output(pdata->source);
        if (in == NULL) {
                dev_err(&pdev->dev, "Failed to find video source\n");
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
 
        ddata->in = in;
index c582671..9abe2c0 100644 (file)
@@ -290,7 +290,7 @@ static int hdmic_probe_pdata(struct platform_device *pdev)
        in = omap_dss_find_output(pdata->source);
        if (in == NULL) {
                dev_err(&pdev->dev, "Failed to find video source\n");
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
 
        ddata->in = in;
index 02a7340..4779750 100644 (file)
@@ -3691,6 +3691,7 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
        }
 
        pm_runtime_enable(&pdev->dev);
+       pm_runtime_irq_safe(&pdev->dev);
 
        r = dispc_runtime_get();
        if (r)
index 47ca86c..d838ba8 100644 (file)
@@ -1336,14 +1336,7 @@ static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                        (info->var.bits_per_pixel * info->var.xres_virtual);
        if (info->var.yres_virtual < info->var.yres) {
                dev_err(info->device, "virtual vertical size smaller than real\n");
-               goto err_find_mode;
-       }
-
-       /* maximize virtual vertical size for fast scrolling */
-       info->var.yres_virtual = info->fix.smem_len * 8 /
-                       (info->var.bits_per_pixel * info->var.xres_virtual);
-       if (info->var.yres_virtual < info->var.yres) {
-               dev_err(info->device, "virtual vertical size smaller than real\n");
+               rc = -EINVAL;
                goto err_find_mode;
        }
 
index c7c64f1..fa932c2 100644 (file)
@@ -613,6 +613,9 @@ static int w1_bus_notify(struct notifier_block *nb, unsigned long action,
        sl = dev_to_w1_slave(dev);
        fops = sl->family->fops;
 
+       if (!fops)
+               return 0;
+
        switch (action) {
        case BUS_NOTIFY_ADD_DEVICE:
                /* if the family driver needs to initialize something... */
@@ -713,7 +716,10 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
        atomic_set(&sl->refcnt, 0);
        init_completion(&sl->released);
 
+       /* slave modules need to be loaded in a context with unlocked mutex */
+       mutex_unlock(&dev->mutex);
        request_module("w1-family-0x%0x", rn->family);
+       mutex_lock(&dev->mutex);
 
        spin_lock(&w1_flock);
        f = w1_family_registered(rn->family);
index 5be5e3d..19f3c3f 100644 (file)
@@ -802,6 +802,12 @@ static int hpwdt_init_one(struct pci_dev *dev,
                return -ENODEV;
        }
 
+       /*
+        * Ignore all auxilary iLO devices with the following PCI ID
+        */
+       if (dev->subsystem_device == 0x1979)
+               return -ENODEV;
+
        if (pci_enable_device(dev)) {
                dev_warn(&dev->dev,
                        "Not possible to enable PCI Device: 0x%x:0x%x.\n",
index 491419e..5c3d4df 100644 (file)
@@ -35,7 +35,7 @@
 #define KEMPLD_WDT_STAGE_TIMEOUT(x)    (0x1b + (x) * 4)
 #define KEMPLD_WDT_STAGE_CFG(x)                (0x18 + (x))
 #define STAGE_CFG_GET_PRESCALER(x)     (((x) & 0x30) >> 4)
-#define STAGE_CFG_SET_PRESCALER(x)     (((x) & 0x30) << 4)
+#define STAGE_CFG_SET_PRESCALER(x)     (((x) & 0x3) << 4)
 #define STAGE_CFG_PRESCALER_MASK       0x30
 #define STAGE_CFG_ACTION_MASK          0x7
 #define STAGE_CFG_ASSERT               (1 << 3)
index 1f94b42..f6caa77 100644 (file)
@@ -146,7 +146,7 @@ static const struct watchdog_ops sunxi_wdt_ops = {
        .set_timeout    = sunxi_wdt_set_timeout,
 };
 
-static int __init sunxi_wdt_probe(struct platform_device *pdev)
+static int sunxi_wdt_probe(struct platform_device *pdev)
 {
        struct sunxi_wdt_dev *sunxi_wdt;
        struct resource *res;
@@ -187,7 +187,7 @@ static int __init sunxi_wdt_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int __exit sunxi_wdt_remove(struct platform_device *pdev)
+static int sunxi_wdt_remove(struct platform_device *pdev)
 {
        struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev);
 
index 42913f1..c9b0c62 100644 (file)
@@ -310,7 +310,8 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
 
        case WDIOC_GETSTATUS:
        case WDIOC_GETBOOTSTATUS:
-               return put_user(0, p);
+               error = put_user(0, p);
+               break;
 
        case WDIOC_KEEPALIVE:
                ts72xx_wdt_kick(wdt);
index a50c6e3..b232908 100644 (file)
@@ -398,8 +398,6 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
        if (nr_pages > ARRAY_SIZE(frame_list))
                nr_pages = ARRAY_SIZE(frame_list);
 
-       scratch_page = get_balloon_scratch_page();
-
        for (i = 0; i < nr_pages; i++) {
                page = alloc_page(gfp);
                if (page == NULL) {
@@ -413,6 +411,12 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
 
                scrub_page(page);
 
+               /*
+                * Ballooned out frames are effectively replaced with
+                * a scratch frame.  Ensure direct mappings and the
+                * p2m are consistent.
+                */
+               scratch_page = get_balloon_scratch_page();
 #ifdef CONFIG_XEN_HAVE_PVMMU
                if (xen_pv_domain() && !PageHighMem(page)) {
                        ret = HYPERVISOR_update_va_mapping(
@@ -422,24 +426,19 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
                        BUG_ON(ret);
                }
 #endif
-       }
-
-       /* Ensure that ballooned highmem pages don't have kmaps. */
-       kmap_flush_unused();
-       flush_tlb_all();
-
-       /* No more mappings: invalidate P2M and add to balloon. */
-       for (i = 0; i < nr_pages; i++) {
-               pfn = mfn_to_pfn(frame_list[i]);
                if (!xen_feature(XENFEAT_auto_translated_physmap)) {
                        unsigned long p;
                        p = page_to_pfn(scratch_page);
                        __set_phys_to_machine(pfn, pfn_to_mfn(p));
                }
+               put_balloon_scratch_page();
+
                balloon_append(pfn_to_page(pfn));
        }
 
-       put_balloon_scratch_page();
+       /* Ensure that ballooned highmem pages don't have kmaps. */
+       kmap_flush_unused();
+       flush_tlb_all();
 
        set_xen_guest_handle(reservation.extent_start, frame_list);
        reservation.nr_extents   = nr_pages;
index 646337d..5293003 100644 (file)
@@ -600,9 +600,6 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
 
        /* lock down the parent dentry so we can peer at it */
        parent = dget_parent(dentry);
-       if (!parent->d_inode)
-               goto out_bad;
-
        dir = AFS_FS_I(parent->d_inode);
 
        /* validate the parent directory */
index 6b868f0..067e3d3 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -167,10 +167,25 @@ static int __init aio_setup(void)
 }
 __initcall(aio_setup);
 
+static void put_aio_ring_file(struct kioctx *ctx)
+{
+       struct file *aio_ring_file = ctx->aio_ring_file;
+       if (aio_ring_file) {
+               truncate_setsize(aio_ring_file->f_inode, 0);
+
+               /* Prevent further access to the kioctx from migratepages */
+               spin_lock(&aio_ring_file->f_inode->i_mapping->private_lock);
+               aio_ring_file->f_inode->i_mapping->private_data = NULL;
+               ctx->aio_ring_file = NULL;
+               spin_unlock(&aio_ring_file->f_inode->i_mapping->private_lock);
+
+               fput(aio_ring_file);
+       }
+}
+
 static void aio_free_ring(struct kioctx *ctx)
 {
        int i;
-       struct file *aio_ring_file = ctx->aio_ring_file;
 
        for (i = 0; i < ctx->nr_pages; i++) {
                pr_debug("pid(%d) [%d] page->count=%d\n", current->pid, i,
@@ -178,14 +193,10 @@ static void aio_free_ring(struct kioctx *ctx)
                put_page(ctx->ring_pages[i]);
        }
 
+       put_aio_ring_file(ctx);
+
        if (ctx->ring_pages && ctx->ring_pages != ctx->internal_pages)
                kfree(ctx->ring_pages);
-
-       if (aio_ring_file) {
-               truncate_setsize(aio_ring_file->f_inode, 0);
-               fput(aio_ring_file);
-               ctx->aio_ring_file = NULL;
-       }
 }
 
 static int aio_ring_mmap(struct file *file, struct vm_area_struct *vma)
@@ -207,9 +218,8 @@ static int aio_set_page_dirty(struct page *page)
 static int aio_migratepage(struct address_space *mapping, struct page *new,
                        struct page *old, enum migrate_mode mode)
 {
-       struct kioctx *ctx = mapping->private_data;
+       struct kioctx *ctx;
        unsigned long flags;
-       unsigned idx = old->index;
        int rc;
 
        /* Writeback must be complete */
@@ -224,10 +234,23 @@ static int aio_migratepage(struct address_space *mapping, struct page *new,
 
        get_page(new);
 
-       spin_lock_irqsave(&ctx->completion_lock, flags);
-       migrate_page_copy(new, old);
-       ctx->ring_pages[idx] = new;
-       spin_unlock_irqrestore(&ctx->completion_lock, flags);
+       /* We can potentially race against kioctx teardown here.  Use the
+        * address_space's private data lock to protect the mapping's
+        * private_data.
+        */
+       spin_lock(&mapping->private_lock);
+       ctx = mapping->private_data;
+       if (ctx) {
+               pgoff_t idx;
+               spin_lock_irqsave(&ctx->completion_lock, flags);
+               migrate_page_copy(new, old);
+               idx = old->index;
+               if (idx < (pgoff_t)ctx->nr_pages)
+                       ctx->ring_pages[idx] = new;
+               spin_unlock_irqrestore(&ctx->completion_lock, flags);
+       } else
+               rc = -EBUSY;
+       spin_unlock(&mapping->private_lock);
 
        return rc;
 }
@@ -617,8 +640,7 @@ out_freepcpu:
 out_freeref:
        free_percpu(ctx->users.pcpu_count);
 out_freectx:
-       if (ctx->aio_ring_file)
-               fput(ctx->aio_ring_file);
+       put_aio_ring_file(ctx);
        kmem_cache_free(kioctx_cachep, ctx);
        pr_debug("error allocating ioctx %d\n", err);
        return ERR_PTR(err);
index 100edcc..4c94a79 100644 (file)
@@ -1413,7 +1413,7 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata,
  *   long file_ofs
  * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
  */
-static void fill_files_note(struct memelfnote *note)
+static int fill_files_note(struct memelfnote *note)
 {
        struct vm_area_struct *vma;
        unsigned count, size, names_ofs, remaining, n;
@@ -1428,11 +1428,11 @@ static void fill_files_note(struct memelfnote *note)
        names_ofs = (2 + 3 * count) * sizeof(data[0]);
  alloc:
        if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */
-               goto err;
+               return -EINVAL;
        size = round_up(size, PAGE_SIZE);
        data = vmalloc(size);
        if (!data)
-               goto err;
+               return -ENOMEM;
 
        start_end_ofs = data + 2;
        name_base = name_curpos = ((char *)data) + names_ofs;
@@ -1485,7 +1485,7 @@ static void fill_files_note(struct memelfnote *note)
 
        size = name_curpos - (char *)data;
        fill_note(note, "CORE", NT_FILE, size, data);
err: ;
      return 0;
 }
 
 #ifdef CORE_DUMP_USE_REGSET
@@ -1686,8 +1686,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
        fill_auxv_note(&info->auxv, current->mm);
        info->size += notesize(&info->auxv);
 
-       fill_files_note(&info->files);
-       info->size += notesize(&info->files);
+       if (fill_files_note(&info->files) == 0)
+               info->size += notesize(&info->files);
 
        return 1;
 }
@@ -1719,7 +1719,8 @@ static int write_note_info(struct elf_note_info *info,
                        return 0;
                if (first && !writenote(&info->auxv, file, foffset))
                        return 0;
-               if (first && !writenote(&info->files, file, foffset))
+               if (first && info->files.data &&
+                               !writenote(&info->files, file, foffset))
                        return 0;
 
                for (i = 1; i < info->thread_notes; ++i)
@@ -1806,6 +1807,7 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
 
 struct elf_note_info {
        struct memelfnote *notes;
+       struct memelfnote *notes_files;
        struct elf_prstatus *prstatus;  /* NT_PRSTATUS */
        struct elf_prpsinfo *psinfo;    /* NT_PRPSINFO */
        struct list_head thread_list;
@@ -1896,9 +1898,12 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
 
        fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo);
        fill_auxv_note(info->notes + 3, current->mm);
-       fill_files_note(info->notes + 4);
+       info->numnote = 4;
 
-       info->numnote = 5;
+       if (fill_files_note(info->notes + info->numnote) == 0) {
+               info->notes_files = info->notes + info->numnote;
+               info->numnote++;
+       }
 
        /* Try to dump the FPU. */
        info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs,
@@ -1960,8 +1965,9 @@ static void free_note_info(struct elf_note_info *info)
                kfree(list_entry(tmp, struct elf_thread_status, list));
        }
 
-       /* Free data allocated by fill_files_note(): */
-       vfree(info->notes[4].data);
+       /* Free data possibly allocated by fill_files_note(): */
+       if (info->notes_files)
+               vfree(info->notes_files->data);
 
        kfree(info->prstatus);
        kfree(info->psinfo);
@@ -2044,7 +2050,7 @@ static int elf_core_dump(struct coredump_params *cprm)
        struct vm_area_struct *vma, *gate_vma;
        struct elfhdr *elf = NULL;
        loff_t offset = 0, dataoff, foffset;
-       struct elf_note_info info;
+       struct elf_note_info info = { };
        struct elf_phdr *phdr4note = NULL;
        struct elf_shdr *shdr4extnum = NULL;
        Elf_Half e_phnum;
index 6025084..fc60b31 100644 (file)
@@ -735,7 +735,7 @@ void bioset_integrity_free(struct bio_set *bs)
                mempool_destroy(bs->bio_integrity_pool);
 
        if (bs->bvec_integrity_pool)
-               mempool_destroy(bs->bio_integrity_pool);
+               mempool_destroy(bs->bvec_integrity_pool);
 }
 EXPORT_SYMBOL(bioset_integrity_free);
 
index b3b20ed..ea5035d 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -917,8 +917,8 @@ void bio_copy_data(struct bio *dst, struct bio *src)
                src_p = kmap_atomic(src_bv->bv_page);
                dst_p = kmap_atomic(dst_bv->bv_page);
 
-               memcpy(dst_p + dst_bv->bv_offset,
-                      src_p + src_bv->bv_offset,
+               memcpy(dst_p + dst_offset,
+                      src_p + src_offset,
                       bytes);
 
                kunmap_atomic(dst_p);
index 58b7d14..08cc08f 100644 (file)
@@ -107,7 +107,8 @@ static void check_idle_worker(struct btrfs_worker_thread *worker)
                worker->idle = 1;
 
                /* the list may be empty if the worker is just starting */
-               if (!list_empty(&worker->worker_list)) {
+               if (!list_empty(&worker->worker_list) &&
+                   !worker->workers->stopping) {
                        list_move(&worker->worker_list,
                                 &worker->workers->idle_list);
                }
@@ -127,7 +128,8 @@ static void check_busy_worker(struct btrfs_worker_thread *worker)
                spin_lock_irqsave(&worker->workers->lock, flags);
                worker->idle = 0;
 
-               if (!list_empty(&worker->worker_list)) {
+               if (!list_empty(&worker->worker_list) &&
+                   !worker->workers->stopping) {
                        list_move_tail(&worker->worker_list,
                                      &worker->workers->worker_list);
                }
@@ -412,6 +414,7 @@ void btrfs_stop_workers(struct btrfs_workers *workers)
        int can_stop;
 
        spin_lock_irq(&workers->lock);
+       workers->stopping = 1;
        list_splice_init(&workers->idle_list, &workers->worker_list);
        while (!list_empty(&workers->worker_list)) {
                cur = workers->worker_list.next;
@@ -455,6 +458,7 @@ void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
        workers->ordered = 0;
        workers->atomic_start_pending = 0;
        workers->atomic_worker_start = async_helper;
+       workers->stopping = 0;
 }
 
 /*
@@ -480,15 +484,19 @@ static int __btrfs_start_workers(struct btrfs_workers *workers)
        atomic_set(&worker->num_pending, 0);
        atomic_set(&worker->refs, 1);
        worker->workers = workers;
-       worker->task = kthread_run(worker_loop, worker,
-                                  "btrfs-%s-%d", workers->name,
-                                  workers->num_workers + 1);
+       worker->task = kthread_create(worker_loop, worker,
+                                     "btrfs-%s-%d", workers->name,
+                                     workers->num_workers + 1);
        if (IS_ERR(worker->task)) {
                ret = PTR_ERR(worker->task);
-               kfree(worker);
                goto fail;
        }
+
        spin_lock_irq(&workers->lock);
+       if (workers->stopping) {
+               spin_unlock_irq(&workers->lock);
+               goto fail_kthread;
+       }
        list_add_tail(&worker->worker_list, &workers->idle_list);
        worker->idle = 1;
        workers->num_workers++;
@@ -496,8 +504,13 @@ static int __btrfs_start_workers(struct btrfs_workers *workers)
        WARN_ON(workers->num_workers_starting < 0);
        spin_unlock_irq(&workers->lock);
 
+       wake_up_process(worker->task);
        return 0;
+
+fail_kthread:
+       kthread_stop(worker->task);
 fail:
+       kfree(worker);
        spin_lock_irq(&workers->lock);
        workers->num_workers_starting--;
        spin_unlock_irq(&workers->lock);
index 063698b..1f26792 100644 (file)
@@ -107,6 +107,8 @@ struct btrfs_workers {
 
        /* extra name for this worker, used for current->name */
        char *name;
+
+       int stopping;
 };
 
 void btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work);
index d0ae226..71f074e 100644 (file)
@@ -213,7 +213,10 @@ static inline bool btrfs_is_free_space_inode(struct inode *inode)
 static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
 {
        if (BTRFS_I(inode)->logged_trans == generation &&
-           BTRFS_I(inode)->last_sub_trans <= BTRFS_I(inode)->last_log_commit)
+           BTRFS_I(inode)->last_sub_trans <=
+           BTRFS_I(inode)->last_log_commit &&
+           BTRFS_I(inode)->last_sub_trans <=
+           BTRFS_I(inode)->root->last_log_commit)
                return 1;
        return 0;
 }
index 6434672..61b5bcd 100644 (file)
@@ -1005,8 +1005,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                return ret;
        }
 
-       if (root->ref_cows)
-               btrfs_reloc_cow_block(trans, root, buf, cow);
+       if (root->ref_cows) {
+               ret = btrfs_reloc_cow_block(trans, root, buf, cow);
+               if (ret)
+                       return ret;
+       }
 
        if (buf == root->node) {
                WARN_ON(parent && parent != buf);
index 3c1da6f..0506f40 100644 (file)
@@ -1118,15 +1118,6 @@ struct btrfs_space_info {
         */
        struct percpu_counter total_bytes_pinned;
 
-       /*
-        * we bump reservation progress every time we decrement
-        * bytes_reserved.  This way people waiting for reservations
-        * know something good has happened and they can check
-        * for progress.  The number here isn't to be trusted, it
-        * just shows reclaim activity
-        */
-       unsigned long reservation_progress;
-
        unsigned int full:1;    /* indicates that we cannot allocate any more
                                   chunks for this space */
        unsigned int chunk_alloc:1;     /* set if we are allocating a chunk */
@@ -3135,7 +3126,7 @@ static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_root *root,
                                                 unsigned num_items)
 {
        return (root->leafsize + root->nodesize * (BTRFS_MAX_LEVEL - 1)) *
-               3 * num_items;
+               2 * num_items;
 }
 
 /*
@@ -3939,9 +3930,9 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
                            struct btrfs_root *root);
 int btrfs_recover_relocation(struct btrfs_root *root);
 int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
-void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, struct extent_buffer *buf,
-                          struct extent_buffer *cow);
+int btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *root, struct extent_buffer *buf,
+                         struct extent_buffer *cow);
 void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
                              struct btrfs_pending_snapshot *pending,
                              u64 *bytes_to_reserve);
index a644353..9efb94e 100644 (file)
@@ -400,7 +400,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
        args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
        btrfs_dev_replace_unlock(dev_replace);
 
-       btrfs_wait_all_ordered_extents(root->fs_info, 0);
+       btrfs_wait_all_ordered_extents(root->fs_info);
 
        /* force writing the updated state information to disk */
        trans = btrfs_start_transaction(root, 0);
@@ -475,7 +475,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
        }
-       btrfs_wait_all_ordered_extents(root->fs_info, 0);
+       btrfs_wait_all_ordered_extents(root->fs_info);
 
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
@@ -535,10 +535,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list);
 
        btrfs_rm_dev_replace_srcdev(fs_info, src_device);
-       if (src_device->bdev) {
-               /* zero out the old super */
-               btrfs_scratch_superblock(src_device);
-       }
+
        /*
         * this is again a consistent state where no dev_replace procedure
         * is running, the target device is part of the filesystem, the
index 4cbb00a..62176ad 100644 (file)
@@ -157,6 +157,7 @@ static struct btrfs_lockdep_keyset {
        { .id = BTRFS_TREE_LOG_OBJECTID,        .name_stem = "log"      },
        { .id = BTRFS_TREE_RELOC_OBJECTID,      .name_stem = "treloc"   },
        { .id = BTRFS_DATA_RELOC_TREE_OBJECTID, .name_stem = "dreloc"   },
+       { .id = BTRFS_UUID_TREE_OBJECTID,       .name_stem = "uuid"     },
        { .id = 0,                              .name_stem = "tree"     },
 };
 
@@ -1560,8 +1561,9 @@ int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
        return ret;
 }
 
-struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
-                                             struct btrfs_key *location)
+struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info,
+                                    struct btrfs_key *location,
+                                    bool check_ref)
 {
        struct btrfs_root *root;
        int ret;
@@ -1585,7 +1587,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
 again:
        root = btrfs_lookup_fs_root(fs_info, location->objectid);
        if (root) {
-               if (btrfs_root_refs(&root->root_item) == 0)
+               if (check_ref && btrfs_root_refs(&root->root_item) == 0)
                        return ERR_PTR(-ENOENT);
                return root;
        }
@@ -1594,7 +1596,7 @@ again:
        if (IS_ERR(root))
                return root;
 
-       if (btrfs_root_refs(&root->root_item) == 0) {
+       if (check_ref && btrfs_root_refs(&root->root_item) == 0) {
                ret = -ENOENT;
                goto fail;
        }
@@ -3415,6 +3417,7 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors)
        if (total_errors > max_errors) {
                printk(KERN_ERR "btrfs: %d errors while writing supers\n",
                       total_errors);
+               mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
 
                /* FUA is masked off if unsupported and can't be the reason */
                btrfs_error(root->fs_info, -EIO,
index b71acd6..5ce2a7d 100644 (file)
@@ -68,8 +68,17 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
 int btrfs_init_fs_root(struct btrfs_root *root);
 int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
                         struct btrfs_root *root);
-struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
-                                             struct btrfs_key *location);
+
+struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info,
+                                    struct btrfs_key *key,
+                                    bool check_ref);
+static inline struct btrfs_root *
+btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
+                          struct btrfs_key *location)
+{
+       return btrfs_get_fs_root(fs_info, location, true);
+}
+
 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info);
 void btrfs_btree_balance_dirty(struct btrfs_root *root);
 void btrfs_btree_balance_dirty_nodelay(struct btrfs_root *root);
index cfb3cf7..d58bef1 100644 (file)
@@ -3925,7 +3925,6 @@ static int can_overcommit(struct btrfs_root *root,
        u64 space_size;
        u64 avail;
        u64 used;
-       u64 to_add;
 
        used = space_info->bytes_used + space_info->bytes_reserved +
                space_info->bytes_pinned + space_info->bytes_readonly;
@@ -3959,25 +3958,17 @@ static int can_overcommit(struct btrfs_root *root,
                       BTRFS_BLOCK_GROUP_RAID10))
                avail >>= 1;
 
-       to_add = space_info->total_bytes;
-
        /*
         * If we aren't flushing all things, let us overcommit up to
         * 1/2th of the space. If we can flush, don't let us overcommit
         * too much, let it overcommit up to 1/8 of the space.
         */
        if (flush == BTRFS_RESERVE_FLUSH_ALL)
-               to_add >>= 3;
+               avail >>= 3;
        else
-               to_add >>= 1;
-
-       /*
-        * Limit the overcommit to the amount of free space we could possibly
-        * allocate for chunks.
-        */
-       to_add = min(avail, to_add);
+               avail >>= 1;
 
-       if (used + bytes < space_info->total_bytes + to_add)
+       if (used + bytes < space_info->total_bytes + avail)
                return 1;
        return 0;
 }
@@ -4000,7 +3991,7 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root,
                 */
                btrfs_start_all_delalloc_inodes(root->fs_info, 0);
                if (!current->journal_info)
-                       btrfs_wait_all_ordered_extents(root->fs_info, 0);
+                       btrfs_wait_all_ordered_extents(root->fs_info);
        }
 }
 
@@ -4030,7 +4021,7 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
        if (delalloc_bytes == 0) {
                if (trans)
                        return;
-               btrfs_wait_all_ordered_extents(root->fs_info, 0);
+               btrfs_wait_all_ordered_extents(root->fs_info);
                return;
        }
 
@@ -4058,7 +4049,7 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
 
                loops++;
                if (wait_ordered && !trans) {
-                       btrfs_wait_all_ordered_extents(root->fs_info, 0);
+                       btrfs_wait_all_ordered_extents(root->fs_info);
                } else {
                        time_left = schedule_timeout_killable(1);
                        if (time_left)
@@ -4465,7 +4456,6 @@ static void block_rsv_release_bytes(struct btrfs_fs_info *fs_info,
                        space_info->bytes_may_use -= num_bytes;
                        trace_btrfs_space_reservation(fs_info, "space_info",
                                        space_info->flags, num_bytes, 0);
-                       space_info->reservation_progress++;
                        spin_unlock(&space_info->lock);
                }
        }
@@ -4666,7 +4656,6 @@ static void update_global_block_rsv(struct btrfs_fs_info *fs_info)
                sinfo->bytes_may_use -= num_bytes;
                trace_btrfs_space_reservation(fs_info, "space_info",
                                      sinfo->flags, num_bytes, 0);
-               sinfo->reservation_progress++;
                block_rsv->reserved = block_rsv->size;
                block_rsv->full = 1;
        }
@@ -5446,7 +5435,6 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
                        space_info->bytes_readonly += num_bytes;
                cache->reserved -= num_bytes;
                space_info->bytes_reserved -= num_bytes;
-               space_info->reservation_progress++;
        }
        spin_unlock(&cache->lock);
        spin_unlock(&space_info->lock);
@@ -6117,10 +6105,13 @@ enum btrfs_loop_type {
 /*
  * walks the btree of allocated extents and find a hole of a given size.
  * The key ins is changed to record the hole:
- * ins->objectid == block start
+ * ins->objectid == start position
  * ins->flags = BTRFS_EXTENT_ITEM_KEY
- * ins->offset == number of blocks
+ * ins->offset == the size of the hole.
  * Any available blocks before search_start are skipped.
+ *
+ * If there is no suitable free space, we will record the max size of
+ * the free space extent currently.
  */
 static noinline int find_free_extent(struct btrfs_root *orig_root,
                                     u64 num_bytes, u64 empty_size,
@@ -6133,6 +6124,7 @@ static noinline int find_free_extent(struct btrfs_root *orig_root,
        struct btrfs_block_group_cache *block_group = NULL;
        struct btrfs_block_group_cache *used_block_group;
        u64 search_start = 0;
+       u64 max_extent_size = 0;
        int empty_cluster = 2 * 1024 * 1024;
        struct btrfs_space_info *space_info;
        int loop = 0;
@@ -6292,7 +6284,10 @@ have_block_group:
                                btrfs_get_block_group(used_block_group);
 
                        offset = btrfs_alloc_from_cluster(used_block_group,
-                         last_ptr, num_bytes, used_block_group->key.objectid);
+                                               last_ptr,
+                                               num_bytes,
+                                               used_block_group->key.objectid,
+                                               &max_extent_size);
                        if (offset) {
                                /* we have a block, we're done */
                                spin_unlock(&last_ptr->refill_lock);
@@ -6355,8 +6350,10 @@ refill_cluster:
                                 * cluster
                                 */
                                offset = btrfs_alloc_from_cluster(block_group,
-                                                 last_ptr, num_bytes,
-                                                 search_start);
+                                                       last_ptr,
+                                                       num_bytes,
+                                                       search_start,
+                                                       &max_extent_size);
                                if (offset) {
                                        /* we found one, proceed */
                                        spin_unlock(&last_ptr->refill_lock);
@@ -6391,13 +6388,18 @@ unclustered_alloc:
                if (cached &&
                    block_group->free_space_ctl->free_space <
                    num_bytes + empty_cluster + empty_size) {
+                       if (block_group->free_space_ctl->free_space >
+                           max_extent_size)
+                               max_extent_size =
+                                       block_group->free_space_ctl->free_space;
                        spin_unlock(&block_group->free_space_ctl->tree_lock);
                        goto loop;
                }
                spin_unlock(&block_group->free_space_ctl->tree_lock);
 
                offset = btrfs_find_space_for_alloc(block_group, search_start,
-                                                   num_bytes, empty_size);
+                                                   num_bytes, empty_size,
+                                                   &max_extent_size);
                /*
                 * If we didn't find a chunk, and we haven't failed on this
                 * block group before, and this block group is in the middle of
@@ -6515,7 +6517,8 @@ loop:
                ret = 0;
        }
 out:
-
+       if (ret == -ENOSPC)
+               ins->offset = max_extent_size;
        return ret;
 }
 
@@ -6573,8 +6576,8 @@ again:
                               flags);
 
        if (ret == -ENOSPC) {
-               if (!final_tried) {
-                       num_bytes = num_bytes >> 1;
+               if (!final_tried && ins->offset) {
+                       num_bytes = min(num_bytes >> 1, ins->offset);
                        num_bytes = round_down(num_bytes, root->sectorsize);
                        num_bytes = max(num_bytes, min_alloc_size);
                        if (num_bytes == min_alloc_size)
index 09582b8..51731b7 100644 (file)
@@ -145,8 +145,16 @@ int __init extent_io_init(void)
                                     offsetof(struct btrfs_io_bio, bio));
        if (!btrfs_bioset)
                goto free_buffer_cache;
+
+       if (bioset_integrity_create(btrfs_bioset, BIO_POOL_SIZE))
+               goto free_bioset;
+
        return 0;
 
+free_bioset:
+       bioset_free(btrfs_bioset);
+       btrfs_bioset = NULL;
+
 free_buffer_cache:
        kmem_cache_destroy(extent_buffer_cache);
        extent_buffer_cache = NULL;
@@ -1481,11 +1489,11 @@ static noinline u64 find_delalloc_range(struct extent_io_tree *tree,
                *end = state->end;
                cur_start = state->end + 1;
                node = rb_next(node);
-               if (!node)
-                       break;
                total_bytes += state->end - state->start + 1;
                if (total_bytes >= max_bytes)
                        break;
+               if (!node)
+                       break;
        }
 out:
        spin_unlock(&tree->lock);
@@ -1612,7 +1620,7 @@ again:
                *start = delalloc_start;
                *end = delalloc_end;
                free_extent_state(cached_state);
-               return found;
+               return 0;
        }
 
        /*
@@ -1625,10 +1633,9 @@ again:
 
        /*
         * make sure to limit the number of pages we try to lock down
-        * if we're looping.
         */
-       if (delalloc_end + 1 - delalloc_start > max_bytes && loops)
-               delalloc_end = delalloc_start + PAGE_CACHE_SIZE - 1;
+       if (delalloc_end + 1 - delalloc_start > max_bytes)
+               delalloc_end = delalloc_start + max_bytes - 1;
 
        /* step two, lock all the pages after the page that has start */
        ret = lock_delalloc_pages(inode, locked_page,
@@ -1639,8 +1646,7 @@ again:
                 */
                free_extent_state(cached_state);
                if (!loops) {
-                       unsigned long offset = (*start) & (PAGE_CACHE_SIZE - 1);
-                       max_bytes = PAGE_CACHE_SIZE - offset;
+                       max_bytes = PAGE_CACHE_SIZE;
                        loops = 1;
                        goto again;
                } else {
index bc5072b..72da4df 100644 (file)
@@ -1859,8 +1859,8 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 
        ret = btrfs_log_dentry_safe(trans, root, dentry);
        if (ret < 0) {
-               mutex_unlock(&inode->i_mutex);
-               goto out;
+               /* Fallthrough and commit/free transaction. */
+               ret = 1;
        }
 
        /* we've logged all the items and now have a consistent
index 3f0ddfc..b4f9904 100644 (file)
@@ -1431,13 +1431,19 @@ static void bitmap_set_bits(struct btrfs_free_space_ctl *ctl,
        ctl->free_space += bytes;
 }
 
+/*
+ * If we can not find suitable extent, we will use bytes to record
+ * the size of the max extent.
+ */
 static int search_bitmap(struct btrfs_free_space_ctl *ctl,
                         struct btrfs_free_space *bitmap_info, u64 *offset,
                         u64 *bytes)
 {
        unsigned long found_bits = 0;
+       unsigned long max_bits = 0;
        unsigned long bits, i;
        unsigned long next_zero;
+       unsigned long extent_bits;
 
        i = offset_to_bit(bitmap_info->offset, ctl->unit,
                          max_t(u64, *offset, bitmap_info->offset));
@@ -1446,9 +1452,12 @@ static int search_bitmap(struct btrfs_free_space_ctl *ctl,
        for_each_set_bit_from(i, bitmap_info->bitmap, BITS_PER_BITMAP) {
                next_zero = find_next_zero_bit(bitmap_info->bitmap,
                                               BITS_PER_BITMAP, i);
-               if ((next_zero - i) >= bits) {
-                       found_bits = next_zero - i;
+               extent_bits = next_zero - i;
+               if (extent_bits >= bits) {
+                       found_bits = extent_bits;
                        break;
+               } else if (extent_bits > max_bits) {
+                       max_bits = extent_bits;
                }
                i = next_zero;
        }
@@ -1459,38 +1468,41 @@ static int search_bitmap(struct btrfs_free_space_ctl *ctl,
                return 0;
        }
 
+       *bytes = (u64)(max_bits) * ctl->unit;
        return -1;
 }
 
+/* Cache the size of the max extent in bytes */
 static struct btrfs_free_space *
 find_free_space(struct btrfs_free_space_ctl *ctl, u64 *offset, u64 *bytes,
-               unsigned long align)
+               unsigned long align, u64 *max_extent_size)
 {
        struct btrfs_free_space *entry;
        struct rb_node *node;
-       u64 ctl_off;
        u64 tmp;
        u64 align_off;
        int ret;
 
        if (!ctl->free_space_offset.rb_node)
-               return NULL;
+               goto out;
 
        entry = tree_search_offset(ctl, offset_to_bitmap(ctl, *offset), 0, 1);
        if (!entry)
-               return NULL;
+               goto out;
 
        for (node = &entry->offset_index; node; node = rb_next(node)) {
                entry = rb_entry(node, struct btrfs_free_space, offset_index);
-               if (entry->bytes < *bytes)
+               if (entry->bytes < *bytes) {
+                       if (entry->bytes > *max_extent_size)
+                               *max_extent_size = entry->bytes;
                        continue;
+               }
 
                /* make sure the space returned is big enough
                 * to match our requested alignment
                 */
                if (*bytes >= align) {
-                       ctl_off = entry->offset - ctl->start;
-                       tmp = ctl_off + align - 1;;
+                       tmp = entry->offset - ctl->start + align - 1;
                        do_div(tmp, align);
                        tmp = tmp * align + ctl->start;
                        align_off = tmp - entry->offset;
@@ -1499,14 +1511,22 @@ find_free_space(struct btrfs_free_space_ctl *ctl, u64 *offset, u64 *bytes,
                        tmp = entry->offset;
                }
 
-               if (entry->bytes < *bytes + align_off)
+               if (entry->bytes < *bytes + align_off) {
+                       if (entry->bytes > *max_extent_size)
+                               *max_extent_size = entry->bytes;
                        continue;
+               }
 
                if (entry->bitmap) {
-                       ret = search_bitmap(ctl, entry, &tmp, bytes);
+                       u64 size = *bytes;
+
+                       ret = search_bitmap(ctl, entry, &tmp, &size);
                        if (!ret) {
                                *offset = tmp;
+                               *bytes = size;
                                return entry;
+                       } else if (size > *max_extent_size) {
+                               *max_extent_size = size;
                        }
                        continue;
                }
@@ -1515,7 +1535,7 @@ find_free_space(struct btrfs_free_space_ctl *ctl, u64 *offset, u64 *bytes,
                *bytes = entry->bytes - align_off;
                return entry;
        }
-
+out:
        return NULL;
 }
 
@@ -2116,7 +2136,8 @@ void btrfs_remove_free_space_cache(struct btrfs_block_group_cache *block_group)
 }
 
 u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
-                              u64 offset, u64 bytes, u64 empty_size)
+                              u64 offset, u64 bytes, u64 empty_size,
+                              u64 *max_extent_size)
 {
        struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
        struct btrfs_free_space *entry = NULL;
@@ -2127,7 +2148,7 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
 
        spin_lock(&ctl->tree_lock);
        entry = find_free_space(ctl, &offset, &bytes_search,
-                               block_group->full_stripe_len);
+                               block_group->full_stripe_len, max_extent_size);
        if (!entry)
                goto out;
 
@@ -2137,7 +2158,6 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
                if (!entry->bytes)
                        free_bitmap(ctl, entry);
        } else {
-
                unlink_free_space(ctl, entry);
                align_gap_len = offset - entry->offset;
                align_gap = entry->offset;
@@ -2151,7 +2171,6 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
                else
                        link_free_space(ctl, entry);
        }
-
 out:
        spin_unlock(&ctl->tree_lock);
 
@@ -2206,7 +2225,8 @@ int btrfs_return_cluster_to_free_space(
 static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group,
                                   struct btrfs_free_cluster *cluster,
                                   struct btrfs_free_space *entry,
-                                  u64 bytes, u64 min_start)
+                                  u64 bytes, u64 min_start,
+                                  u64 *max_extent_size)
 {
        struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
        int err;
@@ -2218,8 +2238,11 @@ static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group,
        search_bytes = bytes;
 
        err = search_bitmap(ctl, entry, &search_start, &search_bytes);
-       if (err)
+       if (err) {
+               if (search_bytes > *max_extent_size)
+                       *max_extent_size = search_bytes;
                return 0;
+       }
 
        ret = search_start;
        __bitmap_clear_bits(ctl, entry, ret, bytes);
@@ -2234,7 +2257,7 @@ static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group,
  */
 u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
                             struct btrfs_free_cluster *cluster, u64 bytes,
-                            u64 min_start)
+                            u64 min_start, u64 *max_extent_size)
 {
        struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
        struct btrfs_free_space *entry = NULL;
@@ -2254,6 +2277,9 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
 
        entry = rb_entry(node, struct btrfs_free_space, offset_index);
        while(1) {
+               if (entry->bytes < bytes && entry->bytes > *max_extent_size)
+                       *max_extent_size = entry->bytes;
+
                if (entry->bytes < bytes ||
                    (!entry->bitmap && entry->offset < min_start)) {
                        node = rb_next(&entry->offset_index);
@@ -2267,7 +2293,8 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
                if (entry->bitmap) {
                        ret = btrfs_alloc_from_bitmap(block_group,
                                                      cluster, entry, bytes,
-                                                     cluster->window_start);
+                                                     cluster->window_start,
+                                                     max_extent_size);
                        if (ret == 0) {
                                node = rb_next(&entry->offset_index);
                                if (!node)
index c749041..e737f92 100644 (file)
@@ -94,7 +94,8 @@ void __btrfs_remove_free_space_cache(struct btrfs_free_space_ctl *ctl);
 void btrfs_remove_free_space_cache(struct btrfs_block_group_cache
                                     *block_group);
 u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group,
-                              u64 offset, u64 bytes, u64 empty_size);
+                              u64 offset, u64 bytes, u64 empty_size,
+                              u64 *max_extent_size);
 u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root);
 void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
                           u64 bytes);
@@ -105,7 +106,7 @@ int btrfs_find_space_cluster(struct btrfs_root *root,
 void btrfs_init_free_cluster(struct btrfs_free_cluster *cluster);
 u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
                             struct btrfs_free_cluster *cluster, u64 bytes,
-                            u64 min_start);
+                            u64 min_start, u64 *max_extent_size);
 int btrfs_return_cluster_to_free_space(
                               struct btrfs_block_group_cache *block_group,
                               struct btrfs_free_cluster *cluster);
index f338c56..51e3afa 100644 (file)
@@ -4688,11 +4688,11 @@ static void inode_tree_add(struct inode *inode)
        struct btrfs_inode *entry;
        struct rb_node **p;
        struct rb_node *parent;
+       struct rb_node *new = &BTRFS_I(inode)->rb_node;
        u64 ino = btrfs_ino(inode);
 
        if (inode_unhashed(inode))
                return;
-again:
        parent = NULL;
        spin_lock(&root->inode_lock);
        p = &root->inode_tree.rb_node;
@@ -4707,14 +4707,14 @@ again:
                else {
                        WARN_ON(!(entry->vfs_inode.i_state &
                                  (I_WILL_FREE | I_FREEING)));
-                       rb_erase(parent, &root->inode_tree);
+                       rb_replace_node(parent, new, &root->inode_tree);
                        RB_CLEAR_NODE(parent);
                        spin_unlock(&root->inode_lock);
-                       goto again;
+                       return;
                }
        }
-       rb_link_node(&BTRFS_I(inode)->rb_node, parent, p);
-       rb_insert_color(&BTRFS_I(inode)->rb_node, &root->inode_tree);
+       rb_link_node(new, parent, p);
+       rb_insert_color(new, &root->inode_tree);
        spin_unlock(&root->inode_lock);
 }
 
@@ -6437,6 +6437,7 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
 
        if (btrfs_extent_readonly(root, disk_bytenr))
                goto out;
+       btrfs_release_path(path);
 
        /*
         * look for other files referencing this extent, if we
@@ -7986,7 +7987,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 
 
        /* check for collisions, even if the  name isn't there */
-       ret = btrfs_check_dir_item_collision(root, new_dir->i_ino,
+       ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino,
                             new_dentry->d_name.name,
                             new_dentry->d_name.len);
 
@@ -8216,6 +8217,10 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
 
                work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
                if (unlikely(!work)) {
+                       if (delay_iput)
+                               btrfs_add_delayed_iput(inode);
+                       else
+                               iput(inode);
                        ret = -ENOMEM;
                        goto out;
                }
@@ -8613,11 +8618,13 @@ static const struct inode_operations btrfs_dir_inode_operations = {
        .removexattr    = btrfs_removexattr,
        .permission     = btrfs_permission,
        .get_acl        = btrfs_get_acl,
+       .update_time    = btrfs_update_time,
 };
 static const struct inode_operations btrfs_dir_ro_inode_operations = {
        .lookup         = btrfs_lookup,
        .permission     = btrfs_permission,
        .get_acl        = btrfs_get_acl,
+       .update_time    = btrfs_update_time,
 };
 
 static const struct file_operations btrfs_dir_file_operations = {
index 1a5b946..9d46f60 100644 (file)
@@ -574,7 +574,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        if (ret)
                return ret;
 
-       btrfs_wait_ordered_extents(root, 0);
+       btrfs_wait_ordered_extents(root);
 
        pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS);
        if (!pending_snapshot)
@@ -2696,9 +2696,9 @@ out_unlock:
 static long btrfs_ioctl_file_extent_same(struct file *file,
                                         void __user *argp)
 {
-       struct btrfs_ioctl_same_args *args = argp;
-       struct btrfs_ioctl_same_args same;
-       struct btrfs_ioctl_same_extent_info info;
+       struct btrfs_ioctl_same_args tmp;
+       struct btrfs_ioctl_same_args *same;
+       struct btrfs_ioctl_same_extent_info *info;
        struct inode *src = file->f_dentry->d_inode;
        struct file *dst_file = NULL;
        struct inode *dst;
@@ -2706,6 +2706,7 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
        u64 len;
        int i;
        int ret;
+       unsigned long size;
        u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
        bool is_admin = capable(CAP_SYS_ADMIN);
 
@@ -2716,15 +2717,30 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
        if (ret)
                return ret;
 
-       if (copy_from_user(&same,
+       if (copy_from_user(&tmp,
                           (struct btrfs_ioctl_same_args __user *)argp,
-                          sizeof(same))) {
+                          sizeof(tmp))) {
                ret = -EFAULT;
                goto out;
        }
 
-       off = same.logical_offset;
-       len = same.length;
+       size = sizeof(tmp) +
+               tmp.dest_count * sizeof(struct btrfs_ioctl_same_extent_info);
+
+       same = kmalloc(size, GFP_NOFS);
+       if (!same) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       if (copy_from_user(same,
+                          (struct btrfs_ioctl_same_args __user *)argp, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       off = same->logical_offset;
+       len = same->length;
 
        /*
         * Limit the total length we will dedupe for each operation.
@@ -2752,27 +2768,28 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
        if (!S_ISREG(src->i_mode))
                goto out;
 
-       ret = 0;
-       for (i = 0; i < same.dest_count; i++) {
-               if (copy_from_user(&info, &args->info[i], sizeof(info))) {
-                       ret = -EFAULT;
-                       goto out;
-               }
+       /* pre-format output fields to sane values */
+       for (i = 0; i < same->dest_count; i++) {
+               same->info[i].bytes_deduped = 0ULL;
+               same->info[i].status = 0;
+       }
 
-               info.bytes_deduped = 0;
+       ret = 0;
+       for (i = 0; i < same->dest_count; i++) {
+               info = &same->info[i];
 
-               dst_file = fget(info.fd);
+               dst_file = fget(info->fd);
                if (!dst_file) {
-                       info.status = -EBADF;
+                       info->status = -EBADF;
                        goto next;
                }
 
                if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) {
-                       info.status = -EINVAL;
+                       info->status = -EINVAL;
                        goto next;
                }
 
-               info.status = -EXDEV;
+               info->status = -EXDEV;
                if (file->f_path.mnt != dst_file->f_path.mnt)
                        goto next;
 
@@ -2781,32 +2798,29 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
                        goto next;
 
                if (S_ISDIR(dst->i_mode)) {
-                       info.status = -EISDIR;
+                       info->status = -EISDIR;
                        goto next;
                }
 
                if (!S_ISREG(dst->i_mode)) {
-                       info.status = -EACCES;
+                       info->status = -EACCES;
                        goto next;
                }
 
-               info.status = btrfs_extent_same(src, off, len, dst,
-                                               info.logical_offset);
-               if (info.status == 0)
-                       info.bytes_deduped += len;
+               info->status = btrfs_extent_same(src, off, len, dst,
+                                               info->logical_offset);
+               if (info->status == 0)
+                       info->bytes_deduped += len;
 
 next:
                if (dst_file)
                        fput(dst_file);
-
-               if (__put_user_unaligned(info.status, &args->info[i].status) ||
-                   __put_user_unaligned(info.bytes_deduped,
-                                        &args->info[i].bytes_deduped)) {
-                       ret = -EFAULT;
-                       goto out;
-               }                                                               
        }
 
+       ret = copy_to_user(argp, same, size);
+       if (ret)
+               ret = -EFAULT;
+
 out:
        mnt_drop_write_file(file);
        return ret;
@@ -3310,7 +3324,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
        }
 
        if (!objectid)
-               objectid = root->root_key.objectid;
+               objectid = BTRFS_FS_TREE_OBJECTID;
 
        location.objectid = objectid;
        location.type = BTRFS_ROOT_ITEM_KEY;
index 966b413..c702cb6 100644 (file)
@@ -563,11 +563,10 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work)
  * wait for all the ordered extents in a root.  This is done when balancing
  * space between drives.
  */
-void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput)
+void btrfs_wait_ordered_extents(struct btrfs_root *root)
 {
        struct list_head splice, works;
        struct btrfs_ordered_extent *ordered, *next;
-       struct inode *inode;
 
        INIT_LIST_HEAD(&splice);
        INIT_LIST_HEAD(&works);
@@ -580,15 +579,6 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput)
                                           root_extent_list);
                list_move_tail(&ordered->root_extent_list,
                               &root->ordered_extents);
-               /*
-                * the inode may be getting freed (in sys_unlink path).
-                */
-               inode = igrab(ordered->inode);
-               if (!inode) {
-                       cond_resched_lock(&root->ordered_extent_lock);
-                       continue;
-               }
-
                atomic_inc(&ordered->refs);
                spin_unlock(&root->ordered_extent_lock);
 
@@ -605,21 +595,13 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput)
        list_for_each_entry_safe(ordered, next, &works, work_list) {
                list_del_init(&ordered->work_list);
                wait_for_completion(&ordered->completion);
-
-               inode = ordered->inode;
                btrfs_put_ordered_extent(ordered);
-               if (delay_iput)
-                       btrfs_add_delayed_iput(inode);
-               else
-                       iput(inode);
-
                cond_resched();
        }
        mutex_unlock(&root->fs_info->ordered_operations_mutex);
 }
 
-void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info,
-                                   int delay_iput)
+void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_root *root;
        struct list_head splice;
@@ -637,7 +619,7 @@ void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info,
                               &fs_info->ordered_roots);
                spin_unlock(&fs_info->ordered_root_lock);
 
-               btrfs_wait_ordered_extents(root, delay_iput);
+               btrfs_wait_ordered_extents(root);
                btrfs_put_fs_root(root);
 
                spin_lock(&fs_info->ordered_root_lock);
index d9a5aa0..0c0b356 100644 (file)
@@ -195,9 +195,8 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,
 void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct inode *inode);
-void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput);
-void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info,
-                                   int delay_iput);
+void btrfs_wait_ordered_extents(struct btrfs_root *root);
+void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info);
 void btrfs_get_logged_extents(struct btrfs_root *log, struct inode *inode);
 void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid);
 void btrfs_free_logged_extents(struct btrfs_root *log, u64 transid);
index aacc212..4a35572 100644 (file)
@@ -588,7 +588,7 @@ static struct btrfs_root *read_fs_root(struct btrfs_fs_info *fs_info,
        else
                key.offset = (u64)-1;
 
-       return btrfs_read_fs_root_no_name(fs_info, &key);
+       return btrfs_get_fs_root(fs_info, &key, false);
 }
 
 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
@@ -1548,7 +1548,7 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
               btrfs_file_extent_other_encoding(leaf, fi));
 
        if (num_bytes != btrfs_file_extent_disk_num_bytes(leaf, fi)) {
-               ret = 1;
+               ret = -EINVAL;
                goto out;
        }
 
@@ -1579,7 +1579,7 @@ int replace_file_extents(struct btrfs_trans_handle *trans,
        u64 end;
        u32 nritems;
        u32 i;
-       int ret;
+       int ret = 0;
        int first = 1;
        int dirty = 0;
 
@@ -1642,11 +1642,13 @@ int replace_file_extents(struct btrfs_trans_handle *trans,
 
                ret = get_new_location(rc->data_inode, &new_bytenr,
                                       bytenr, num_bytes);
-               if (ret > 0) {
-                       WARN_ON(1);
-                       continue;
+               if (ret) {
+                       /*
+                        * Don't have to abort since we've not changed anything
+                        * in the file extent yet.
+                        */
+                       break;
                }
-               BUG_ON(ret < 0);
 
                btrfs_set_file_extent_disk_bytenr(leaf, fi, new_bytenr);
                dirty = 1;
@@ -1656,18 +1658,24 @@ int replace_file_extents(struct btrfs_trans_handle *trans,
                                           num_bytes, parent,
                                           btrfs_header_owner(leaf),
                                           key.objectid, key.offset, 1);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       break;
+               }
 
                ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
                                        parent, btrfs_header_owner(leaf),
                                        key.objectid, key.offset, 1);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       break;
+               }
        }
        if (dirty)
                btrfs_mark_buffer_dirty(leaf);
        if (inode)
                btrfs_add_delayed_iput(inode);
-       return 0;
+       return ret;
 }
 
 static noinline_for_stack
@@ -4238,7 +4246,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
                err = ret;
                goto out;
        }
-       btrfs_wait_all_ordered_extents(fs_info, 0);
+       btrfs_wait_all_ordered_extents(fs_info);
 
        while (1) {
                mutex_lock(&fs_info->cleaner_mutex);
@@ -4499,19 +4507,19 @@ out:
        return ret;
 }
 
-void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, struct extent_buffer *buf,
-                          struct extent_buffer *cow)
+int btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *root, struct extent_buffer *buf,
+                         struct extent_buffer *cow)
 {
        struct reloc_control *rc;
        struct backref_node *node;
        int first_cow = 0;
        int level;
-       int ret;
+       int ret = 0;
 
        rc = root->fs_info->reloc_ctl;
        if (!rc)
-               return;
+               return 0;
 
        BUG_ON(rc->stage == UPDATE_DATA_PTRS &&
               root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID);
@@ -4547,10 +4555,9 @@ void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
                        rc->nodes_relocated += buf->len;
        }
 
-       if (level == 0 && first_cow && rc->stage == UPDATE_DATA_PTRS) {
+       if (level == 0 && first_cow && rc->stage == UPDATE_DATA_PTRS)
                ret = replace_file_extents(trans, rc, root, cow);
-               BUG_ON(ret);
-       }
+       return ret;
 }
 
 /*
index 0b1f4ef..ec71ea4 100644 (file)
@@ -299,11 +299,6 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
                        continue;
                }
 
-               if (btrfs_root_refs(&root->root_item) == 0) {
-                       btrfs_add_dead_root(root);
-                       continue;
-               }
-
                err = btrfs_init_fs_root(root);
                if (err) {
                        btrfs_free_fs_root(root);
@@ -318,6 +313,9 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
                        btrfs_free_fs_root(root);
                        break;
                }
+
+               if (btrfs_root_refs(&root->root_item) == 0)
+                       btrfs_add_dead_root(root);
        }
 
        btrfs_free_path(path);
index 0afcd45..a18e0e2 100644 (file)
@@ -158,12 +158,20 @@ struct scrub_fixup_nodatasum {
        int                     mirror_num;
 };
 
+struct scrub_nocow_inode {
+       u64                     inum;
+       u64                     offset;
+       u64                     root;
+       struct list_head        list;
+};
+
 struct scrub_copy_nocow_ctx {
        struct scrub_ctx        *sctx;
        u64                     logical;
        u64                     len;
        int                     mirror_num;
        u64                     physical_for_dev_replace;
+       struct list_head        inodes;
        struct btrfs_work       work;
 };
 
@@ -245,7 +253,7 @@ static void scrub_wr_bio_end_io_worker(struct btrfs_work *work);
 static int write_page_nocow(struct scrub_ctx *sctx,
                            u64 physical_for_dev_replace, struct page *page);
 static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root,
-                                     void *ctx);
+                                     struct scrub_copy_nocow_ctx *ctx);
 static int copy_nocow_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
                            int mirror_num, u64 physical_for_dev_replace);
 static void copy_nocow_pages_worker(struct btrfs_work *work);
@@ -3126,12 +3134,30 @@ static int copy_nocow_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
        nocow_ctx->mirror_num = mirror_num;
        nocow_ctx->physical_for_dev_replace = physical_for_dev_replace;
        nocow_ctx->work.func = copy_nocow_pages_worker;
+       INIT_LIST_HEAD(&nocow_ctx->inodes);
        btrfs_queue_worker(&fs_info->scrub_nocow_workers,
                           &nocow_ctx->work);
 
        return 0;
 }
 
+static int record_inode_for_nocow(u64 inum, u64 offset, u64 root, void *ctx)
+{
+       struct scrub_copy_nocow_ctx *nocow_ctx = ctx;
+       struct scrub_nocow_inode *nocow_inode;
+
+       nocow_inode = kzalloc(sizeof(*nocow_inode), GFP_NOFS);
+       if (!nocow_inode)
+               return -ENOMEM;
+       nocow_inode->inum = inum;
+       nocow_inode->offset = offset;
+       nocow_inode->root = root;
+       list_add_tail(&nocow_inode->list, &nocow_ctx->inodes);
+       return 0;
+}
+
+#define COPY_COMPLETE 1
+
 static void copy_nocow_pages_worker(struct btrfs_work *work)
 {
        struct scrub_copy_nocow_ctx *nocow_ctx =
@@ -3167,8 +3193,7 @@ static void copy_nocow_pages_worker(struct btrfs_work *work)
        }
 
        ret = iterate_inodes_from_logical(logical, fs_info, path,
-                                         copy_nocow_pages_for_inode,
-                                         nocow_ctx);
+                                         record_inode_for_nocow, nocow_ctx);
        if (ret != 0 && ret != -ENOENT) {
                pr_warn("iterate_inodes_from_logical() failed: log %llu, phys %llu, len %llu, mir %u, ret %d\n",
                        logical, physical_for_dev_replace, len, mirror_num,
@@ -3177,7 +3202,33 @@ static void copy_nocow_pages_worker(struct btrfs_work *work)
                goto out;
        }
 
+       btrfs_end_transaction(trans, root);
+       trans = NULL;
+       while (!list_empty(&nocow_ctx->inodes)) {
+               struct scrub_nocow_inode *entry;
+               entry = list_first_entry(&nocow_ctx->inodes,
+                                        struct scrub_nocow_inode,
+                                        list);
+               list_del_init(&entry->list);
+               ret = copy_nocow_pages_for_inode(entry->inum, entry->offset,
+                                                entry->root, nocow_ctx);
+               kfree(entry);
+               if (ret == COPY_COMPLETE) {
+                       ret = 0;
+                       break;
+               } else if (ret) {
+                       break;
+               }
+       }
 out:
+       while (!list_empty(&nocow_ctx->inodes)) {
+               struct scrub_nocow_inode *entry;
+               entry = list_first_entry(&nocow_ctx->inodes,
+                                        struct scrub_nocow_inode,
+                                        list);
+               list_del_init(&entry->list);
+               kfree(entry);
+       }
        if (trans && !IS_ERR(trans))
                btrfs_end_transaction(trans, root);
        if (not_written)
@@ -3190,20 +3241,25 @@ out:
        scrub_pending_trans_workers_dec(sctx);
 }
 
-static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
+static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root,
+                                     struct scrub_copy_nocow_ctx *nocow_ctx)
 {
-       struct scrub_copy_nocow_ctx *nocow_ctx = ctx;
        struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info;
        struct btrfs_key key;
        struct inode *inode;
        struct page *page;
        struct btrfs_root *local_root;
+       struct btrfs_ordered_extent *ordered;
+       struct extent_map *em;
+       struct extent_state *cached_state = NULL;
+       struct extent_io_tree *io_tree;
        u64 physical_for_dev_replace;
-       u64 len;
+       u64 len = nocow_ctx->len;
+       u64 lockstart = offset, lockend = offset + len - 1;
        unsigned long index;
        int srcu_index;
-       int ret;
-       int err;
+       int ret = 0;
+       int err = 0;
 
        key.objectid = root;
        key.type = BTRFS_ROOT_ITEM_KEY;
@@ -3229,9 +3285,33 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
        mutex_lock(&inode->i_mutex);
        inode_dio_wait(inode);
 
-       ret = 0;
        physical_for_dev_replace = nocow_ctx->physical_for_dev_replace;
-       len = nocow_ctx->len;
+       io_tree = &BTRFS_I(inode)->io_tree;
+
+       lock_extent_bits(io_tree, lockstart, lockend, 0, &cached_state);
+       ordered = btrfs_lookup_ordered_range(inode, lockstart, len);
+       if (ordered) {
+               btrfs_put_ordered_extent(ordered);
+               goto out_unlock;
+       }
+
+       em = btrfs_get_extent(inode, NULL, 0, lockstart, len, 0);
+       if (IS_ERR(em)) {
+               ret = PTR_ERR(em);
+               goto out_unlock;
+       }
+
+       /*
+        * This extent does not actually cover the logical extent anymore,
+        * move on to the next inode.
+        */
+       if (em->block_start > nocow_ctx->logical ||
+           em->block_start + em->block_len < nocow_ctx->logical + len) {
+               free_extent_map(em);
+               goto out_unlock;
+       }
+       free_extent_map(em);
+
        while (len >= PAGE_CACHE_SIZE) {
                index = offset >> PAGE_CACHE_SHIFT;
 again:
@@ -3247,10 +3327,9 @@ again:
                                goto next_page;
                } else {
                        ClearPageError(page);
-                       err = extent_read_full_page(&BTRFS_I(inode)->
-                                                        io_tree,
-                                                       page, btrfs_get_extent,
-                                                       nocow_ctx->mirror_num);
+                       err = extent_read_full_page_nolock(io_tree, page,
+                                                          btrfs_get_extent,
+                                                          nocow_ctx->mirror_num);
                        if (err) {
                                ret = err;
                                goto next_page;
@@ -3264,6 +3343,7 @@ again:
                         * page in the page cache.
                         */
                        if (page->mapping != inode->i_mapping) {
+                               unlock_page(page);
                                page_cache_release(page);
                                goto again;
                        }
@@ -3287,6 +3367,10 @@ next_page:
                physical_for_dev_replace += PAGE_CACHE_SIZE;
                len -= PAGE_CACHE_SIZE;
        }
+       ret = COPY_COMPLETE;
+out_unlock:
+       unlock_extent_cached(io_tree, lockstart, lockend, &cached_state,
+                            GFP_NOFS);
 out:
        mutex_unlock(&inode->i_mutex);
        iput(inode);
index 3aab10c..e913328 100644 (file)
@@ -921,7 +921,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
                return 0;
        }
 
-       btrfs_wait_all_ordered_extents(fs_info, 1);
+       btrfs_wait_all_ordered_extents(fs_info);
 
        trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
@@ -1340,6 +1340,12 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                if (ret)
                        goto restore;
        } else {
+               if (test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state)) {
+                       btrfs_err(fs_info,
+                               "Remounting read-write after error is not allowed\n");
+                       ret = -EINVAL;
+                       goto restore;
+               }
                if (fs_info->fs_devices->rw_devices == 0) {
                        ret = -EACCES;
                        goto restore;
@@ -1377,6 +1383,16 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                        pr_warn("btrfs: failed to resume dev_replace\n");
                        goto restore;
                }
+
+               if (!fs_info->uuid_root) {
+                       pr_info("btrfs: creating UUID tree\n");
+                       ret = btrfs_create_uuid_tree(fs_info);
+                       if (ret) {
+                               pr_warn("btrfs: failed to create the uuid tree"
+                                       "%d\n", ret);
+                               goto restore;
+                       }
+               }
                sb->s_flags &= ~MS_RDONLY;
        }
 out:
@@ -1762,6 +1778,9 @@ static void btrfs_print_info(void)
 #ifdef CONFIG_BTRFS_DEBUG
                        ", debug=on"
 #endif
+#ifdef CONFIG_BTRFS_ASSERT
+                       ", assert=on"
+#endif
 #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
                        ", integrity-checker=on"
 #endif
index cac4a3f..8c81bdc 100644 (file)
@@ -1603,7 +1603,7 @@ static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info)
 static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info)
 {
        if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT))
-               btrfs_wait_all_ordered_extents(fs_info, 1);
+               btrfs_wait_all_ordered_extents(fs_info);
 }
 
 int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
@@ -1838,11 +1838,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        assert_qgroups_uptodate(trans);
        update_super_roots(root);
 
-       if (!root->fs_info->log_root_recovering) {
-               btrfs_set_super_log_root(root->fs_info->super_copy, 0);
-               btrfs_set_super_log_root_level(root->fs_info->super_copy, 0);
-       }
-
+       btrfs_set_super_log_root(root->fs_info->super_copy, 0);
+       btrfs_set_super_log_root_level(root->fs_info->super_copy, 0);
        memcpy(root->fs_info->super_for_commit, root->fs_info->super_copy,
               sizeof(*root->fs_info->super_copy));
 
index 0d9613c..79f057c 100644 (file)
@@ -93,7 +93,8 @@
  */
 #define LOG_WALK_PIN_ONLY 0
 #define LOG_WALK_REPLAY_INODES 1
-#define LOG_WALK_REPLAY_ALL 2
+#define LOG_WALK_REPLAY_DIR_INDEX 2
+#define LOG_WALK_REPLAY_ALL 3
 
 static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root, struct inode *inode,
@@ -393,6 +394,7 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,
                if (inode_item) {
                        struct btrfs_inode_item *item;
                        u64 nbytes;
+                       u32 mode;
 
                        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
                                              struct btrfs_inode_item);
@@ -400,9 +402,19 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,
                        item = btrfs_item_ptr(eb, slot,
                                              struct btrfs_inode_item);
                        btrfs_set_inode_nbytes(eb, item, nbytes);
+
+                       /*
+                        * If this is a directory we need to reset the i_size to
+                        * 0 so that we can set it up properly when replaying
+                        * the rest of the items in this log.
+                        */
+                       mode = btrfs_inode_mode(eb, item);
+                       if (S_ISDIR(mode))
+                               btrfs_set_inode_size(eb, item, 0);
                }
        } else if (inode_item) {
                struct btrfs_inode_item *item;
+               u32 mode;
 
                /*
                 * New inode, set nbytes to 0 so that the nbytes comes out
@@ -410,6 +422,15 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,
                 */
                item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item);
                btrfs_set_inode_nbytes(eb, item, 0);
+
+               /*
+                * If this is a directory we need to reset the i_size to 0 so
+                * that we can set it up properly when replaying the rest of
+                * the items in this log.
+                */
+               mode = btrfs_inode_mode(eb, item);
+               if (S_ISDIR(mode))
+                       btrfs_set_inode_size(eb, item, 0);
        }
 insert:
        btrfs_release_path(path);
@@ -1496,6 +1517,7 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,
                iput(inode);
                return -EIO;
        }
+
        ret = btrfs_add_link(trans, dir, inode, name, name_len, 1, index);
 
        /* FIXME, put inode into FIXUP list */
@@ -1534,6 +1556,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
        u8 log_type;
        int exists;
        int ret = 0;
+       bool update_size = (key->type == BTRFS_DIR_INDEX_KEY);
 
        dir = read_one_inode(root, key->objectid);
        if (!dir)
@@ -1604,6 +1627,10 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
                goto insert;
 out:
        btrfs_release_path(path);
+       if (!ret && update_size) {
+               btrfs_i_size_write(dir, dir->i_size + name_len * 2);
+               ret = btrfs_update_inode(trans, root, dir);
+       }
        kfree(name);
        iput(dir);
        return ret;
@@ -1614,6 +1641,7 @@ insert:
                              name, name_len, log_type, &log_key);
        if (ret && ret != -ENOENT)
                goto out;
+       update_size = false;
        ret = 0;
        goto out;
 }
@@ -2027,6 +2055,15 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                        if (ret)
                                break;
                }
+
+               if (key.type == BTRFS_DIR_INDEX_KEY &&
+                   wc->stage == LOG_WALK_REPLAY_DIR_INDEX) {
+                       ret = replay_one_dir_item(wc->trans, root, path,
+                                                 eb, i, &key);
+                       if (ret)
+                               break;
+               }
+
                if (wc->stage < LOG_WALK_REPLAY_ALL)
                        continue;
 
@@ -2048,8 +2085,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                                                eb, i, &key);
                        if (ret)
                                break;
-               } else if (key.type == BTRFS_DIR_ITEM_KEY ||
-                          key.type == BTRFS_DIR_INDEX_KEY) {
+               } else if (key.type == BTRFS_DIR_ITEM_KEY) {
                        ret = replay_one_dir_item(wc->trans, root, path,
                                                  eb, i, &key);
                        if (ret)
@@ -3805,6 +3841,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
        int ret = 0;
        struct btrfs_root *root;
        struct dentry *old_parent = NULL;
+       struct inode *orig_inode = inode;
 
        /*
         * for regular files, if its inode is already on disk, we don't
@@ -3824,7 +3861,14 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
        }
 
        while (1) {
-               BTRFS_I(inode)->logged_trans = trans->transid;
+               /*
+                * If we are logging a directory then we start with our inode,
+                * not our parents inode, so we need to skipp setting the
+                * logged_trans so that further down in the log code we don't
+                * think this inode has already been logged.
+                */
+               if (inode != orig_inode)
+                       BTRFS_I(inode)->logged_trans = trans->transid;
                smp_mb();
 
                if (BTRFS_I(inode)->last_unlink_trans > last_committed) {
index 0052ca8..043b215 100644 (file)
@@ -796,7 +796,8 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
                        fs_devices->rotating = 1;
 
                fs_devices->open_devices++;
-               if (device->writeable && !device->is_tgtdev_for_dev_replace) {
+               if (device->writeable &&
+                   device->devid != BTRFS_DEV_REPLACE_DEVID) {
                        fs_devices->rw_devices++;
                        list_add(&device->dev_alloc_list,
                                 &fs_devices->alloc_list);
@@ -911,9 +912,9 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
        if (disk_super->label[0]) {
                if (disk_super->label[BTRFS_LABEL_SIZE - 1])
                        disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0';
-               printk(KERN_INFO "device label %s ", disk_super->label);
+               printk(KERN_INFO "btrfs: device label %s ", disk_super->label);
        } else {
-               printk(KERN_INFO "device fsid %pU ", disk_super->fsid);
+               printk(KERN_INFO "btrfs: device fsid %pU ", disk_super->fsid);
        }
 
        printk(KERN_CONT "devid %llu transid %llu %s\n", devid, transid, path);
@@ -1715,6 +1716,7 @@ void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info,
                                 struct btrfs_device *srcdev)
 {
        WARN_ON(!mutex_is_locked(&fs_info->fs_devices->device_list_mutex));
+
        list_del_rcu(&srcdev->dev_list);
        list_del_rcu(&srcdev->dev_alloc_list);
        fs_info->fs_devices->num_devices--;
@@ -1724,9 +1726,13 @@ void btrfs_rm_dev_replace_srcdev(struct btrfs_fs_info *fs_info,
        }
        if (srcdev->can_discard)
                fs_info->fs_devices->num_can_discard--;
-       if (srcdev->bdev)
+       if (srcdev->bdev) {
                fs_info->fs_devices->open_devices--;
 
+               /* zero out the old super */
+               btrfs_scratch_superblock(srcdev);
+       }
+
        call_rcu(&srcdev->rcu, free_device);
 }
 
index 4d74335..6024877 100644 (file)
@@ -1005,9 +1005,19 @@ grow_dev_page(struct block_device *bdev, sector_t block,
        struct buffer_head *bh;
        sector_t end_block;
        int ret = 0;            /* Will call free_more_memory() */
+       gfp_t gfp_mask;
 
-       page = find_or_create_page(inode->i_mapping, index,
-               (mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS)|__GFP_MOVABLE);
+       gfp_mask = mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS;
+       gfp_mask |= __GFP_MOVABLE;
+       /*
+        * XXX: __getblk_slow() can not really deal with failure and
+        * will endlessly loop on improvised global reclaim.  Prefer
+        * looping in the allocator rather than here, at least that
+        * code knows what it's doing.
+        */
+       gfp_mask |= __GFP_NOFAIL;
+
+       page = find_or_create_page(inode->i_mapping, index, gfp_mask);
        if (!page)
                return ret;
 
index 25badd1..f4a08d7 100644 (file)
@@ -56,7 +56,7 @@ void __cachefiles_printk_object(struct cachefiles_object *object,
                       object->fscache.cookie->parent,
                       object->fscache.cookie->netfs_data,
                       object->fscache.cookie->flags);
-               if (keybuf)
+               if (keybuf && cookie->def)
                        keylen = cookie->def->get_key(cookie->netfs_data, keybuf,
                                                      CACHEFILES_KEYBUF_SIZE);
                else
index 34c88b8..12b0eef 100644 (file)
@@ -162,8 +162,9 @@ int cachefiles_update_object_xattr(struct cachefiles_object *object,
 int cachefiles_check_auxdata(struct cachefiles_object *object)
 {
        struct cachefiles_xattr *auxbuf;
+       enum fscache_checkaux validity;
        struct dentry *dentry = object->dentry;
-       unsigned int dlen;
+       ssize_t xlen;
        int ret;
 
        ASSERT(dentry);
@@ -174,22 +175,22 @@ int cachefiles_check_auxdata(struct cachefiles_object *object)
        if (!auxbuf)
                return -ENOMEM;
 
-       auxbuf->len = vfs_getxattr(dentry, cachefiles_xattr_cache,
-                                  &auxbuf->type, 512 + 1);
-       if (auxbuf->len < 1)
-               return -ESTALE;
-
-       if (auxbuf->type != object->fscache.cookie->def->type)
-               return -ESTALE;
+       xlen = vfs_getxattr(dentry, cachefiles_xattr_cache,
+                           &auxbuf->type, 512 + 1);
+       ret = -ESTALE;
+       if (xlen < 1 ||
+           auxbuf->type != object->fscache.cookie->def->type)
+               goto error;
 
-       dlen = auxbuf->len - 1;
-       ret = fscache_check_aux(&object->fscache, &auxbuf->data, dlen);
+       xlen--;
+       validity = fscache_check_aux(&object->fscache, &auxbuf->data, xlen);
+       if (validity != FSCACHE_CHECKAUX_OKAY)
+               goto error;
 
+       ret = 0;
+error:
        kfree(auxbuf);
-       if (ret != FSCACHE_CHECKAUX_OKAY)
-               return -ESTALE;
-
-       return 0;
+       return ret;
 }
 
 /*
index a16b4e5..77fc5e1 100644 (file)
@@ -120,14 +120,16 @@ cifs_read_super(struct super_block *sb)
 {
        struct inode *inode;
        struct cifs_sb_info *cifs_sb;
+       struct cifs_tcon *tcon;
        int rc = 0;
 
        cifs_sb = CIFS_SB(sb);
+       tcon = cifs_sb_master_tcon(cifs_sb);
 
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
                sb->s_flags |= MS_POSIXACL;
 
-       if (cifs_sb_master_tcon(cifs_sb)->ses->capabilities & CAP_LARGE_FILES)
+       if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files)
                sb->s_maxbytes = MAX_LFS_FILESIZE;
        else
                sb->s_maxbytes = MAX_NON_LFS;
@@ -147,7 +149,7 @@ cifs_read_super(struct super_block *sb)
                goto out_no_root;
        }
 
-       if (cifs_sb_master_tcon(cifs_sb)->nocase)
+       if (tcon->nocase)
                sb->s_d_op = &cifs_ci_dentry_ops;
        else
                sb->s_d_op = &cifs_dentry_ops;
index ea723a5..6d0b072 100644 (file)
@@ -132,5 +132,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 extern const struct export_operations cifs_export_ops;
 #endif /* CONFIG_CIFS_NFSD_EXPORT */
 
-#define CIFS_VERSION   "2.01"
+#define CIFS_VERSION   "2.02"
 #endif                         /* _CIFSFS_H */
index cfa14c8..52b6f6c 100644 (file)
@@ -547,9 +547,6 @@ struct TCP_Server_Info {
        unsigned int max_rw;    /* maxRw specifies the maximum */
        /* message size the server can send or receive for */
        /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
-       unsigned int max_vcs;   /* maximum number of smb sessions, at least
-                                  those that can be specified uniquely with
-                                  vcnumbers */
        unsigned int capabilities; /* selective disabling of caps by smb sess */
        int timeAdj;  /* Adjust for difference in server time zone in sec */
        __u64 CurrentMid;         /* multiplex id - rotating counter */
@@ -715,7 +712,6 @@ struct cifs_ses {
        enum statusEnum status;
        unsigned overrideSecFlg;  /* if non-zero override global sec flags */
        __u16 ipc_tid;          /* special tid for connection to IPC share */
-       __u16 vcnum;
        char *serverOS;         /* name of operating system underlying server */
        char *serverNOS;        /* name of network operating system of server */
        char *serverDomain;     /* security realm of server */
@@ -1272,6 +1268,7 @@ struct dfs_info3_param {
 #define CIFS_FATTR_DELETE_PENDING      0x2
 #define CIFS_FATTR_NEED_REVAL          0x4
 #define CIFS_FATTR_INO_COLLISION       0x8
+#define CIFS_FATTR_UNKNOWN_NLINK       0x10
 
 struct cifs_fattr {
        u32             cf_flags;
index 948676d..08f9dfb 100644 (file)
@@ -1491,15 +1491,30 @@ struct file_notify_information {
        __u8  FileName[0];
 } __attribute__((packed));
 
-struct reparse_data {
-       __u32   ReparseTag;
-       __u16   ReparseDataLength;
+/* For IO_REPARSE_TAG_SYMLINK */
+struct reparse_symlink_data {
+       __le32  ReparseTag;
+       __le16  ReparseDataLength;
        __u16   Reserved;
-       __u16   SubstituteNameOffset;
-       __u16   SubstituteNameLength;
-       __u16   PrintNameOffset;
-       __u16   PrintNameLength;
-       __u32   Flags;
+       __le16  SubstituteNameOffset;
+       __le16  SubstituteNameLength;
+       __le16  PrintNameOffset;
+       __le16  PrintNameLength;
+       __le32  Flags;
+       char    PathBuffer[0];
+} __attribute__((packed));
+
+/* For IO_REPARSE_TAG_NFS */
+#define NFS_SPECFILE_LNK       0x00000000014B4E4C
+#define NFS_SPECFILE_CHR       0x0000000000524843
+#define NFS_SPECFILE_BLK       0x00000000004B4C42
+#define NFS_SPECFILE_FIFO      0x000000004F464946
+#define NFS_SPECFILE_SOCK      0x000000004B434F53
+struct reparse_posix_data {
+       __le32  ReparseTag;
+       __le16  ReparseDataLength;
+       __u16   Reserved;
+       __le64  InodeType; /* LNK, FIFO, CHR etc. */
        char    PathBuffer[0];
 } __attribute__((packed));
 
@@ -2652,26 +2667,7 @@ typedef struct file_xattr_info {
 } __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info
                                              level 0x205 */
 
-
-/* flags for chattr command */
-#define EXT_SECURE_DELETE              0x00000001 /* EXT3_SECRM_FL */
-#define EXT_ENABLE_UNDELETE            0x00000002 /* EXT3_UNRM_FL */
-/* Reserved for compress file 0x4 */
-#define EXT_SYNCHRONOUS                        0x00000008 /* EXT3_SYNC_FL */
-#define EXT_IMMUTABLE_FL               0x00000010 /* EXT3_IMMUTABLE_FL */
-#define EXT_OPEN_APPEND_ONLY           0x00000020 /* EXT3_APPEND_FL */
-#define EXT_DO_NOT_BACKUP              0x00000040 /* EXT3_NODUMP_FL */
-#define EXT_NO_UPDATE_ATIME            0x00000080 /* EXT3_NOATIME_FL */
-/* 0x100 through 0x800 reserved for compression flags and are GET-ONLY */
-#define EXT_HASH_TREE_INDEXED_DIR      0x00001000 /* GET-ONLY EXT3_INDEX_FL */
-/* 0x2000 reserved for IMAGIC_FL */
-#define EXT_JOURNAL_THIS_FILE  0x00004000 /* GET-ONLY EXT3_JOURNAL_DATA_FL */
-/* 0x8000 reserved for EXT3_NOTAIL_FL */
-#define EXT_SYNCHRONOUS_DIR            0x00010000 /* EXT3_DIRSYNC_FL */
-#define EXT_TOPDIR                     0x00020000 /* EXT3_TOPDIR_FL */
-
-#define EXT_SET_MASK                   0x000300FF
-#define EXT_GET_MASK                   0x0003DFFF
+/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */
 
 typedef struct file_chattr_info {
        __le64  mask; /* list of all possible attribute bits */
index a3d74fe..ccd31ab 100644 (file)
@@ -463,7 +463,6 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
                               cifs_max_pending);
        set_credits(server, server->maxReq);
        server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
-       server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
        /* even though we do not use raw we might as well set this
        accurately, in case we ever find a need for it */
        if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
@@ -3089,7 +3088,8 @@ CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
        bool is_unicode;
        unsigned int sub_len;
        char *sub_start;
-       struct reparse_data *reparse_buf;
+       struct reparse_symlink_data *reparse_buf;
+       struct reparse_posix_data *posix_buf;
        __u32 data_offset, data_count;
        char *end_of_smb;
 
@@ -3138,20 +3138,47 @@ CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
                goto qreparse_out;
        }
        end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
-       reparse_buf = (struct reparse_data *)
+       reparse_buf = (struct reparse_symlink_data *)
                                ((char *)&pSMBr->hdr.Protocol + data_offset);
        if ((char *)reparse_buf >= end_of_smb) {
                rc = -EIO;
                goto qreparse_out;
        }
-       if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset +
-                               reparse_buf->PrintNameLength) > end_of_smb) {
+       if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) {
+               cifs_dbg(FYI, "NFS style reparse tag\n");
+               posix_buf =  (struct reparse_posix_data *)reparse_buf;
+
+               if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) {
+                       cifs_dbg(FYI, "unsupported file type 0x%llx\n",
+                                le64_to_cpu(posix_buf->InodeType));
+                       rc = -EOPNOTSUPP;
+                       goto qreparse_out;
+               }
+               is_unicode = true;
+               sub_len = le16_to_cpu(reparse_buf->ReparseDataLength);
+               if (posix_buf->PathBuffer + sub_len > end_of_smb) {
+                       cifs_dbg(FYI, "reparse buf beyond SMB\n");
+                       rc = -EIO;
+                       goto qreparse_out;
+               }
+               *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer,
+                               sub_len, is_unicode, nls_codepage);
+               goto qreparse_out;
+       } else if (reparse_buf->ReparseTag !=
+                       cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) {
+               rc = -EOPNOTSUPP;
+               goto qreparse_out;
+       }
+
+       /* Reparse tag is NTFS symlink */
+       sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) +
+                               reparse_buf->PathBuffer;
+       sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength);
+       if (sub_start + sub_len > end_of_smb) {
                cifs_dbg(FYI, "reparse buf beyond SMB\n");
                rc = -EIO;
                goto qreparse_out;
        }
-       sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer;
-       sub_len = reparse_buf->SubstituteNameLength;
        if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
                is_unicode = true;
        else
index eb955b5..7ddddf2 100644 (file)
@@ -3254,6 +3254,9 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
        /*
         * Reads as many pages as possible from fscache. Returns -ENOBUFS
         * immediately if the cookie is negative
+        *
+        * After this point, every page in the list might have PG_fscache set,
+        * so we will need to clean that up off of every page we don't use.
         */
        rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list,
                                         &num_pages);
@@ -3376,6 +3379,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                kref_put(&rdata->refcount, cifs_readdata_release);
        }
 
+       /* Any pages that have been shown to fscache but didn't get added to
+        * the pagecache must be uncached before they get returned to the
+        * allocator.
+        */
+       cifs_fscache_readpages_cancel(mapping->host, page_list);
        return rc;
 }
 
index 2f4bc5a..b3258f3 100644 (file)
@@ -223,6 +223,13 @@ void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)
                fscache_uncache_page(CIFS_I(inode)->fscache, page);
 }
 
+void __cifs_fscache_readpages_cancel(struct inode *inode, struct list_head *pages)
+{
+       cifs_dbg(FYI, "%s: (fsc: %p, i: %p)\n",
+                __func__, CIFS_I(inode)->fscache, inode);
+       fscache_readpages_cancel(CIFS_I(inode)->fscache, pages);
+}
+
 void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode)
 {
        struct cifsInodeInfo *cifsi = CIFS_I(inode);
index 6353932..24794b6 100644 (file)
@@ -54,6 +54,7 @@ extern int __cifs_readpages_from_fscache(struct inode *,
                                         struct address_space *,
                                         struct list_head *,
                                         unsigned *);
+extern void __cifs_fscache_readpages_cancel(struct inode *, struct list_head *);
 
 extern void __cifs_readpage_to_fscache(struct inode *, struct page *);
 
@@ -91,6 +92,13 @@ static inline void cifs_readpage_to_fscache(struct inode *inode,
                __cifs_readpage_to_fscache(inode, page);
 }
 
+static inline void cifs_fscache_readpages_cancel(struct inode *inode,
+                                                struct list_head *pages)
+{
+       if (CIFS_I(inode)->fscache)
+               return __cifs_fscache_readpages_cancel(inode, pages);
+}
+
 #else /* CONFIG_CIFS_FSCACHE */
 static inline int cifs_fscache_register(void) { return 0; }
 static inline void cifs_fscache_unregister(void) {}
@@ -131,6 +139,11 @@ static inline int cifs_readpages_from_fscache(struct inode *inode,
 static inline void cifs_readpage_to_fscache(struct inode *inode,
                        struct page *page) {}
 
+static inline void cifs_fscache_readpages_cancel(struct inode *inode,
+                                                struct list_head *pages)
+{
+}
+
 #endif /* CONFIG_CIFS_FSCACHE */
 
 #endif /* _CIFS_FSCACHE_H */
index f9ff9c1..867b7cd 100644 (file)
@@ -120,6 +120,33 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
        cifs_i->invalid_mapping = true;
 }
 
+/*
+ * copy nlink to the inode, unless it wasn't provided.  Provide
+ * sane values if we don't have an existing one and none was provided
+ */
+static void
+cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
+{
+       /*
+        * if we're in a situation where we can't trust what we
+        * got from the server (readdir, some non-unix cases)
+        * fake reasonable values
+        */
+       if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) {
+               /* only provide fake values on a new inode */
+               if (inode->i_state & I_NEW) {
+                       if (fattr->cf_cifsattrs & ATTR_DIRECTORY)
+                               set_nlink(inode, 2);
+                       else
+                               set_nlink(inode, 1);
+               }
+               return;
+       }
+
+       /* we trust the server, so update it */
+       set_nlink(inode, fattr->cf_nlink);
+}
+
 /* populate an inode with info from a cifs_fattr struct */
 void
 cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
@@ -134,7 +161,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
        inode->i_mtime = fattr->cf_mtime;
        inode->i_ctime = fattr->cf_ctime;
        inode->i_rdev = fattr->cf_rdev;
-       set_nlink(inode, fattr->cf_nlink);
+       cifs_nlink_fattr_to_inode(inode, fattr);
        inode->i_uid = fattr->cf_uid;
        inode->i_gid = fattr->cf_gid;
 
@@ -541,6 +568,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
        fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
        fattr->cf_createtime = le64_to_cpu(info->CreationTime);
 
+       fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
        if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
                fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
                fattr->cf_dtype = DT_DIR;
@@ -548,7 +576,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
                 * Server can return wrong NumberOfLinks value for directories
                 * when Unix extensions are disabled - fake it.
                 */
-               fattr->cf_nlink = 2;
+               if (!tcon->unix_ext)
+                       fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
        } else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
                fattr->cf_mode = S_IFLNK;
                fattr->cf_dtype = DT_LNK;
@@ -561,11 +590,15 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
                if (fattr->cf_cifsattrs & ATTR_READONLY)
                        fattr->cf_mode &= ~(S_IWUGO);
 
-               fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
-               if (fattr->cf_nlink < 1) {
-                       cifs_dbg(1, "replacing bogus file nlink value %u\n",
+               /*
+                * Don't accept zero nlink from non-unix servers unless
+                * delete is pending.  Instead mark it as unknown.
+                */
+               if ((fattr->cf_nlink < 1) && !tcon->unix_ext &&
+                   !info->DeletePending) {
+                       cifs_dbg(1, "bogus file nlink value %u\n",
                                fattr->cf_nlink);
-                       fattr->cf_nlink = 1;
+                       fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
                }
        }
 
index af847e1..651a527 100644 (file)
@@ -780,7 +780,9 @@ static const struct {
        ERRDOS, ERRnoaccess, 0xc0000290}, {
        ERRDOS, ERRbadfunc, 0xc000029c}, {
        ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, {
-       ERRDOS, ERRinvlevel, 0x007c0001}, };
+       ERRDOS, ERRinvlevel, 0x007c0001}, {
+       0, 0, 0 }
+};
 
 /*****************************************************************************
  Print an error message from the status code
index 42ef03b..53a75f3 100644 (file)
@@ -180,6 +180,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
                fattr->cf_dtype = DT_REG;
        }
 
+       /* non-unix readdir doesn't provide nlink */
+       fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
+
        if (fattr->cf_cifsattrs & ATTR_READONLY)
                fattr->cf_mode &= ~S_IWUGO;
 
index 5f99b7f..e87387d 100644 (file)
 #include <linux/slab.h>
 #include "cifs_spnego.h"
 
-/*
- * Checks if this is the first smb session to be reconnected after
- * the socket has been reestablished (so we know whether to use vc 0).
- * Called while holding the cifs_tcp_ses_lock, so do not block
- */
-static bool is_first_ses_reconnect(struct cifs_ses *ses)
-{
-       struct list_head *tmp;
-       struct cifs_ses *tmp_ses;
-
-       list_for_each(tmp, &ses->server->smb_ses_list) {
-               tmp_ses = list_entry(tmp, struct cifs_ses,
-                                    smb_ses_list);
-               if (tmp_ses->need_reconnect == false)
-                       return false;
-       }
-       /* could not find a session that was already connected,
-          this must be the first one we are reconnecting */
-       return true;
-}
-
-/*
- *     vc number 0 is treated specially by some servers, and should be the
- *      first one we request.  After that we can use vcnumbers up to maxvcs,
- *     one for each smb session (some Windows versions set maxvcs incorrectly
- *     so maxvc=1 can be ignored).  If we have too many vcs, we can reuse
- *     any vc but zero (some servers reset the connection on vcnum zero)
- *
- */
-static __le16 get_next_vcnum(struct cifs_ses *ses)
-{
-       __u16 vcnum = 0;
-       struct list_head *tmp;
-       struct cifs_ses *tmp_ses;
-       __u16 max_vcs = ses->server->max_vcs;
-       __u16 i;
-       int free_vc_found = 0;
-
-       /* Quoting the MS-SMB specification: "Windows-based SMB servers set this
-       field to one but do not enforce this limit, which allows an SMB client
-       to establish more virtual circuits than allowed by this value ... but
-       other server implementations can enforce this limit." */
-       if (max_vcs < 2)
-               max_vcs = 0xFFFF;
-
-       spin_lock(&cifs_tcp_ses_lock);
-       if ((ses->need_reconnect) && is_first_ses_reconnect(ses))
-                       goto get_vc_num_exit;  /* vcnum will be zero */
-       for (i = ses->server->srv_count - 1; i < max_vcs; i++) {
-               if (i == 0) /* this is the only connection, use vc 0 */
-                       break;
-
-               free_vc_found = 1;
-
-               list_for_each(tmp, &ses->server->smb_ses_list) {
-                       tmp_ses = list_entry(tmp, struct cifs_ses,
-                                            smb_ses_list);
-                       if (tmp_ses->vcnum == i) {
-                               free_vc_found = 0;
-                               break; /* found duplicate, try next vcnum */
-                       }
-               }
-               if (free_vc_found)
-                       break; /* we found a vcnumber that will work - use it */
-       }
-
-       if (i == 0)
-               vcnum = 0; /* for most common case, ie if one smb session, use
-                             vc zero.  Also for case when no free vcnum, zero
-                             is safest to send (some clients only send zero) */
-       else if (free_vc_found == 0)
-               vcnum = 1;  /* we can not reuse vc=0 safely, since some servers
-                               reset all uids on that, but 1 is ok. */
-       else
-               vcnum = i;
-       ses->vcnum = vcnum;
-get_vc_num_exit:
-       spin_unlock(&cifs_tcp_ses_lock);
-
-       return cpu_to_le16(vcnum);
-}
-
 static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
 {
        __u32 capabilities = 0;
@@ -128,7 +46,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
                                        CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
                                        USHRT_MAX));
        pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
-       pSMB->req.VcNumber = get_next_vcnum(ses);
+       pSMB->req.VcNumber = __constant_cpu_to_le16(1);
 
        /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
 
@@ -582,9 +500,9 @@ select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
                                return NTLMv2;
                        if (global_secflags & CIFSSEC_MAY_NTLM)
                                return NTLM;
-                       /* Fallthrough */
                default:
-                       return Unspecified;
+                       /* Fallthrough to attempt LANMAN authentication next */
+                       break;
                }
        case CIFS_NEGFLAVOR_LANMAN:
                switch (requested) {
index eba0efd..edccb52 100644 (file)
@@ -687,6 +687,10 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
        else
                return -EIO;
 
+       /* no need to send SMB logoff if uid already closed due to reconnect */
+       if (ses->need_reconnect)
+               goto smb2_session_already_dead;
+
        rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req);
        if (rc)
                return rc;
@@ -701,6 +705,8 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
         * No tcon so can't do
         * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
         */
+
+smb2_session_already_dead:
        return rc;
 }
 
index d952ee4..a4b2391 100644 (file)
 #define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */
 #define FSCTL_SRV_READ_HASH          0x001441BB /* BB add struct */
 
+/* See FSCC 2.1.2.5 */
 #define IO_REPARSE_TAG_MOUNT_POINT   0xA0000003
 #define IO_REPARSE_TAG_HSM           0xC0000004
 #define IO_REPARSE_TAG_SIS           0x80000007
+#define IO_REPARSE_TAG_HSM2          0x80000006
+#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005
+/* Used by the DFS filter. See MS-DFSC */
+#define IO_REPARSE_TAG_DFS           0x8000000A
+/* Used by the DFS filter See MS-DFSC */
+#define IO_REPARSE_TAG_DFSR          0x80000012
+#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B
+/* See section MS-FSCC 2.1.2.4 */
+#define IO_REPARSE_TAG_SYMLINK       0xA000000C
+#define IO_REPARSE_TAG_DEDUP         0x80000013
+#define IO_REPARSE_APPXSTREAM       0xC0000014
+/* NFS symlinks, Win 8/SMB3 and later */
+#define IO_REPARSE_TAG_NFS           0x80000014
 
 /* fsctl flags */
 /* If Flags is set to this value, the request is an FSCTL not ioctl request */
index 6fdcb1b..800b938 100644 (file)
@@ -410,8 +410,13 @@ static int
 wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
                      const int optype)
 {
-       return wait_for_free_credits(server, timeout,
-                               server->ops->get_credits_field(server, optype));
+       int *val;
+
+       val = server->ops->get_credits_field(server, optype);
+       /* Since an echo is already inflight, no need to wait to send another */
+       if (*val <= 0 && optype == CIFS_ECHO_OP)
+               return -EAGAIN;
+       return wait_for_free_credits(server, timeout, val);
 }
 
 static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
index 4100030..ae6ebb8 100644 (file)
@@ -542,7 +542,7 @@ EXPORT_SYMBOL(d_drop);
  * If ref is non-zero, then decrement the refcount too.
  * Returns dentry requiring refcount drop, or NULL if we're done.
  */
-static inline struct dentry *
+static struct dentry *
 dentry_kill(struct dentry *dentry, int unlock_on_failure)
        __releases(dentry->d_lock)
 {
@@ -630,7 +630,8 @@ repeat:
                        goto kill_it;
        }
 
-       dentry->d_flags |= DCACHE_REFERENCED;
+       if (!(dentry->d_flags & DCACHE_REFERENCED))
+               dentry->d_flags |= DCACHE_REFERENCED;
        dentry_lru_add(dentry);
 
        dentry->d_lockref.count--;
@@ -1331,14 +1332,6 @@ rename_retry:
  * list is non-empty and continue searching.
  */
 
-/**
- * have_submounts - check for mounts over a dentry
- * @parent: dentry to check.
- *
- * Return true if the parent or its subdirectories contain
- * a mount point
- */
-
 static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
 {
        int *ret = data;
@@ -1349,6 +1342,13 @@ static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
        return D_WALK_CONTINUE;
 }
 
+/**
+ * have_submounts - check for mounts over a dentry
+ * @parent: dentry to check.
+ *
+ * Return true if the parent or its subdirectories contain
+ * a mount point
+ */
 int have_submounts(struct dentry *parent)
 {
        int ret = 0;
index c88e355..000eae2 100644 (file)
@@ -408,7 +408,7 @@ static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat,
                                    struct page *page)
 {
        return ecryptfs_lower_header_size(crypt_stat) +
-              (page->index << PAGE_CACHE_SHIFT);
+              ((loff_t)page->index << PAGE_CACHE_SHIFT);
 }
 
 /**
index 7d52806..4725a07 100644 (file)
@@ -1149,7 +1149,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
        struct ecryptfs_msg_ctx *msg_ctx;
        struct ecryptfs_message *msg = NULL;
        char *auth_tok_sig;
-       char *payload;
+       char *payload = NULL;
        size_t payload_len = 0;
        int rc;
 
@@ -1203,6 +1203,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok,
        }
 out:
        kfree(msg);
+       kfree(payload);
        return rc;
 }
 
index 473e09d..810c28f 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/mutex.h>
 #include <linux/anon_inodes.h>
 #include <linux/device.h>
-#include <linux/freezer.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/mman.h>
@@ -1605,8 +1604,7 @@ fetch_events:
                        }
 
                        spin_unlock_irqrestore(&ep->lock, flags);
-                       if (!freezable_schedule_hrtimeout_range(to, slack,
-                                                               HRTIMER_MODE_ABS))
+                       if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
                                timed_out = 1;
 
                        spin_lock_irqsave(&ep->lock, flags);
index 1194b1f..f8cde46 100644 (file)
@@ -1783,7 +1783,7 @@ retry:
                d_tmpfile(dentry, inode);
                err = ext3_orphan_add(handle, inode);
                if (err)
-                       goto err_drop_inode;
+                       goto err_unlock_inode;
                mark_inode_dirty(inode);
                unlock_new_inode(inode);
        }
@@ -1791,10 +1791,9 @@ retry:
        if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
                goto retry;
        return err;
-err_drop_inode:
+err_unlock_inode:
        ext3_journal_stop(handle);
        unlock_new_inode(inode);
-       iput(inode);
        return err;
 }
 
index 0d424d7..e274e9c 100644 (file)
@@ -2563,7 +2563,7 @@ retry:
                        break;
        }
        blk_finish_plug(&plug);
-       if (!ret && !cycled) {
+       if (!ret && !cycled && wbc->nr_to_write > 0) {
                cycled = 1;
                mpd.last_page = writeback_index - 1;
                mpd.first_page = 0;
index 1bec5a5..5a0408d 100644 (file)
@@ -2319,7 +2319,7 @@ retry:
                d_tmpfile(dentry, inode);
                err = ext4_orphan_add(handle, inode);
                if (err)
-                       goto err_drop_inode;
+                       goto err_unlock_inode;
                mark_inode_dirty(inode);
                unlock_new_inode(inode);
        }
@@ -2328,10 +2328,9 @@ retry:
        if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
                goto retry;
        return err;
-err_drop_inode:
+err_unlock_inode:
        ext4_journal_stop(handle);
        unlock_new_inode(inode);
-       iput(inode);
        return err;
 }
 
index c081e34..03e9beb 100644 (file)
@@ -1350,6 +1350,8 @@ retry:
                                    s_min_extra_isize) {
                                        tried_min_extra_isize++;
                                        new_extra_isize = s_min_extra_isize;
+                                       kfree(is); is = NULL;
+                                       kfree(bs); bs = NULL;
                                        goto retry;
                                }
                                error = -1;
index abdd15a..e900ca5 100644 (file)
@@ -297,7 +297,7 @@ void flush_delayed_fput(void)
        delayed_fput(NULL);
 }
 
-static DECLARE_WORK(delayed_fput_work, delayed_fput);
+static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
 
 void fput(struct file *file)
 {
@@ -317,7 +317,7 @@ void fput(struct file *file)
                }
 
                if (llist_add(&file->f_u.fu_llist, &delayed_fput_list))
-                       schedule_work(&delayed_fput_work);
+                       schedule_delayed_work(&delayed_fput_work, 1);
        }
 }
 
index 62b43b5..b7989f2 100644 (file)
@@ -182,6 +182,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
        struct inode *inode;
        struct dentry *parent;
        struct fuse_conn *fc;
+       struct fuse_inode *fi;
        int ret;
 
        inode = ACCESS_ONCE(entry->d_inode);
@@ -228,7 +229,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
                if (!err && !outarg.nodeid)
                        err = -ENOENT;
                if (!err) {
-                       struct fuse_inode *fi = get_fuse_inode(inode);
+                       fi = get_fuse_inode(inode);
                        if (outarg.nodeid != get_node_id(inode)) {
                                fuse_queue_forget(fc, forget, outarg.nodeid, 1);
                                goto invalid;
@@ -246,8 +247,11 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
                                       attr_version);
                fuse_change_entry_timeout(entry, &outarg);
        } else if (inode) {
-               fc = get_fuse_conn(inode);
-               if (fc->readdirplus_auto) {
+               fi = get_fuse_inode(inode);
+               if (flags & LOOKUP_RCU) {
+                       if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
+                               return -ECHILD;
+               } else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
                        parent = dget_parent(entry);
                        fuse_advise_use_readdirplus(parent->d_inode);
                        dput(parent);
@@ -259,7 +263,8 @@ out:
 
 invalid:
        ret = 0;
-       if (check_submounts_and_drop(entry) != 0)
+
+       if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0)
                ret = 1;
        goto out;
 }
@@ -1063,6 +1068,8 @@ static int fuse_access(struct inode *inode, int mask)
        struct fuse_access_in inarg;
        int err;
 
+       BUG_ON(mask & MAY_NOT_BLOCK);
+
        if (fc->no_access)
                return 0;
 
@@ -1150,9 +1157,6 @@ static int fuse_permission(struct inode *inode, int mask)
                   noticed immediately, only after the attribute
                   timeout has expired */
        } else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
-               if (mask & MAY_NOT_BLOCK)
-                       return -ECHILD;
-
                err = fuse_access(inode, mask);
        } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
                if (!(inode->i_mode & S_IXUGO)) {
@@ -1291,6 +1295,8 @@ static int fuse_direntplus_link(struct file *file,
        }
 
 found:
+       if (fc->readdirplus_auto)
+               set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
        fuse_change_entry_timeout(dentry, o);
 
        err = 0;
index d409dea..4598345 100644 (file)
@@ -2467,6 +2467,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
 {
        struct fuse_file *ff = file->private_data;
        struct inode *inode = file->f_inode;
+       struct fuse_inode *fi = get_fuse_inode(inode);
        struct fuse_conn *fc = ff->fc;
        struct fuse_req *req;
        struct fuse_fallocate_in inarg = {
@@ -2484,10 +2485,20 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
 
        if (lock_inode) {
                mutex_lock(&inode->i_mutex);
-               if (mode & FALLOC_FL_PUNCH_HOLE)
-                       fuse_set_nowrite(inode);
+               if (mode & FALLOC_FL_PUNCH_HOLE) {
+                       loff_t endbyte = offset + length - 1;
+                       err = filemap_write_and_wait_range(inode->i_mapping,
+                                                          offset, endbyte);
+                       if (err)
+                               goto out;
+
+                       fuse_sync_writes(inode);
+               }
        }
 
+       if (!(mode & FALLOC_FL_KEEP_SIZE))
+               set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+
        req = fuse_get_req_nopages(fc);
        if (IS_ERR(req)) {
                err = PTR_ERR(req);
@@ -2520,11 +2531,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
        fuse_invalidate_attr(inode);
 
 out:
-       if (lock_inode) {
-               if (mode & FALLOC_FL_PUNCH_HOLE)
-                       fuse_release_nowrite(inode);
+       if (!(mode & FALLOC_FL_KEEP_SIZE))
+               clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+
+       if (lock_inode)
                mutex_unlock(&inode->i_mutex);
-       }
 
        return err;
 }
index 5ced199..5b9e6f3 100644 (file)
@@ -115,6 +115,8 @@ struct fuse_inode {
 enum {
        /** Advise readdirplus  */
        FUSE_I_ADVISE_RDPLUS,
+       /** Initialized with readdirplus */
+       FUSE_I_INIT_RDPLUS,
        /** An operation changing file size is in progress  */
        FUSE_I_SIZE_UNSTABLE,
 };
index c1a3e60..7f464c5 100644 (file)
@@ -95,7 +95,7 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
 
        if (insert_inode_locked(inode) < 0) {
                rc = -EINVAL;
-               goto fail_unlock;
+               goto fail_put;
        }
 
        inode_init_owner(inode, parent, mode);
@@ -156,7 +156,6 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
 fail_drop:
        dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
-fail_unlock:
        clear_nlink(inode);
        unlock_new_inode(inode);
 fail_put:
index 645268f..caa2805 100644 (file)
@@ -2294,10 +2294,11 @@ out:
  * path_mountpoint - look up a path to be umounted
  * @dfd:       directory file descriptor to start walk from
  * @name:      full pathname to walk
+ * @path:      pointer to container for result
  * @flags:     lookup flags
  *
  * Look up the given name, but don't attempt to revalidate the last component.
- * Returns 0 and "path" will be valid on success; Retuns error otherwise.
+ * Returns 0 and "path" will be valid on success; Returns error otherwise.
  */
 static int
 path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags)
index 854a8f0..02b0df7 100644 (file)
@@ -1458,7 +1458,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
 
        trace_nfs_atomic_open_enter(dir, ctx, open_flags);
        nfs_block_sillyrename(dentry->d_parent);
-       inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
+       inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr, opened);
        nfs_unblock_sillyrename(dentry->d_parent);
        if (IS_ERR(inode)) {
                err = PTR_ERR(inode);
index e5b804d..77efaf1 100644 (file)
@@ -19,6 +19,7 @@ nfs4_file_open(struct inode *inode, struct file *filp)
        struct inode *dir;
        unsigned openflags = filp->f_flags;
        struct iattr attr;
+       int opened = 0;
        int err;
 
        /*
@@ -55,7 +56,7 @@ nfs4_file_open(struct inode *inode, struct file *filp)
                nfs_wb_all(inode);
        }
 
-       inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
+       inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, &opened);
        if (IS_ERR(inode)) {
                err = PTR_ERR(inode);
                switch (err) {
index 95604f6..c7c295e 100644 (file)
@@ -185,6 +185,7 @@ nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds)
        if (status)
                goto out_put;
 
+       smp_wmb();
        ds->ds_clp = clp;
        dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
 out:
@@ -801,34 +802,35 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
        struct nfs4_file_layout_dsaddr *dsaddr = FILELAYOUT_LSEG(lseg)->dsaddr;
        struct nfs4_pnfs_ds *ds = dsaddr->ds_list[ds_idx];
        struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg);
-
-       if (filelayout_test_devid_unavailable(devid))
-               return NULL;
+       struct nfs4_pnfs_ds *ret = ds;
 
        if (ds == NULL) {
                printk(KERN_ERR "NFS: %s: No data server for offset index %d\n",
                        __func__, ds_idx);
                filelayout_mark_devid_invalid(devid);
-               return NULL;
+               goto out;
        }
+       smp_rmb();
        if (ds->ds_clp)
-               return ds;
+               goto out_test_devid;
 
        if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
                struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode);
                int err;
 
                err = nfs4_ds_connect(s, ds);
-               if (err) {
+               if (err)
                        nfs4_mark_deviceid_unavailable(devid);
-                       ds = NULL;
-               }
                nfs4_clear_ds_conn_bit(ds);
        } else {
                /* Either ds is connected, or ds is NULL */
                nfs4_wait_ds_connect(ds);
        }
-       return ds;
+out_test_devid:
+       if (filelayout_test_devid_unavailable(devid))
+               ret = NULL;
+out:
+       return ret;
 }
 
 module_param(dataserver_retrans, uint, 0644);
index 989bb9d..d53d678 100644 (file)
@@ -912,6 +912,7 @@ struct nfs4_opendata {
        struct iattr attrs;
        unsigned long timestamp;
        unsigned int rpc_done : 1;
+       unsigned int file_created : 1;
        unsigned int is_recover : 1;
        int rpc_status;
        int cancelled;
@@ -1946,8 +1947,13 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
 
        nfs_fattr_map_and_free_names(server, &data->f_attr);
 
-       if (o_arg->open_flags & O_CREAT)
+       if (o_arg->open_flags & O_CREAT) {
                update_changeattr(dir, &o_res->cinfo);
+               if (o_arg->open_flags & O_EXCL)
+                       data->file_created = 1;
+               else if (o_res->cinfo.before != o_res->cinfo.after)
+                       data->file_created = 1;
+       }
        if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
                server->caps &= ~NFS_CAP_POSIX_LOCK;
        if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
@@ -2191,7 +2197,8 @@ static int _nfs4_do_open(struct inode *dir,
                        struct nfs_open_context *ctx,
                        int flags,
                        struct iattr *sattr,
-                       struct nfs4_label *label)
+                       struct nfs4_label *label,
+                       int *opened)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
@@ -2261,6 +2268,8 @@ static int _nfs4_do_open(struct inode *dir,
                        nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
                }
        }
+       if (opendata->file_created)
+               *opened |= FILE_CREATED;
 
        if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
                *ctx_th = opendata->f_attr.mdsthreshold;
@@ -2289,7 +2298,8 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
                                        struct nfs_open_context *ctx,
                                        int flags,
                                        struct iattr *sattr,
-                                       struct nfs4_label *label)
+                                       struct nfs4_label *label,
+                                       int *opened)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs4_exception exception = { };
@@ -2297,7 +2307,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
        int status;
 
        do {
-               status = _nfs4_do_open(dir, ctx, flags, sattr, label);
+               status = _nfs4_do_open(dir, ctx, flags, sattr, label, opened);
                res = ctx->state;
                trace_nfs4_open_file(ctx, flags, status);
                if (status == 0)
@@ -2659,7 +2669,8 @@ out:
 }
 
 static struct inode *
-nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
+nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx,
+               int open_flags, struct iattr *attr, int *opened)
 {
        struct nfs4_state *state;
        struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL;
@@ -2667,7 +2678,7 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags
        label = nfs4_label_init_security(dir, ctx->dentry, attr, &l);
 
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, ctx, open_flags, attr, label);
+       state = nfs4_do_open(dir, ctx, open_flags, attr, label, opened);
 
        nfs4_label_release_security(label);
 
@@ -3332,6 +3343,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        struct nfs4_label l, *ilabel = NULL;
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
+       int opened = 0;
        int status = 0;
 
        ctx = alloc_nfs_open_context(dentry, FMODE_READ);
@@ -3341,7 +3353,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
 
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, ctx, flags, sattr, ilabel);
+       state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, &opened);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
                goto out;
@@ -7564,8 +7576,10 @@ nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
 {
        int err;
        struct page *page;
-       rpc_authflavor_t flavor;
+       rpc_authflavor_t flavor = RPC_AUTH_MAXFLAVOR;
        struct nfs4_secinfo_flavors *flavors;
+       struct nfs4_secinfo4 *secinfo;
+       int i;
 
        page = alloc_page(GFP_KERNEL);
        if (!page) {
@@ -7587,9 +7601,31 @@ nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
        if (err)
                goto out_freepage;
 
-       flavor = nfs_find_best_sec(flavors);
-       if (err == 0)
-               err = nfs4_lookup_root_sec(server, fhandle, info, flavor);
+       for (i = 0; i < flavors->num_flavors; i++) {
+               secinfo = &flavors->flavors[i];
+
+               switch (secinfo->flavor) {
+               case RPC_AUTH_NULL:
+               case RPC_AUTH_UNIX:
+               case RPC_AUTH_GSS:
+                       flavor = rpcauth_get_pseudoflavor(secinfo->flavor,
+                                       &secinfo->flavor_info);
+                       break;
+               default:
+                       flavor = RPC_AUTH_MAXFLAVOR;
+                       break;
+               }
+
+               if (flavor != RPC_AUTH_MAXFLAVOR) {
+                       err = nfs4_lookup_root_sec(server, fhandle,
+                                                  info, flavor);
+                       if (!err)
+                               break;
+               }
+       }
+
+       if (flavor == RPC_AUTH_MAXFLAVOR)
+               err = -EPERM;
 
 out_freepage:
        put_page(page);
index 0ba6798..da27664 100644 (file)
@@ -94,6 +94,7 @@ void nilfs_forget_buffer(struct buffer_head *bh)
        clear_buffer_nilfs_volatile(bh);
        clear_buffer_nilfs_checked(bh);
        clear_buffer_nilfs_redirected(bh);
+       clear_buffer_async_write(bh);
        clear_buffer_dirty(bh);
        if (nilfs_page_buffers_clean(page))
                __nilfs_clear_page_dirty(page);
@@ -429,6 +430,7 @@ void nilfs_clear_dirty_page(struct page *page, bool silent)
                                        "discard block %llu, size %zu",
                                        (u64)bh->b_blocknr, bh->b_size);
                        }
+                       clear_buffer_async_write(bh);
                        clear_buffer_dirty(bh);
                        clear_buffer_nilfs_volatile(bh);
                        clear_buffer_nilfs_checked(bh);
index bd88a74..9f6b486 100644 (file)
@@ -665,7 +665,7 @@ static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode,
 
                bh = head = page_buffers(page);
                do {
-                       if (!buffer_dirty(bh))
+                       if (!buffer_dirty(bh) || buffer_async_write(bh))
                                continue;
                        get_bh(bh);
                        list_add_tail(&bh->b_assoc_buffers, listp);
@@ -699,7 +699,8 @@ static void nilfs_lookup_dirty_node_buffers(struct inode *inode,
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        bh = head = page_buffers(pvec.pages[i]);
                        do {
-                               if (buffer_dirty(bh)) {
+                               if (buffer_dirty(bh) &&
+                                               !buffer_async_write(bh)) {
                                        get_bh(bh);
                                        list_add_tail(&bh->b_assoc_buffers,
                                                      listp);
@@ -1579,6 +1580,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci)
 
                list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
                                    b_assoc_buffers) {
+                       set_buffer_async_write(bh);
                        if (bh->b_page != bd_page) {
                                if (bd_page) {
                                        lock_page(bd_page);
@@ -1592,6 +1594,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci)
 
                list_for_each_entry(bh, &segbuf->sb_payload_buffers,
                                    b_assoc_buffers) {
+                       set_buffer_async_write(bh);
                        if (bh == segbuf->sb_super_root) {
                                if (bh->b_page != bd_page) {
                                        lock_page(bd_page);
@@ -1677,6 +1680,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err)
        list_for_each_entry(segbuf, logs, sb_list) {
                list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
                                    b_assoc_buffers) {
+                       clear_buffer_async_write(bh);
                        if (bh->b_page != bd_page) {
                                if (bd_page)
                                        end_page_writeback(bd_page);
@@ -1686,6 +1690,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err)
 
                list_for_each_entry(bh, &segbuf->sb_payload_buffers,
                                    b_assoc_buffers) {
+                       clear_buffer_async_write(bh);
                        if (bh == segbuf->sb_super_root) {
                                if (bh->b_page != bd_page) {
                                        end_page_writeback(bd_page);
@@ -1755,6 +1760,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
                                    b_assoc_buffers) {
                        set_buffer_uptodate(bh);
                        clear_buffer_dirty(bh);
+                       clear_buffer_async_write(bh);
                        if (bh->b_page != bd_page) {
                                if (bd_page)
                                        end_page_writeback(bd_page);
@@ -1776,6 +1782,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
                                    b_assoc_buffers) {
                        set_buffer_uptodate(bh);
                        clear_buffer_dirty(bh);
+                       clear_buffer_async_write(bh);
                        clear_buffer_delay(bh);
                        clear_buffer_nilfs_volatile(bh);
                        clear_buffer_nilfs_redirected(bh);
index ef99972..0d3a97d 100644 (file)
@@ -70,9 +70,10 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry, unsigned int flags)
         */
        if (inode == NULL) {
                unsigned long gen = (unsigned long) dentry->d_fsdata;
-               unsigned long pgen =
-                       OCFS2_I(dentry->d_parent->d_inode)->ip_dir_lock_gen;
-
+               unsigned long pgen;
+               spin_lock(&dentry->d_lock);
+               pgen = OCFS2_I(dentry->d_parent->d_inode)->ip_dir_lock_gen;
+               spin_unlock(&dentry->d_lock);
                trace_ocfs2_dentry_revalidate_negative(dentry->d_name.len,
                                                       dentry->d_name.name,
                                                       pgen, gen);
index 121da2d..d4e81e4 100644 (file)
@@ -1924,7 +1924,7 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
 {
        int tmp, hangup_needed = 0;
        struct ocfs2_super *osb = NULL;
-       char nodestr[8];
+       char nodestr[12];
 
        trace_ocfs2_dismount_volume(sb);
 
index 9f8ef9b..8eaa1ba 100644 (file)
@@ -288,10 +288,14 @@ static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma)
 static unsigned long proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
        struct proc_dir_entry *pde = PDE(file_inode(file));
-       int rv = -EIO;
-       unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+       unsigned long rv = -EIO;
+       unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long) = NULL;
        if (use_pde(pde)) {
-               get_unmapped_area = pde->proc_fops->get_unmapped_area;
+#ifdef CONFIG_MMU
+               get_unmapped_area = current->mm->get_unmapped_area;
+#endif
+               if (pde->proc_fops->get_unmapped_area)
+                       get_unmapped_area = pde->proc_fops->get_unmapped_area;
                if (get_unmapped_area)
                        rv = get_unmapped_area(file, orig_addr, len, pgoff, flags);
                unuse_pde(pde);
index 7366e9d..390bdab 100644 (file)
@@ -941,6 +941,8 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm,
                frame = pte_pfn(pte);
                flags = PM_PRESENT;
                page = vm_normal_page(vma, addr, pte);
+               if (pte_soft_dirty(pte))
+                       flags2 |= __PM_SOFT_DIRTY;
        } else if (is_swap_pte(pte)) {
                swp_entry_t entry;
                if (pte_swp_soft_dirty(pte))
@@ -960,7 +962,7 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm,
 
        if (page && !PageAnon(page))
                flags |= PM_FILE;
-       if ((vma->vm_flags & VM_SOFTDIRTY) || pte_soft_dirty(pte))
+       if ((vma->vm_flags & VM_SOFTDIRTY))
                flags2 |= __PM_SOFT_DIRTY;
 
        *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags);
index 73feacc..fd77703 100644 (file)
@@ -1163,21 +1163,6 @@ static struct reiserfs_journal_list *find_newer_jl_for_cn(struct
        return NULL;
 }
 
-static int newer_jl_done(struct reiserfs_journal_cnode *cn)
-{
-       struct super_block *sb = cn->sb;
-       b_blocknr_t blocknr = cn->blocknr;
-
-       cn = cn->hprev;
-       while (cn) {
-               if (cn->sb == sb && cn->blocknr == blocknr && cn->jlist &&
-                   atomic_read(&cn->jlist->j_commit_left) != 0)
-                                   return 0;
-               cn = cn->hprev;
-       }
-       return 1;
-}
-
 static void remove_journal_hash(struct super_block *,
                                struct reiserfs_journal_cnode **,
                                struct reiserfs_journal_list *, unsigned long,
@@ -1353,7 +1338,6 @@ static int flush_journal_list(struct super_block *s,
                reiserfs_warning(s, "clm-2048", "called with wcount %d",
                                 atomic_read(&journal->j_wcount));
        }
-       BUG_ON(jl->j_trans_id == 0);
 
        /* if flushall == 0, the lock is already held */
        if (flushall) {
@@ -1593,31 +1577,6 @@ static int flush_journal_list(struct super_block *s,
        return err;
 }
 
-static int test_transaction(struct super_block *s,
-                            struct reiserfs_journal_list *jl)
-{
-       struct reiserfs_journal_cnode *cn;
-
-       if (jl->j_len == 0 || atomic_read(&jl->j_nonzerolen) == 0)
-               return 1;
-
-       cn = jl->j_realblock;
-       while (cn) {
-               /* if the blocknr == 0, this has been cleared from the hash,
-                ** skip it
-                */
-               if (cn->blocknr == 0) {
-                       goto next;
-               }
-               if (cn->bh && !newer_jl_done(cn))
-                       return 0;
-             next:
-               cn = cn->next;
-               cond_resched();
-       }
-       return 0;
-}
-
 static int write_one_transaction(struct super_block *s,
                                 struct reiserfs_journal_list *jl,
                                 struct buffer_chunk *chunk)
@@ -1805,6 +1764,8 @@ static int flush_used_journal_lists(struct super_block *s,
                        break;
                tjl = JOURNAL_LIST_ENTRY(tjl->j_list.next);
        }
+       get_journal_list(jl);
+       get_journal_list(flush_jl);
        /* try to find a group of blocks we can flush across all the
         ** transactions, but only bother if we've actually spanned
         ** across multiple lists
@@ -1813,6 +1774,8 @@ static int flush_used_journal_lists(struct super_block *s,
                ret = kupdate_transactions(s, jl, &tjl, &trans_id, len, i);
        }
        flush_journal_list(s, flush_jl, 1);
+       put_journal_list(s, flush_jl);
+       put_journal_list(s, jl);
        return 0;
 }
 
@@ -3868,27 +3831,6 @@ int reiserfs_prepare_for_journal(struct super_block *sb,
        return 1;
 }
 
-static void flush_old_journal_lists(struct super_block *s)
-{
-       struct reiserfs_journal *journal = SB_JOURNAL(s);
-       struct reiserfs_journal_list *jl;
-       struct list_head *entry;
-       time_t now = get_seconds();
-
-       while (!list_empty(&journal->j_journal_list)) {
-               entry = journal->j_journal_list.next;
-               jl = JOURNAL_LIST_ENTRY(entry);
-               /* this check should always be run, to send old lists to disk */
-               if (jl->j_timestamp < (now - (JOURNAL_MAX_TRANS_AGE * 4)) &&
-                   atomic_read(&jl->j_commit_left) == 0 &&
-                   test_transaction(s, jl)) {
-                       flush_used_journal_lists(s, jl);
-               } else {
-                       break;
-               }
-       }
-}
-
 /*
 ** long and ugly.  If flush, will not return until all commit
 ** blocks and all real buffers in the trans are on disk.
@@ -4232,7 +4174,6 @@ static int do_journal_end(struct reiserfs_transaction_handle *th,
                        }
                }
        }
-       flush_old_journal_lists(sb);
 
        journal->j_current_jl->j_list_bitmap =
            get_list_bitmap(sb, journal->j_current_jl);
index 35d4adc..dfd5cb1 100644 (file)
@@ -238,8 +238,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
 
        set_current_state(state);
        if (!pwq->triggered)
-               rc = freezable_schedule_hrtimeout_range(expires, slack,
-                                                       HRTIMER_MODE_ABS);
+               rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
        __set_current_state(TASK_RUNNING);
 
        /*
index 3135c25..a290157 100644 (file)
@@ -328,6 +328,8 @@ loff_t seq_lseek(struct file *file, loff_t offset, int whence)
                                m->read_pos = offset;
                                retval = file->f_pos = offset;
                        }
+               } else {
+                       file->f_pos = offset;
                }
        }
        file->f_version = m->version;
index c219e73..083dc0a 100644 (file)
@@ -94,7 +94,7 @@ retry:
 
 int fd_statfs(int fd, struct kstatfs *st)
 {
-       struct fd f = fdget(fd);
+       struct fd f = fdget_raw(fd);
        int error = -EBADF;
        if (f.file) {
                error = vfs_statfs(&f.file->f_path, st);
index 3a96c97..0225c20 100644 (file)
@@ -264,6 +264,8 @@ out_free_sb:
  */
 static inline void destroy_super(struct super_block *s)
 {
+       list_lru_destroy(&s->s_dentry_lru);
+       list_lru_destroy(&s->s_inode_lru);
 #ifdef CONFIG_SMP
        free_percpu(s->s_files);
 #endif
@@ -323,8 +325,6 @@ void deactivate_locked_super(struct super_block *s)
 
                /* caches are now gone, we can safely kill the shrinker now */
                unregister_shrinker(&s->s_shrink);
-               list_lru_destroy(&s->s_dentry_lru);
-               list_lru_destroy(&s->s_inode_lru);
 
                put_filesystem(fs);
                put_super(s);
index d0c6a00..eda1095 100644 (file)
@@ -487,6 +487,7 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_sb = sb;
        sbi->s_block_base = 0;
        sbi->s_type = FSTYPE_V7;
+       mutex_init(&sbi->s_lock);
        sb->s_fs_info = sbi;
        
        sb_set_blocksize(sb, 512);
index 7e5aae4..6eaf5ed 100644 (file)
@@ -30,18 +30,17 @@ void udf_free_inode(struct inode *inode)
 {
        struct super_block *sb = inode->i_sb;
        struct udf_sb_info *sbi = UDF_SB(sb);
+       struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
 
-       mutex_lock(&sbi->s_alloc_mutex);
-       if (sbi->s_lvid_bh) {
-               struct logicalVolIntegrityDescImpUse *lvidiu =
-                                                       udf_sb_lvidiu(sbi);
+       if (lvidiu) {
+               mutex_lock(&sbi->s_alloc_mutex);
                if (S_ISDIR(inode->i_mode))
                        le32_add_cpu(&lvidiu->numDirs, -1);
                else
                        le32_add_cpu(&lvidiu->numFiles, -1);
                udf_updated_lvid(sb);
+               mutex_unlock(&sbi->s_alloc_mutex);
        }
-       mutex_unlock(&sbi->s_alloc_mutex);
 
        udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1);
 }
@@ -55,6 +54,7 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err)
        uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
        struct udf_inode_info *iinfo;
        struct udf_inode_info *dinfo = UDF_I(dir);
+       struct logicalVolIntegrityDescImpUse *lvidiu;
 
        inode = new_inode(sb);
 
@@ -92,12 +92,10 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err)
                return NULL;
        }
 
-       if (sbi->s_lvid_bh) {
-               struct logicalVolIntegrityDescImpUse *lvidiu;
-
+       lvidiu = udf_sb_lvidiu(sb);
+       if (lvidiu) {
                iinfo->i_unique = lvid_get_unique_id(sb);
                mutex_lock(&sbi->s_alloc_mutex);
-               lvidiu = udf_sb_lvidiu(sbi);
                if (S_ISDIR(mode))
                        le32_add_cpu(&lvidiu->numDirs, 1);
                else
index 839a2ba..9121938 100644 (file)
@@ -94,13 +94,25 @@ static unsigned int udf_count_free(struct super_block *);
 static int udf_statfs(struct dentry *, struct kstatfs *);
 static int udf_show_options(struct seq_file *, struct dentry *);
 
-struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi)
+struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
 {
-       struct logicalVolIntegrityDesc *lvid =
-               (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data;
-       __u32 number_of_partitions = le32_to_cpu(lvid->numOfPartitions);
-       __u32 offset = number_of_partitions * 2 *
-                               sizeof(uint32_t)/sizeof(uint8_t);
+       struct logicalVolIntegrityDesc *lvid;
+       unsigned int partnum;
+       unsigned int offset;
+
+       if (!UDF_SB(sb)->s_lvid_bh)
+               return NULL;
+       lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data;
+       partnum = le32_to_cpu(lvid->numOfPartitions);
+       if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) -
+            offsetof(struct logicalVolIntegrityDesc, impUse)) /
+            (2 * sizeof(uint32_t)) < partnum) {
+               udf_err(sb, "Logical volume integrity descriptor corrupted "
+                       "(numOfPartitions = %u)!\n", partnum);
+               return NULL;
+       }
+       /* The offset is to skip freeSpaceTable and sizeTable arrays */
+       offset = partnum * 2 * sizeof(uint32_t);
        return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]);
 }
 
@@ -629,9 +641,10 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
        struct udf_options uopt;
        struct udf_sb_info *sbi = UDF_SB(sb);
        int error = 0;
+       struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
 
-       if (sbi->s_lvid_bh) {
-               int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev);
+       if (lvidiu) {
+               int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev);
                if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY))
                        return -EACCES;
        }
@@ -1905,11 +1918,12 @@ static void udf_open_lvid(struct super_block *sb)
 
        if (!bh)
                return;
-
-       mutex_lock(&sbi->s_alloc_mutex);
        lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
-       lvidiu = udf_sb_lvidiu(sbi);
+       lvidiu = udf_sb_lvidiu(sb);
+       if (!lvidiu)
+               return;
 
+       mutex_lock(&sbi->s_alloc_mutex);
        lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
        lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
        udf_time_to_disk_stamp(&lvid->recordingDateAndTime,
@@ -1937,10 +1951,12 @@ static void udf_close_lvid(struct super_block *sb)
 
        if (!bh)
                return;
+       lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
+       lvidiu = udf_sb_lvidiu(sb);
+       if (!lvidiu)
+               return;
 
        mutex_lock(&sbi->s_alloc_mutex);
-       lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
-       lvidiu = udf_sb_lvidiu(sbi);
        lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
        lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
        udf_time_to_disk_stamp(&lvid->recordingDateAndTime, CURRENT_TIME);
@@ -2093,15 +2109,19 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
 
        if (sbi->s_lvid_bh) {
                struct logicalVolIntegrityDescImpUse *lvidiu =
-                                                       udf_sb_lvidiu(sbi);
-               uint16_t minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev);
-               uint16_t minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev);
-               /* uint16_t maxUDFWriteRev =
-                               le16_to_cpu(lvidiu->maxUDFWriteRev); */
+                                                       udf_sb_lvidiu(sb);
+               uint16_t minUDFReadRev;
+               uint16_t minUDFWriteRev;
 
+               if (!lvidiu) {
+                       ret = -EINVAL;
+                       goto error_out;
+               }
+               minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev);
+               minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev);
                if (minUDFReadRev > UDF_MAX_READ_VERSION) {
                        udf_err(sb, "minUDFReadRev=%x (max is %x)\n",
-                               le16_to_cpu(lvidiu->minUDFReadRev),
+                               minUDFReadRev,
                                UDF_MAX_READ_VERSION);
                        ret = -EINVAL;
                        goto error_out;
@@ -2265,11 +2285,7 @@ static int udf_statfs(struct dentry *dentry, struct kstatfs *buf)
        struct logicalVolIntegrityDescImpUse *lvidiu;
        u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
 
-       if (sbi->s_lvid_bh != NULL)
-               lvidiu = udf_sb_lvidiu(sbi);
-       else
-               lvidiu = NULL;
-
+       lvidiu = udf_sb_lvidiu(sb);
        buf->f_type = UDF_SUPER_MAGIC;
        buf->f_bsize = sb->s_blocksize;
        buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len;
index ed401e9..1f32c7b 100644 (file)
@@ -162,7 +162,7 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb)
        return sb->s_fs_info;
 }
 
-struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi);
+struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb);
 
 int udf_compute_nr_groups(struct super_block *sb, u32 partition);
 
index 88c5ea7..f1d85cf 100644 (file)
@@ -628,6 +628,7 @@ xfs_buf_item_unlock(
                else if (aborted) {
                        ASSERT(XFS_FORCED_SHUTDOWN(lip->li_mountp));
                        if (lip->li_flags & XFS_LI_IN_AIL) {
+                               spin_lock(&lip->li_ailp->xa_lock);
                                xfs_trans_ail_delete(lip->li_ailp, lip,
                                                     SHUTDOWN_LOG_IO_ERROR);
                        }
index 069537c..20bf8e8 100644 (file)
@@ -1224,6 +1224,7 @@ xfs_da3_node_toosmall(
        /* start with smaller blk num */
        forward = nodehdr.forw < nodehdr.back;
        for (i = 0; i < 2; forward = !forward, i++) {
+               struct xfs_da3_icnode_hdr thdr;
                if (forward)
                        blkno = nodehdr.forw;
                else
@@ -1236,10 +1237,10 @@ xfs_da3_node_toosmall(
                        return(error);
 
                node = bp->b_addr;
-               xfs_da3_node_hdr_from_disk(&nodehdr, node);
+               xfs_da3_node_hdr_from_disk(&thdr, node);
                xfs_trans_brelse(state->args->trans, bp);
 
-               if (count - nodehdr.count >= 0)
+               if (count - thdr.count >= 0)
                        break;  /* fits with at least 25% to spare */
        }
        if (i >= 2) {
index 0957aa9..12dad18 100644 (file)
@@ -1158,7 +1158,7 @@ xfs_dir2_sf_to_block(
        /*
         * Create entry for .
         */
-       dep = xfs_dir3_data_dot_entry_p(hdr);
+       dep = xfs_dir3_data_dot_entry_p(mp, hdr);
        dep->inumber = cpu_to_be64(dp->i_ino);
        dep->namelen = 1;
        dep->name[0] = '.';
@@ -1172,7 +1172,7 @@ xfs_dir2_sf_to_block(
        /*
         * Create entry for ..
         */
-       dep = xfs_dir3_data_dotdot_entry_p(hdr);
+       dep = xfs_dir3_data_dotdot_entry_p(mp, hdr);
        dep->inumber = cpu_to_be64(xfs_dir2_sf_get_parent_ino(sfp));
        dep->namelen = 2;
        dep->name[0] = dep->name[1] = '.';
@@ -1183,7 +1183,7 @@ xfs_dir2_sf_to_block(
        blp[1].hashval = cpu_to_be32(xfs_dir_hash_dotdot);
        blp[1].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
                                (char *)dep - (char *)hdr));
-       offset = xfs_dir3_data_first_offset(hdr);
+       offset = xfs_dir3_data_first_offset(mp);
        /*
         * Loop over existing entries, stuff them in.
         */
index a0961a6..9cf6738 100644 (file)
@@ -497,69 +497,58 @@ xfs_dir3_data_unused_p(struct xfs_dir2_data_hdr *hdr)
 /*
  * Offsets of . and .. in data space (always block 0)
  *
- * The macros are used for shortform directories as they have no headers to read
- * the magic number out of. Shortform directories need to know the size of the
- * data block header because the sfe embeds the block offset of the entry into
- * it so that it doesn't change when format conversion occurs. Bad Things Happen
- * if we don't follow this rule.
- *
  * XXX: there is scope for significant optimisation of the logic here. Right
  * now we are checking for "dir3 format" over and over again. Ideally we should
  * only do it once for each operation.
  */
-#define        XFS_DIR3_DATA_DOT_OFFSET(mp)    \
-       xfs_dir3_data_hdr_size(xfs_sb_version_hascrc(&(mp)->m_sb))
-#define        XFS_DIR3_DATA_DOTDOT_OFFSET(mp) \
-       (XFS_DIR3_DATA_DOT_OFFSET(mp) + xfs_dir3_data_entsize(mp, 1))
-#define        XFS_DIR3_DATA_FIRST_OFFSET(mp)          \
-       (XFS_DIR3_DATA_DOTDOT_OFFSET(mp) + xfs_dir3_data_entsize(mp, 2))
-
 static inline xfs_dir2_data_aoff_t
-xfs_dir3_data_dot_offset(struct xfs_dir2_data_hdr *hdr)
+xfs_dir3_data_dot_offset(struct xfs_mount *mp)
 {
-       return xfs_dir3_data_entry_offset(hdr);
+       return xfs_dir3_data_hdr_size(xfs_sb_version_hascrc(&mp->m_sb));
 }
 
 static inline xfs_dir2_data_aoff_t
-xfs_dir3_data_dotdot_offset(struct xfs_dir2_data_hdr *hdr)
+xfs_dir3_data_dotdot_offset(struct xfs_mount *mp)
 {
-       bool dir3 = hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-                   hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
-       return xfs_dir3_data_dot_offset(hdr) +
-               __xfs_dir3_data_entsize(dir3, 1);
+       return xfs_dir3_data_dot_offset(mp) +
+               xfs_dir3_data_entsize(mp, 1);
 }
 
 static inline xfs_dir2_data_aoff_t
-xfs_dir3_data_first_offset(struct xfs_dir2_data_hdr *hdr)
+xfs_dir3_data_first_offset(struct xfs_mount *mp)
 {
-       bool dir3 = hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-                   hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
-       return xfs_dir3_data_dotdot_offset(hdr) +
-               __xfs_dir3_data_entsize(dir3, 2);
+       return xfs_dir3_data_dotdot_offset(mp) +
+               xfs_dir3_data_entsize(mp, 2);
 }
 
 /*
  * location of . and .. in data space (always block 0)
  */
 static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_dot_entry_p(struct xfs_dir2_data_hdr *hdr)
+xfs_dir3_data_dot_entry_p(
+       struct xfs_mount        *mp,
+       struct xfs_dir2_data_hdr *hdr)
 {
        return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_dot_offset(hdr));
+               ((char *)hdr + xfs_dir3_data_dot_offset(mp));
 }
 
 static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_dotdot_entry_p(struct xfs_dir2_data_hdr *hdr)
+xfs_dir3_data_dotdot_entry_p(
+       struct xfs_mount        *mp,
+       struct xfs_dir2_data_hdr *hdr)
 {
        return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_dotdot_offset(hdr));
+               ((char *)hdr + xfs_dir3_data_dotdot_offset(mp));
 }
 
 static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_first_entry_p(struct xfs_dir2_data_hdr *hdr)
+xfs_dir3_data_first_entry_p(
+       struct xfs_mount        *mp,
+       struct xfs_dir2_data_hdr *hdr)
 {
        return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_first_offset(hdr));
+               ((char *)hdr + xfs_dir3_data_first_offset(mp));
 }
 
 /*
index 8993ec1..8f84153 100644 (file)
@@ -119,9 +119,9 @@ xfs_dir2_sf_getdents(
         * mp->m_dirdatablk.
         */
        dot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
-                                            XFS_DIR3_DATA_DOT_OFFSET(mp));
+                                            xfs_dir3_data_dot_offset(mp));
        dotdot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
-                                               XFS_DIR3_DATA_DOTDOT_OFFSET(mp));
+                                               xfs_dir3_data_dotdot_offset(mp));
 
        /*
         * Put . entry unless we're starting past it.
index bb6e284..3ef6d40 100644 (file)
@@ -557,7 +557,7 @@ xfs_dir2_sf_addname_hard(
         * to insert the new entry.
         * If it's going to end up at the end then oldsfep will point there.
         */
-       for (offset = XFS_DIR3_DATA_FIRST_OFFSET(mp),
+       for (offset = xfs_dir3_data_first_offset(mp),
              oldsfep = xfs_dir2_sf_firstentry(oldsfp),
              add_datasize = xfs_dir3_data_entsize(mp, args->namelen),
              eof = (char *)oldsfep == &buf[old_isize];
@@ -640,7 +640,7 @@ xfs_dir2_sf_addname_pick(
 
        sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
        size = xfs_dir3_data_entsize(mp, args->namelen);
-       offset = XFS_DIR3_DATA_FIRST_OFFSET(mp);
+       offset = xfs_dir3_data_first_offset(mp);
        sfep = xfs_dir2_sf_firstentry(sfp);
        holefit = 0;
        /*
@@ -713,7 +713,7 @@ xfs_dir2_sf_check(
        mp = dp->i_mount;
 
        sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
-       offset = XFS_DIR3_DATA_FIRST_OFFSET(mp);
+       offset = xfs_dir3_data_first_offset(mp);
        ino = xfs_dir2_sf_get_parent_ino(sfp);
        i8count = ino > XFS_DIR2_MAX_SHORT_INUM;
 
index 71520e6..1ee776d 100644 (file)
@@ -64,7 +64,8 @@ int xfs_dqerror_mod = 33;
 struct kmem_zone               *xfs_qm_dqtrxzone;
 static struct kmem_zone                *xfs_qm_dqzone;
 
-static struct lock_class_key xfs_dquot_other_class;
+static struct lock_class_key xfs_dquot_group_class;
+static struct lock_class_key xfs_dquot_project_class;
 
 /*
  * This is called to free all the memory associated with a dquot
@@ -703,8 +704,20 @@ xfs_qm_dqread(
         * Make sure group quotas have a different lock class than user
         * quotas.
         */
-       if (!(type & XFS_DQ_USER))
-               lockdep_set_class(&dqp->q_qlock, &xfs_dquot_other_class);
+       switch (type) {
+       case XFS_DQ_USER:
+               /* uses the default lock class */
+               break;
+       case XFS_DQ_GROUP:
+               lockdep_set_class(&dqp->q_qlock, &xfs_dquot_group_class);
+               break;
+       case XFS_DQ_PROJ:
+               lockdep_set_class(&dqp->q_qlock, &xfs_dquot_project_class);
+               break;
+       default:
+               ASSERT(0);
+               break;
+       }
 
        XFS_STATS_INC(xs_qm_dquot);
 
index 1edb5cc..18272c7 100644 (file)
@@ -515,7 +515,7 @@ typedef struct xfs_swapext
 /*     XFS_IOC_GETBIOSIZE ---- deprecated 47      */
 #define XFS_IOC_GETBMAPX       _IOWR('X', 56, struct getbmap)
 #define XFS_IOC_ZERO_RANGE     _IOW ('X', 57, struct xfs_flock64)
-#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_eofblocks)
+#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
index 193206b..474807a 100644 (file)
@@ -119,11 +119,6 @@ xfs_inode_free(
                ip->i_itemp = NULL;
        }
 
-       /* asserts to verify all state is correct here */
-       ASSERT(atomic_read(&ip->i_pincount) == 0);
-       ASSERT(!spin_is_locked(&ip->i_flags_lock));
-       ASSERT(!xfs_isiflocked(ip));
-
        /*
         * Because we use RCU freeing we need to ensure the inode always
         * appears to be reclaimed with an invalid inode number when in the
@@ -135,6 +130,10 @@ xfs_inode_free(
        ip->i_ino = 0;
        spin_unlock(&ip->i_flags_lock);
 
+       /* asserts to verify all state is correct here */
+       ASSERT(atomic_read(&ip->i_pincount) == 0);
+       ASSERT(!xfs_isiflocked(ip));
+
        call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
 }
 
index dabda95..3979749 100644 (file)
@@ -1585,6 +1585,7 @@ xlog_recover_add_to_trans(
                "bad number of regions (%d) in inode log format",
                                  in_f->ilf_size);
                        ASSERT(0);
+                       kmem_free(ptr);
                        return XFS_ERROR(EIO);
                }
 
@@ -1970,6 +1971,13 @@ xlog_recover_do_inode_buffer(
  * magic number.  If we don't recognise the magic number in the buffer, then
  * return a LSN of -1 so that the caller knows it was an unrecognised block and
  * so can recover the buffer.
+ *
+ * Note: we cannot rely solely on magic number matches to determine that the
+ * buffer has a valid LSN - we also need to verify that it belongs to this
+ * filesystem, so we need to extract the object's LSN and compare it to that
+ * which we read from the superblock. If the UUIDs don't match, then we've got a
+ * stale metadata block from an old filesystem instance that we need to recover
+ * over the top of.
  */
 static xfs_lsn_t
 xlog_recover_get_buf_lsn(
@@ -1980,6 +1988,8 @@ xlog_recover_get_buf_lsn(
        __uint16_t              magic16;
        __uint16_t              magicda;
        void                    *blk = bp->b_addr;
+       uuid_t                  *uuid;
+       xfs_lsn_t               lsn = -1;
 
        /* v4 filesystems always recover immediately */
        if (!xfs_sb_version_hascrc(&mp->m_sb))
@@ -1992,43 +2002,79 @@ xlog_recover_get_buf_lsn(
        case XFS_ABTB_MAGIC:
        case XFS_ABTC_MAGIC:
        case XFS_IBT_CRC_MAGIC:
-       case XFS_IBT_MAGIC:
-               return be64_to_cpu(
-                               ((struct xfs_btree_block *)blk)->bb_u.s.bb_lsn);
+       case XFS_IBT_MAGIC: {
+               struct xfs_btree_block *btb = blk;
+
+               lsn = be64_to_cpu(btb->bb_u.s.bb_lsn);
+               uuid = &btb->bb_u.s.bb_uuid;
+               break;
+       }
        case XFS_BMAP_CRC_MAGIC:
-       case XFS_BMAP_MAGIC:
-               return be64_to_cpu(
-                               ((struct xfs_btree_block *)blk)->bb_u.l.bb_lsn);
+       case XFS_BMAP_MAGIC: {
+               struct xfs_btree_block *btb = blk;
+
+               lsn = be64_to_cpu(btb->bb_u.l.bb_lsn);
+               uuid = &btb->bb_u.l.bb_uuid;
+               break;
+       }
        case XFS_AGF_MAGIC:
-               return be64_to_cpu(((struct xfs_agf *)blk)->agf_lsn);
+               lsn = be64_to_cpu(((struct xfs_agf *)blk)->agf_lsn);
+               uuid = &((struct xfs_agf *)blk)->agf_uuid;
+               break;
        case XFS_AGFL_MAGIC:
-               return be64_to_cpu(((struct xfs_agfl *)blk)->agfl_lsn);
+               lsn = be64_to_cpu(((struct xfs_agfl *)blk)->agfl_lsn);
+               uuid = &((struct xfs_agfl *)blk)->agfl_uuid;
+               break;
        case XFS_AGI_MAGIC:
-               return be64_to_cpu(((struct xfs_agi *)blk)->agi_lsn);
+               lsn = be64_to_cpu(((struct xfs_agi *)blk)->agi_lsn);
+               uuid = &((struct xfs_agi *)blk)->agi_uuid;
+               break;
        case XFS_SYMLINK_MAGIC:
-               return be64_to_cpu(((struct xfs_dsymlink_hdr *)blk)->sl_lsn);
+               lsn = be64_to_cpu(((struct xfs_dsymlink_hdr *)blk)->sl_lsn);
+               uuid = &((struct xfs_dsymlink_hdr *)blk)->sl_uuid;
+               break;
        case XFS_DIR3_BLOCK_MAGIC:
        case XFS_DIR3_DATA_MAGIC:
        case XFS_DIR3_FREE_MAGIC:
-               return be64_to_cpu(((struct xfs_dir3_blk_hdr *)blk)->lsn);
+               lsn = be64_to_cpu(((struct xfs_dir3_blk_hdr *)blk)->lsn);
+               uuid = &((struct xfs_dir3_blk_hdr *)blk)->uuid;
+               break;
        case XFS_ATTR3_RMT_MAGIC:
-               return be64_to_cpu(((struct xfs_attr3_rmt_hdr *)blk)->rm_lsn);
+               lsn = be64_to_cpu(((struct xfs_attr3_rmt_hdr *)blk)->rm_lsn);
+               uuid = &((struct xfs_attr3_rmt_hdr *)blk)->rm_uuid;
+               break;
        case XFS_SB_MAGIC:
-               return be64_to_cpu(((struct xfs_dsb *)blk)->sb_lsn);
+               lsn = be64_to_cpu(((struct xfs_dsb *)blk)->sb_lsn);
+               uuid = &((struct xfs_dsb *)blk)->sb_uuid;
+               break;
        default:
                break;
        }
 
+       if (lsn != (xfs_lsn_t)-1) {
+               if (!uuid_equal(&mp->m_sb.sb_uuid, uuid))
+                       goto recover_immediately;
+               return lsn;
+       }
+
        magicda = be16_to_cpu(((struct xfs_da_blkinfo *)blk)->magic);
        switch (magicda) {
        case XFS_DIR3_LEAF1_MAGIC:
        case XFS_DIR3_LEAFN_MAGIC:
        case XFS_DA3_NODE_MAGIC:
-               return be64_to_cpu(((struct xfs_da3_blkinfo *)blk)->lsn);
+               lsn = be64_to_cpu(((struct xfs_da3_blkinfo *)blk)->lsn);
+               uuid = &((struct xfs_da3_blkinfo *)blk)->uuid;
+               break;
        default:
                break;
        }
 
+       if (lsn != (xfs_lsn_t)-1) {
+               if (!uuid_equal(&mp->m_sb.sb_uuid, uuid))
+                       goto recover_immediately;
+               return lsn;
+       }
+
        /*
         * We do individual object checks on dquot and inode buffers as they
         * have their own individual LSN records. Also, we could have a stale
index 02e113b..d901982 100644 (file)
@@ -311,7 +311,6 @@ struct acpi_device {
        unsigned int physical_node_count;
        struct list_head physical_node_list;
        struct mutex physical_node_lock;
-       struct list_head power_dependent;
        void (*remove)(struct acpi_device *);
 };
 
@@ -456,8 +455,6 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
 acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
                                    acpi_notify_handler handler);
 int acpi_pm_device_sleep_state(struct device *, int *, int);
-void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev);
-void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev);
 #else
 static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
                                               acpi_notify_handler handler,
@@ -478,10 +475,6 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m)
        return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ?
                m : ACPI_STATE_D0;
 }
-static inline void acpi_dev_pm_add_dependent(acpi_handle handle,
-                                            struct device *depdev) {}
-static inline void acpi_dev_pm_remove_dependent(acpi_handle handle,
-                                               struct device *depdev) {}
 #endif
 
 #ifdef CONFIG_PM_RUNTIME
index d06079c..99b490b 100644 (file)
@@ -6,12 +6,12 @@ static inline pte_t mk_huge_pte(struct page *page, pgprot_t pgprot)
        return mk_pte(page, pgprot);
 }
 
-static inline int huge_pte_write(pte_t pte)
+static inline unsigned long huge_pte_write(pte_t pte)
 {
        return pte_write(pte);
 }
 
-static inline int huge_pte_dirty(pte_t pte)
+static inline unsigned long huge_pte_dirty(pte_t pte)
 {
        return pte_dirty(pte);
 }
index e69de29..b1a4967 100644 (file)
@@ -0,0 +1 @@
+/* no content, but patch(1) dislikes empty files */
index 2907341..b46fb45 100644 (file)
@@ -1322,10 +1322,9 @@ extern int drm_newctx(struct drm_device *dev, void *data,
 extern int drm_rmctx(struct drm_device *dev, void *data,
                     struct drm_file *file_priv);
 
-extern void drm_legacy_ctxbitmap_init(struct drm_device *dev);
-extern void drm_legacy_ctxbitmap_cleanup(struct drm_device *dev);
-extern void drm_legacy_ctxbitmap_release(struct drm_device *dev,
-                                        struct drm_file *file_priv);
+extern int drm_ctxbitmap_init(struct drm_device *dev);
+extern void drm_ctxbitmap_cleanup(struct drm_device *dev);
+extern void drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle);
 
 extern int drm_setsareactx(struct drm_device *dev, void *data,
                           struct drm_file *file_priv);
index edbd250..bed35e3 100644 (file)
@@ -23,7 +23,7 @@
 #define PULL_UP                        (1 << 4)
 #define ALTELECTRICALSEL       (1 << 5)
 
-/* 34xx specific mux bit defines */
+/* omap3/4/5 specific mux bit defines */
 #define INPUT_EN               (1 << 8)
 #define OFF_EN                 (1 << 9)
 #define OFFOUT_EN              (1 << 10)
@@ -31,8 +31,6 @@
 #define OFF_PULL_EN            (1 << 12)
 #define OFF_PULL_UP            (1 << 13)
 #define WAKEUP_EN              (1 << 14)
-
-/* 44xx specific mux bit defines */
 #define WAKEUP_EVENT           (1 << 15)
 
 /* Active pin states */
index f7f1d71..089743a 100644 (file)
@@ -158,6 +158,26 @@ static inline bool balloon_page_movable(struct page *page)
        return false;
 }
 
+/*
+ * isolated_balloon_page - identify an isolated balloon page on private
+ *                        compaction/migration page lists.
+ *
+ * After a compaction thread isolates a balloon page for migration, it raises
+ * the page refcount to prevent concurrent compaction threads from re-isolating
+ * the same page. For that reason putback_movable_pages(), or other routines
+ * that need to identify isolated balloon pages on private pagelists, cannot
+ * rely on balloon_page_movable() to accomplish the task.
+ */
+static inline bool isolated_balloon_page(struct page *page)
+{
+       /* Already isolated balloon pages, by default, have a raised refcount */
+       if (page_flags_cleared(page) && !page_mapped(page) &&
+           page_count(page) >= 2)
+               return __is_movable_balloon_page(page);
+
+       return false;
+}
+
 /*
  * balloon_page_insert - insert a page into the balloon's page list and make
  *                      the page->mapping assignment accordingly.
@@ -243,6 +263,11 @@ static inline bool balloon_page_movable(struct page *page)
        return false;
 }
 
+static inline bool isolated_balloon_page(struct page *page)
+{
+       return false;
+}
+
 static inline bool balloon_page_isolate(struct page *page)
 {
        return false;
index 2fdb4a4..0e6f765 100644 (file)
@@ -862,6 +862,17 @@ static inline unsigned int blk_rq_get_max_sectors(struct request *rq)
        return blk_queue_get_max_sectors(q, rq->cmd_flags);
 }
 
+static inline unsigned int blk_rq_count_bios(struct request *rq)
+{
+       unsigned int nr_bios = 0;
+       struct bio *bio;
+
+       __rq_for_each_bio(bio, rq)
+               nr_bios++;
+
+       return nr_bios;
+}
+
 /*
  * Request issue related functions.
  */
index 842de22..ded4299 100644 (file)
 #define __visible __attribute__((externally_visible))
 #endif
 
+/*
+ * GCC 'asm goto' miscompiles certain code sequences:
+ *
+ *   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670
+ *
+ * Work it around via a compiler barrier quirk suggested by Jakub Jelinek.
+ * Fixed in GCC 4.8.2 and later versions.
+ *
+ * (asm goto is automatically volatile - the naming reflects this.)
+ */
+#if GCC_VERSION <= 40801
+# define asm_volatile_goto(x...)       do { asm goto(x); asm (""); } while (0)
+#else
+# define asm_volatile_goto(x...)       do { asm goto(x); } while (0)
+#endif
 
 #ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP
 #if GCC_VERSION >= 40400
index 68267b6..7d275c4 100644 (file)
 extern u32  crc32_le(u32 crc, unsigned char const *p, size_t len);
 extern u32  crc32_be(u32 crc, unsigned char const *p, size_t len);
 
+/**
+ * crc32_le_combine - Combine two crc32 check values into one. For two
+ *                   sequences of bytes, seq1 and seq2 with lengths len1
+ *                   and len2, crc32_le() check values were calculated
+ *                   for each, crc1 and crc2.
+ *
+ * @crc1: crc32 of the first block
+ * @crc2: crc32 of the second block
+ * @len2: length of the second block
+ *
+ * Return: The crc32_le() check value of seq1 and seq2 concatenated,
+ *        requiring only crc1, crc2, and len2. Note: If seq_full denotes
+ *        the concatenated memory area of seq1 with seq2, and crc_full
+ *        the crc32_le() value of seq_full, then crc_full ==
+ *        crc32_le_combine(crc1, crc2, len2) when crc_full was seeded
+ *        with the same initializer as crc1, and crc2 seed was 0. See
+ *        also crc32_combine_test().
+ */
+extern u32  crc32_le_combine(u32 crc1, u32 crc2, size_t len2);
+
 extern u32  __crc32c_le(u32 crc, unsigned char const *p, size_t len);
 
+/**
+ * __crc32c_le_combine - Combine two crc32c check values into one. For two
+ *                      sequences of bytes, seq1 and seq2 with lengths len1
+ *                      and len2, __crc32c_le() check values were calculated
+ *                      for each, crc1 and crc2.
+ *
+ * @crc1: crc32c of the first block
+ * @crc2: crc32c of the second block
+ * @len2: length of the second block
+ *
+ * Return: The __crc32c_le() check value of seq1 and seq2 concatenated,
+ *        requiring only crc1, crc2, and len2. Note: If seq_full denotes
+ *        the concatenated memory area of seq1 with seq2, and crc_full
+ *        the __crc32c_le() value of seq_full, then crc_full ==
+ *        __crc32c_le_combine(crc1, crc2, len2) when crc_full was
+ *        seeded with the same initializer as crc1, and crc2 seed
+ *        was 0. See also crc32c_combine_test().
+ */
+extern u32  __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2);
+
 #define crc32(seed, data, length)  crc32_le(seed, (unsigned char const *)(data), length)
 
 /*
index 653073d..ed419c6 100644 (file)
@@ -406,13 +406,14 @@ int dm_noflush_suspending(struct dm_target *ti);
 union map_info *dm_get_mapinfo(struct bio *bio);
 union map_info *dm_get_rq_mapinfo(struct request *rq);
 
+struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+
 /*
  * Geometry functions.
  */
 int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo);
 int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo);
 
-
 /*-----------------------------------------------------------------
  * Functions for manipulating device-mapper tables.
  *---------------------------------------------------------------*/
index d8b5124..fc4a9aa 100644 (file)
 #include <asm/unaligned.h>
 
 #ifdef __KERNEL__
-extern __be16          eth_type_trans(struct sk_buff *skb, struct net_device *dev);
+__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
 extern const struct header_ops eth_header_ops;
 
-extern int eth_header(struct sk_buff *skb, struct net_device *dev,
-                     unsigned short type,
-                     const void *daddr, const void *saddr, unsigned len);
-extern int eth_rebuild_header(struct sk_buff *skb);
-extern int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
-extern int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);
-extern void eth_header_cache_update(struct hh_cache *hh,
-                                   const struct net_device *dev,
-                                   const unsigned char *haddr);
-extern int eth_prepare_mac_addr_change(struct net_device *dev, void *p);
-extern void eth_commit_mac_addr_change(struct net_device *dev, void *p);
-extern int eth_mac_addr(struct net_device *dev, void *p);
-extern int eth_change_mtu(struct net_device *dev, int new_mtu);
-extern int eth_validate_addr(struct net_device *dev);
-
-
-
-extern struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
+int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+              const void *daddr, const void *saddr, unsigned len);
+int eth_rebuild_header(struct sk_buff *skb);
+int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
+int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh,
+                    __be16 type);
+void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev,
+                            const unsigned char *haddr);
+int eth_prepare_mac_addr_change(struct net_device *dev, void *p);
+void eth_commit_mac_addr_change(struct net_device *dev, void *p);
+int eth_mac_addr(struct net_device *dev, void *p);
+int eth_change_mtu(struct net_device *dev, int new_mtu);
+int eth_validate_addr(struct net_device *dev);
+
+struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
                                            unsigned int rxqs);
 #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
 #define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
index e460ef8..5009fa1 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/if_fc.h>
 
 #ifdef __KERNEL__
-extern struct net_device *alloc_fcdev(int sizeof_priv);
+struct net_device *alloc_fcdev(int sizeof_priv);
 #endif
 
 #endif /* _LINUX_FCDEVICE_H */
index 155bafd..9a79f01 100644 (file)
 #include <linux/if_fddi.h>
 
 #ifdef __KERNEL__
-extern __be16  fddi_type_trans(struct sk_buff *skb,
-                               struct net_device *dev);
-extern int fddi_change_mtu(struct net_device *dev, int new_mtu);
-extern struct net_device *alloc_fddidev(int sizeof_priv);
+__be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev);
+int fddi_change_mtu(struct net_device *dev, int new_mtu);
+struct net_device *alloc_fddidev(int sizeof_priv);
 #endif
 
 #endif /* _LINUX_FDDIDEVICE_H */
index a6ac848..ff4e40c 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/atomic.h>
 #include <linux/compat.h>
+#include <linux/workqueue.h>
 #include <uapi/linux/filter.h>
 
 #ifdef CONFIG_COMPAT
@@ -25,15 +26,19 @@ struct sk_filter
 {
        atomic_t                refcnt;
        unsigned int            len;    /* Number of filter blocks */
+       struct rcu_head         rcu;
        unsigned int            (*bpf_func)(const struct sk_buff *skb,
                                            const struct sock_filter *filter);
-       struct rcu_head         rcu;
-       struct sock_filter      insns[0];
+       union {
+               struct sock_filter      insns[0];
+               struct work_struct      work;
+       };
 };
 
-static inline unsigned int sk_filter_len(const struct sk_filter *fp)
+static inline unsigned int sk_filter_size(unsigned int proglen)
 {
-       return fp->len * sizeof(struct sock_filter) + sizeof(*fp);
+       return max(sizeof(struct sk_filter),
+                  offsetof(struct sk_filter, insns[proglen]));
 }
 
 extern int sk_filter(struct sock *sk, struct sk_buff *skb);
@@ -67,11 +72,13 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
 }
 #define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
 #else
+#include <linux/slab.h>
 static inline void bpf_jit_compile(struct sk_filter *fp)
 {
 }
 static inline void bpf_jit_free(struct sk_filter *fp)
 {
+       kfree(fp);
 }
 #define SK_RUN_FILTER(FILTER, SKB) sk_run_filter(SKB, FILTER->insns)
 #endif
index f148e49..8ec23fb 100644 (file)
@@ -31,11 +31,11 @@ struct hippi_cb {
        __u32   ifield;
 };
 
-extern __be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev);
-extern int hippi_change_mtu(struct net_device *dev, int new_mtu);
-extern int hippi_mac_addr(struct net_device *dev, void *p);
-extern int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p);
-extern struct net_device *alloc_hippi_dev(int sizeof_priv);
+__be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev);
+int hippi_change_mtu(struct net_device *dev, int new_mtu);
+int hippi_mac_addr(struct net_device *dev, void *p);
+int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p);
+struct net_device *alloc_hippi_dev(int sizeof_priv);
 #endif
 
 #endif /* _LINUX_HIPPIDEVICE_H */
index a3b8b2e..d98503b 100644 (file)
 /*
  * Framework version for util services.
  */
+#define UTIL_FW_MINOR  0
+
+#define UTIL_WS2K8_FW_MAJOR  1
+#define UTIL_WS2K8_FW_VERSION     (UTIL_WS2K8_FW_MAJOR << 16 | UTIL_FW_MINOR)
 
 #define UTIL_FW_MAJOR  3
-#define UTIL_FW_MINOR  0
-#define UTIL_FW_MAJOR_MINOR     (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
+#define UTIL_FW_VERSION     (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
 
 
 /*
index a5b598a..8c3b26a 100644 (file)
@@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie {
        u8 sec_chan_offs;
 } __packed;
 
+/**
+ * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
+ *
+ * This structure represents the "Mesh Channel Switch Paramters element"
+ */
+struct ieee80211_mesh_chansw_params_ie {
+       u8 mesh_ttl;
+       u8 mesh_flags;
+       __le16 mesh_reason;
+       __le16 mesh_pre_value;
+} __packed;
+
 /**
  * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
  */
@@ -750,6 +762,14 @@ enum mesh_config_capab_flags {
        IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL       = 0x40,
 };
 
+/**
+ * mesh channel switch parameters element's flag indicator
+ *
+ */
+#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
+#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
+#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
+
 /**
  * struct ieee80211_rann_ie
  *
@@ -1391,8 +1411,8 @@ struct ieee80211_vht_operation {
 #define IEEE80211_VHT_CAP_RXSTBC_MASK                          0x00000700
 #define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE                        0x00000800
 #define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE                        0x00001000
-#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX              0x00006000
-#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX              0x00030000
+#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX                   0x0000e000
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX              0x00070000
 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE                        0x00080000
 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE                        0x00100000
 #define IEEE80211_VHT_CAP_VHT_TXOP_PS                          0x00200000
index ddd33fd..c270285 100644 (file)
@@ -61,6 +61,7 @@ struct macvlan_dev {
        struct hlist_node       hlist;
        struct macvlan_port     *port;
        struct net_device       *lowerdev;
+       void                    *fwd_priv;
        struct macvlan_pcpu_stats __percpu *pcpu_stats;
 
        DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
index 715c343..f252deb 100644 (file)
@@ -89,6 +89,101 @@ extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
 extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
 extern u16 vlan_dev_vlan_id(const struct net_device *dev);
 
+/**
+ *     struct vlan_priority_tci_mapping - vlan egress priority mappings
+ *     @priority: skb priority
+ *     @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000
+ *     @next: pointer to next struct
+ */
+struct vlan_priority_tci_mapping {
+       u32                                     priority;
+       u16                                     vlan_qos;
+       struct vlan_priority_tci_mapping        *next;
+};
+
+/**
+ *     struct vlan_pcpu_stats - VLAN percpu rx/tx stats
+ *     @rx_packets: number of received packets
+ *     @rx_bytes: number of received bytes
+ *     @rx_multicast: number of received multicast packets
+ *     @tx_packets: number of transmitted packets
+ *     @tx_bytes: number of transmitted bytes
+ *     @syncp: synchronization point for 64bit counters
+ *     @rx_errors: number of rx errors
+ *     @tx_dropped: number of tx drops
+ */
+struct vlan_pcpu_stats {
+       u64                     rx_packets;
+       u64                     rx_bytes;
+       u64                     rx_multicast;
+       u64                     tx_packets;
+       u64                     tx_bytes;
+       struct u64_stats_sync   syncp;
+       u32                     rx_errors;
+       u32                     tx_dropped;
+};
+
+struct proc_dir_entry;
+struct netpoll;
+
+/**
+ *     struct vlan_dev_priv - VLAN private device data
+ *     @nr_ingress_mappings: number of ingress priority mappings
+ *     @ingress_priority_map: ingress priority mappings
+ *     @nr_egress_mappings: number of egress priority mappings
+ *     @egress_priority_map: hash of egress priority mappings
+ *     @vlan_proto: VLAN encapsulation protocol
+ *     @vlan_id: VLAN identifier
+ *     @flags: device flags
+ *     @real_dev: underlying netdevice
+ *     @real_dev_addr: address of underlying netdevice
+ *     @dent: proc dir entry
+ *     @vlan_pcpu_stats: ptr to percpu rx stats
+ */
+struct vlan_dev_priv {
+       unsigned int                            nr_ingress_mappings;
+       u32                                     ingress_priority_map[8];
+       unsigned int                            nr_egress_mappings;
+       struct vlan_priority_tci_mapping        *egress_priority_map[16];
+
+       __be16                                  vlan_proto;
+       u16                                     vlan_id;
+       u16                                     flags;
+
+       struct net_device                       *real_dev;
+       unsigned char                           real_dev_addr[ETH_ALEN];
+
+       struct proc_dir_entry                   *dent;
+       struct vlan_pcpu_stats __percpu         *vlan_pcpu_stats;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       struct netpoll                          *netpoll;
+#endif
+};
+
+static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
+{
+       return netdev_priv(dev);
+}
+
+static inline u16
+vlan_dev_get_egress_qos_mask(struct net_device *dev, u32 skprio)
+{
+       struct vlan_priority_tci_mapping *mp;
+
+       smp_rmb(); /* coupled with smp_wmb() in vlan_dev_set_egress_priority() */
+
+       mp = vlan_dev_priv(dev)->egress_priority_map[(skprio & 0xF)];
+       while (mp) {
+               if (mp->priority == skprio) {
+                       return mp->vlan_qos; /* This should already be shifted
+                                             * to mask correctly with the
+                                             * VLAN's TCI */
+               }
+               mp = mp->next;
+       }
+       return 0;
+}
+
 extern bool vlan_do_receive(struct sk_buff **skb);
 extern struct sk_buff *vlan_untag(struct sk_buff *skb);
 
@@ -121,6 +216,12 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev)
        return 0;
 }
 
+static inline u16 vlan_dev_get_egress_qos_mask(struct net_device *dev,
+                                              u32 skprio)
+{
+       return 0;
+}
+
 static inline bool vlan_do_receive(struct sk_buff **skb)
 {
        return false;
index 79640e0..0d678ae 100644 (file)
@@ -147,25 +147,27 @@ struct in_ifaddr {
        unsigned long           ifa_tstamp; /* updated timestamp */
 };
 
-extern int register_inetaddr_notifier(struct notifier_block *nb);
-extern int unregister_inetaddr_notifier(struct notifier_block *nb);
+int register_inetaddr_notifier(struct notifier_block *nb);
+int unregister_inetaddr_notifier(struct notifier_block *nb);
 
-extern void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
-                                       struct ipv4_devconf *devconf);
+void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
+                                struct ipv4_devconf *devconf);
 
-extern struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref);
+struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref);
 static inline struct net_device *ip_dev_find(struct net *net, __be32 addr)
 {
        return __ip_dev_find(net, addr, true);
 }
 
-extern int             inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b);
-extern int             devinet_ioctl(struct net *net, unsigned int cmd, void __user *);
-extern void            devinet_init(void);
-extern struct in_device        *inetdev_by_index(struct net *, int);
-extern __be32          inet_select_addr(const struct net_device *dev, __be32 dst, int scope);
-extern __be32          inet_confirm_addr(struct in_device *in_dev, __be32 dst, __be32 local, int scope);
-extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, __be32 mask);
+int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b);
+int devinet_ioctl(struct net *net, unsigned int cmd, void __user *);
+void devinet_init(void);
+struct in_device *inetdev_by_index(struct net *, int);
+__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope);
+__be32 inet_confirm_addr(struct in_device *in_dev, __be32 dst, __be32 local,
+                        int scope);
+struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
+                                   __be32 mask);
 
 static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
 {
@@ -218,7 +220,7 @@ static inline struct in_device *__in_dev_get_rtnl(const struct net_device *dev)
        return rtnl_dereference(dev->ip_ptr);
 }
 
-extern void in_dev_finish_destroy(struct in_device *idev);
+void in_dev_finish_destroy(struct in_device *idev);
 
 static inline void in_dev_put(struct in_device *idev)
 {
index 78e2ada..d380c5e 100644 (file)
@@ -55,7 +55,7 @@
 #define DMAR_IQT_REG   0x88    /* Invalidation queue tail register */
 #define DMAR_IQ_SHIFT  4       /* Invalidation queue head/tail shift */
 #define DMAR_IQA_REG   0x90    /* Invalidation queue addr register */
-#define DMAR_ICS_REG   0x98    /* Invalidation complete status register */
+#define DMAR_ICS_REG   0x9c    /* Invalidation complete status register */
 #define DMAR_IRTA_REG  0xb8    /* Interrupt remapping table addr register */
 
 #define OFFSET_STRIDE          (9)
index 19c19a5..f6c82de 100644 (file)
@@ -34,9 +34,9 @@ struct ipc_namespace {
        int             sem_ctls[4];
        int             used_sems;
 
-       int             msg_ctlmax;
-       int             msg_ctlmnb;
-       int             msg_ctlmni;
+       unsigned int    msg_ctlmax;
+       unsigned int    msg_ctlmnb;
+       unsigned int    msg_ctlmni;
        atomic_t        msg_bytes;
        atomic_t        msg_hdrs;
        int             auto_msgmni;
index 28ea384..5d89d1b 100644 (file)
@@ -21,13 +21,11 @@ struct ipv6_devconf {
        __s32           force_mld_version;
        __s32           mldv1_unsolicited_report_interval;
        __s32           mldv2_unsolicited_report_interval;
-#ifdef CONFIG_IPV6_PRIVACY
        __s32           use_tempaddr;
        __s32           temp_valid_lft;
        __s32           temp_prefered_lft;
        __s32           regen_max_retry;
        __s32           max_desync_factor;
-#endif
        __s32           max_addresses;
        __s32           accept_ra_defrtr;
        __s32           accept_ra_pinfo;
@@ -115,16 +113,8 @@ static inline int inet6_iif(const struct sk_buff *skb)
        return IP6CB(skb)->iif;
 }
 
-struct inet6_request_sock {
-       struct in6_addr         loc_addr;
-       struct in6_addr         rmt_addr;
-       struct sk_buff          *pktopts;
-       int                     iif;
-};
-
 struct tcp6_request_sock {
        struct tcp_request_sock   tcp6rsk_tcp;
-       struct inet6_request_sock tcp6rsk_inet6;
 };
 
 struct ipv6_mc_socklist;
@@ -141,8 +131,6 @@ struct ipv6_fl_socklist;
  */
 struct ipv6_pinfo {
        struct in6_addr         saddr;
-       struct in6_addr         rcv_saddr;
-       struct in6_addr         daddr;
        struct in6_pktinfo      sticky_pktinfo;
        const struct in6_addr           *daddr_cache;
 #ifdef CONFIG_IPV6_SUBTREES
@@ -256,48 +244,22 @@ struct tcp6_sock {
 
 extern int inet6_sk_rebuild_header(struct sock *sk);
 
-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   tcp6tw_tcp;
-       struct inet6_timewait_sock tcp6tw_inet6;
 };
 
-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);
-}
-
 #if IS_ENABLED(CONFIG_IPV6)
 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);
-               inet6_rsk(req)->pktopts = NULL;
-       }
+       if (req)
+               inet_rsk(req)->pktopts = NULL;
 
        return req;
 }
@@ -321,21 +283,11 @@ 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))
 
-static inline u16 inet6_tw_offset(const struct proto *prot)
+static inline const struct in6_addr *inet6_rcv_saddr(const struct sock *sk)
 {
-       return prot->twsk_prot->twsk_obj_size -
-                       sizeof(struct inet6_timewait_sock);
-}
-
-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 : &inet6_twsk(sk)->tw_v6_rcv_saddr;
-}
-
-static inline struct in6_addr *inet6_rcv_saddr(const struct sock *sk)
-{
-       return sk->sk_family == AF_INET6 ? __inet6_rcv_saddr(sk) : NULL;
+       if (sk->sk_family == AF_INET6)
+               return &sk->sk_v6_rcv_saddr;
+       return NULL;
 }
 
 static inline int inet_v6_ipv6only(const struct sock *sk)
@@ -363,28 +315,18 @@ static inline struct raw6_sock *raw6_sk(const struct sock *sk)
        return 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 /* IS_ENABLED(CONFIG_IPV6) */
 
 #define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif)     \
-       ((inet_sk(__sk)->inet_portpair == (__ports))            &&      \
+       (((__sk)->sk_portpair == (__ports))                     &&      \
         ((__sk)->sk_family == AF_INET6)                        &&      \
-        ipv6_addr_equal(&inet6_sk(__sk)->daddr, (__saddr))     &&      \
-        ipv6_addr_equal(&inet6_sk(__sk)->rcv_saddr, (__daddr)) &&      \
+        ipv6_addr_equal(&(__sk)->sk_v6_daddr, (__saddr))               &&      \
+        ipv6_addr_equal(&(__sk)->sk_v6_rcv_saddr, (__daddr))   &&      \
         (!(__sk)->sk_bound_dev_if      ||                              \
           ((__sk)->sk_bound_dev_if == (__dif)))                &&      \
         net_eq(sock_net(__sk), (__net)))
 
-#define INET6_TW_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif)     \
-       ((inet_twsk(__sk)->tw_portpair == (__ports))                    && \
-        ((__sk)->sk_family == AF_INET6)                                && \
-        ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_daddr, (__saddr))     && \
-        ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_rcv_saddr, (__daddr)) && \
-        (!(__sk)->sk_bound_dev_if      ||                                 \
-         ((__sk)->sk_bound_dev_if == (__dif)))                         && \
-        net_eq(sock_net(__sk), (__net)))
-
 #endif /* _IPV6_H */
index a507907..e96be72 100644 (file)
 
 #include <linux/types.h>
 #include <linux/compiler.h>
+#include <linux/bug.h>
+
+extern bool static_key_initialized;
+
+#define STATIC_KEY_CHECK_USE() WARN(!static_key_initialized,                 \
+                                   "%s used before call to jump_label_init", \
+                                   __func__)
 
 #if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL)
 
@@ -128,6 +135,7 @@ struct static_key {
 
 static __always_inline void jump_label_init(void)
 {
+       static_key_initialized = true;
 }
 
 static __always_inline bool static_key_false(struct static_key *key)
@@ -146,11 +154,13 @@ static __always_inline bool static_key_true(struct static_key *key)
 
 static inline void static_key_slow_inc(struct static_key *key)
 {
+       STATIC_KEY_CHECK_USE();
        atomic_inc(&key->enabled);
 }
 
 static inline void static_key_slow_dec(struct static_key *key)
 {
+       STATIC_KEY_CHECK_USE();
        atomic_dec(&key->enabled);
 }
 
index 1137883..089f70f 100644 (file)
@@ -23,12 +23,14 @@ struct static_key_deferred {
 };
 static inline void static_key_slow_dec_deferred(struct static_key_deferred *key)
 {
+       STATIC_KEY_CHECK_USE();
        static_key_slow_dec(&key->key);
 }
 static inline void
 jump_label_rate_limit(struct static_key_deferred *key,
                unsigned long rl)
 {
+       STATIC_KEY_CHECK_USE();
 }
 #endif /* HAVE_JUMP_LABEL */
 #endif /* _LINUX_JUMP_LABEL_RATELIMIT_H */
index 482ad2d..672ddc4 100644 (file)
@@ -439,6 +439,17 @@ static inline char *hex_byte_pack(char *buf, u8 byte)
        return buf;
 }
 
+extern const char hex_asc_upper[];
+#define hex_asc_upper_lo(x)    hex_asc_upper[((x) & 0x0f)]
+#define hex_asc_upper_hi(x)    hex_asc_upper[((x) & 0xf0) >> 4]
+
+static inline char *hex_byte_pack_upper(char *buf, u8 byte)
+{
+       *buf++ = hex_asc_upper_hi(byte);
+       *buf++ = hex_asc_upper_lo(byte);
+       return buf;
+}
+
 static inline char * __deprecated pack_hex_byte(char *buf, u8 byte)
 {
        return hex_byte_pack(buf, byte);
index 60e9587..b3e7a66 100644 (file)
@@ -53,23 +53,6 @@ struct mem_cgroup_reclaim_cookie {
        unsigned int generation;
 };
 
-enum mem_cgroup_filter_t {
-       VISIT,          /* visit current node */
-       SKIP,           /* skip the current node and continue traversal */
-       SKIP_TREE,      /* skip the whole subtree and continue traversal */
-};
-
-/*
- * mem_cgroup_filter_t predicate might instruct mem_cgroup_iter_cond how to
- * iterate through the hierarchy tree. Each tree element is checked by the
- * predicate before it is returned by the iterator. If a filter returns
- * SKIP or SKIP_TREE then the iterator code continues traversal (with the
- * next node down the hierarchy or the next node that doesn't belong under the
- * memcg's subtree).
- */
-typedef enum mem_cgroup_filter_t
-(*mem_cgroup_iter_filter)(struct mem_cgroup *memcg, struct mem_cgroup *root);
-
 #ifdef CONFIG_MEMCG
 /*
  * All "charge" functions with gfp_mask should use GFP_KERNEL or
@@ -137,18 +120,9 @@ mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
 extern void mem_cgroup_end_migration(struct mem_cgroup *memcg,
        struct page *oldpage, struct page *newpage, bool migration_ok);
 
-struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root,
-                                  struct mem_cgroup *prev,
-                                  struct mem_cgroup_reclaim_cookie *reclaim,
-                                  mem_cgroup_iter_filter cond);
-
-static inline struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
-                                  struct mem_cgroup *prev,
-                                  struct mem_cgroup_reclaim_cookie *reclaim)
-{
-       return mem_cgroup_iter_cond(root, prev, reclaim, NULL);
-}
-
+struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *,
+                                  struct mem_cgroup *,
+                                  struct mem_cgroup_reclaim_cookie *);
 void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *);
 
 /*
@@ -163,47 +137,24 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
 extern void mem_cgroup_replace_page_cache(struct page *oldpage,
                                        struct page *newpage);
 
-/**
- * mem_cgroup_toggle_oom - toggle the memcg OOM killer for the current task
- * @new: true to enable, false to disable
- *
- * Toggle whether a failed memcg charge should invoke the OOM killer
- * or just return -ENOMEM.  Returns the previous toggle state.
- *
- * NOTE: Any path that enables the OOM killer before charging must
- *       call mem_cgroup_oom_synchronize() afterward to finalize the
- *       OOM handling and clean up.
- */
-static inline bool mem_cgroup_toggle_oom(bool new)
+static inline void mem_cgroup_oom_enable(void)
 {
-       bool old;
-
-       old = current->memcg_oom.may_oom;
-       current->memcg_oom.may_oom = new;
-
-       return old;
+       WARN_ON(current->memcg_oom.may_oom);
+       current->memcg_oom.may_oom = 1;
 }
 
-static inline void mem_cgroup_enable_oom(void)
+static inline void mem_cgroup_oom_disable(void)
 {
-       bool old = mem_cgroup_toggle_oom(true);
-
-       WARN_ON(old == true);
-}
-
-static inline void mem_cgroup_disable_oom(void)
-{
-       bool old = mem_cgroup_toggle_oom(false);
-
-       WARN_ON(old == false);
+       WARN_ON(!current->memcg_oom.may_oom);
+       current->memcg_oom.may_oom = 0;
 }
 
 static inline bool task_in_memcg_oom(struct task_struct *p)
 {
-       return p->memcg_oom.in_memcg_oom;
+       return p->memcg_oom.memcg;
 }
 
-bool mem_cgroup_oom_synchronize(void);
+bool mem_cgroup_oom_synchronize(bool wait);
 
 #ifdef CONFIG_MEMCG_SWAP
 extern int do_swap_account;
@@ -260,9 +211,9 @@ static inline void mem_cgroup_dec_page_stat(struct page *page,
        mem_cgroup_update_page_stat(page, idx, -1);
 }
 
-enum mem_cgroup_filter_t
-mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg,
-               struct mem_cgroup *root);
+unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
+                                               gfp_t gfp_mask,
+                                               unsigned long *total_scanned);
 
 void __mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx);
 static inline void mem_cgroup_count_vm_event(struct mm_struct *mm,
@@ -376,15 +327,6 @@ static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg,
                struct page *oldpage, struct page *newpage, bool migration_ok)
 {
 }
-static inline struct mem_cgroup *
-mem_cgroup_iter_cond(struct mem_cgroup *root,
-               struct mem_cgroup *prev,
-               struct mem_cgroup_reclaim_cookie *reclaim,
-               mem_cgroup_iter_filter cond)
-{
-       /* first call must return non-NULL, second return NULL */
-       return (struct mem_cgroup *)(unsigned long)!prev;
-}
 
 static inline struct mem_cgroup *
 mem_cgroup_iter(struct mem_cgroup *root,
@@ -437,16 +379,11 @@ static inline void mem_cgroup_end_update_page_stat(struct page *page,
 {
 }
 
-static inline bool mem_cgroup_toggle_oom(bool new)
+static inline void mem_cgroup_oom_enable(void)
 {
-       return false;
 }
 
-static inline void mem_cgroup_enable_oom(void)
-{
-}
-
-static inline void mem_cgroup_disable_oom(void)
+static inline void mem_cgroup_oom_disable(void)
 {
 }
 
@@ -455,7 +392,7 @@ static inline bool task_in_memcg_oom(struct task_struct *p)
        return false;
 }
 
-static inline bool mem_cgroup_oom_synchronize(void)
+static inline bool mem_cgroup_oom_synchronize(bool wait)
 {
        return false;
 }
@@ -471,11 +408,11 @@ static inline void mem_cgroup_dec_page_stat(struct page *page,
 }
 
 static inline
-enum mem_cgroup_filter_t
-mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg,
-               struct mem_cgroup *root)
+unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
+                                           gfp_t gfp_mask,
+                                           unsigned long *total_scanned)
 {
-       return VISIT;
+       return 0;
 }
 
 static inline void mem_cgroup_split_huge_fixup(struct page *head)
index 09c2300..cb35835 100644 (file)
@@ -45,6 +45,7 @@
 #define MAPPER_CTRL_MINOR      236
 #define LOOP_CTRL_MINOR                237
 #define VHOST_NET_MINOR                238
+#define UHID_MINOR             239
 #define MISC_DYNAMIC_MINOR     255
 
 struct device;
index cd1fdf7..8df61bc 100644 (file)
@@ -154,10 +154,6 @@ enum {
        MLX4_CMD_QUERY_IF_STAT   = 0X54,
        MLX4_CMD_SET_IF_STAT     = 0X55,
 
-       /* set port opcode modifiers */
-       MLX4_SET_PORT_PRIO2TC = 0x8,
-       MLX4_SET_PORT_SCHEDULER  = 0x9,
-
        /* register/delete flow steering network rules */
        MLX4_QP_FLOW_STEERING_ATTACH = 0x65,
        MLX4_QP_FLOW_STEERING_DETACH = 0x66,
@@ -182,6 +178,8 @@ enum {
        MLX4_SET_PORT_VLAN_TABLE = 0x3,
        MLX4_SET_PORT_PRIO_MAP  = 0x4,
        MLX4_SET_PORT_GID_TABLE = 0x5,
+       MLX4_SET_PORT_PRIO2TC   = 0x8,
+       MLX4_SET_PORT_SCHEDULER = 0x9,
 };
 
 enum {
index 24ce6bd..7d3a523 100644 (file)
@@ -54,6 +54,7 @@ enum {
        MLX4_FLAG_MASTER        = 1 << 2,
        MLX4_FLAG_SLAVE         = 1 << 3,
        MLX4_FLAG_SRIOV         = 1 << 4,
+       MLX4_FLAG_OLD_REG_MAC   = 1 << 6,
 };
 
 enum {
@@ -155,7 +156,7 @@ enum {
        MLX4_DEV_CAP_FLAG2_RSS_TOP              = 1LL <<  1,
        MLX4_DEV_CAP_FLAG2_RSS_XOR              = 1LL <<  2,
        MLX4_DEV_CAP_FLAG2_FS_EN                = 1LL <<  3,
-       MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN     = 1LL <<  4,
+       MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN      = 1LL <<  4,
        MLX4_DEV_CAP_FLAG2_TS                   = 1LL <<  5,
        MLX4_DEV_CAP_FLAG2_VLAN_CONTROL         = 1LL <<  6,
        MLX4_DEV_CAP_FLAG2_FSM                  = 1LL <<  7,
@@ -640,16 +641,28 @@ struct mlx4_counter {
        __be64  tx_bytes;
 };
 
+struct mlx4_quotas {
+       int qp;
+       int cq;
+       int srq;
+       int mpt;
+       int mtt;
+       int counter;
+       int xrcd;
+};
+
 struct mlx4_dev {
        struct pci_dev         *pdev;
        unsigned long           flags;
        unsigned long           num_slaves;
        struct mlx4_caps        caps;
        struct mlx4_phys_caps   phys_caps;
+       struct mlx4_quotas      quotas;
        struct radix_tree_root  qp_table_tree;
        u8                      rev_id;
        char                    board_id[MLX4_BOARD_ID_LEN];
        int                     num_vfs;
+       int                     numa_node;
        int                     oper_log_mgm_entry_size;
        u64                     regid_promisc_array[MLX4_MAX_PORTS + 1];
        u64                     regid_allmulti_array[MLX4_MAX_PORTS + 1];
@@ -771,6 +784,12 @@ static inline int mlx4_is_master(struct mlx4_dev *dev)
        return dev->flags & MLX4_FLAG_MASTER;
 }
 
+static inline int mlx4_num_reserved_sqps(struct mlx4_dev *dev)
+{
+       return dev->phys_caps.base_sqpn + 8 +
+               16 * MLX4_MFUNC_MAX * !!mlx4_is_master(dev);
+}
+
 static inline int mlx4_is_qp_reserved(struct mlx4_dev *dev, u32 qpn)
 {
        return (qpn < dev->phys_caps.base_sqpn + 8 +
@@ -816,7 +835,7 @@ void mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn);
 
 int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar);
 void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar);
-int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf);
+int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf, int node);
 void mlx4_bf_free(struct mlx4_dev *dev, struct mlx4_bf *bf);
 
 int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift,
@@ -1078,7 +1097,7 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
                u8 *pg, u16 *ratelimit);
 int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx);
 int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
-void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index);
+void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan);
 
 int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list,
                      int npages, u64 iova, u32 *lkey, u32 *rkey);
index 68029b3..5eb4e31 100644 (file)
@@ -181,7 +181,7 @@ enum {
        MLX5_DEV_CAP_FLAG_TLP_HINTS     = 1LL << 39,
        MLX5_DEV_CAP_FLAG_SIG_HAND_OVER = 1LL << 40,
        MLX5_DEV_CAP_FLAG_DCT           = 1LL << 41,
-       MLX5_DEV_CAP_FLAG_CMDIF_CSUM    = 1LL << 46,
+       MLX5_DEV_CAP_FLAG_CMDIF_CSUM    = 3LL << 46,
 };
 
 enum {
@@ -417,7 +417,7 @@ struct mlx5_init_seg {
        struct health_buffer    health;
        __be32                  rsvd2[884];
        __be32                  health_counter;
-       __be32                  rsvd3[1023];
+       __be32                  rsvd3[1019];
        __be64                  ieee1588_clk;
        __be32                  ieee1588_clk_type;
        __be32                  clr_intx;
index 8888381..6b8c496 100644 (file)
@@ -82,7 +82,7 @@ enum {
 };
 
 enum {
-       MLX5_MAX_EQ_NAME        = 20
+       MLX5_MAX_EQ_NAME        = 32
 };
 
 enum {
@@ -747,8 +747,7 @@ static inline u32 mlx5_idx_to_mkey(u32 mkey_idx)
 
 enum {
        MLX5_PROF_MASK_QP_SIZE          = (u64)1 << 0,
-       MLX5_PROF_MASK_CMDIF_CSUM       = (u64)1 << 1,
-       MLX5_PROF_MASK_MR_CACHE         = (u64)1 << 2,
+       MLX5_PROF_MASK_MR_CACHE         = (u64)1 << 1,
 };
 
 enum {
@@ -758,7 +757,6 @@ enum {
 struct mlx5_profile {
        u64     mask;
        u32     log_max_qp;
-       int     cmdif_csum;
        struct {
                int     size;
                int     limit;
index ccd4260..bab49da 100644 (file)
@@ -15,8 +15,8 @@
 #include <linux/spinlock_types.h>
 #include <linux/linkage.h>
 #include <linux/lockdep.h>
-
 #include <linux/atomic.h>
+#include <asm/processor.h>
 
 /*
  * Simple, straightforward mutexes with strict semantics:
@@ -175,8 +175,8 @@ extern void mutex_unlock(struct mutex *lock);
 
 extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
 
-#ifndef CONFIG_HAVE_ARCH_MUTEX_CPU_RELAX
-#define arch_mutex_cpu_relax() cpu_relax()
+#ifndef arch_mutex_cpu_relax
+# define arch_mutex_cpu_relax() cpu_relax()
 #endif
 
 #endif
index 4f27575..b292a04 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/fcntl.h>       /* For O_CLOEXEC and O_NONBLOCK */
 #include <linux/kmemcheck.h>
 #include <linux/rcupdate.h>
+#include <linux/jump_label.h>
 #include <uapi/linux/net.h>
 
 struct poll_table_struct;
@@ -195,27 +196,23 @@ enum {
        SOCK_WAKE_URG,
 };
 
-extern int          sock_wake_async(struct socket *sk, int how, int band);
-extern int          sock_register(const struct net_proto_family *fam);
-extern void         sock_unregister(int family);
-extern int          __sock_create(struct net *net, int family, int type, int proto,
-                                struct socket **res, int kern);
-extern int          sock_create(int family, int type, int proto,
-                                struct socket **res);
-extern int          sock_create_kern(int family, int type, int proto,
-                                     struct socket **res);
-extern int          sock_create_lite(int family, int type, int proto,
-                                     struct socket **res); 
-extern void         sock_release(struct socket *sock);
-extern int          sock_sendmsg(struct socket *sock, struct msghdr *msg,
-                                 size_t len);
-extern int          sock_recvmsg(struct socket *sock, struct msghdr *msg,
-                                 size_t size, int flags);
-extern struct file  *sock_alloc_file(struct socket *sock, int flags, const char *dname);
-extern struct socket *sockfd_lookup(int fd, int *err);
-extern struct socket *sock_from_file(struct file *file, int *err);
+int sock_wake_async(struct socket *sk, int how, int band);
+int sock_register(const struct net_proto_family *fam);
+void sock_unregister(int family);
+int __sock_create(struct net *net, int family, int type, int proto,
+                 struct socket **res, int kern);
+int sock_create(int family, int type, int proto, struct socket **res);
+int sock_create_kern(int family, int type, int proto, struct socket **res);
+int sock_create_lite(int family, int type, int proto, struct socket **res);
+void sock_release(struct socket *sock);
+int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len);
+int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
+                int flags);
+struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname);
+struct socket *sockfd_lookup(int fd, int *err);
+struct socket *sock_from_file(struct file *file, int *err);
 #define                     sockfd_put(sock) fput(sock->file)
-extern int          net_ratelimit(void);
+int net_ratelimit(void);
 
 #define net_ratelimited_function(function, ...)                        \
 do {                                                           \
@@ -243,32 +240,52 @@ do {                                                              \
 #define net_random()           prandom_u32()
 #define net_srandom(seed)      prandom_seed((__force u32)(seed))
 
-extern int          kernel_sendmsg(struct socket *sock, struct msghdr *msg,
-                                   struct kvec *vec, size_t num, size_t len);
-extern int          kernel_recvmsg(struct socket *sock, struct msghdr *msg,
-                                   struct kvec *vec, size_t num,
-                                   size_t len, int flags);
-
-extern int kernel_bind(struct socket *sock, struct sockaddr *addr,
-                      int addrlen);
-extern int kernel_listen(struct socket *sock, int backlog);
-extern int kernel_accept(struct socket *sock, struct socket **newsock,
-                        int flags);
-extern int kernel_connect(struct socket *sock, struct sockaddr *addr,
-                         int addrlen, int flags);
-extern int kernel_getsockname(struct socket *sock, struct sockaddr *addr,
-                             int *addrlen);
-extern int kernel_getpeername(struct socket *sock, struct sockaddr *addr,
-                             int *addrlen);
-extern int kernel_getsockopt(struct socket *sock, int level, int optname,
-                            char *optval, int *optlen);
-extern int kernel_setsockopt(struct socket *sock, int level, int optname,
-                            char *optval, unsigned int optlen);
-extern int kernel_sendpage(struct socket *sock, struct page *page, int offset,
-                          size_t size, int flags);
-extern int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
-extern int kernel_sock_shutdown(struct socket *sock,
-                               enum sock_shutdown_cmd how);
+bool __net_get_random_once(void *buf, int nbytes, bool *done,
+                          struct static_key *done_key);
+
+#ifdef HAVE_JUMP_LABEL
+#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \
+               { .enabled = ATOMIC_INIT(0), .entries = (void *)1 })
+#else /* !HAVE_JUMP_LABEL */
+#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE
+#endif /* HAVE_JUMP_LABEL */
+
+#define net_get_random_once(buf, nbytes)                               \
+       ({                                                              \
+               bool ___ret = false;                                    \
+               static bool ___done = false;                            \
+               static struct static_key ___done_key =                  \
+                       ___NET_RANDOM_STATIC_KEY_INIT;                  \
+               if (!static_key_true(&___done_key))                     \
+                       ___ret = __net_get_random_once(buf,             \
+                                                      nbytes,          \
+                                                      &___done,        \
+                                                      &___done_key);   \
+               ___ret;                                                 \
+       })
+
+int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec,
+                  size_t num, size_t len);
+int kernel_recvmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec,
+                  size_t num, size_t len, int flags);
+
+int kernel_bind(struct socket *sock, struct sockaddr *addr, int addrlen);
+int kernel_listen(struct socket *sock, int backlog);
+int kernel_accept(struct socket *sock, struct socket **newsock, int flags);
+int kernel_connect(struct socket *sock, struct sockaddr *addr, int addrlen,
+                  int flags);
+int kernel_getsockname(struct socket *sock, struct sockaddr *addr,
+                      int *addrlen);
+int kernel_getpeername(struct socket *sock, struct sockaddr *addr,
+                      int *addrlen);
+int kernel_getsockopt(struct socket *sock, int level, int optname, char *optval,
+                     int *optlen);
+int kernel_setsockopt(struct socket *sock, int level, int optname, char *optval,
+                     unsigned int optlen);
+int kernel_sendpage(struct socket *sock, struct page *page, int offset,
+                   size_t size, int flags);
+int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
+int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how);
 
 #define MODULE_ALIAS_NETPROTO(proto) \
        MODULE_ALIAS("net-pf-" __stringify(proto))
index a2a89a5..1005ebf 100644 (file)
@@ -42,6 +42,8 @@ enum {
        NETIF_F_TSO6_BIT,               /* ... TCPv6 segmentation */
        NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
+       NETIF_F_GSO_IPIP_BIT,           /* ... IPIP tunnel with TSO */
+       NETIF_F_GSO_SIT_BIT,            /* ... SIT tunnel with TSO */
        NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
        NETIF_F_GSO_MPLS_BIT,           /* ... MPLS segmentation */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
@@ -60,6 +62,7 @@ enum {
        NETIF_F_HW_VLAN_STAG_TX_BIT,    /* Transmit VLAN STAG HW acceleration */
        NETIF_F_HW_VLAN_STAG_RX_BIT,    /* Receive VLAN STAG HW acceleration */
        NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */
+       NETIF_F_HW_L2FW_DOFFLOAD_BIT,   /* Allow L2 Forwarding in Hardware */
 
        /*
         * Add your fresh new feature above and remember to update
@@ -107,11 +110,14 @@ enum {
 #define NETIF_F_RXFCS          __NETIF_F(RXFCS)
 #define NETIF_F_RXALL          __NETIF_F(RXALL)
 #define NETIF_F_GSO_GRE                __NETIF_F(GSO_GRE)
+#define NETIF_F_GSO_IPIP       __NETIF_F(GSO_IPIP)
+#define NETIF_F_GSO_SIT                __NETIF_F(GSO_SIT)
 #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
 #define NETIF_F_GSO_MPLS       __NETIF_F(GSO_MPLS)
 #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
 #define NETIF_F_HW_VLAN_STAG_RX        __NETIF_F(HW_VLAN_STAG_RX)
 #define NETIF_F_HW_VLAN_STAG_TX        __NETIF_F(HW_VLAN_STAG_TX)
+#define NETIF_F_HW_L2FW_DOFFLOAD       __NETIF_F(HW_L2FW_DOFFLOAD)
 
 /* Features valid for ethtool to change */
 /* = all defined minus driver/device-class-related */
index 3de49ac..15fa01c 100644 (file)
@@ -60,8 +60,8 @@ struct wireless_dev;
 #define SET_ETHTOOL_OPS(netdev,ops) \
        ( (netdev)->ethtool_ops = (ops) )
 
-extern void netdev_set_default_ethtool_ops(struct net_device *dev,
-                                          const struct ethtool_ops *ops);
+void netdev_set_default_ethtool_ops(struct net_device *dev,
+                                   const struct ethtool_ops *ops);
 
 /* hardware address assignment types */
 #define NET_ADDR_PERM          0       /* address is permanent (default) */
@@ -298,7 +298,7 @@ struct netdev_boot_setup {
 };
 #define NETDEV_BOOT_SETUP_MAX 8
 
-extern int __init netdev_boot_setup(char *str);
+int __init netdev_boot_setup(char *str);
 
 /*
  * Structure for NAPI scheduling similar to tasklet but with weighting
@@ -394,7 +394,7 @@ enum rx_handler_result {
 typedef enum rx_handler_result rx_handler_result_t;
 typedef rx_handler_result_t rx_handler_func_t(struct sk_buff **pskb);
 
-extern void __napi_schedule(struct napi_struct *n);
+void __napi_schedule(struct napi_struct *n);
 
 static inline bool napi_disable_pending(struct napi_struct *n)
 {
@@ -445,8 +445,8 @@ static inline bool napi_reschedule(struct napi_struct *napi)
  *
  * Mark NAPI processing as complete.
  */
-extern void __napi_complete(struct napi_struct *n);
-extern void napi_complete(struct napi_struct *n);
+void __napi_complete(struct napi_struct *n);
+void napi_complete(struct napi_struct *n);
 
 /**
  *     napi_by_id - lookup a NAPI by napi_id
@@ -455,7 +455,7 @@ extern void napi_complete(struct napi_struct *n);
  * lookup @napi_id in napi_hash table
  * must be called under rcu_read_lock()
  */
-extern struct napi_struct *napi_by_id(unsigned int napi_id);
+struct napi_struct *napi_by_id(unsigned int napi_id);
 
 /**
  *     napi_hash_add - add a NAPI to global hashtable
@@ -463,7 +463,7 @@ extern struct napi_struct *napi_by_id(unsigned int napi_id);
  *
  * generate a new napi_id and store a @napi under it in napi_hash
  */
-extern void napi_hash_add(struct napi_struct *napi);
+void napi_hash_add(struct napi_struct *napi);
 
 /**
  *     napi_hash_del - remove a NAPI from global table
@@ -472,7 +472,7 @@ extern void napi_hash_add(struct napi_struct *napi);
  * Warning: caller must observe rcu grace period
  * before freeing memory containing @napi
  */
-extern void napi_hash_del(struct napi_struct *napi);
+void napi_hash_del(struct napi_struct *napi);
 
 /**
  *     napi_disable - prevent NAPI from scheduling
@@ -483,6 +483,7 @@ extern void napi_hash_del(struct napi_struct *napi);
  */
 static inline void napi_disable(struct napi_struct *n)
 {
+       might_sleep();
        set_bit(NAPI_STATE_DISABLE, &n->state);
        while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
                msleep(1);
@@ -664,8 +665,8 @@ static inline void rps_reset_sock_flow(struct rps_sock_flow_table *table,
 extern struct rps_sock_flow_table __rcu *rps_sock_flow_table;
 
 #ifdef CONFIG_RFS_ACCEL
-extern bool rps_may_expire_flow(struct net_device *dev, u16 rxq_index,
-                               u32 flow_id, u16 filter_id);
+bool rps_may_expire_flow(struct net_device *dev, u16 rxq_index, u32 flow_id,
+                        u16 filter_id);
 #endif
 
 /* This structure contains an instance of an RX queue. */
@@ -961,6 +962,25 @@ struct netdev_phys_port_id {
  *     Called by vxlan to notify the driver about a UDP port and socket
  *     address family that vxlan is not listening to anymore. The operation
  *     is protected by the vxlan_net->sock_lock.
+ *
+ * void* (*ndo_dfwd_add_station)(struct net_device *pdev,
+ *                              struct net_device *dev)
+ *     Called by upper layer devices to accelerate switching or other
+ *     station functionality into hardware. 'pdev is the lowerdev
+ *     to use for the offload and 'dev' is the net device that will
+ *     back the offload. Returns a pointer to the private structure
+ *     the upper layer will maintain.
+ * void (*ndo_dfwd_del_station)(struct net_device *pdev, void *priv)
+ *     Called by upper layer device to delete the station created
+ *     by 'ndo_dfwd_add_station'. 'pdev' is the net device backing
+ *     the station and priv is the structure returned by the add
+ *     operation.
+ * netdev_tx_t (*ndo_dfwd_start_xmit)(struct sk_buff *skb,
+ *                                   struct net_device *dev,
+ *                                   void *priv);
+ *     Callback to use for xmit over the accelerated station. This
+ *     is used in place of ndo_start_xmit on accelerated net
+ *     devices.
  */
 struct net_device_ops {
        int                     (*ndo_init)(struct net_device *dev);
@@ -1097,6 +1117,15 @@ struct net_device_ops {
        void                    (*ndo_del_vxlan_port)(struct  net_device *dev,
                                                      sa_family_t sa_family,
                                                      __be16 port);
+
+       void*                   (*ndo_dfwd_add_station)(struct net_device *pdev,
+                                                       struct net_device *dev);
+       void                    (*ndo_dfwd_del_station)(struct net_device *pdev,
+                                                       void *priv);
+
+       netdev_tx_t             (*ndo_dfwd_start_xmit) (struct sk_buff *skb,
+                                                       struct net_device *dev,
+                                                       void *priv);
 };
 
 /*
@@ -1131,7 +1160,7 @@ struct net_device {
        unsigned long           mem_end;        /* shared mem end       */
        unsigned long           mem_start;      /* shared mem start     */
        unsigned long           base_addr;      /* device I/O address   */
-       unsigned int            irq;            /* device IRQ number    */
+       int                     irq;            /* device IRQ number    */
 
        /*
         *      Some hardware also needs these fields, but they are not
@@ -1143,8 +1172,19 @@ struct net_device {
        struct list_head        dev_list;
        struct list_head        napi_list;
        struct list_head        unreg_list;
-       struct list_head        upper_dev_list; /* List of upper devices */
-       struct list_head        lower_dev_list;
+       struct list_head        close_list;
+
+       /* directly linked devices, like slaves for bonding */
+       struct {
+               struct list_head upper;
+               struct list_head lower;
+       } adj_list;
+
+       /* all linked devices, *including* neighbours */
+       struct {
+               struct list_head upper;
+               struct list_head lower;
+       } all_adj_list;
 
 
        /* currently active device features */
@@ -1183,6 +1223,7 @@ struct net_device {
        /* Management operations */
        const struct net_device_ops *netdev_ops;
        const struct ethtool_ops *ethtool_ops;
+       const struct forwarding_accel_ops *fwd_ops;
 
        /* Hardware header description */
        const struct header_ops *header_ops;
@@ -1487,9 +1528,9 @@ static inline void netdev_for_each_tx_queue(struct net_device *dev,
                f(dev, &dev->_tx[i], arg);
 }
 
-extern struct netdev_queue *netdev_pick_tx(struct net_device *dev,
-                                          struct sk_buff *skb);
-extern u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb);
+struct netdev_queue *netdev_pick_tx(struct net_device *dev,
+                                   struct sk_buff *skb);
+u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb);
 
 /*
  * Net namespace inlines
@@ -1673,8 +1714,8 @@ struct packet_offload {
 #define NETDEV_CHANGEUPPER     0x0015
 #define NETDEV_RESEND_IGMP     0x0016
 
-extern int register_netdevice_notifier(struct notifier_block *nb);
-extern int unregister_netdevice_notifier(struct notifier_block *nb);
+int register_netdevice_notifier(struct notifier_block *nb);
+int unregister_netdevice_notifier(struct notifier_block *nb);
 
 struct netdev_notifier_info {
        struct net_device *dev;
@@ -1697,9 +1738,9 @@ netdev_notifier_info_to_dev(const struct netdev_notifier_info *info)
        return info->dev;
 }
 
-extern int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev,
-                                        struct netdev_notifier_info *info);
-extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
+int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev,
+                                 struct netdev_notifier_info *info);
+int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
 
 
 extern rwlock_t                                dev_base_lock;          /* Device list lock */
@@ -1754,54 +1795,53 @@ static inline struct net_device *first_net_device_rcu(struct net *net)
        return lh == &net->dev_base_head ? NULL : net_device_entry(lh);
 }
 
-extern int                     netdev_boot_setup_check(struct net_device *dev);
-extern unsigned long           netdev_boot_base(const char *prefix, int unit);
-extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type,
-                                             const char *hwaddr);
-extern struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type);
-extern struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type);
-extern void            dev_add_pack(struct packet_type *pt);
-extern void            dev_remove_pack(struct packet_type *pt);
-extern void            __dev_remove_pack(struct packet_type *pt);
-extern void            dev_add_offload(struct packet_offload *po);
-extern void            dev_remove_offload(struct packet_offload *po);
-extern void            __dev_remove_offload(struct packet_offload *po);
-
-extern struct net_device       *dev_get_by_flags_rcu(struct net *net, unsigned short flags,
-                                                     unsigned short mask);
-extern struct net_device       *dev_get_by_name(struct net *net, const char *name);
-extern struct net_device       *dev_get_by_name_rcu(struct net *net, const char *name);
-extern struct net_device       *__dev_get_by_name(struct net *net, const char *name);
-extern int             dev_alloc_name(struct net_device *dev, const char *name);
-extern int             dev_open(struct net_device *dev);
-extern int             dev_close(struct net_device *dev);
-extern void            dev_disable_lro(struct net_device *dev);
-extern int             dev_loopback_xmit(struct sk_buff *newskb);
-extern int             dev_queue_xmit(struct sk_buff *skb);
-extern int             register_netdevice(struct net_device *dev);
-extern void            unregister_netdevice_queue(struct net_device *dev,
-                                                  struct list_head *head);
-extern void            unregister_netdevice_many(struct list_head *head);
+int netdev_boot_setup_check(struct net_device *dev);
+unsigned long netdev_boot_base(const char *prefix, int unit);
+struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type,
+                                      const char *hwaddr);
+struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type);
+struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type);
+void dev_add_pack(struct packet_type *pt);
+void dev_remove_pack(struct packet_type *pt);
+void __dev_remove_pack(struct packet_type *pt);
+void dev_add_offload(struct packet_offload *po);
+void dev_remove_offload(struct packet_offload *po);
+void __dev_remove_offload(struct packet_offload *po);
+
+struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags,
+                                       unsigned short mask);
+struct net_device *dev_get_by_name(struct net *net, const char *name);
+struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
+struct net_device *__dev_get_by_name(struct net *net, const char *name);
+int dev_alloc_name(struct net_device *dev, const char *name);
+int dev_open(struct net_device *dev);
+int dev_close(struct net_device *dev);
+void dev_disable_lro(struct net_device *dev);
+int dev_loopback_xmit(struct sk_buff *newskb);
+int dev_queue_xmit(struct sk_buff *skb);
+int register_netdevice(struct net_device *dev);
+void unregister_netdevice_queue(struct net_device *dev, struct list_head *head);
+void unregister_netdevice_many(struct list_head *head);
 static inline void unregister_netdevice(struct net_device *dev)
 {
        unregister_netdevice_queue(dev, NULL);
 }
 
-extern int             netdev_refcnt_read(const struct net_device *dev);
-extern void            free_netdev(struct net_device *dev);
-extern void            synchronize_net(void);
-extern int             init_dummy_netdev(struct net_device *dev);
+int netdev_refcnt_read(const struct net_device *dev);
+void free_netdev(struct net_device *dev);
+void netdev_freemem(struct net_device *dev);
+void synchronize_net(void);
+int init_dummy_netdev(struct net_device *dev);
 
-extern struct net_device       *dev_get_by_index(struct net *net, int ifindex);
-extern struct net_device       *__dev_get_by_index(struct net *net, int ifindex);
-extern struct net_device       *dev_get_by_index_rcu(struct net *net, int ifindex);
-extern int             netdev_get_name(struct net *net, char *name, int ifindex);
-extern int             dev_restart(struct net_device *dev);
+struct net_device *dev_get_by_index(struct net *net, int ifindex);
+struct net_device *__dev_get_by_index(struct net *net, int ifindex);
+struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
+int netdev_get_name(struct net *net, char *name, int ifindex);
+int dev_restart(struct net_device *dev);
 #ifdef CONFIG_NETPOLL_TRAP
-extern int             netpoll_trap(void);
+int netpoll_trap(void);
 #endif
-extern int            skb_gro_receive(struct sk_buff **head,
-                                      struct sk_buff *skb);
+int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb);
 
 static inline unsigned int skb_gro_offset(const struct sk_buff *skb)
 {
@@ -1873,7 +1913,7 @@ static inline int dev_parse_header(const struct sk_buff *skb,
 }
 
 typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
-extern int             register_gifconf(unsigned int family, gifconf_func_t * gifconf);
+int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
 static inline int unregister_gifconf(unsigned int family)
 {
        return register_gifconf(family, NULL);
@@ -1944,7 +1984,7 @@ static inline void input_queue_tail_incr_save(struct softnet_data *sd,
 
 DECLARE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
 
-extern void __netif_schedule(struct Qdisc *q);
+void __netif_schedule(struct Qdisc *q);
 
 static inline void netif_schedule_queue(struct netdev_queue *txq)
 {
@@ -2264,11 +2304,11 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index)
 }
 
 #ifdef CONFIG_XPS
-extern int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask,
-                              u16 index);
+int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask,
+                       u16 index);
 #else
 static inline int netif_set_xps_queue(struct net_device *dev,
-                                     struct cpumask *mask,
+                                     const struct cpumask *mask,
                                      u16 index)
 {
        return 0;
@@ -2296,12 +2336,10 @@ static inline bool netif_is_multiqueue(const struct net_device *dev)
        return dev->num_tx_queues > 1;
 }
 
-extern int netif_set_real_num_tx_queues(struct net_device *dev,
-                                       unsigned int txq);
+int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq);
 
 #ifdef CONFIG_RPS
-extern int netif_set_real_num_rx_queues(struct net_device *dev,
-                                       unsigned int rxq);
+int netif_set_real_num_rx_queues(struct net_device *dev, unsigned int rxq);
 #else
 static inline int netif_set_real_num_rx_queues(struct net_device *dev,
                                                unsigned int rxq)
@@ -2328,28 +2366,27 @@ static inline int netif_copy_real_num_queues(struct net_device *to_dev,
 }
 
 #define DEFAULT_MAX_NUM_RSS_QUEUES     (8)
-extern int netif_get_num_default_rss_queues(void);
+int netif_get_num_default_rss_queues(void);
 
 /* Use this variant when it is known for sure that it
  * is executing from hardware interrupt context or with hardware interrupts
  * disabled.
  */
-extern void dev_kfree_skb_irq(struct sk_buff *skb);
+void dev_kfree_skb_irq(struct sk_buff *skb);
 
 /* Use this variant in places where it could be invoked
  * from either hardware interrupt or other context, with hardware interrupts
  * either disabled or enabled.
  */
-extern void dev_kfree_skb_any(struct sk_buff *skb);
+void dev_kfree_skb_any(struct sk_buff *skb);
 
-extern int             netif_rx(struct sk_buff *skb);
-extern int             netif_rx_ni(struct sk_buff *skb);
-extern int             netif_receive_skb(struct sk_buff *skb);
-extern gro_result_t    napi_gro_receive(struct napi_struct *napi,
-                                        struct sk_buff *skb);
-extern void            napi_gro_flush(struct napi_struct *napi, bool flush_old);
-extern struct sk_buff *        napi_get_frags(struct napi_struct *napi);
-extern gro_result_t    napi_gro_frags(struct napi_struct *napi);
+int netif_rx(struct sk_buff *skb);
+int netif_rx_ni(struct sk_buff *skb);
+int netif_receive_skb(struct sk_buff *skb);
+gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
+void napi_gro_flush(struct napi_struct *napi, bool flush_old);
+struct sk_buff *napi_get_frags(struct napi_struct *napi);
+gro_result_t napi_gro_frags(struct napi_struct *napi);
 
 static inline void napi_free_frags(struct napi_struct *napi)
 {
@@ -2357,40 +2394,36 @@ static inline void napi_free_frags(struct napi_struct *napi)
        napi->skb = NULL;
 }
 
-extern int netdev_rx_handler_register(struct net_device *dev,
-                                     rx_handler_func_t *rx_handler,
-                                     void *rx_handler_data);
-extern void netdev_rx_handler_unregister(struct net_device *dev);
-
-extern bool            dev_valid_name(const char *name);
-extern int             dev_ioctl(struct net *net, unsigned int cmd, void __user *);
-extern int             dev_ethtool(struct net *net, struct ifreq *);
-extern unsigned int    dev_get_flags(const struct net_device *);
-extern int             __dev_change_flags(struct net_device *, unsigned int flags);
-extern int             dev_change_flags(struct net_device *, unsigned int);
-extern void            __dev_notify_flags(struct net_device *, unsigned int old_flags);
-extern int             dev_change_name(struct net_device *, const char *);
-extern int             dev_set_alias(struct net_device *, const char *, size_t);
-extern int             dev_change_net_namespace(struct net_device *,
-                                                struct net *, const char *);
-extern int             dev_set_mtu(struct net_device *, int);
-extern void            dev_set_group(struct net_device *, int);
-extern int             dev_set_mac_address(struct net_device *,
-                                           struct sockaddr *);
-extern int             dev_change_carrier(struct net_device *,
-                                          bool new_carrier);
-extern int             dev_get_phys_port_id(struct net_device *dev,
-                                            struct netdev_phys_port_id *ppid);
-extern int             dev_hard_start_xmit(struct sk_buff *skb,
-                                           struct net_device *dev,
-                                           struct netdev_queue *txq);
-extern int             dev_forward_skb(struct net_device *dev,
-                                       struct sk_buff *skb);
+int netdev_rx_handler_register(struct net_device *dev,
+                              rx_handler_func_t *rx_handler,
+                              void *rx_handler_data);
+void netdev_rx_handler_unregister(struct net_device *dev);
+
+bool dev_valid_name(const char *name);
+int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
+int dev_ethtool(struct net *net, struct ifreq *);
+unsigned int dev_get_flags(const struct net_device *);
+int __dev_change_flags(struct net_device *, unsigned int flags);
+int dev_change_flags(struct net_device *, unsigned int);
+void __dev_notify_flags(struct net_device *, unsigned int old_flags,
+                       unsigned int gchanges);
+int dev_change_name(struct net_device *, const char *);
+int dev_set_alias(struct net_device *, const char *, size_t);
+int dev_change_net_namespace(struct net_device *, struct net *, const char *);
+int dev_set_mtu(struct net_device *, int);
+void dev_set_group(struct net_device *, int);
+int dev_set_mac_address(struct net_device *, struct sockaddr *);
+int dev_change_carrier(struct net_device *, bool new_carrier);
+int dev_get_phys_port_id(struct net_device *dev,
+                        struct netdev_phys_port_id *ppid);
+int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
+                       struct netdev_queue *txq, void *accel_priv);
+int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 
 extern int             netdev_budget;
 
 /* Called by rtnetlink.c:rtnl_unlock() */
-extern void netdev_run_todo(void);
+void netdev_run_todo(void);
 
 /**
  *     dev_put - release reference to device
@@ -2423,9 +2456,9 @@ static inline void dev_hold(struct net_device *dev)
  * kind of lower layer not just hardware media.
  */
 
-extern void linkwatch_init_dev(struct net_device *dev);
-extern void linkwatch_fire_event(struct net_device *dev);
-extern void linkwatch_forget_dev(struct net_device *dev);
+void linkwatch_init_dev(struct net_device *dev);
+void linkwatch_fire_event(struct net_device *dev);
+void linkwatch_forget_dev(struct net_device *dev);
 
 /**
  *     netif_carrier_ok - test if carrier present
@@ -2438,13 +2471,13 @@ static inline bool netif_carrier_ok(const struct net_device *dev)
        return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
 }
 
-extern unsigned long dev_trans_start(struct net_device *dev);
+unsigned long dev_trans_start(struct net_device *dev);
 
-extern void __netdev_watchdog_up(struct net_device *dev);
+void __netdev_watchdog_up(struct net_device *dev);
 
-extern void netif_carrier_on(struct net_device *dev);
+void netif_carrier_on(struct net_device *dev);
 
-extern void netif_carrier_off(struct net_device *dev);
+void netif_carrier_off(struct net_device *dev);
 
 /**
  *     netif_dormant_on - mark device as dormant.
@@ -2512,9 +2545,9 @@ static inline bool netif_device_present(struct net_device *dev)
        return test_bit(__LINK_STATE_PRESENT, &dev->state);
 }
 
-extern void netif_device_detach(struct net_device *dev);
+void netif_device_detach(struct net_device *dev);
 
-extern void netif_device_attach(struct net_device *dev);
+void netif_device_attach(struct net_device *dev);
 
 /*
  * Network interface message level settings
@@ -2723,119 +2756,138 @@ static inline void netif_addr_unlock_bh(struct net_device *dev)
 
 /* These functions live elsewhere (drivers/net/net_init.c, but related) */
 
-extern void            ether_setup(struct net_device *dev);
+void ether_setup(struct net_device *dev);
 
 /* Support for loadable net-drivers */
-extern struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
-                                      void (*setup)(struct net_device *),
-                                      unsigned int txqs, unsigned int rxqs);
+struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
+                                   void (*setup)(struct net_device *),
+                                   unsigned int txqs, unsigned int rxqs);
 #define alloc_netdev(sizeof_priv, name, setup) \
        alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)
 
 #define alloc_netdev_mq(sizeof_priv, name, setup, count) \
        alloc_netdev_mqs(sizeof_priv, name, setup, count, count)
 
-extern int             register_netdev(struct net_device *dev);
-extern void            unregister_netdev(struct net_device *dev);
+int register_netdev(struct net_device *dev);
+void unregister_netdev(struct net_device *dev);
 
 /* General hardware address lists handling functions */
-extern int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
-                                 struct netdev_hw_addr_list *from_list,
-                                 int addr_len, unsigned char addr_type);
-extern void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
-                                  struct netdev_hw_addr_list *from_list,
-                                  int addr_len, unsigned char addr_type);
-extern int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
-                         struct netdev_hw_addr_list *from_list,
-                         int addr_len);
-extern void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
-                            struct netdev_hw_addr_list *from_list,
-                            int addr_len);
-extern void __hw_addr_flush(struct netdev_hw_addr_list *list);
-extern void __hw_addr_init(struct netdev_hw_addr_list *list);
+int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
+                          struct netdev_hw_addr_list *from_list,
+                          int addr_len, unsigned char addr_type);
+void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
+                           struct netdev_hw_addr_list *from_list,
+                           int addr_len, unsigned char addr_type);
+int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
+                  struct netdev_hw_addr_list *from_list, int addr_len);
+void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
+                     struct netdev_hw_addr_list *from_list, int addr_len);
+void __hw_addr_flush(struct netdev_hw_addr_list *list);
+void __hw_addr_init(struct netdev_hw_addr_list *list);
 
 /* Functions used for device addresses handling */
-extern int dev_addr_add(struct net_device *dev, const unsigned char *addr,
-                       unsigned char addr_type);
-extern int dev_addr_del(struct net_device *dev, const unsigned char *addr,
-                       unsigned char addr_type);
-extern int dev_addr_add_multiple(struct net_device *to_dev,
-                                struct net_device *from_dev,
-                                unsigned char addr_type);
-extern int dev_addr_del_multiple(struct net_device *to_dev,
-                                struct net_device *from_dev,
-                                unsigned char addr_type);
-extern void dev_addr_flush(struct net_device *dev);
-extern int dev_addr_init(struct net_device *dev);
+int dev_addr_add(struct net_device *dev, const unsigned char *addr,
+                unsigned char addr_type);
+int dev_addr_del(struct net_device *dev, const unsigned char *addr,
+                unsigned char addr_type);
+int dev_addr_add_multiple(struct net_device *to_dev,
+                         struct net_device *from_dev, unsigned char addr_type);
+int dev_addr_del_multiple(struct net_device *to_dev,
+                         struct net_device *from_dev, unsigned char addr_type);
+void dev_addr_flush(struct net_device *dev);
+int dev_addr_init(struct net_device *dev);
 
 /* Functions used for unicast addresses handling */
-extern int dev_uc_add(struct net_device *dev, const unsigned char *addr);
-extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr);
-extern int dev_uc_del(struct net_device *dev, const unsigned char *addr);
-extern int dev_uc_sync(struct net_device *to, struct net_device *from);
-extern int dev_uc_sync_multiple(struct net_device *to, struct net_device *from);
-extern void dev_uc_unsync(struct net_device *to, struct net_device *from);
-extern void dev_uc_flush(struct net_device *dev);
-extern void dev_uc_init(struct net_device *dev);
+int dev_uc_add(struct net_device *dev, const unsigned char *addr);
+int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr);
+int dev_uc_del(struct net_device *dev, const unsigned char *addr);
+int dev_uc_sync(struct net_device *to, struct net_device *from);
+int dev_uc_sync_multiple(struct net_device *to, struct net_device *from);
+void dev_uc_unsync(struct net_device *to, struct net_device *from);
+void dev_uc_flush(struct net_device *dev);
+void dev_uc_init(struct net_device *dev);
 
 /* Functions used for multicast addresses handling */
-extern int dev_mc_add(struct net_device *dev, const unsigned char *addr);
-extern int dev_mc_add_global(struct net_device *dev, const unsigned char *addr);
-extern int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr);
-extern int dev_mc_del(struct net_device *dev, const unsigned char *addr);
-extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr);
-extern int dev_mc_sync(struct net_device *to, struct net_device *from);
-extern int dev_mc_sync_multiple(struct net_device *to, struct net_device *from);
-extern void dev_mc_unsync(struct net_device *to, struct net_device *from);
-extern void dev_mc_flush(struct net_device *dev);
-extern void dev_mc_init(struct net_device *dev);
+int dev_mc_add(struct net_device *dev, const unsigned char *addr);
+int dev_mc_add_global(struct net_device *dev, const unsigned char *addr);
+int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr);
+int dev_mc_del(struct net_device *dev, const unsigned char *addr);
+int dev_mc_del_global(struct net_device *dev, const unsigned char *addr);
+int dev_mc_sync(struct net_device *to, struct net_device *from);
+int dev_mc_sync_multiple(struct net_device *to, struct net_device *from);
+void dev_mc_unsync(struct net_device *to, struct net_device *from);
+void dev_mc_flush(struct net_device *dev);
+void dev_mc_init(struct net_device *dev);
 
 /* Functions used for secondary unicast and multicast support */
-extern void            dev_set_rx_mode(struct net_device *dev);
-extern void            __dev_set_rx_mode(struct net_device *dev);
-extern int             dev_set_promiscuity(struct net_device *dev, int inc);
-extern int             dev_set_allmulti(struct net_device *dev, int inc);
-extern void            netdev_state_change(struct net_device *dev);
-extern void            netdev_notify_peers(struct net_device *dev);
-extern void            netdev_features_change(struct net_device *dev);
+void dev_set_rx_mode(struct net_device *dev);
+void __dev_set_rx_mode(struct net_device *dev);
+int dev_set_promiscuity(struct net_device *dev, int inc);
+int dev_set_allmulti(struct net_device *dev, int inc);
+void netdev_state_change(struct net_device *dev);
+void netdev_notify_peers(struct net_device *dev);
+void netdev_features_change(struct net_device *dev);
 /* Load a device via the kmod */
-extern void            dev_load(struct net *net, const char *name);
-extern struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
-                                              struct rtnl_link_stats64 *storage);
-extern void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
-                                   const struct net_device_stats *netdev_stats);
+void dev_load(struct net *net, const char *name);
+struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
+                                       struct rtnl_link_stats64 *storage);
+void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
+                            const struct net_device_stats *netdev_stats);
 
 extern int             netdev_max_backlog;
 extern int             netdev_tstamp_prequeue;
 extern int             weight_p;
 extern int             bpf_jit_enable;
 
-extern bool netdev_has_upper_dev(struct net_device *dev,
-                                struct net_device *upper_dev);
-extern bool netdev_has_any_upper_dev(struct net_device *dev);
-extern struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
-                                                       struct list_head **iter);
+bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev);
+bool netdev_has_any_upper_dev(struct net_device *dev);
+struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev,
+                                                    struct list_head **iter);
 
 /* iterate through upper list, must be called under RCU read lock */
-#define netdev_for_each_upper_dev_rcu(dev, upper, iter) \
-       for (iter = &(dev)->upper_dev_list, \
-            upper = netdev_upper_get_next_dev_rcu(dev, &(iter)); \
-            upper; \
-            upper = netdev_upper_get_next_dev_rcu(dev, &(iter)))
-
-extern struct net_device *netdev_master_upper_dev_get(struct net_device *dev);
-extern struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev);
-extern int netdev_upper_dev_link(struct net_device *dev,
+#define netdev_for_each_all_upper_dev_rcu(dev, updev, iter) \
+       for (iter = &(dev)->all_adj_list.upper, \
+            updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)); \
+            updev; \
+            updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)))
+
+void *netdev_lower_get_next_private(struct net_device *dev,
+                                   struct list_head **iter);
+void *netdev_lower_get_next_private_rcu(struct net_device *dev,
+                                       struct list_head **iter);
+
+#define netdev_for_each_lower_private(dev, priv, iter) \
+       for (iter = (dev)->adj_list.lower.next, \
+            priv = netdev_lower_get_next_private(dev, &(iter)); \
+            priv; \
+            priv = netdev_lower_get_next_private(dev, &(iter)))
+
+#define netdev_for_each_lower_private_rcu(dev, priv, iter) \
+       for (iter = &(dev)->adj_list.lower, \
+            priv = netdev_lower_get_next_private_rcu(dev, &(iter)); \
+            priv; \
+            priv = netdev_lower_get_next_private_rcu(dev, &(iter)))
+
+void *netdev_adjacent_get_private(struct list_head *adj_list);
+struct net_device *netdev_master_upper_dev_get(struct net_device *dev);
+struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev);
+int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev);
+int netdev_master_upper_dev_link(struct net_device *dev,
                                 struct net_device *upper_dev);
-extern int netdev_master_upper_dev_link(struct net_device *dev,
-                                       struct net_device *upper_dev);
-extern void netdev_upper_dev_unlink(struct net_device *dev,
-                                   struct net_device *upper_dev);
-extern int skb_checksum_help(struct sk_buff *skb);
-extern struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
-       netdev_features_t features, bool tx_path);
-extern struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
-                                         netdev_features_t features);
+int netdev_master_upper_dev_link_private(struct net_device *dev,
+                                        struct net_device *upper_dev,
+                                        void *private);
+void netdev_upper_dev_unlink(struct net_device *dev,
+                            struct net_device *upper_dev);
+void *netdev_lower_dev_get_private_rcu(struct net_device *dev,
+                                      struct net_device *lower_dev);
+void *netdev_lower_dev_get_private(struct net_device *dev,
+                                  struct net_device *lower_dev);
+int skb_checksum_help(struct sk_buff *skb);
+struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
+                                 netdev_features_t features, bool tx_path);
+struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
+                                   netdev_features_t features);
 
 static inline
 struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
@@ -2857,30 +2909,30 @@ static inline bool can_checksum_protocol(netdev_features_t features,
 }
 
 #ifdef CONFIG_BUG
-extern void netdev_rx_csum_fault(struct net_device *dev);
+void netdev_rx_csum_fault(struct net_device *dev);
 #else
 static inline void netdev_rx_csum_fault(struct net_device *dev)
 {
 }
 #endif
 /* rx skb timestamps */
-extern void            net_enable_timestamp(void);
-extern void            net_disable_timestamp(void);
+void net_enable_timestamp(void);
+void net_disable_timestamp(void);
 
 #ifdef CONFIG_PROC_FS
-extern int __init dev_proc_init(void);
+int __init dev_proc_init(void);
 #else
 #define dev_proc_init() 0
 #endif
 
-extern int netdev_class_create_file(struct class_attribute *class_attr);
-extern void netdev_class_remove_file(struct class_attribute *class_attr);
+int netdev_class_create_file(struct class_attribute *class_attr);
+void netdev_class_remove_file(struct class_attribute *class_attr);
 
 extern struct kobj_ns_type_operations net_ns_type_operations;
 
-extern const char *netdev_drivername(const struct net_device *dev);
+const char *netdev_drivername(const struct net_device *dev);
 
-extern void linkwatch_run_queue(void);
+void linkwatch_run_queue(void);
 
 static inline netdev_features_t netdev_get_wanted_features(
        struct net_device *dev)
@@ -2944,6 +2996,11 @@ static inline void netif_set_gso_max_size(struct net_device *dev,
        dev->gso_max_size = size;
 }
 
+static inline bool netif_is_macvlan(struct net_device *dev)
+{
+       return dev->priv_flags & IFF_MACVLAN;
+}
+
 static inline bool netif_is_bond_master(struct net_device *dev)
 {
        return dev->flags & IFF_MASTER && dev->priv_flags & IFF_BONDING;
@@ -2972,22 +3029,22 @@ static inline const char *netdev_name(const struct net_device *dev)
        return dev->name;
 }
 
-extern __printf(3, 4)
+__printf(3, 4)
 int netdev_printk(const char *level, const struct net_device *dev,
                  const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_emerg(const struct net_device *dev, const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_alert(const struct net_device *dev, const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_crit(const struct net_device *dev, const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_err(const struct net_device *dev, const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_warn(const struct net_device *dev, const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_notice(const struct net_device *dev, const char *format, ...);
-extern __printf(2, 3)
+__printf(2, 3)
 int netdev_info(const struct net_device *dev, const char *format, ...);
 
 #define MODULE_ALIAS_NETDEV(device) \
@@ -3028,7 +3085,7 @@ do {                                                              \
  * file/line information and a backtrace.
  */
 #define netdev_WARN(dev, format, args...)                      \
-       WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args);
+       WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args)
 
 /* netif printk helpers, similar to netdev_printk */
 
index 708fe72..2077489 100644 (file)
@@ -35,14 +35,15 @@ static inline void nf_inet_addr_mask(const union nf_inet_addr *a1,
        result->all[3] = a1->all[3] & mask->all[3];
 }
 
-extern int netfilter_init(void);
+int netfilter_init(void);
 
 /* Largest hook number + 1 */
 #define NF_MAX_HOOKS 8
 
 struct sk_buff;
 
-typedef unsigned int nf_hookfn(unsigned int hooknum,
+struct nf_hook_ops;
+typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops,
                               struct sk_buff *skb,
                               const struct net_device *in,
                               const struct net_device *out,
@@ -52,12 +53,13 @@ struct nf_hook_ops {
        struct list_head list;
 
        /* User fills in from here down. */
-       nf_hookfn *hook;
-       struct module *owner;
-       u_int8_t pf;
-       unsigned int hooknum;
+       nf_hookfn       *hook;
+       struct module   *owner;
+       void            *priv;
+       u_int8_t        pf;
+       unsigned int    hooknum;
        /* Hooks are ordered in ascending priority. */
-       int priority;
+       int             priority;
 };
 
 struct nf_sockopt_ops {
@@ -208,7 +210,7 @@ int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, int optval,
 /* Call this before modifying an existing packet: ensures it is
    modifiable and linear to the point you care about (writable_len).
    Returns true or false. */
-extern int skb_make_writable(struct sk_buff *skb, unsigned int writable_len);
+int skb_make_writable(struct sk_buff *skb, unsigned int writable_len);
 
 struct flowi;
 struct nf_queue_entry;
@@ -269,8 +271,8 @@ nf_checksum_partial(struct sk_buff *skb, unsigned int hook,
        return csum;
 }
 
-extern int nf_register_afinfo(const struct nf_afinfo *afinfo);
-extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo);
+int nf_register_afinfo(const struct nf_afinfo *afinfo);
+void nf_unregister_afinfo(const struct nf_afinfo *afinfo);
 
 #include <net/flow.h>
 extern void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *);
@@ -315,7 +317,7 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
 
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 extern void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) __rcu;
-extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *);
+void nf_ct_attach(struct sk_buff *, const struct sk_buff *);
 extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu;
 
 struct nf_conn;
index 9ac9fbd..c7174b8 100644 (file)
@@ -49,31 +49,68 @@ enum ip_set_feature {
 
 /* Set extensions */
 enum ip_set_extension {
-       IPSET_EXT_NONE = 0,
-       IPSET_EXT_BIT_TIMEOUT = 1,
+       IPSET_EXT_BIT_TIMEOUT = 0,
        IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT),
-       IPSET_EXT_BIT_COUNTER = 2,
+       IPSET_EXT_BIT_COUNTER = 1,
        IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER),
-};
-
-/* Extension offsets */
-enum ip_set_offset {
-       IPSET_OFFSET_TIMEOUT = 0,
-       IPSET_OFFSET_COUNTER,
-       IPSET_OFFSET_MAX,
+       IPSET_EXT_BIT_COMMENT = 2,
+       IPSET_EXT_COMMENT = (1 << IPSET_EXT_BIT_COMMENT),
+       /* Mark set with an extension which needs to call destroy */
+       IPSET_EXT_BIT_DESTROY = 7,
+       IPSET_EXT_DESTROY = (1 << IPSET_EXT_BIT_DESTROY),
 };
 
 #define SET_WITH_TIMEOUT(s)    ((s)->extensions & IPSET_EXT_TIMEOUT)
 #define SET_WITH_COUNTER(s)    ((s)->extensions & IPSET_EXT_COUNTER)
+#define SET_WITH_COMMENT(s)    ((s)->extensions & IPSET_EXT_COMMENT)
+
+/* Extension id, in size order */
+enum ip_set_ext_id {
+       IPSET_EXT_ID_COUNTER = 0,
+       IPSET_EXT_ID_TIMEOUT,
+       IPSET_EXT_ID_COMMENT,
+       IPSET_EXT_ID_MAX,
+};
+
+/* Extension type */
+struct ip_set_ext_type {
+       /* Destroy extension private data (can be NULL) */
+       void (*destroy)(void *ext);
+       enum ip_set_extension type;
+       enum ipset_cadt_flags flag;
+       /* Size and minimal alignment */
+       u8 len;
+       u8 align;
+};
+
+extern const struct ip_set_ext_type ip_set_extensions[];
 
 struct ip_set_ext {
-       unsigned long timeout;
        u64 packets;
        u64 bytes;
+       u32 timeout;
+       char *comment;
+};
+
+struct ip_set_counter {
+       atomic64_t bytes;
+       atomic64_t packets;
+};
+
+struct ip_set_comment {
+       char *str;
 };
 
 struct ip_set;
 
+#define ext_timeout(e, s)      \
+(unsigned long *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_TIMEOUT])
+#define ext_counter(e, s)      \
+(struct ip_set_counter *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COUNTER])
+#define ext_comment(e, s)      \
+(struct ip_set_comment *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COMMENT])
+
+
 typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
                           const struct ip_set_ext *ext,
                           struct ip_set_ext *mext, u32 cmdflags);
@@ -147,7 +184,8 @@ struct ip_set_type {
        u8 revision_min, revision_max;
 
        /* Create set */
-       int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags);
+       int (*create)(struct net *net, struct ip_set *set,
+                     struct nlattr *tb[], u32 flags);
 
        /* Attribute policies */
        const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1];
@@ -179,14 +217,45 @@ struct ip_set {
        u8 revision;
        /* Extensions */
        u8 extensions;
+       /* Default timeout value, if enabled */
+       u32 timeout;
+       /* Element data size */
+       size_t dsize;
+       /* Offsets to extensions in elements */
+       size_t offset[IPSET_EXT_ID_MAX];
        /* The type specific data */
        void *data;
 };
 
-struct ip_set_counter {
-       atomic64_t bytes;
-       atomic64_t packets;
-};
+static inline void
+ip_set_ext_destroy(struct ip_set *set, void *data)
+{
+       /* Check that the extension is enabled for the set and
+        * call it's destroy function for its extension part in data.
+        */
+       if (SET_WITH_COMMENT(set))
+               ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(
+                       ext_comment(data, set));
+}
+
+static inline int
+ip_set_put_flags(struct sk_buff *skb, struct ip_set *set)
+{
+       u32 cadt_flags = 0;
+
+       if (SET_WITH_TIMEOUT(set))
+               if (unlikely(nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+                                          htonl(set->timeout))))
+                       return -EMSGSIZE;
+       if (SET_WITH_COUNTER(set))
+               cadt_flags |= IPSET_FLAG_WITH_COUNTERS;
+       if (SET_WITH_COMMENT(set))
+               cadt_flags |= IPSET_FLAG_WITH_COMMENT;
+
+       if (!cadt_flags)
+               return 0;
+       return nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(cadt_flags));
+}
 
 static inline void
 ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter)
@@ -247,13 +316,24 @@ ip_set_init_counter(struct ip_set_counter *counter,
                atomic64_set(&(counter)->packets, (long long)(ext->packets));
 }
 
+/* Netlink CB args */
+enum {
+       IPSET_CB_NET = 0,
+       IPSET_CB_DUMP,
+       IPSET_CB_INDEX,
+       IPSET_CB_ARG0,
+       IPSET_CB_ARG1,
+       IPSET_CB_ARG2,
+};
+
 /* register and unregister set references */
-extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
-extern void ip_set_put_byindex(ip_set_id_t index);
-extern const char *ip_set_name_byindex(ip_set_id_t index);
-extern ip_set_id_t ip_set_nfnl_get(const char *name);
-extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index);
-extern void ip_set_nfnl_put(ip_set_id_t index);
+extern ip_set_id_t ip_set_get_byname(struct net *net,
+                                    const char *name, struct ip_set **set);
+extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
+extern const char *ip_set_name_byindex(struct net *net, ip_set_id_t index);
+extern ip_set_id_t ip_set_nfnl_get(struct net *net, const char *name);
+extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);
+extern void ip_set_nfnl_put(struct net *net, ip_set_id_t index);
 
 /* API for iptables set match, and SET target */
 
@@ -272,6 +352,8 @@ extern void *ip_set_alloc(size_t size);
 extern void ip_set_free(void *members);
 extern int ip_set_get_ipaddr4(struct nlattr *nla,  __be32 *ipaddr);
 extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
+extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[],
+                             size_t len);
 extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
                                 struct ip_set_ext *ext);
 
@@ -389,13 +471,40 @@ bitmap_bytes(u32 a, u32 b)
 }
 
 #include <linux/netfilter/ipset/ip_set_timeout.h>
+#include <linux/netfilter/ipset/ip_set_comment.h>
+
+static inline int
+ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set,
+                     const void *e, bool active)
+{
+       if (SET_WITH_TIMEOUT(set)) {
+               unsigned long *timeout = ext_timeout(e, set);
+
+               if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+                       htonl(active ? ip_set_timeout_get(timeout)
+                               : *timeout)))
+                       return -EMSGSIZE;
+       }
+       if (SET_WITH_COUNTER(set) &&
+           ip_set_put_counter(skb, ext_counter(e, set)))
+               return -EMSGSIZE;
+       if (SET_WITH_COMMENT(set) &&
+           ip_set_put_comment(skb, ext_comment(e, set)))
+               return -EMSGSIZE;
+       return 0;
+}
 
-#define IP_SET_INIT_KEXT(skb, opt, map)                        \
+#define IP_SET_INIT_KEXT(skb, opt, set)                        \
        { .bytes = (skb)->len, .packets = 1,            \
-         .timeout = ip_set_adt_opt_timeout(opt, map) }
+         .timeout = ip_set_adt_opt_timeout(opt, set) }
 
-#define IP_SET_INIT_UEXT(map)                          \
+#define IP_SET_INIT_UEXT(set)                          \
        { .bytes = ULLONG_MAX, .packets = ULLONG_MAX,   \
-         .timeout = (map)->timeout }
+         .timeout = (set)->timeout }
+
+#define IP_SET_INIT_CIDR(a, b) ((a) ? (a) : (b))
+
+#define IPSET_CONCAT(a, b)             a##b
+#define IPSET_TOKEN(a, b)              IPSET_CONCAT(a, b)
 
 #endif /*_IP_SET_H */
diff --git a/include/linux/netfilter/ipset/ip_set_comment.h b/include/linux/netfilter/ipset/ip_set_comment.h
new file mode 100644 (file)
index 0000000..21217ea
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _IP_SET_COMMENT_H
+#define _IP_SET_COMMENT_H
+
+/* Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+
+static inline char*
+ip_set_comment_uget(struct nlattr *tb)
+{
+       return nla_data(tb);
+}
+
+static inline void
+ip_set_init_comment(struct ip_set_comment *comment,
+                   const struct ip_set_ext *ext)
+{
+       size_t len = ext->comment ? strlen(ext->comment) : 0;
+
+       if (unlikely(comment->str)) {
+               kfree(comment->str);
+               comment->str = NULL;
+       }
+       if (!len)
+               return;
+       if (unlikely(len > IPSET_MAX_COMMENT_SIZE))
+               len = IPSET_MAX_COMMENT_SIZE;
+       comment->str = kzalloc(len + 1, GFP_ATOMIC);
+       if (unlikely(!comment->str))
+               return;
+       strlcpy(comment->str, ext->comment, len + 1);
+}
+
+static inline int
+ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment)
+{
+       if (!comment->str)
+               return 0;
+       return nla_put_string(skb, IPSET_ATTR_COMMENT, comment->str);
+}
+
+static inline void
+ip_set_comment_free(struct ip_set_comment *comment)
+{
+       if (unlikely(!comment->str))
+               return;
+       kfree(comment->str);
+       comment->str = NULL;
+}
+
+#endif
+#endif
index 3aac041..83c2f9e 100644 (file)
@@ -23,8 +23,8 @@
 /* Set is defined with timeout support: timeout value may be 0 */
 #define IPSET_NO_TIMEOUT       UINT_MAX
 
-#define ip_set_adt_opt_timeout(opt, map)       \
-((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (map)->timeout)
+#define ip_set_adt_opt_timeout(opt, set)       \
+((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (set)->timeout)
 
 static inline unsigned int
 ip_set_timeout_uget(struct nlattr *tb)
index 127d0b9..2755057 100644 (file)
@@ -23,6 +23,6 @@ struct ip_conntrack_stat {
 };
 
 /* call to create an explicit dependency on nf_conntrack. */
-extern void need_conntrack(void);
+void need_conntrack(void);
 
 #endif /* _NF_CONNTRACK_COMMON_H */
index f381020..858d9b2 100644 (file)
@@ -29,13 +29,13 @@ struct nf_ct_h323_master {
 
 struct nf_conn;
 
-extern int get_h225_addr(struct nf_conn *ct, unsigned char *data,
-                        TransportAddress *taddr,
-                        union nf_inet_addr *addr, __be16 *port);
-extern void nf_conntrack_h245_expect(struct nf_conn *new,
-                                    struct nf_conntrack_expect *this);
-extern void nf_conntrack_q931_expect(struct nf_conn *new,
-                                    struct nf_conntrack_expect *this);
+int get_h225_addr(struct nf_conn *ct, unsigned char *data,
+                 TransportAddress *taddr, union nf_inet_addr *addr,
+                 __be16 *port);
+void nf_conntrack_h245_expect(struct nf_conn *new,
+                             struct nf_conntrack_expect *this);
+void nf_conntrack_q931_expect(struct nf_conn *new,
+                             struct nf_conntrack_expect *this);
 extern int (*set_h245_addr_hook) (struct sk_buff *skb, unsigned int protoff,
                                  unsigned char **data, int dataoff,
                                  H245_TransportAddress *taddr,
index 6a0664c..ec2ffaf 100644 (file)
@@ -87,8 +87,8 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
 /* delete keymap entries */
 void nf_ct_gre_keymap_destroy(struct nf_conn *ct);
 
-extern void nf_ct_gre_keymap_flush(struct net *net);
-extern void nf_nat_need_gre(void);
+void nf_ct_gre_keymap_flush(struct net *net);
+void nf_nat_need_gre(void);
 
 #endif /* __KERNEL__ */
 #endif /* _CONNTRACK_PROTO_GRE_H */
index ba7f571..d5af3c2 100644 (file)
@@ -107,85 +107,93 @@ enum sdp_header_types {
        SDP_HDR_MEDIA,
 };
 
-extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
-                                      unsigned int protoff,
-                                      unsigned int dataoff,
-                                      const char **dptr,
-                                      unsigned int *datalen);
-extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb,
-                                         unsigned int protoff, s16 off);
-extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
-                                             unsigned int protoff,
-                                             unsigned int dataoff,
-                                             const char **dptr,
-                                             unsigned int *datalen,
-                                             struct nf_conntrack_expect *exp,
-                                             unsigned int matchoff,
-                                             unsigned int matchlen);
-extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb,
-                                           unsigned int protoff,
-                                           unsigned int dataoff,
-                                           const char **dptr,
-                                           unsigned int *datalen,
-                                           unsigned int sdpoff,
-                                           enum sdp_header_types type,
-                                           enum sdp_header_types term,
-                                           const union nf_inet_addr *addr);
-extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb,
-                                           unsigned int protoff,
-                                           unsigned int dataoff,
-                                           const char **dptr,
-                                           unsigned int *datalen,
-                                           unsigned int matchoff,
-                                           unsigned int matchlen,
-                                           u_int16_t port);
-extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
-                                              unsigned int protoff,
-                                              unsigned int dataoff,
-                                              const char **dptr,
-                                              unsigned int *datalen,
-                                              unsigned int sdpoff,
-                                              const union nf_inet_addr *addr);
-extern unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb,
-                                            unsigned int protoff,
-                                            unsigned int dataoff,
-                                            const char **dptr,
-                                            unsigned int *datalen,
-                                            struct nf_conntrack_expect *rtp_exp,
-                                            struct nf_conntrack_expect *rtcp_exp,
-                                            unsigned int mediaoff,
-                                            unsigned int medialen,
-                                            union nf_inet_addr *rtp_addr);
-
-extern int ct_sip_parse_request(const struct nf_conn *ct,
-                               const char *dptr, unsigned int datalen,
-                               unsigned int *matchoff, unsigned int *matchlen,
-                               union nf_inet_addr *addr, __be16 *port);
-extern int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
-                            unsigned int dataoff, unsigned int datalen,
-                            enum sip_header_types type,
-                            unsigned int *matchoff, unsigned int *matchlen);
-extern int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
-                                  unsigned int *dataoff, unsigned int datalen,
-                                  enum sip_header_types type, int *in_header,
-                                  unsigned int *matchoff, unsigned int *matchlen,
-                                  union nf_inet_addr *addr, __be16 *port);
-extern int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
-                                     unsigned int dataoff, unsigned int datalen,
-                                     const char *name,
-                                     unsigned int *matchoff, unsigned int *matchlen,
-                                     union nf_inet_addr *addr, bool delim);
-extern int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
-                                       unsigned int off, unsigned int datalen,
-                                       const char *name,
-                                       unsigned int *matchoff, unsigned int *matchen,
-                                       unsigned int *val);
-
-extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
-                                unsigned int dataoff, unsigned int datalen,
+struct nf_nat_sip_hooks {
+       unsigned int (*msg)(struct sk_buff *skb,
+                           unsigned int protoff,
+                           unsigned int dataoff,
+                           const char **dptr,
+                           unsigned int *datalen);
+
+       void (*seq_adjust)(struct sk_buff *skb,
+                          unsigned int protoff, s16 off);
+
+       unsigned int (*expect)(struct sk_buff *skb,
+                              unsigned int protoff,
+                              unsigned int dataoff,
+                              const char **dptr,
+                              unsigned int *datalen,
+                              struct nf_conntrack_expect *exp,
+                              unsigned int matchoff,
+                              unsigned int matchlen);
+
+       unsigned int (*sdp_addr)(struct sk_buff *skb,
+                                unsigned int protoff,
+                                unsigned int dataoff,
+                                const char **dptr,
+                                unsigned int *datalen,
+                                unsigned int sdpoff,
                                 enum sdp_header_types type,
                                 enum sdp_header_types term,
-                                unsigned int *matchoff, unsigned int *matchlen);
+                                const union nf_inet_addr *addr);
+
+       unsigned int (*sdp_port)(struct sk_buff *skb,
+                                unsigned int protoff,
+                                unsigned int dataoff,
+                                const char **dptr,
+                                unsigned int *datalen,
+                                unsigned int matchoff,
+                                unsigned int matchlen,
+                                u_int16_t port);
+
+       unsigned int (*sdp_session)(struct sk_buff *skb,
+                                   unsigned int protoff,
+                                   unsigned int dataoff,
+                                   const char **dptr,
+                                   unsigned int *datalen,
+                                   unsigned int sdpoff,
+                                   const union nf_inet_addr *addr);
+
+       unsigned int (*sdp_media)(struct sk_buff *skb,
+                                 unsigned int protoff,
+                                 unsigned int dataoff,
+                                 const char **dptr,
+                                 unsigned int *datalen,
+                                 struct nf_conntrack_expect *rtp_exp,
+                                 struct nf_conntrack_expect *rtcp_exp,
+                                 unsigned int mediaoff,
+                                 unsigned int medialen,
+                                 union nf_inet_addr *rtp_addr);
+};
+extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
+
+int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr,
+                        unsigned int datalen, unsigned int *matchoff,
+                        unsigned int *matchlen, union nf_inet_addr *addr,
+                        __be16 *port);
+int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
+                     unsigned int dataoff, unsigned int datalen,
+                     enum sip_header_types type, unsigned int *matchoff,
+                     unsigned int *matchlen);
+int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
+                           unsigned int *dataoff, unsigned int datalen,
+                           enum sip_header_types type, int *in_header,
+                           unsigned int *matchoff, unsigned int *matchlen,
+                           union nf_inet_addr *addr, __be16 *port);
+int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
+                              unsigned int dataoff, unsigned int datalen,
+                              const char *name, unsigned int *matchoff,
+                              unsigned int *matchlen, union nf_inet_addr *addr,
+                              bool delim);
+int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
+                                unsigned int off, unsigned int datalen,
+                                const char *name, unsigned int *matchoff,
+                                unsigned int *matchen, unsigned int *val);
+
+int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
+                         unsigned int dataoff, unsigned int datalen,
+                         enum sdp_header_types type,
+                         enum sdp_header_types term,
+                         unsigned int *matchoff, unsigned int *matchlen);
 
 #endif /* __KERNEL__ */
 #endif /* __NF_CONNTRACK_SIP_H__ */
index cadb740..28c7436 100644 (file)
@@ -14,6 +14,9 @@ struct nfnl_callback {
        int (*call_rcu)(struct sock *nl, struct sk_buff *skb, 
                    const struct nlmsghdr *nlh,
                    const struct nlattr * const cda[]);
+       int (*call_batch)(struct sock *nl, struct sk_buff *skb,
+                         const struct nlmsghdr *nlh,
+                         const struct nlattr * const cda[]);
        const struct nla_policy *policy;        /* netlink attribute policy */
        const u_int16_t attr_count;             /* number of nlattr's */
 };
@@ -23,22 +26,24 @@ struct nfnetlink_subsystem {
        __u8 subsys_id;                 /* nfnetlink subsystem ID */
        __u8 cb_count;                  /* number of callbacks */
        const struct nfnl_callback *cb; /* callback for individual types */
+       int (*commit)(struct sk_buff *skb);
+       int (*abort)(struct sk_buff *skb);
 };
 
-extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
-extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
+int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
+int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
 
-extern int nfnetlink_has_listeners(struct net *net, unsigned int group);
-extern struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size,
-                                          u32 dst_portid, gfp_t gfp_mask);
-extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
-                         unsigned int group, int echo, gfp_t flags);
-extern int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error);
-extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net,
-                            u32 portid, int flags);
+int nfnetlink_has_listeners(struct net *net, unsigned int group);
+struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size,
+                                   u32 dst_portid, gfp_t gfp_mask);
+int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
+                  unsigned int group, int echo, gfp_t flags);
+int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error);
+int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
+                     int flags);
 
-extern void nfnl_lock(__u8 subsys_id);
-extern void nfnl_unlock(__u8 subsys_id);
+void nfnl_lock(__u8 subsys_id);
+void nfnl_unlock(__u8 subsys_id);
 
 #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
        MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
index bb4bbc9..b2e85e5 100644 (file)
@@ -6,8 +6,8 @@
 
 struct nf_acct;
 
-extern struct nf_acct *nfnl_acct_find_get(const char *filter_name);
-extern void nfnl_acct_put(struct nf_acct *acct);
-extern void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct);
+struct nf_acct *nfnl_acct_find_get(const char *filter_name);
+void nfnl_acct_put(struct nf_acct *acct);
+void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct);
 
 #endif /* _NFNL_ACCT_H */
index dd49566..a3e215b 100644 (file)
@@ -229,50 +229,48 @@ struct xt_table_info {
 
 #define XT_TABLE_INFO_SZ (offsetof(struct xt_table_info, entries) \
                          + nr_cpu_ids * sizeof(char *))
-extern int xt_register_target(struct xt_target *target);
-extern void xt_unregister_target(struct xt_target *target);
-extern int xt_register_targets(struct xt_target *target, unsigned int n);
-extern void xt_unregister_targets(struct xt_target *target, unsigned int n);
-
-extern int xt_register_match(struct xt_match *target);
-extern void xt_unregister_match(struct xt_match *target);
-extern int xt_register_matches(struct xt_match *match, unsigned int n);
-extern void xt_unregister_matches(struct xt_match *match, unsigned int n);
-
-extern int xt_check_match(struct xt_mtchk_param *,
-                         unsigned int size, u_int8_t proto, bool inv_proto);
-extern int xt_check_target(struct xt_tgchk_param *,
-                          unsigned int size, u_int8_t proto, bool inv_proto);
-
-extern struct xt_table *xt_register_table(struct net *net,
-                                         const struct xt_table *table,
-                                         struct xt_table_info *bootstrap,
-                                         struct xt_table_info *newinfo);
-extern void *xt_unregister_table(struct xt_table *table);
-
-extern struct xt_table_info *xt_replace_table(struct xt_table *table,
-                                             unsigned int num_counters,
-                                             struct xt_table_info *newinfo,
-                                             int *error);
-
-extern struct xt_match *xt_find_match(u8 af, const char *name, u8 revision);
-extern struct xt_target *xt_find_target(u8 af, const char *name, u8 revision);
-extern struct xt_match *xt_request_find_match(u8 af, const char *name,
-                                             u8 revision);
-extern struct xt_target *xt_request_find_target(u8 af, const char *name,
-                                               u8 revision);
-extern int xt_find_revision(u8 af, const char *name, u8 revision,
-                           int target, int *err);
-
-extern struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
-                                          const char *name);
-extern void xt_table_unlock(struct xt_table *t);
-
-extern int xt_proto_init(struct net *net, u_int8_t af);
-extern void xt_proto_fini(struct net *net, u_int8_t af);
-
-extern struct xt_table_info *xt_alloc_table_info(unsigned int size);
-extern void xt_free_table_info(struct xt_table_info *info);
+int xt_register_target(struct xt_target *target);
+void xt_unregister_target(struct xt_target *target);
+int xt_register_targets(struct xt_target *target, unsigned int n);
+void xt_unregister_targets(struct xt_target *target, unsigned int n);
+
+int xt_register_match(struct xt_match *target);
+void xt_unregister_match(struct xt_match *target);
+int xt_register_matches(struct xt_match *match, unsigned int n);
+void xt_unregister_matches(struct xt_match *match, unsigned int n);
+
+int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
+                  bool inv_proto);
+int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
+                   bool inv_proto);
+
+struct xt_table *xt_register_table(struct net *net,
+                                  const struct xt_table *table,
+                                  struct xt_table_info *bootstrap,
+                                  struct xt_table_info *newinfo);
+void *xt_unregister_table(struct xt_table *table);
+
+struct xt_table_info *xt_replace_table(struct xt_table *table,
+                                      unsigned int num_counters,
+                                      struct xt_table_info *newinfo,
+                                      int *error);
+
+struct xt_match *xt_find_match(u8 af, const char *name, u8 revision);
+struct xt_target *xt_find_target(u8 af, const char *name, u8 revision);
+struct xt_match *xt_request_find_match(u8 af, const char *name, u8 revision);
+struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision);
+int xt_find_revision(u8 af, const char *name, u8 revision, int target,
+                    int *err);
+
+struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
+                                   const char *name);
+void xt_table_unlock(struct xt_table *t);
+
+int xt_proto_init(struct net *net, u_int8_t af);
+void xt_proto_fini(struct net *net, u_int8_t af);
+
+struct xt_table_info *xt_alloc_table_info(unsigned int size);
+void xt_free_table_info(struct xt_table_info *info);
 
 /**
  * xt_recseq - recursive seqcount for netfilter use
@@ -353,8 +351,8 @@ static inline unsigned long ifname_compare_aligned(const char *_a,
        return ret;
 }
 
-extern struct nf_hook_ops *xt_hook_link(const struct xt_table *, nf_hookfn *);
-extern void xt_hook_unlink(const struct xt_table *, struct nf_hook_ops *);
+struct nf_hook_ops *xt_hook_link(const struct xt_table *, nf_hookfn *);
+void xt_hook_unlink(const struct xt_table *, struct nf_hook_ops *);
 
 #ifdef CONFIG_COMPAT
 #include <net/compat.h>
@@ -414,25 +412,25 @@ struct _compat_xt_align {
 
 #define COMPAT_XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _compat_xt_align))
 
-extern void xt_compat_lock(u_int8_t af);
-extern void xt_compat_unlock(u_int8_t af);
-
-extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta);
-extern void xt_compat_flush_offsets(u_int8_t af);
-extern void xt_compat_init_offsets(u_int8_t af, unsigned int number);
-extern int xt_compat_calc_jump(u_int8_t af, unsigned int offset);
-
-extern int xt_compat_match_offset(const struct xt_match *match);
-extern int xt_compat_match_from_user(struct xt_entry_match *m,
-                                    void **dstptr, unsigned int *size);
-extern int xt_compat_match_to_user(const struct xt_entry_match *m,
-                                  void __user **dstptr, unsigned int *size);
-
-extern int xt_compat_target_offset(const struct xt_target *target);
-extern void xt_compat_target_from_user(struct xt_entry_target *t,
-                                      void **dstptr, unsigned int *size);
-extern int xt_compat_target_to_user(const struct xt_entry_target *t,
-                                   void __user **dstptr, unsigned int *size);
+void xt_compat_lock(u_int8_t af);
+void xt_compat_unlock(u_int8_t af);
+
+int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta);
+void xt_compat_flush_offsets(u_int8_t af);
+void xt_compat_init_offsets(u_int8_t af, unsigned int number);
+int xt_compat_calc_jump(u_int8_t af, unsigned int offset);
+
+int xt_compat_match_offset(const struct xt_match *match);
+int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+                             unsigned int *size);
+int xt_compat_match_to_user(const struct xt_entry_match *m,
+                           void __user **dstptr, unsigned int *size);
+
+int xt_compat_target_offset(const struct xt_target *target);
+void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
+                               unsigned int *size);
+int xt_compat_target_to_user(const struct xt_entry_target *t,
+                            void __user **dstptr, unsigned int *size);
 
 #endif /* CONFIG_COMPAT */
 #endif /* _X_TABLES_H */
index dfb4d9e..8ab1c27 100644 (file)
@@ -25,7 +25,7 @@ enum nf_br_hook_priorities {
 #define BRNF_PPPoE                     0x20
 
 /* Only used in br_forward.c */
-extern int nf_bridge_copy_header(struct sk_buff *skb);
+int nf_bridge_copy_header(struct sk_buff *skb);
 static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb)
 {
        if (skb->nf_bridge &&
@@ -53,7 +53,7 @@ static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
        return 0;
 }
 
-extern int br_handle_frame_finish(struct sk_buff *skb);
+int br_handle_frame_finish(struct sk_buff *skb);
 /* Only used in br_device.c */
 static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
 {
index dfaf116..6e4591b 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <uapi/linux/netfilter_ipv4.h>
 
-extern int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type);
-extern __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
-                                  unsigned int dataoff, u_int8_t protocol);
+int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type);
+__sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
+                      unsigned int dataoff, u_int8_t protocol);
 #endif /*__LINUX_IP_NETFILTER_H*/
index 2d4df6c..64dad1c 100644 (file)
 
 
 #ifdef CONFIG_NETFILTER
-extern int ip6_route_me_harder(struct sk_buff *skb);
-extern __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
-                                   unsigned int dataoff, u_int8_t protocol);
+int ip6_route_me_harder(struct sk_buff *skb);
+__sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
+                       unsigned int dataoff, u_int8_t protocol);
 
-extern int ipv6_netfilter_init(void);
-extern void ipv6_netfilter_fini(void);
+int ipv6_netfilter_init(void);
+void ipv6_netfilter_fini(void);
 
 /*
  * Hook functions for ipv6 to allow xt_* modules to be built-in even
index f3c7c24..fbfdb9d 100644 (file)
@@ -24,7 +24,8 @@ struct netpoll {
        struct net_device *dev;
        char dev_name[IFNAMSIZ];
        const char *name;
-       void (*rx_hook)(struct netpoll *, int, char *, int);
+       void (*rx_skb_hook)(struct netpoll *np, int source, struct sk_buff *skb,
+                           int offset, int len);
 
        union inet_addr local_ip, remote_ip;
        bool ipv6;
@@ -41,7 +42,7 @@ struct netpoll_info {
        unsigned long rx_flags;
        spinlock_t rx_lock;
        struct semaphore dev_lock;
-       struct list_head rx_np; /* netpolls that registered an rx_hook */
+       struct list_head rx_np; /* netpolls that registered an rx_skb_hook */
 
        struct sk_buff_head neigh_tx; /* list of neigh requests to reply to */
        struct sk_buff_head txq;
index 01fd84b..49f52c8 100644 (file)
@@ -1455,7 +1455,8 @@ struct nfs_rpc_ops {
        struct inode * (*open_context) (struct inode *dir,
                                struct nfs_open_context *ctx,
                                int open_flags,
-                               struct iattr *iattr);
+                               struct iattr *iattr,
+                               int *);
        int (*have_delegation)(struct inode *, fmode_t);
        int (*return_delegation)(struct inode *);
        struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *);
index 535cecf..fcd63ba 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef __OF_IRQ_H
 #define __OF_IRQ_H
 
-#if defined(CONFIG_OF)
-struct of_irq;
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/irq.h>
@@ -10,14 +8,6 @@ struct of_irq;
 #include <linux/ioport.h>
 #include <linux/of.h>
 
-/*
- * irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC
- * implements it differently.  However, the prototype is the same for all,
- * so declare it here regardless of the CONFIG_OF_IRQ setting.
- */
-extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
-
-#if defined(CONFIG_OF_IRQ)
 /**
  * of_irq - container for device_node/irq_specifier pair for an irq controller
  * @controller: pointer to interrupt controller device tree node
@@ -71,11 +61,17 @@ extern int of_irq_to_resource(struct device_node *dev, int index,
 extern int of_irq_count(struct device_node *dev);
 extern int of_irq_to_resource_table(struct device_node *dev,
                struct resource *res, int nr_irqs);
-extern struct device_node *of_irq_find_parent(struct device_node *child);
 
 extern void of_irq_init(const struct of_device_id *matches);
 
-#endif /* CONFIG_OF_IRQ */
+#if defined(CONFIG_OF)
+/*
+ * irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC
+ * implements it differently.  However, the prototype is the same for all,
+ * so declare it here regardless of the CONFIG_OF_IRQ setting.
+ */
+extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
+extern struct device_node *of_irq_find_parent(struct device_node *child);
 
 #else /* !CONFIG_OF */
 static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
deleted file mode 100644 (file)
index c841282..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef __OF_RESERVED_MEM_H
-#define __OF_RESERVED_MEM_H
-
-#ifdef CONFIG_OF_RESERVED_MEM
-void of_reserved_mem_device_init(struct device *dev);
-void of_reserved_mem_device_release(struct device *dev);
-void early_init_dt_scan_reserved_mem(void);
-#else
-static inline void of_reserved_mem_device_init(struct device *dev) { }
-static inline void of_reserved_mem_device_release(struct device *dev) { }
-static inline void early_init_dt_scan_reserved_mem(void) { }
-#endif
-
-#endif /* __OF_RESERVED_MEM_H */
index cc88172..c74088a 100644 (file)
@@ -332,7 +332,7 @@ do {                                                                        \
 #endif
 
 #ifndef this_cpu_sub
-# define this_cpu_sub(pcp, val)                this_cpu_add((pcp), -(val))
+# define this_cpu_sub(pcp, val)                this_cpu_add((pcp), -(typeof(pcp))(val))
 #endif
 
 #ifndef this_cpu_inc
@@ -418,7 +418,7 @@ do {                                                                        \
 # define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val)
 #endif
 
-#define this_cpu_sub_return(pcp, val)  this_cpu_add_return(pcp, -(val))
+#define this_cpu_sub_return(pcp, val)  this_cpu_add_return(pcp, -(typeof(pcp))(val))
 #define this_cpu_inc_return(pcp)       this_cpu_add_return(pcp, 1)
 #define this_cpu_dec_return(pcp)       this_cpu_add_return(pcp, -1)
 
@@ -586,7 +586,7 @@ do {                                                                        \
 #endif
 
 #ifndef __this_cpu_sub
-# define __this_cpu_sub(pcp, val)      __this_cpu_add((pcp), -(val))
+# define __this_cpu_sub(pcp, val)      __this_cpu_add((pcp), -(typeof(pcp))(val))
 #endif
 
 #ifndef __this_cpu_inc
@@ -668,7 +668,7 @@ do {                                                                        \
        __pcpu_size_call_return2(__this_cpu_add_return_, pcp, val)
 #endif
 
-#define __this_cpu_sub_return(pcp, val)        __this_cpu_add_return(pcp, -(val))
+#define __this_cpu_sub_return(pcp, val)        __this_cpu_add_return(pcp, -(typeof(pcp))(val))
 #define __this_cpu_inc_return(pcp)     __this_cpu_add_return(pcp, 1)
 #define __this_cpu_dec_return(pcp)     __this_cpu_add_return(pcp, -1)
 
index 866e85c..c8ba627 100644 (file)
@@ -294,9 +294,31 @@ struct ring_buffer;
  */
 struct perf_event {
 #ifdef CONFIG_PERF_EVENTS
-       struct list_head                group_entry;
+       /*
+        * entry onto perf_event_context::event_list;
+        *   modifications require ctx->lock
+        *   RCU safe iterations.
+        */
        struct list_head                event_entry;
+
+       /*
+        * XXX: group_entry and sibling_list should be mutually exclusive;
+        * either you're a sibling on a group, or you're the group leader.
+        * Rework the code to always use the same list element.
+        *
+        * Locked for modification by both ctx->mutex and ctx->lock; holding
+        * either sufficies for read.
+        */
+       struct list_head                group_entry;
        struct list_head                sibling_list;
+
+       /*
+        * We need storage to track the entries in perf_pmu_migrate_context; we
+        * cannot use the event_entry because of RCU and we want to keep the
+        * group in tact which avoids us using the other two entries.
+        */
+       struct list_head                migrate_entry;
+
        struct hlist_node               hlist_entry;
        int                             nr_siblings;
        int                             group_flags;
index 3b9377d..6312dd9 100644 (file)
@@ -17,6 +17,7 @@ extern void add_interrupt_randomness(int irq, int irq_flags);
 extern void get_random_bytes(void *buf, int nbytes);
 extern void get_random_bytes_arch(void *buf, int nbytes);
 void generate_random_uuid(unsigned char uuid_out[16]);
+extern int random_int_secret_init(void);
 
 #ifndef MODULE
 extern const struct file_operations random_fops, urandom_fops;
index 67e13aa..9bdad43 100644 (file)
@@ -40,6 +40,8 @@ enum regulator_status {
 };
 
 /**
+ * struct regulator_linear_range - specify linear voltage ranges
+ *
  * Specify a range of voltages for regulator_map_linar_range() and
  * regulator_list_linear_range().
  *
index f28544b..939428a 100644 (file)
@@ -15,7 +15,7 @@ extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics);
 extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst,
                              u32 id, long expires, u32 error);
 
-extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change);
+void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags);
 
 /* RTNL is used as a global lock for all changes to network configuration  */
 extern void rtnl_lock(void);
index 6682da3..e27baee 100644 (file)
@@ -1394,11 +1394,10 @@ struct task_struct {
        } memcg_batch;
        unsigned int memcg_kmem_skip_account;
        struct memcg_oom_info {
+               struct mem_cgroup *memcg;
+               gfp_t gfp_mask;
+               int order;
                unsigned int may_oom:1;
-               unsigned int in_memcg_oom:1;
-               unsigned int oom_locked:1;
-               int wakeups;
-               struct mem_cgroup *wait_on_memcg;
        } memcg_oom;
 #endif
 #ifdef CONFIG_UPROBES
index 2ddb48d..215b5ea 100644 (file)
@@ -318,9 +318,13 @@ enum {
 
        SKB_GSO_GRE = 1 << 6,
 
-       SKB_GSO_UDP_TUNNEL = 1 << 7,
+       SKB_GSO_IPIP = 1 << 7,
 
-       SKB_GSO_MPLS = 1 << 8,
+       SKB_GSO_SIT = 1 << 8,
+
+       SKB_GSO_UDP_TUNNEL = 1 << 9,
+
+       SKB_GSO_MPLS = 1 << 10,
 };
 
 #if BITS_PER_LONG > 32
@@ -333,11 +337,6 @@ typedef unsigned int sk_buff_data_t;
 typedef unsigned char *sk_buff_data_t;
 #endif
 
-#if defined(CONFIG_NF_DEFRAG_IPV4) || defined(CONFIG_NF_DEFRAG_IPV4_MODULE) || \
-    defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE)
-#define NET_SKBUFF_NF_DEFRAG_NEEDED 1
-#endif
-
 /** 
  *     struct sk_buff - socket buffer
  *     @next: Next buffer in list
@@ -370,7 +369,6 @@ typedef unsigned char *sk_buff_data_t;
  *     @protocol: Packet protocol from driver
  *     @destructor: Destruct function
  *     @nfct: Associated connection, if any
- *     @nfct_reasm: netfilter conntrack re-assembly pointer
  *     @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
  *     @skb_iif: ifindex of device we arrived on
  *     @tc_index: Traffic control index
@@ -459,9 +457,6 @@ struct sk_buff {
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        struct nf_conntrack     *nfct;
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-       struct sk_buff          *nfct_reasm;
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
        struct nf_bridge_info   *nf_bridge;
 #endif
@@ -498,7 +493,7 @@ struct sk_buff {
         * headers if needed
         */
        __u8                    encapsulation:1;
-       /* 7/9 bit hole (depending on ndisc_nodetype presence) */
+       /* 6/8 bit hole (depending on ndisc_nodetype presence) */
        kmemcheck_bitfield_end(flags2);
 
 #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
@@ -585,8 +580,8 @@ static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst)
        skb->_skb_refdst = (unsigned long)dst;
 }
 
-extern void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst,
-                               bool force);
+void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst,
+                        bool force);
 
 /**
  * skb_dst_set_noref - sets skb dst, hopefully, without taking reference
@@ -634,20 +629,20 @@ static inline struct rtable *skb_rtable(const struct sk_buff *skb)
        return (struct rtable *)skb_dst(skb);
 }
 
-extern void kfree_skb(struct sk_buff *skb);
-extern void kfree_skb_list(struct sk_buff *segs);
-extern void skb_tx_error(struct sk_buff *skb);
-extern void consume_skb(struct sk_buff *skb);
-extern void           __kfree_skb(struct sk_buff *skb);
+void kfree_skb(struct sk_buff *skb);
+void kfree_skb_list(struct sk_buff *segs);
+void skb_tx_error(struct sk_buff *skb);
+void consume_skb(struct sk_buff *skb);
+void  __kfree_skb(struct sk_buff *skb);
 extern struct kmem_cache *skbuff_head_cache;
 
-extern void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
-extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
-                            bool *fragstolen, int *delta_truesize);
+void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
+bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
+                     bool *fragstolen, int *delta_truesize);
 
-extern struct sk_buff *__alloc_skb(unsigned int size,
-                                  gfp_t priority, int flags, int node);
-extern struct sk_buff *build_skb(void *data, unsigned int frag_size);
+struct sk_buff *__alloc_skb(unsigned int size, gfp_t priority, int flags,
+                           int node);
+struct sk_buff *build_skb(void *data, unsigned int frag_size);
 static inline struct sk_buff *alloc_skb(unsigned int size,
                                        gfp_t priority)
 {
@@ -660,41 +655,33 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
        return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE);
 }
 
-extern struct sk_buff *__alloc_skb_head(gfp_t priority, int node);
+struct sk_buff *__alloc_skb_head(gfp_t priority, int node);
 static inline struct sk_buff *alloc_skb_head(gfp_t priority)
 {
        return __alloc_skb_head(priority, -1);
 }
 
-extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src);
-extern int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask);
-extern struct sk_buff *skb_clone(struct sk_buff *skb,
-                                gfp_t priority);
-extern struct sk_buff *skb_copy(const struct sk_buff *skb,
-                               gfp_t priority);
-extern struct sk_buff *__pskb_copy(struct sk_buff *skb,
-                                int headroom, gfp_t gfp_mask);
-
-extern int            pskb_expand_head(struct sk_buff *skb,
-                                       int nhead, int ntail,
-                                       gfp_t gfp_mask);
-extern struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
-                                           unsigned int headroom);
-extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
-                                      int newheadroom, int newtailroom,
-                                      gfp_t priority);
-extern int            skb_to_sgvec(struct sk_buff *skb,
-                                   struct scatterlist *sg, int offset,
-                                   int len);
-extern int            skb_cow_data(struct sk_buff *skb, int tailbits,
-                                   struct sk_buff **trailer);
-extern int            skb_pad(struct sk_buff *skb, int pad);
+struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src);
+int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask);
+struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority);
+struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority);
+struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask);
+
+int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask);
+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
+                                    unsigned int headroom);
+struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom,
+                               int newtailroom, gfp_t priority);
+int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset,
+                int len);
+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer);
+int skb_pad(struct sk_buff *skb, int pad);
 #define dev_kfree_skb(a)       consume_skb(a)
 
-extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
-                       int getfrag(void *from, char *to, int offset,
-                       int len,int odd, struct sk_buff *skb),
-                       void *from, int length);
+int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
+                           int getfrag(void *from, char *to, int offset,
+                                       int len, int odd, struct sk_buff *skb),
+                           void *from, int length);
 
 struct skb_seq_state {
        __u32           lower_offset;
@@ -706,18 +693,17 @@ struct skb_seq_state {
        __u8            *frag_data;
 };
 
-extern void          skb_prepare_seq_read(struct sk_buff *skb,
-                                          unsigned int from, unsigned int to,
-                                          struct skb_seq_state *st);
-extern unsigned int   skb_seq_read(unsigned int consumed, const u8 **data,
-                                  struct skb_seq_state *st);
-extern void          skb_abort_seq_read(struct skb_seq_state *st);
+void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from,
+                         unsigned int to, struct skb_seq_state *st);
+unsigned int skb_seq_read(unsigned int consumed, const u8 **data,
+                         struct skb_seq_state *st);
+void skb_abort_seq_read(struct skb_seq_state *st);
 
-extern unsigned int   skb_find_text(struct sk_buff *skb, unsigned int from,
-                                   unsigned int to, struct ts_config *config,
-                                   struct ts_state *state);
+unsigned int skb_find_text(struct sk_buff *skb, unsigned int from,
+                          unsigned int to, struct ts_config *config,
+                          struct ts_state *state);
 
-extern void __skb_get_rxhash(struct sk_buff *skb);
+void __skb_get_rxhash(struct sk_buff *skb);
 static inline __u32 skb_get_rxhash(struct sk_buff *skb)
 {
        if (!skb->l4_rxhash)
@@ -1095,7 +1081,8 @@ static inline void skb_queue_head_init_class(struct sk_buff_head *list,
  *     The "__skb_xxxx()" functions are the non-atomic ones that
  *     can only be called with interrupts disabled.
  */
-extern void        skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list);
+void skb_insert(struct sk_buff *old, struct sk_buff *newsk,
+               struct sk_buff_head *list);
 static inline void __skb_insert(struct sk_buff *newsk,
                                struct sk_buff *prev, struct sk_buff *next,
                                struct sk_buff_head *list)
@@ -1201,8 +1188,8 @@ static inline void __skb_queue_after(struct sk_buff_head *list,
        __skb_insert(newsk, prev, prev->next, list);
 }
 
-extern void skb_append(struct sk_buff *old, struct sk_buff *newsk,
-                      struct sk_buff_head *list);
+void skb_append(struct sk_buff *old, struct sk_buff *newsk,
+               struct sk_buff_head *list);
 
 static inline void __skb_queue_before(struct sk_buff_head *list,
                                      struct sk_buff *next,
@@ -1221,7 +1208,7 @@ static inline void __skb_queue_before(struct sk_buff_head *list,
  *
  *     A buffer cannot be placed on two lists at the same time.
  */
-extern void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
+void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
 static inline void __skb_queue_head(struct sk_buff_head *list,
                                    struct sk_buff *newsk)
 {
@@ -1238,7 +1225,7 @@ static inline void __skb_queue_head(struct sk_buff_head *list,
  *
  *     A buffer cannot be placed on two lists at the same time.
  */
-extern void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk);
+void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk);
 static inline void __skb_queue_tail(struct sk_buff_head *list,
                                   struct sk_buff *newsk)
 {
@@ -1249,7 +1236,7 @@ static inline void __skb_queue_tail(struct sk_buff_head *list,
  * remove sk_buff from list. _Must_ be called atomically, and with
  * the list known..
  */
-extern void       skb_unlink(struct sk_buff *skb, struct sk_buff_head *list);
+void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list);
 static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
 {
        struct sk_buff *next, *prev;
@@ -1270,7 +1257,7 @@ static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
  *     so must be used with appropriate locks held only. The head item is
  *     returned or %NULL if the list is empty.
  */
-extern struct sk_buff *skb_dequeue(struct sk_buff_head *list);
+struct sk_buff *skb_dequeue(struct sk_buff_head *list);
 static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
 {
        struct sk_buff *skb = skb_peek(list);
@@ -1287,7 +1274,7 @@ static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
  *     so must be used with appropriate locks held only. The tail item is
  *     returned or %NULL if the list is empty.
  */
-extern struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list);
+struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list);
 static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
 {
        struct sk_buff *skb = skb_peek_tail(list);
@@ -1361,7 +1348,7 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
  * @size: the length of the data
  *
  * As per __skb_fill_page_desc() -- initialises the @i'th fragment of
- * @skb to point to &size bytes at offset @off within @page. In
+ * @skb to point to @size bytes at offset @off within @page. In
  * addition updates @skb such that @i is the last fragment.
  *
  * Does not take any additional reference on the fragment.
@@ -1373,8 +1360,11 @@ static inline void skb_fill_page_desc(struct sk_buff *skb, int i,
        skb_shinfo(skb)->nr_frags = i + 1;
 }
 
-extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page,
-                           int off, int size, unsigned int truesize);
+void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
+                    int size, unsigned int truesize);
+
+void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size,
+                         unsigned int truesize);
 
 #define SKB_PAGE_ASSERT(skb)   BUG_ON(skb_shinfo(skb)->nr_frags)
 #define SKB_FRAG_ASSERT(skb)   BUG_ON(skb_has_frag_list(skb))
@@ -1418,7 +1408,8 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
 /*
  *     Add data to an sk_buff
  */
-extern unsigned char *skb_put(struct sk_buff *skb, unsigned int len);
+unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len);
+unsigned char *skb_put(struct sk_buff *skb, unsigned int len);
 static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
 {
        unsigned char *tmp = skb_tail_pointer(skb);
@@ -1428,7 +1419,7 @@ static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
        return tmp;
 }
 
-extern unsigned char *skb_push(struct sk_buff *skb, unsigned int len);
+unsigned char *skb_push(struct sk_buff *skb, unsigned int len);
 static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
 {
        skb->data -= len;
@@ -1436,7 +1427,7 @@ static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
        return skb->data;
 }
 
-extern unsigned char *skb_pull(struct sk_buff *skb, unsigned int len);
+unsigned char *skb_pull(struct sk_buff *skb, unsigned int len);
 static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
 {
        skb->len -= len;
@@ -1449,7 +1440,7 @@ static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int l
        return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);
 }
 
-extern unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta);
+unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta);
 
 static inline unsigned char *__pskb_pull(struct sk_buff *skb, unsigned int len)
 {
@@ -1753,7 +1744,7 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len)
 #define NET_SKB_PAD    max(32, L1_CACHE_BYTES)
 #endif
 
-extern int ___pskb_trim(struct sk_buff *skb, unsigned int len);
+int ___pskb_trim(struct sk_buff *skb, unsigned int len);
 
 static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
 {
@@ -1765,7 +1756,7 @@ static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
        skb_set_tail_pointer(skb, len);
 }
 
-extern void skb_trim(struct sk_buff *skb, unsigned int len);
+void skb_trim(struct sk_buff *skb, unsigned int len);
 
 static inline int __pskb_trim(struct sk_buff *skb, unsigned int len)
 {
@@ -1838,7 +1829,7 @@ static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask)
  *     the list and one reference dropped. This function does not take the
  *     list lock and the caller must hold the relevant locks to use it.
  */
-extern void skb_queue_purge(struct sk_buff_head *list);
+void skb_queue_purge(struct sk_buff_head *list);
 static inline void __skb_queue_purge(struct sk_buff_head *list)
 {
        struct sk_buff *skb;
@@ -1850,11 +1841,10 @@ static inline void __skb_queue_purge(struct sk_buff_head *list)
 #define NETDEV_FRAG_PAGE_MAX_SIZE  (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER)
 #define NETDEV_PAGECNT_MAX_BIAS           NETDEV_FRAG_PAGE_MAX_SIZE
 
-extern void *netdev_alloc_frag(unsigned int fragsz);
+void *netdev_alloc_frag(unsigned int fragsz);
 
-extern struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
-                                         unsigned int length,
-                                         gfp_t gfp_mask);
+struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int length,
+                                  gfp_t gfp_mask);
 
 /**
  *     netdev_alloc_skb - allocate an skbuff for rx on a specific device
@@ -2071,6 +2061,8 @@ static inline void skb_frag_set_page(struct sk_buff *skb, int f,
        __skb_frag_set_page(&skb_shinfo(skb)->frags[f], page);
 }
 
+bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t prio);
+
 /**
  * skb_frag_dma_map - maps a paged fragment via the DMA API
  * @dev: the device to map the fragment to
@@ -2342,60 +2334,49 @@ static inline void skb_frag_add_head(struct sk_buff *skb, struct sk_buff *frag)
 #define skb_walk_frags(skb, iter)      \
        for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next)
 
-extern struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
-                                          int *peeked, int *off, int *err);
-extern struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
-                                        int noblock, int *err);
-extern unsigned int    datagram_poll(struct file *file, struct socket *sock,
-                                    struct poll_table_struct *wait);
-extern int            skb_copy_datagram_iovec(const struct sk_buff *from,
-                                              int offset, struct iovec *to,
-                                              int size);
-extern int            skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
-                                                       int hlen,
-                                                       struct iovec *iov);
-extern int            skb_copy_datagram_from_iovec(struct sk_buff *skb,
-                                                   int offset,
-                                                   const struct iovec *from,
-                                                   int from_offset,
-                                                   int len);
-extern int            zerocopy_sg_from_iovec(struct sk_buff *skb,
-                                             const struct iovec *frm,
-                                             int offset,
-                                             size_t count);
-extern int            skb_copy_datagram_const_iovec(const struct sk_buff *from,
-                                                    int offset,
-                                                    const struct iovec *to,
-                                                    int to_offset,
-                                                    int size);
-extern void           skb_free_datagram(struct sock *sk, struct sk_buff *skb);
-extern void           skb_free_datagram_locked(struct sock *sk,
-                                               struct sk_buff *skb);
-extern int            skb_kill_datagram(struct sock *sk, struct sk_buff *skb,
-                                        unsigned int flags);
-extern __wsum         skb_checksum(const struct sk_buff *skb, int offset,
-                                   int len, __wsum csum);
-extern int            skb_copy_bits(const struct sk_buff *skb, int offset,
-                                    void *to, int len);
-extern int            skb_store_bits(struct sk_buff *skb, int offset,
-                                     const void *from, int len);
-extern __wsum         skb_copy_and_csum_bits(const struct sk_buff *skb,
-                                             int offset, u8 *to, int len,
-                                             __wsum csum);
-extern int             skb_splice_bits(struct sk_buff *skb,
-                                               unsigned int offset,
-                                               struct pipe_inode_info *pipe,
-                                               unsigned int len,
-                                               unsigned int flags);
-extern void           skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
-extern void           skb_split(struct sk_buff *skb,
-                                struct sk_buff *skb1, const u32 len);
-extern int            skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
-                                int shiftlen);
-extern void           skb_scrub_packet(struct sk_buff *skb, bool xnet);
-
-extern struct sk_buff *skb_segment(struct sk_buff *skb,
-                                  netdev_features_t features);
+struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
+                                   int *peeked, int *off, int *err);
+struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
+                                 int *err);
+unsigned int datagram_poll(struct file *file, struct socket *sock,
+                          struct poll_table_struct *wait);
+int skb_copy_datagram_iovec(const struct sk_buff *from, int offset,
+                           struct iovec *to, int size);
+int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen,
+                                    struct iovec *iov);
+int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
+                                const struct iovec *from, int from_offset,
+                                int len);
+int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *frm,
+                          int offset, size_t count);
+int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset,
+                                 const struct iovec *to, int to_offset,
+                                 int size);
+void skb_free_datagram(struct sock *sk, struct sk_buff *skb);
+void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb);
+int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags);
+int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);
+int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len);
+__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to,
+                             int len, __wsum csum);
+int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
+                   struct pipe_inode_info *pipe, unsigned int len,
+                   unsigned int flags);
+void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
+void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
+int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
+void skb_scrub_packet(struct sk_buff *skb, bool xnet);
+struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
+
+struct skb_checksum_ops {
+       __wsum (*update)(const void *mem, int len, __wsum wsum);
+       __wsum (*combine)(__wsum csum, __wsum csum2, int offset, int len);
+};
+
+__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len,
+                     __wsum csum, const struct skb_checksum_ops *ops);
+__wsum skb_checksum(const struct sk_buff *skb, int offset, int len,
+                   __wsum csum);
 
 static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
                                       int len, void *buffer)
@@ -2440,7 +2421,7 @@ static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb,
        memcpy(skb->data + offset, from, len);
 }
 
-extern void skb_init(void);
+void skb_init(void);
 
 static inline ktime_t skb_get_ktime(const struct sk_buff *skb)
 {
@@ -2483,12 +2464,12 @@ static inline ktime_t net_invalid_timestamp(void)
        return ktime_set(0, 0);
 }
 
-extern void skb_timestamping_init(void);
+void skb_timestamping_init(void);
 
 #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
 
-extern void skb_clone_tx_timestamp(struct sk_buff *skb);
-extern bool skb_defer_rx_timestamp(struct sk_buff *skb);
+void skb_clone_tx_timestamp(struct sk_buff *skb);
+bool skb_defer_rx_timestamp(struct sk_buff *skb);
 
 #else /* CONFIG_NETWORK_PHY_TIMESTAMPING */
 
@@ -2529,8 +2510,8 @@ void skb_complete_tx_timestamp(struct sk_buff *skb,
  * generates a software time stamp (otherwise), then queues the clone
  * to the error queue of the socket.  Errors are silently ignored.
  */
-extern void skb_tstamp_tx(struct sk_buff *orig_skb,
-                       struct skb_shared_hwtstamps *hwtstamps);
+void skb_tstamp_tx(struct sk_buff *orig_skb,
+                  struct skb_shared_hwtstamps *hwtstamps);
 
 static inline void sw_tx_timestamp(struct sk_buff *skb)
 {
@@ -2562,8 +2543,8 @@ static inline void skb_tx_timestamp(struct sk_buff *skb)
  */
 void skb_complete_wifi_ack(struct sk_buff *skb, bool acked);
 
-extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len);
-extern __sum16 __skb_checksum_complete(struct sk_buff *skb);
+__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len);
+__sum16 __skb_checksum_complete(struct sk_buff *skb);
 
 static inline int skb_csum_unnecessary(const struct sk_buff *skb)
 {
@@ -2593,7 +2574,7 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb)
 }
 
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
-extern void nf_conntrack_destroy(struct nf_conntrack *nfct);
+void nf_conntrack_destroy(struct nf_conntrack *nfct);
 static inline void nf_conntrack_put(struct nf_conntrack *nfct)
 {
        if (nfct && atomic_dec_and_test(&nfct->use))
@@ -2605,18 +2586,6 @@ static inline void nf_conntrack_get(struct nf_conntrack *nfct)
                atomic_inc(&nfct->use);
 }
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-static inline void nf_conntrack_get_reasm(struct sk_buff *skb)
-{
-       if (skb)
-               atomic_inc(&skb->users);
-}
-static inline void nf_conntrack_put_reasm(struct sk_buff *skb)
-{
-       if (skb)
-               kfree_skb(skb);
-}
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
 static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
 {
@@ -2635,10 +2604,6 @@ static inline void nf_reset(struct sk_buff *skb)
        nf_conntrack_put(skb->nfct);
        skb->nfct = NULL;
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-       nf_conntrack_put_reasm(skb->nfct_reasm);
-       skb->nfct_reasm = NULL;
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
        nf_bridge_put(skb->nf_bridge);
        skb->nf_bridge = NULL;
@@ -2660,10 +2625,6 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src)
        nf_conntrack_get(src->nfct);
        dst->nfctinfo = src->nfctinfo;
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-       dst->nfct_reasm = src->nfct_reasm;
-       nf_conntrack_get_reasm(src->nfct_reasm);
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
        dst->nf_bridge  = src->nf_bridge;
        nf_bridge_get(src->nf_bridge);
@@ -2675,9 +2636,6 @@ static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src)
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        nf_conntrack_put(dst->nfct);
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-       nf_conntrack_put_reasm(dst->nfct_reasm);
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
        nf_bridge_put(dst->nf_bridge);
 #endif
@@ -2732,28 +2690,27 @@ static inline bool skb_rx_queue_recorded(const struct sk_buff *skb)
        return skb->queue_mapping != 0;
 }
 
-extern u16 __skb_tx_hash(const struct net_device *dev,
-                        const struct sk_buff *skb,
-                        unsigned int num_tx_queues);
+u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb,
+                 unsigned int num_tx_queues);
 
-#ifdef CONFIG_XFRM
 static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
 {
+#ifdef CONFIG_XFRM
        return skb->sp;
-}
 #else
-static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
-{
        return NULL;
-}
 #endif
+}
 
 /* Keeps track of mac header offset relative to skb->head.
  * It is useful for TSO of Tunneling protocol. e.g. GRE.
  * For non-tunnel skb it points to skb_mac_header() and for
- * tunnel skb it points to outer mac header. */
+ * tunnel skb it points to outer mac header.
+ * Keeps track of level of encapsulation of network headers.
+ */
 struct skb_gso_cb {
-       int mac_offset;
+       int     mac_offset;
+       int     encap_level;
 };
 #define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
 
@@ -2783,12 +2740,13 @@ static inline bool skb_is_gso(const struct sk_buff *skb)
        return skb_shinfo(skb)->gso_size;
 }
 
+/* Note: Should be called only if skb_is_gso(skb) is true */
 static inline bool skb_is_gso_v6(const struct sk_buff *skb)
 {
        return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6;
 }
 
-extern void __skb_warn_lro_forwarding(const struct sk_buff *skb);
+void __skb_warn_lro_forwarding(const struct sk_buff *skb);
 
 static inline bool skb_warn_if_lro(const struct sk_buff *skb)
 {
index cfb7ca0..731f523 100644 (file)
@@ -155,6 +155,12 @@ smp_call_function_any(const struct cpumask *mask, smp_call_func_t func,
 
 static inline void kick_all_cpus_sync(void) {  }
 
+static inline void __smp_call_function_single(int cpuid,
+               struct call_single_data *data, int wait)
+{
+       on_each_cpu(data->func, data->info, wait);
+}
+
 #endif /* !SMP */
 
 /*
index 86a12b0..0688472 100644 (file)
@@ -108,6 +108,16 @@ static inline int ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr)
        return 0;
 }
 
+/* Get the device phy address */
+static inline int ssb_gige_get_phyaddr(struct pci_dev *pdev)
+{
+       struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+       if (!dev)
+               return -ENODEV;
+
+       return dev->dev->bus->sprom.et0phyaddr;
+}
+
 extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
                                          struct pci_dev *pdev);
 extern int ssb_gige_map_irq(struct ssb_device *sdev,
@@ -174,6 +184,10 @@ static inline int ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr)
 {
        return -ENODEV;
 }
+static inline int ssb_gige_get_phyaddr(struct pci_dev *pdev)
+{
+       return -ENODEV;
+}
 
 #endif /* CONFIG_SSB_DRIVER_GIGE */
 #endif /* LINUX_SSB_DRIVER_GIGE_H_ */
diff --git a/include/linux/tc_act/tc_defact.h b/include/linux/tc_act/tc_defact.h
deleted file mode 100644 (file)
index 6f65d07..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef __LINUX_TC_DEF_H
-#define __LINUX_TC_DEF_H
-
-#include <linux/pkt_cls.h>
-
-struct tc_defact {
-       tc_gen;
-};
-                                                                                
-enum {
-       TCA_DEF_UNSPEC,
-       TCA_DEF_TM,
-       TCA_DEF_PARMS,
-       TCA_DEF_DATA,
-       __TCA_DEF_MAX
-};
-#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
-
-#endif
index dd3edd7..9d3f1a5 100644 (file)
 
 #include <asm/timex.h>
 
+#ifndef random_get_entropy
+/*
+ * The random_get_entropy() function is used by the /dev/random driver
+ * in order to extract entropy via the relative unpredictability of
+ * when an interrupt takes places versus a high speed, fine-grained
+ * timing source or cycle counter.  Since it will be occurred on every
+ * single interrupt, it must have a very low cost/overhead.
+ *
+ * By default we use get_cycles() for this purpose, but individual
+ * architectures may override this in their asm/timex.h header file.
+ */
+#define random_get_entropy()   get_cycles()
+#endif
+
 /*
  * SHIFT_PLL is used as a dampening factor to define how much we
  * adjust the frequency correction for a given offset in PLL mode.
index cc25b70..c3fa807 100644 (file)
@@ -36,6 +36,9 @@
  * SUCH DAMAGE.
  */
 
+#ifndef __LINUX_USB_CDC_NCM_H
+#define __LINUX_USB_CDC_NCM_H
+
 #define CDC_NCM_COMM_ALTSETTING_NCM            0
 #define CDC_NCM_COMM_ALTSETTING_MBIM           1
 
 #define cdc_ncm_data_intf_is_mbim(x)  ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB)
 
 struct cdc_ncm_ctx {
-       struct usb_cdc_ncm_ntb_parameters ncm_parm;
        struct hrtimer tx_timer;
        struct tasklet_struct bh;
 
        const struct usb_cdc_ncm_desc *func_desc;
-       const struct usb_cdc_mbim_desc   *mbim_desc;
-       const struct usb_cdc_header_desc *header_desc;
-       const struct usb_cdc_union_desc *union_desc;
+       const struct usb_cdc_mbim_desc *mbim_desc;
        const struct usb_cdc_ether_desc *ether_desc;
 
-       struct net_device *netdev;
-       struct usb_device *udev;
-       struct usb_host_endpoint *in_ep;
-       struct usb_host_endpoint *out_ep;
-       struct usb_host_endpoint *status_ep;
-       struct usb_interface *intf;
        struct usb_interface *control;
        struct usb_interface *data;
 
@@ -113,8 +107,6 @@ struct cdc_ncm_ctx {
 
        u32 tx_timer_pending;
        u32 tx_curr_frame_num;
-       u32 rx_speed;
-       u32 tx_speed;
        u32 rx_max;
        u32 tx_max;
        u32 max_datagram_size;
@@ -127,9 +119,14 @@ struct cdc_ncm_ctx {
        u16 connected;
 };
 
-extern u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
-extern int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
-extern void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
-extern struct sk_buff *cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign);
-extern int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
-extern int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset);
+u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
+int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
+void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
+struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
+int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);
+int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset);
+struct sk_buff *
+cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
+int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in);
+
+#endif /* __LINUX_USB_CDC_NCM_H */
index f9a7e7b..11d85b9 100644 (file)
@@ -12,7 +12,7 @@ struct usb_phy_gen_xceiv_platform_data {
        unsigned int needs_reset:1;
 };
 
-#if IS_ENABLED(CONFIG_NOP_USB_XCEIV)
+#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE))
 /* sometimes transceivers are accessed only through e.g. ULPI */
 extern void usb_nop_xceiv_register(void);
 extern void usb_nop_xceiv_unregister(void);
index 9cb2fe8..e303eef 100644 (file)
@@ -42,6 +42,7 @@ struct usbnet {
        struct usb_host_endpoint *status;
        unsigned                maxpacket;
        struct timer_list       delay;
+       const char              *padding_pkt;
 
        /* protocol/interface state */
        struct net_device       *net;
index bf99cd0..6303568 100644 (file)
@@ -66,7 +66,9 @@
        US_FLAG(INITIAL_READ10, 0x00100000)                     \
                /* Initial READ(10) (and others) must be retried */     \
        US_FLAG(WRITE_CACHE,    0x00200000)                     \
-               /* Write Cache status is not available */
+               /* Write Cache status is not available */       \
+       US_FLAG(NEEDS_CAP16,    0x00400000)
+               /* cannot handle READ_CAPACITY_10 */
 
 #define US_FLAG(name, value)   US_FL_##name = value ,
 enum { US_DO_ALL_FLAGS };
index 80cf817..2c02f3a 100644 (file)
@@ -65,15 +65,8 @@ struct pci_dev;
  *     out of the arbitration process (and can be safe to take
  *     interrupts at any time.
  */
-#if defined(CONFIG_VGA_ARB)
 extern void vga_set_legacy_decoding(struct pci_dev *pdev,
                                    unsigned int decodes);
-#else
-static inline void vga_set_legacy_decoding(struct pci_dev *pdev,
-                                          unsigned int decodes)
-{
-}
-#endif
 
 /**
  *     vga_get         - acquire & locks VGA resources
index 7fe2822..512cdc2 100644 (file)
@@ -77,6 +77,6 @@ struct yamdrv_ioctl_cfg {
 
 struct yamdrv_ioctl_mcs {
        int cmd;
-       int bitrate;
+       unsigned int bitrate;
        unsigned char bits[YAM_FPGA_SIZE];
 };
index fb314de..86505bf 100644 (file)
@@ -67,6 +67,10 @@ int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
 int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr);
 #endif
 
+bool ipv6_chk_custom_prefix(const struct in6_addr *addr,
+                                  const unsigned int prefix_len,
+                                  struct net_device *dev);
+
 int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev);
 
 struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net,
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
deleted file mode 100644 (file)
index 487b54c..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-   Copyright (c) 2010,2011 Code Aurora Forum.  All rights reserved.
-   Copyright (c) 2011,2012 Intel Corp.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 and
-   only version 2 as published by the Free Software Foundation.
-
-   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.
-*/
-
-#ifndef __A2MP_H
-#define __A2MP_H
-
-#include <net/bluetooth/l2cap.h>
-
-#define A2MP_FEAT_EXT  0x8000
-
-enum amp_mgr_state {
-       READ_LOC_AMP_INFO,
-       READ_LOC_AMP_ASSOC,
-       READ_LOC_AMP_ASSOC_FINAL,
-       WRITE_REMOTE_AMP_ASSOC,
-};
-
-struct amp_mgr {
-       struct list_head        list;
-       struct l2cap_conn       *l2cap_conn;
-       struct l2cap_chan       *a2mp_chan;
-       struct l2cap_chan       *bredr_chan;
-       struct kref             kref;
-       __u8                    ident;
-       __u8                    handle;
-       unsigned long           state;
-       unsigned long           flags;
-
-       struct list_head        amp_ctrls;
-       struct mutex            amp_ctrls_lock;
-};
-
-struct a2mp_cmd {
-       __u8    code;
-       __u8    ident;
-       __le16  len;
-       __u8    data[0];
-} __packed;
-
-/* A2MP command codes */
-#define A2MP_COMMAND_REJ         0x01
-struct a2mp_cmd_rej {
-       __le16  reason;
-       __u8    data[0];
-} __packed;
-
-#define A2MP_DISCOVER_REQ        0x02
-struct a2mp_discov_req {
-       __le16  mtu;
-       __le16  ext_feat;
-} __packed;
-
-struct a2mp_cl {
-       __u8    id;
-       __u8    type;
-       __u8    status;
-} __packed;
-
-#define A2MP_DISCOVER_RSP        0x03
-struct a2mp_discov_rsp {
-       __le16     mtu;
-       __le16     ext_feat;
-       struct a2mp_cl cl[0];
-} __packed;
-
-#define A2MP_CHANGE_NOTIFY       0x04
-#define A2MP_CHANGE_RSP          0x05
-
-#define A2MP_GETINFO_REQ         0x06
-struct a2mp_info_req {
-       __u8       id;
-} __packed;
-
-#define A2MP_GETINFO_RSP         0x07
-struct a2mp_info_rsp {
-       __u8    id;
-       __u8    status;
-       __le32  total_bw;
-       __le32  max_bw;
-       __le32  min_latency;
-       __le16  pal_cap;
-       __le16  assoc_size;
-} __packed;
-
-#define A2MP_GETAMPASSOC_REQ     0x08
-struct a2mp_amp_assoc_req {
-       __u8    id;
-} __packed;
-
-#define A2MP_GETAMPASSOC_RSP     0x09
-struct a2mp_amp_assoc_rsp {
-       __u8    id;
-       __u8    status;
-       __u8    amp_assoc[0];
-} __packed;
-
-#define A2MP_CREATEPHYSLINK_REQ  0x0A
-#define A2MP_DISCONNPHYSLINK_REQ 0x0C
-struct a2mp_physlink_req {
-       __u8    local_id;
-       __u8    remote_id;
-       __u8    amp_assoc[0];
-} __packed;
-
-#define A2MP_CREATEPHYSLINK_RSP  0x0B
-#define A2MP_DISCONNPHYSLINK_RSP 0x0D
-struct a2mp_physlink_rsp {
-       __u8    local_id;
-       __u8    remote_id;
-       __u8    status;
-} __packed;
-
-/* A2MP response status */
-#define A2MP_STATUS_SUCCESS                    0x00
-#define A2MP_STATUS_INVALID_CTRL_ID            0x01
-#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
-#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS    0x02
-#define A2MP_STATUS_COLLISION_OCCURED          0x03
-#define A2MP_STATUS_DISCONN_REQ_RECVD          0x04
-#define A2MP_STATUS_PHYS_LINK_EXISTS           0x05
-#define A2MP_STATUS_SECURITY_VIOLATION         0x06
-
-extern struct list_head amp_mgr_list;
-extern struct mutex amp_mgr_list_lock;
-
-struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
-int amp_mgr_put(struct amp_mgr *mgr);
-u8 __next_ident(struct amp_mgr *mgr);
-struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
-                                      struct sk_buff *skb);
-struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
-void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
-void a2mp_discover_amp(struct l2cap_chan *chan);
-void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
-void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
-void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
-void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
-
-#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h
deleted file mode 100644 (file)
index 7ea3db7..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-   Copyright (c) 2011,2012 Intel Corp.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 and
-   only version 2 as published by the Free Software Foundation.
-
-   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.
-*/
-
-#ifndef __AMP_H
-#define __AMP_H
-
-struct amp_ctrl {
-       struct list_head        list;
-       struct kref             kref;
-       __u8                    id;
-       __u16                   assoc_len_so_far;
-       __u16                   assoc_rem_len;
-       __u16                   assoc_len;
-       __u8                    *assoc;
-};
-
-int amp_ctrl_put(struct amp_ctrl *ctrl);
-void amp_ctrl_get(struct amp_ctrl *ctrl);
-struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
-struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
-void amp_ctrl_list_flush(struct amp_mgr *mgr);
-
-struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
-                            u8 remote_id, bool out);
-
-int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
-
-void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
-void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
-void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
-void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
-                                  struct hci_conn *hcon);
-void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
-                       struct hci_conn *hcon);
-void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
-                       struct hci_conn *hcon);
-void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
-void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
-void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
-void amp_create_logical_link(struct l2cap_chan *chan);
-void amp_disconnect_logical_link(struct hci_chan *hchan);
-void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
-
-#endif /* __AMP_H */
index 10d43d8..2a628b2 100644 (file)
@@ -197,8 +197,8 @@ static inline bool bdaddr_type_is_le(__u8 type)
        return false;
 }
 
-#define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} })
-#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} })
+#define BDADDR_ANY  (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_NONE (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
 
 /* Copy, swap, convert BD Address */
 static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
@@ -218,11 +218,10 @@ void baswap(bdaddr_t *dst, bdaddr_t *src);
 
 struct bt_sock {
        struct sock sk;
-       bdaddr_t    src;
-       bdaddr_t    dst;
        struct list_head accept_q;
        struct sock *parent;
        unsigned long flags;
+       void (*skb_msg_name)(struct sk_buff *, void *, int *);
 };
 
 enum {
@@ -249,6 +248,7 @@ int  bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
 int  bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 int  bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
+int  bt_sock_wait_ready(struct sock *sk, unsigned long flags);
 
 void bt_accept_enqueue(struct sock *parent, struct sock *sk);
 void bt_accept_unlink(struct sock *sk);
@@ -282,8 +282,11 @@ struct bt_skb_cb {
        __u8 incoming;
        __u16 expect;
        __u8 force_active;
+       struct l2cap_chan *chan;
        struct l2cap_ctrl control;
        struct hci_req_ctrl req;
+       bdaddr_t bdaddr;
+       __le16 psm;
 };
 #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
 
@@ -331,16 +334,16 @@ out:
 
 int bt_to_errno(__u16 code);
 
-extern int hci_sock_init(void);
-extern void hci_sock_cleanup(void);
+int hci_sock_init(void);
+void hci_sock_cleanup(void);
 
-extern int bt_sysfs_init(void);
-extern void bt_sysfs_cleanup(void);
+int bt_sysfs_init(void);
+void bt_sysfs_cleanup(void);
 
-extern int  bt_procfs_init(struct net *net, const char *name,
-                          struct bt_sock_list* sk_list,
-                          int (* seq_show)(struct seq_file *, void *));
-extern void bt_procfs_cleanup(struct net *net, const char *name);
+int bt_procfs_init(struct net *net, const char *name,
+                  struct bt_sock_list *sk_list,
+                  int (*seq_show)(struct seq_file *, void *));
+void bt_procfs_cleanup(struct net *net, const char *name);
 
 extern struct dentry *bt_debugfs;
 
index 15f1084..1784c48 100644 (file)
@@ -35,6 +35,8 @@
 
 #define HCI_MAX_AMP_ASSOC_SIZE 672
 
+#define HCI_MAX_CSB_DATA_SIZE  252
+
 /* HCI dev events */
 #define HCI_DEV_REG                    1
 #define HCI_DEV_UNREG                  2
 #define HCI_AMP                0x01
 
 /* First BR/EDR Controller shall have ID = 0 */
-#define HCI_BREDR_ID   0
+#define AMP_ID_BREDR   0x00
+
+/* AMP controller types */
+#define AMP_TYPE_BREDR 0x00
+#define AMP_TYPE_80211 0x01
 
 /* AMP controller status */
-#define AMP_CTRL_POWERED_DOWN                  0x00
-#define AMP_CTRL_BLUETOOTH_ONLY                        0x01
-#define AMP_CTRL_NO_CAPACITY                   0x02
-#define AMP_CTRL_LOW_CAPACITY                  0x03
-#define AMP_CTRL_MEDIUM_CAPACITY               0x04
-#define AMP_CTRL_HIGH_CAPACITY                 0x05
-#define AMP_CTRL_FULL_CAPACITY                 0x06
+#define AMP_STATUS_POWERED_DOWN                        0x00
+#define AMP_STATUS_BLUETOOTH_ONLY              0x01
+#define AMP_STATUS_NO_CAPACITY                 0x02
+#define AMP_STATUS_LOW_CAPACITY                        0x03
+#define AMP_STATUS_MEDIUM_CAPACITY             0x04
+#define AMP_STATUS_HIGH_CAPACITY               0x05
+#define AMP_STATUS_FULL_CAPACITY               0x06
 
 /* HCI device quirks */
 enum {
@@ -109,18 +115,22 @@ enum {
        HCI_PAIRABLE,
        HCI_SERVICE_CACHE,
        HCI_DEBUG_KEYS,
+       HCI_DUT_MODE,
        HCI_UNREGISTER,
+       HCI_USER_CHANNEL,
 
        HCI_LE_SCAN,
        HCI_SSP_ENABLED,
        HCI_HS_ENABLED,
        HCI_LE_ENABLED,
-       HCI_LE_PERIPHERAL,
+       HCI_ADVERTISING,
        HCI_CONNECTABLE,
        HCI_DISCOVERABLE,
+       HCI_LIMITED_DISCOVERABLE,
        HCI_LINK_SECURITY,
        HCI_PERIODIC_INQ,
        HCI_FAST_CONNECTABLE,
+       HCI_BREDR_ENABLED,
 };
 
 /* A mask for the flags that are supposed to remain when a reset happens
@@ -624,6 +634,24 @@ struct hci_rp_logical_link_cancel {
        __u8     flow_spec_id;
 } __packed;
 
+#define HCI_OP_SET_CSB                 0x0441
+struct hci_cp_set_csb {
+       __u8    enable;
+       __u8    lt_addr;
+       __u8    lpo_allowed;
+       __le16  packet_type;
+       __le16  interval_min;
+       __le16  interval_max;
+       __le16  csb_sv_tout;
+} __packed;
+struct hci_rp_set_csb {
+       __u8    status;
+       __u8    lt_addr;
+       __le16  interval;
+} __packed;
+
+#define HCI_OP_START_SYNC_TRAIN                0x0443
+
 #define HCI_OP_SNIFF_MODE              0x0803
 struct hci_cp_sniff_mode {
        __le16   handle;
@@ -694,9 +722,6 @@ struct hci_cp_sniff_subrate {
 } __packed;
 
 #define HCI_OP_SET_EVENT_MASK          0x0c01
-struct hci_cp_set_event_mask {
-       __u8     mask[8];
-} __packed;
 
 #define HCI_OP_RESET                   0x0c03
 
@@ -792,6 +817,20 @@ struct hci_cp_host_buffer_size {
        __le16   sco_max_pkt;
 } __packed;
 
+#define HCI_OP_READ_NUM_SUPPORTED_IAC  0x0c38
+struct hci_rp_read_num_supported_iac {
+       __u8    status;
+       __u8    num_iac;
+} __packed;
+
+#define HCI_OP_READ_CURRENT_IAC_LAP    0x0c39
+
+#define HCI_OP_WRITE_CURRENT_IAC_LAP   0x0c3a
+struct hci_cp_write_current_iac_lap {
+       __u8    num_iac;
+       __u8    iac_lap[6];
+} __packed;
+
 #define HCI_OP_WRITE_INQUIRY_MODE      0x0c45
 
 #define HCI_MAX_EIR_LENGTH             240
@@ -826,6 +865,10 @@ struct hci_rp_read_inq_rsp_tx_power {
        __s8     tx_power;
 } __packed;
 
+#define HCI_OP_SET_EVENT_MASK_PAGE_2   0x0c63
+
+#define HCI_OP_READ_LOCATION_DATA      0x0c64
+
 #define HCI_OP_READ_FLOW_CONTROL_MODE  0x0c66
 struct hci_rp_read_flow_control_mode {
        __u8     status;
@@ -838,6 +881,50 @@ struct hci_cp_write_le_host_supported {
        __u8    simul;
 } __packed;
 
+#define HCI_OP_SET_RESERVED_LT_ADDR    0x0c74
+struct hci_cp_set_reserved_lt_addr {
+       __u8    lt_addr;
+} __packed;
+struct hci_rp_set_reserved_lt_addr {
+       __u8    status;
+       __u8    lt_addr;
+} __packed;
+
+#define HCI_OP_DELETE_RESERVED_LT_ADDR 0x0c75
+struct hci_cp_delete_reserved_lt_addr {
+       __u8    lt_addr;
+} __packed;
+struct hci_rp_delete_reserved_lt_addr {
+       __u8    status;
+       __u8    lt_addr;
+} __packed;
+
+#define HCI_OP_SET_CSB_DATA            0x0c76
+struct hci_cp_set_csb_data {
+       __u8    lt_addr;
+       __u8    fragment;
+       __u8    data_length;
+       __u8    data[HCI_MAX_CSB_DATA_SIZE];
+} __packed;
+struct hci_rp_set_csb_data {
+       __u8    status;
+       __u8    lt_addr;
+} __packed;
+
+#define HCI_OP_READ_SYNC_TRAIN_PARAMS  0x0c77
+
+#define HCI_OP_WRITE_SYNC_TRAIN_PARAMS 0x0c78
+struct hci_cp_write_sync_train_params {
+       __le16  interval_min;
+       __le16  interval_max;
+       __le32  sync_train_tout;
+       __u8    service_data;
+} __packed;
+struct hci_rp_write_sync_train_params {
+       __u8    status;
+       __le16  sync_train_int;
+} __packed;
+
 #define HCI_OP_READ_LOCAL_VERSION      0x1001
 struct hci_rp_read_local_version {
        __u8     status;
@@ -957,6 +1044,10 @@ struct hci_rp_write_remote_amp_assoc {
        __u8     phy_handle;
 } __packed;
 
+#define HCI_OP_ENABLE_DUT_MODE         0x1803
+
+#define HCI_OP_WRITE_SSP_DEBUG_MODE    0x1804
+
 #define HCI_OP_LE_SET_EVENT_MASK       0x2001
 struct hci_cp_le_set_event_mask {
        __u8     mask[8];
@@ -975,6 +1066,20 @@ struct hci_rp_le_read_local_features {
        __u8     features[8];
 } __packed;
 
+#define HCI_OP_LE_SET_RANDOM_ADDR      0x2005
+
+#define HCI_OP_LE_SET_ADV_PARAM                0x2006
+struct hci_cp_le_set_adv_param {
+       __le16   min_interval;
+       __le16   max_interval;
+       __u8     type;
+       __u8     own_address_type;
+       __u8     direct_addr_type;
+       bdaddr_t direct_addr;
+       __u8     channel_map;
+       __u8     filter_policy;
+} __packed;
+
 #define HCI_OP_LE_READ_ADV_TX_POWER    0x2007
 struct hci_rp_le_read_adv_tx_power {
        __u8    status;
@@ -989,6 +1094,12 @@ struct hci_cp_le_set_adv_data {
        __u8    data[HCI_MAX_AD_LENGTH];
 } __packed;
 
+#define HCI_OP_LE_SET_SCAN_RSP_DATA    0x2009
+struct hci_cp_le_set_scan_rsp_data {
+       __u8    length;
+       __u8    data[HCI_MAX_AD_LENGTH];
+} __packed;
+
 #define HCI_OP_LE_SET_ADV_ENABLE       0x200a
 
 #define LE_SCAN_PASSIVE                        0x00
@@ -1438,6 +1549,13 @@ struct hci_ev_num_comp_blocks {
        struct hci_comp_blocks_info handles[0];
 } __packed;
 
+#define HCI_EV_SYNC_TRAIN_COMPLETE     0x4F
+struct hci_ev_sync_train_complete {
+       __u8    status;
+} __packed;
+
+#define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54
+
 /* Low energy meta events */
 #define LE_CONN_ROLE_MASTER    0x00
 
@@ -1462,11 +1580,11 @@ struct hci_ev_le_ltk_req {
 } __packed;
 
 /* Advertising report event types */
-#define ADV_IND                0x00
-#define ADV_DIRECT_IND 0x01
-#define ADV_SCAN_IND   0x02
-#define ADV_NONCONN_IND        0x03
-#define ADV_SCAN_RSP   0x04
+#define LE_ADV_IND             0x00
+#define LE_ADV_DIRECT_IND      0x01
+#define LE_ADV_SCAN_IND                0x02
+#define LE_ADV_NONCONN_IND     0x03
+#define LE_ADV_SCAN_RSP                0x04
 
 #define ADDR_LE_DEV_PUBLIC     0x00
 #define ADDR_LE_DEV_RANDOM     0x01
@@ -1571,6 +1689,7 @@ struct sockaddr_hci {
 #define HCI_DEV_NONE   0xffff
 
 #define HCI_CHANNEL_RAW                0
+#define HCI_CHANNEL_USER       1
 #define HCI_CHANNEL_MONITOR    2
 #define HCI_CHANNEL_CONTROL    3
 
@@ -1673,6 +1792,4 @@ struct hci_inquiry_req {
 };
 #define IREQ_CACHE_FLUSH 0x0001
 
-extern bool enable_hs;
-
 #endif /* __HCI_H */
index 3ede820..f8555ad 100644 (file)
@@ -81,6 +81,7 @@ struct hci_conn_hash {
 struct bdaddr_list {
        struct list_head list;
        bdaddr_t bdaddr;
+       u8 bdaddr_type;
 };
 
 struct bt_uuid {
@@ -140,6 +141,8 @@ struct hci_dev {
        __u8            bus;
        __u8            dev_type;
        bdaddr_t        bdaddr;
+       bdaddr_t        static_addr;
+       __u8            own_addr_type;
        __u8            dev_name[HCI_MAX_NAME_LENGTH];
        __u8            short_name[HCI_MAX_SHORT_NAME_LENGTH];
        __u8            eir[HCI_MAX_EIR_LENGTH];
@@ -158,11 +161,17 @@ struct hci_dev {
        __u16           manufacturer;
        __u16           lmp_subver;
        __u16           voice_setting;
+       __u8            num_iac;
        __u8            io_capability;
        __s8            inq_tx_power;
        __u16           page_scan_interval;
        __u16           page_scan_window;
        __u8            page_scan_type;
+       __u16           le_scan_interval;
+       __u16           le_scan_window;
+       __u16           le_conn_min_interval;
+       __u16           le_conn_max_interval;
+       __u8            ssp_debug_mode;
 
        __u16           devid_source;
        __u16           devid_vendor;
@@ -279,14 +288,15 @@ struct hci_dev {
        __s8                    adv_tx_power;
        __u8                    adv_data[HCI_MAX_AD_LENGTH];
        __u8                    adv_data_len;
+       __u8                    scan_rsp_data[HCI_MAX_AD_LENGTH];
+       __u8                    scan_rsp_data_len;
 
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
        int (*flush)(struct hci_dev *hdev);
        int (*setup)(struct hci_dev *hdev);
-       int (*send)(struct sk_buff *skb);
+       int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
        void (*notify)(struct hci_dev *hdev, unsigned int evt);
-       int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
 };
 
 #define HCI_PHY_HANDLE(handle) (handle & 0xff)
@@ -298,6 +308,8 @@ struct hci_conn {
 
        bdaddr_t        dst;
        __u8            dst_type;
+       bdaddr_t        src;
+       __u8            src_type;
        __u16           handle;
        __u16           state;
        __u8            mode;
@@ -306,7 +318,6 @@ struct hci_conn {
        __u8            attempt;
        __u8            dev_class[3];
        __u8            features[HCI_MAX_PAGES][8];
-       __u16           interval;
        __u16           pkt_type;
        __u16           link_policy;
        __u32           link_mode;
@@ -334,8 +345,8 @@ struct hci_conn {
        struct list_head chan_list;
 
        struct delayed_work disc_work;
-       struct timer_list idle_timer;
-       struct timer_list auto_accept_timer;
+       struct delayed_work auto_accept_work;
+       struct delayed_work idle_work;
 
        struct device   dev;
 
@@ -367,18 +378,17 @@ extern rwlock_t hci_dev_list_lock;
 extern rwlock_t hci_cb_list_lock;
 
 /* ----- HCI interface to upper protocols ----- */
-extern int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
-extern void l2cap_connect_cfm(struct hci_conn *hcon, u8 status);
-extern int l2cap_disconn_ind(struct hci_conn *hcon);
-extern void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason);
-extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
-extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
-                             u16 flags);
-
-extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
-extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
-extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
-extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
+int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
+void l2cap_connect_cfm(struct hci_conn *hcon, u8 status);
+int l2cap_disconn_ind(struct hci_conn *hcon);
+void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason);
+int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
+int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
+
+int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
+void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
+void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
+int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
 
 /* ----- Inquiry cache ----- */
 #define INQUIRY_CACHE_AGE_MAX   (HZ*30)   /* 30 seconds */
@@ -644,7 +654,7 @@ static inline void hci_conn_drop(struct hci_conn *conn)
                switch (conn->type) {
                case ACL_LINK:
                case LE_LINK:
-                       del_timer(&conn->idle_timer);
+                       cancel_delayed_work(&conn->idle_work);
                        if (conn->state == BT_CONNECTED) {
                                timeo = conn->disc_timeout;
                                if (!conn->out)
@@ -703,19 +713,6 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
        dev_set_drvdata(&hdev->dev, data);
 }
 
-/* hci_dev_list shall be locked */
-static inline uint8_t __hci_num_ctrl(void)
-{
-       uint8_t count = 0;
-       struct list_head *p;
-
-       list_for_each(p, &hci_dev_list) {
-               count++;
-       }
-
-       return count;
-}
-
 struct hci_dev *hci_dev_get(int index);
 struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src);
 
@@ -738,7 +735,7 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
 int hci_inquiry(void __user *arg);
 
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
-                                        bdaddr_t *bdaddr);
+                                        bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_clear(struct hci_dev *hdev);
 int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
@@ -768,13 +765,11 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
-int hci_recv_frame(struct sk_buff *skb);
+int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
 int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count);
 int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
 
 void hci_init_sysfs(struct hci_dev *hdev);
-int hci_add_sysfs(struct hci_dev *hdev);
-void hci_del_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
 void hci_conn_add_sysfs(struct hci_conn *conn);
 void hci_conn_del_sysfs(struct hci_conn *conn);
@@ -807,22 +802,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define lmp_host_le_capable(dev)   (!!((dev)->features[1][0] & LMP_HOST_LE))
 #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR))
 
-/* returns true if at least one AMP active */
-static inline bool hci_amp_capable(void)
-{
-       struct hci_dev *hdev;
-       bool ret = false;
-
-       read_lock(&hci_dev_list_lock);
-       list_for_each_entry(hdev, &hci_dev_list, list)
-               if (hdev->amp_type == HCI_AMP &&
-                   test_bit(HCI_UP, &hdev->flags))
-                       ret = true;
-       read_unlock(&hci_dev_list_lock);
-
-       return ret;
-}
-
 /* ----- HCI protocols ----- */
 #define HCI_PROTO_DEFER             0x01
 
@@ -1033,34 +1012,6 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
        return false;
 }
 
-static inline size_t eir_get_length(u8 *eir, size_t eir_len)
-{
-       size_t parsed = 0;
-
-       while (parsed < eir_len) {
-               u8 field_len = eir[0];
-
-               if (field_len == 0)
-                       return parsed;
-
-               parsed += field_len + 1;
-               eir += field_len + 1;
-       }
-
-       return eir_len;
-}
-
-static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
-                                 u8 data_len)
-{
-       eir[eir_len++] = sizeof(type) + data_len;
-       eir[eir_len++] = type;
-       memcpy(&eir[eir_len], data, data_len);
-       eir_len += data_len;
-
-       return eir_len;
-}
-
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
@@ -1120,29 +1071,30 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
 #define DISCOV_BREDR_INQUIRY_LEN       0x08
 
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
-int mgmt_index_added(struct hci_dev *hdev);
-int mgmt_index_removed(struct hci_dev *hdev);
-int mgmt_set_powered_failed(struct hci_dev *hdev, int err);
+void mgmt_index_added(struct hci_dev *hdev);
+void mgmt_index_removed(struct hci_dev *hdev);
+void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
 int mgmt_powered(struct hci_dev *hdev, u8 powered);
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
-                     bool persistent);
-int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                         u8 addr_type, u32 flags, u8 *name, u8 name_len,
-                         u8 *dev_class);
-int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                            u8 link_type, u8 addr_type, u8 reason);
-int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                          u8 link_type, u8 addr_type, u8 status);
-int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                       u8 addr_type, u8 status);
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                u8 status);
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 status);
+void mgmt_discoverable_timeout(struct hci_dev *hdev);
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable);
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                      bool persistent);
+void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                          u8 addr_type, u32 flags, u8 *name, u8 name_len,
+                          u8 *dev_class);
+void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                             u8 link_type, u8 addr_type, u8 reason);
+void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 link_type, u8 addr_type, u8 status);
+void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                        u8 addr_type, u8 status);
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                 u8 status);
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                     u8 status);
 int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
                              u8 link_type, u8 addr_type, __le32 value,
                              u8 confirm_hint);
@@ -1159,26 +1111,25 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
                             u8 link_type, u8 addr_type, u32 passkey,
                             u8 entered);
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, u8 status);
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
-                                  u8 status);
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                           u8 *randomizer, u8 status);
-int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
-int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                     u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
-                     u8 ssp, u8 *eir, u16 eir_len);
-int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, s8 rssi, u8 *name, u8 name_len);
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, u8 status);
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+                                   u8 status);
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                            u8 *randomizer, u8 status);
+void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
+                      u8 ssp, u8 *eir, u16 eir_len);
+void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, s8 rssi, u8 *name, u8 name_len);
+void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-bool mgmt_valid_hdev(struct hci_dev *hdev);
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_reenable_advertising(struct hci_dev *hdev);
 
 /* HCI info for socket */
 #define hci_pi(sk) ((struct hci_pinfo *) sk)
@@ -1208,15 +1159,11 @@ struct hci_sec_filter {
 #define hci_req_lock(d)                mutex_lock(&d->req_lock)
 #define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
 
-void hci_update_ad(struct hci_request *req);
-
 void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
                                        u16 latency, u16 to_multiplier);
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
                                                        __u8 ltk[16]);
 
-u8 bdaddr_to_le(u8 bdaddr_type);
-
 #define SCO_AIRMODE_MASK       0x0003
 #define SCO_AIRMODE_CVSD       0x0000
 #define SCO_AIRMODE_TRANSP     0x0003
index 1a966af..5132990 100644 (file)
@@ -131,6 +131,7 @@ struct l2cap_conninfo {
 
 /* L2CAP fixed channels */
 #define L2CAP_FC_L2CAP         0x02
+#define L2CAP_FC_CONNLESS      0x04
 #define L2CAP_FC_A2MP          0x08
 
 /* L2CAP Control Field bit masks */
@@ -237,6 +238,7 @@ struct l2cap_conn_rsp {
 /* protocol/service multiplexer (PSM) */
 #define L2CAP_PSM_SDP          0x0001
 #define L2CAP_PSM_RFCOMM       0x0003
+#define L2CAP_PSM_3DSP         0x0021
 
 /* channel indentifier */
 #define L2CAP_CID_SIGNALING    0x0001
@@ -433,8 +435,6 @@ struct l2cap_seq_list {
 #define L2CAP_SEQ_LIST_TAIL    0x8000
 
 struct l2cap_chan {
-       struct sock *sk;
-
        struct l2cap_conn       *conn;
        struct hci_conn         *hs_hcon;
        struct hci_chan         *hs_hchan;
@@ -442,7 +442,12 @@ struct l2cap_chan {
 
        __u8            state;
 
+       bdaddr_t        dst;
+       __u8            dst_type;
+       bdaddr_t        src;
+       __u8            src_type;
        __le16          psm;
+       __le16          sport;
        __u16           dcid;
        __u16           scid;
 
@@ -453,8 +458,6 @@ struct l2cap_chan {
        __u8            chan_type;
        __u8            chan_policy;
 
-       __le16          sport;
-
        __u8            sec_level;
 
        __u8            ident;
@@ -546,9 +549,12 @@ struct l2cap_ops {
        void                    (*teardown) (struct l2cap_chan *chan, int err);
        void                    (*close) (struct l2cap_chan *chan);
        void                    (*state_change) (struct l2cap_chan *chan,
-                                                int state);
+                                                int state, int err);
        void                    (*ready) (struct l2cap_chan *chan);
        void                    (*defer) (struct l2cap_chan *chan);
+       void                    (*resume) (struct l2cap_chan *chan);
+       void                    (*set_shutdown) (struct l2cap_chan *chan);
+       long                    (*get_sndtimeo) (struct l2cap_chan *chan);
        struct sk_buff          *(*alloc_skb) (struct l2cap_chan *chan,
                                               unsigned long len, int nb);
 };
@@ -557,13 +563,11 @@ struct l2cap_conn {
        struct hci_conn         *hcon;
        struct hci_chan         *hchan;
 
-       bdaddr_t                *dst;
-       bdaddr_t                *src;
-
        unsigned int            mtu;
 
        __u32                   feat_mask;
        __u8                    fixed_chan_mask;
+       bool                    hs_enabled;
 
        __u8                    info_state;
        __u8                    info_ident;
@@ -649,6 +653,7 @@ enum {
        FLAG_FLUSHABLE,
        FLAG_EXT_CTRL,
        FLAG_EFS_ENABLE,
+       FLAG_DEFER_SETUP,
 };
 
 enum {
@@ -790,6 +795,19 @@ static inline void l2cap_chan_no_defer(struct l2cap_chan *chan)
 {
 }
 
+static inline void l2cap_chan_no_resume(struct l2cap_chan *chan)
+{
+}
+
+static inline void l2cap_chan_no_set_shutdown(struct l2cap_chan *chan)
+{
+}
+
+static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
+{
+       return 0;
+}
+
 extern bool disable_ertm;
 
 int l2cap_init_sockets(void);
@@ -797,7 +815,6 @@ void l2cap_cleanup_sockets(void);
 bool l2cap_is_socket(struct socket *sock);
 
 void __l2cap_connect_rsp_defer(struct l2cap_chan *chan);
-int __l2cap_wait_ack(struct sock *sk);
 
 int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
 int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid);
index 9944c3e..518c5c8 100644 (file)
@@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list {
 #define MGMT_SETTING_BREDR             0x00000080
 #define MGMT_SETTING_HS                        0x00000100
 #define MGMT_SETTING_LE                        0x00000200
+#define MGMT_SETTING_ADVERTISING       0x00000400
 
 #define MGMT_OP_READ_INFO              0x0004
 #define MGMT_READ_INFO_SIZE            0
@@ -351,6 +352,23 @@ struct mgmt_cp_set_device_id {
 } __packed;
 #define MGMT_SET_DEVICE_ID_SIZE                8
 
+#define MGMT_OP_SET_ADVERTISING                0x0029
+
+#define MGMT_OP_SET_BREDR              0x002A
+
+#define MGMT_OP_SET_STATIC_ADDRESS     0x002B
+struct mgmt_cp_set_static_address {
+       bdaddr_t bdaddr;
+} __packed;
+#define MGMT_SET_STATIC_ADDRESS_SIZE   6
+
+#define MGMT_OP_SET_SCAN_PARAMS                0x002C
+struct mgmt_cp_set_scan_params {
+       __le16  interval;
+       __le16  window;
+} __packed;
+#define MGMT_SET_SCAN_PARAMS_SIZE      4
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index 7afd419..486213a 100644 (file)
@@ -256,8 +256,8 @@ static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
                rfcomm_dlc_free(d);
 }
 
-extern void __rfcomm_dlc_throttle(struct rfcomm_dlc *d);
-extern void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d);
+void __rfcomm_dlc_throttle(struct rfcomm_dlc *d);
+void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d);
 
 static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d)
 {
@@ -300,6 +300,8 @@ struct rfcomm_conninfo {
 
 struct rfcomm_pinfo {
        struct bt_sock bt;
+       bdaddr_t src;
+       bdaddr_t dst;
        struct rfcomm_dlc   *dlc;
        u8     channel;
        u8     sec_level;
index e252a31..2019d1a 100644 (file)
@@ -55,9 +55,6 @@ struct sco_conninfo {
 struct sco_conn {
        struct hci_conn *hcon;
 
-       bdaddr_t        *dst;
-       bdaddr_t        *src;
-
        spinlock_t      lock;
        struct sock     *sk;
 
@@ -72,6 +69,8 @@ struct sco_conn {
 
 struct sco_pinfo {
        struct bt_sock  bt;
+       bdaddr_t        src;
+       bdaddr_t        dst;
        __u32           flags;
        __u16           setting;
        struct sco_conn *conn;
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
deleted file mode 100644 (file)
index f8ba07f..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
-   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;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
-   SOFTWARE IS DISCLAIMED.
-*/
-
-#ifndef __SMP_H
-#define __SMP_H
-
-struct smp_command_hdr {
-       __u8    code;
-} __packed;
-
-#define SMP_CMD_PAIRING_REQ    0x01
-#define SMP_CMD_PAIRING_RSP    0x02
-struct smp_cmd_pairing {
-       __u8    io_capability;
-       __u8    oob_flag;
-       __u8    auth_req;
-       __u8    max_key_size;
-       __u8    init_key_dist;
-       __u8    resp_key_dist;
-} __packed;
-
-#define SMP_IO_DISPLAY_ONLY    0x00
-#define SMP_IO_DISPLAY_YESNO   0x01
-#define SMP_IO_KEYBOARD_ONLY   0x02
-#define SMP_IO_NO_INPUT_OUTPUT 0x03
-#define SMP_IO_KEYBOARD_DISPLAY        0x04
-
-#define SMP_OOB_NOT_PRESENT    0x00
-#define SMP_OOB_PRESENT                0x01
-
-#define SMP_DIST_ENC_KEY       0x01
-#define SMP_DIST_ID_KEY                0x02
-#define SMP_DIST_SIGN          0x04
-
-#define SMP_AUTH_NONE          0x00
-#define SMP_AUTH_BONDING       0x01
-#define SMP_AUTH_MITM          0x04
-
-#define SMP_CMD_PAIRING_CONFIRM        0x03
-struct smp_cmd_pairing_confirm {
-       __u8    confirm_val[16];
-} __packed;
-
-#define SMP_CMD_PAIRING_RANDOM 0x04
-struct smp_cmd_pairing_random {
-       __u8    rand_val[16];
-} __packed;
-
-#define SMP_CMD_PAIRING_FAIL   0x05
-struct smp_cmd_pairing_fail {
-       __u8    reason;
-} __packed;
-
-#define SMP_CMD_ENCRYPT_INFO   0x06
-struct smp_cmd_encrypt_info {
-       __u8    ltk[16];
-} __packed;
-
-#define SMP_CMD_MASTER_IDENT   0x07
-struct smp_cmd_master_ident {
-       __le16  ediv;
-       __u8    rand[8];
-} __packed;
-
-#define SMP_CMD_IDENT_INFO     0x08
-struct smp_cmd_ident_info {
-       __u8    irk[16];
-} __packed;
-
-#define SMP_CMD_IDENT_ADDR_INFO        0x09
-struct smp_cmd_ident_addr_info {
-       __u8    addr_type;
-       bdaddr_t bdaddr;
-} __packed;
-
-#define SMP_CMD_SIGN_INFO      0x0a
-struct smp_cmd_sign_info {
-       __u8    csrk[16];
-} __packed;
-
-#define SMP_CMD_SECURITY_REQ   0x0b
-struct smp_cmd_security_req {
-       __u8    auth_req;
-} __packed;
-
-#define SMP_PASSKEY_ENTRY_FAILED       0x01
-#define SMP_OOB_NOT_AVAIL              0x02
-#define SMP_AUTH_REQUIREMENTS          0x03
-#define SMP_CONFIRM_FAILED             0x04
-#define SMP_PAIRING_NOTSUPP            0x05
-#define SMP_ENC_KEY_SIZE               0x06
-#define SMP_CMD_NOTSUPP                        0x07
-#define SMP_UNSPECIFIED                        0x08
-#define SMP_REPEATED_ATTEMPTS          0x09
-
-#define SMP_MIN_ENC_KEY_SIZE           7
-#define SMP_MAX_ENC_KEY_SIZE           16
-
-#define SMP_FLAG_TK_VALID      1
-#define SMP_FLAG_CFM_PENDING   2
-#define SMP_FLAG_MITM_AUTH     3
-
-struct smp_chan {
-       struct l2cap_conn *conn;
-       u8              preq[7]; /* SMP Pairing Request */
-       u8              prsp[7]; /* SMP Pairing Response */
-       u8              prnd[16]; /* SMP Pairing Random (local) */
-       u8              rrnd[16]; /* SMP Pairing Random (remote) */
-       u8              pcnf[16]; /* SMP Pairing Confirm */
-       u8              tk[16]; /* SMP Temporary Key */
-       u8              enc_key_size;
-       unsigned long   smp_flags;
-       struct crypto_blkcipher *tfm;
-       struct work_struct confirm;
-       struct work_struct random;
-
-};
-
-/* SMP Commands */
-int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
-int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
-int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
-
-void smp_chan_destroy(struct l2cap_conn *conn);
-
-#endif /* __SMP_H */
index 4795e81..097f69c 100644 (file)
@@ -195,6 +195,6 @@ enum ifla_caif_hsi {
        __IFLA_CAIF_HSI_MAX
 };
 
-extern struct cfhsi_ops *cfhsi_get_ops(void);
+struct cfhsi_ops *cfhsi_get_ops(void);
 
 #endif         /* CAIF_HSI_H_ */
index cb71091..3eae46c 100644 (file)
@@ -436,6 +436,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
                             const struct cfg80211_chan_def *chandef,
                             u32 prohibited_flags);
 
+/**
+ * cfg80211_chandef_dfs_required - checks if radar detection is required
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
+ */
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+                                 const struct cfg80211_chan_def *chandef);
+
 /**
  * ieee80211_chandef_rate_flags - returns rate flags for a channel
  *
@@ -735,6 +744,10 @@ enum station_parameters_apply_mask {
  * @capability: station capability
  * @ext_capab: extended capabilities of the station
  * @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
  */
 struct station_parameters {
        const u8 *supported_rates;
@@ -754,6 +767,10 @@ struct station_parameters {
        u16 capability;
        const u8 *ext_capab;
        u8 ext_capab_len;
+       const u8 *supported_channels;
+       u8 supported_channels_len;
+       const u8 *supported_oper_classes;
+       u8 supported_oper_classes_len;
 };
 
 /**
@@ -1647,6 +1664,9 @@ struct cfg80211_disassoc_request {
  *     sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
  *     required to assume that the port is unauthorized until authorized by
  *     user space. Otherwise, port is marked authorized by default.
+ * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
+ *     changes the channel when a radar is detected. This is required
+ *     to operate on DFS channels.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
@@ -1664,6 +1684,7 @@ struct cfg80211_ibss_params {
        bool channel_fixed;
        bool privacy;
        bool control_port;
+       bool userspace_handles_dfs;
        int mcast_rate[IEEE80211_NUM_BANDS];
        struct ieee80211_ht_cap ht_capa;
        struct ieee80211_ht_cap ht_capa_mask;
@@ -3044,6 +3065,7 @@ struct cfg80211_cached_keys;
  * @conn: (private) cfg80211 software SME connection state machine data
  * @connect_keys: (private) keys to set after connection is established
  * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
  */
@@ -3082,6 +3104,7 @@ struct wireless_dev {
        struct ieee80211_channel *channel;
 
        bool ibss_fixed;
+       bool ibss_dfs_possible;
 
        bool ps;
        int ps_timeout;
@@ -3474,6 +3497,15 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
 const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
                                               u32 center_freq);
 
+/**
+ * reg_initiator_name - map regulatory request initiator enum to name
+ * @initiator: the regulatory request initiator
+ *
+ * You can use this to map the regulatory request initiator enum to a
+ * proper string representation.
+ */
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator);
+
 /*
  * callbacks for asynchronous cfg80211 methods, notification
  * functions and BSS handling helpers
index 8f59ca5..37a0e24 100644 (file)
@@ -78,6 +78,12 @@ csum_block_add(__wsum csum, __wsum csum2, int offset)
        return csum_add(csum, (__force __wsum)sum);
 }
 
+static inline __wsum
+csum_block_add_ext(__wsum csum, __wsum csum2, int offset, int len)
+{
+       return csum_block_add(csum, csum2, offset);
+}
+
 static inline __wsum
 csum_block_sub(__wsum csum, __wsum csum2, int offset)
 {
@@ -92,6 +98,11 @@ static inline __wsum csum_unfold(__sum16 n)
        return (__force __wsum)n;
 }
 
+static inline __wsum csum_partial_ext(const void *buff, int len, __wsum sum)
+{
+       return csum_partial(buff, len, sum);
+}
+
 #define CSUM_MANGLED_0 ((__force __sum16)0xffff)
 
 static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to)
index a7a683e..a8c2ef6 100644 (file)
@@ -290,6 +290,7 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
        unsigned char err_offset = 0;
        u8 opt_len = opt[1];
        u8 opt_iter;
+       u8 tag_len;
 
        if (opt_len < 8) {
                err_offset = 1;
@@ -302,11 +303,12 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
        }
 
        for (opt_iter = 6; opt_iter < opt_len;) {
-               if (opt[opt_iter + 1] > (opt_len - opt_iter)) {
+               tag_len = opt[opt_iter + 1];
+               if ((tag_len == 0) || (opt[opt_iter + 1] > (opt_len - opt_iter))) {
                        err_offset = opt_iter + 1;
                        goto out;
                }
-               opt_iter += opt[opt_iter + 1];
+               opt_iter += tag_len;
        }
 
 out:
index 389cf62..3b04ff5 100644 (file)
@@ -72,10 +72,21 @@ static inline codel_time_t codel_get_time(void)
        return ns >> CODEL_SHIFT;
 }
 
-#define codel_time_after(a, b)         ((s32)(a) - (s32)(b) > 0)
-#define codel_time_after_eq(a, b)      ((s32)(a) - (s32)(b) >= 0)
-#define codel_time_before(a, b)                ((s32)(a) - (s32)(b) < 0)
-#define codel_time_before_eq(a, b)     ((s32)(a) - (s32)(b) <= 0)
+/* Dealing with timer wrapping, according to RFC 1982, as desc in wikipedia:
+ *  https://en.wikipedia.org/wiki/Serial_number_arithmetic#General_Solution
+ * codel_time_after(a,b) returns true if the time a is after time b.
+ */
+#define codel_time_after(a, b)                                         \
+       (typecheck(codel_time_t, a) &&                                  \
+        typecheck(codel_time_t, b) &&                                  \
+        ((s32)((a) - (b)) > 0))
+#define codel_time_before(a, b)        codel_time_after(b, a)
+
+#define codel_time_after_eq(a, b)                                      \
+       (typecheck(codel_time_t, a) &&                                  \
+        typecheck(codel_time_t, b) &&                                  \
+        ((s32)((a) - (b)) >= 0))
+#define codel_time_before_eq(a, b)     codel_time_after_eq(b, a)
 
 /* Qdiscs using codel plugin must use codel_skb_cb in their own cb[] */
 struct codel_skb_cb {
index 6e95653..3b603b1 100644 (file)
@@ -29,8 +29,8 @@ struct compat_cmsghdr {
        compat_int_t    cmsg_type;
 };
 
-extern int compat_sock_get_timestamp(struct sock *, struct timeval __user *);
-extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *);
+int compat_sock_get_timestamp(struct sock *, struct timeval __user *);
+int compat_sock_get_timestampns(struct sock *, struct timespec __user *);
 
 #else /* defined(CONFIG_COMPAT) */
 /*
@@ -40,24 +40,30 @@ extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *);
 #define compat_mmsghdr mmsghdr
 #endif /* defined(CONFIG_COMPAT) */
 
-extern int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *);
-extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr_storage *, int);
-extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned int);
-extern asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *,
-                                          unsigned int, unsigned int);
-extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned int);
-extern asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *,
-                                          unsigned int, unsigned int,
-                                          struct compat_timespec __user *);
-extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *);
-extern int put_cmsg_compat(struct msghdr*, int, int, int, void *);
-
-extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int);
-
-extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int,
-       int (*)(struct sock *, int, int, char __user *, unsigned int));
-extern int compat_mc_getsockopt(struct sock *, int, int, char __user *,
-       int __user *, int (*)(struct sock *, int, int, char __user *,
-                               int __user *));
+int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *);
+int verify_compat_iovec(struct msghdr *, struct iovec *,
+                       struct sockaddr_storage *, int);
+asmlinkage long compat_sys_sendmsg(int, struct compat_msghdr __user *,
+                                  unsigned int);
+asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *,
+                                   unsigned int, unsigned int);
+asmlinkage long compat_sys_recvmsg(int, struct compat_msghdr __user *,
+                                  unsigned int);
+asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *,
+                                   unsigned int, unsigned int,
+                                   struct compat_timespec __user *);
+asmlinkage long compat_sys_getsockopt(int, int, int, char __user *,
+                                     int __user *);
+int put_cmsg_compat(struct msghdr*, int, int, int, void *);
+
+int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *,
+                                    unsigned char *, int);
+
+int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int,
+                        int (*)(struct sock *, int, int, char __user *,
+                                unsigned int));
+int compat_mc_getsockopt(struct sock *, int, int, char __user *, int __user *,
+                        int (*)(struct sock *, int, int, char __user *,
+                                int __user *));
 
 #endif /* NET_COMPAT_H */
index 443626e..d2f3041 100644 (file)
@@ -25,9 +25,9 @@ enum dcbevent_notif_type {
 };
 
 #ifdef CONFIG_DCB
-extern int register_dcbevent_notifier(struct notifier_block *nb);
-extern int unregister_dcbevent_notifier(struct notifier_block *nb);
-extern int call_dcbevent_notifiers(unsigned long val, void *v);
+int register_dcbevent_notifier(struct notifier_block *nb);
+int unregister_dcbevent_notifier(struct notifier_block *nb);
+int call_dcbevent_notifiers(unsigned long val, void *v);
 #else
 static inline int
 register_dcbevent_notifier(struct notifier_block *nb)
index c88bf4e..ccc1558 100644 (file)
@@ -199,24 +199,26 @@ static inline void dn_sk_ports_copy(struct flowidn *fld, struct dn_scp *scp)
        fld->fld_dport = scp->addrrem;
 }
 
-extern unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu);
+unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu);
 
 #define DN_MENUVER_ACC 0x01
 #define DN_MENUVER_USR 0x02
 #define DN_MENUVER_PRX 0x04
 #define DN_MENUVER_UIC 0x08
 
-extern struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr);
-extern struct sock *dn_find_by_skb(struct sk_buff *skb);
+struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr);
+struct sock *dn_find_by_skb(struct sk_buff *skb);
 #define DN_ASCBUF_LEN 9
-extern char *dn_addr2asc(__u16, char *);
-extern int dn_destroy_timer(struct sock *sk);
+char *dn_addr2asc(__u16, char *);
+int dn_destroy_timer(struct sock *sk);
 
-extern int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf, unsigned char type);
-extern int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr, unsigned char *type);
+int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf,
+                        unsigned char type);
+int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr,
+                        unsigned char *type);
 
-extern void dn_start_slow_timer(struct sock *sk);
-extern void dn_stop_slow_timer(struct sock *sk);
+void dn_start_slow_timer(struct sock *sk);
+void dn_stop_slow_timer(struct sock *sk);
 
 extern __le16 decnet_address;
 extern int decnet_debug_level;
index b9e32db..20b5ab0 100644 (file)
@@ -148,27 +148,27 @@ struct rtnode_hello_message {
 } __packed;
 
 
-extern void dn_dev_init(void);
-extern void dn_dev_cleanup(void);
+void dn_dev_init(void);
+void dn_dev_cleanup(void);
 
-extern int dn_dev_ioctl(unsigned int cmd, void __user *arg);
+int dn_dev_ioctl(unsigned int cmd, void __user *arg);
 
-extern void dn_dev_devices_off(void);
-extern void dn_dev_devices_on(void);
+void dn_dev_devices_off(void);
+void dn_dev_devices_on(void);
 
-extern void dn_dev_init_pkt(struct sk_buff *skb);
-extern void dn_dev_veri_pkt(struct sk_buff *skb);
-extern void dn_dev_hello(struct sk_buff *skb);
+void dn_dev_init_pkt(struct sk_buff *skb);
+void dn_dev_veri_pkt(struct sk_buff *skb);
+void dn_dev_hello(struct sk_buff *skb);
 
-extern void dn_dev_up(struct net_device *);
-extern void dn_dev_down(struct net_device *);
+void dn_dev_up(struct net_device *);
+void dn_dev_down(struct net_device *);
 
-extern int dn_dev_set_default(struct net_device *dev, int force);
-extern struct net_device *dn_dev_get_default(void);
-extern int dn_dev_bind_default(__le16 *addr);
+int dn_dev_set_default(struct net_device *dev, int force);
+struct net_device *dn_dev_get_default(void);
+int dn_dev_bind_default(__le16 *addr);
 
-extern int register_dnaddr_notifier(struct notifier_block *nb);
-extern int unregister_dnaddr_notifier(struct notifier_block *nb);
+int register_dnaddr_notifier(struct notifier_block *nb);
+int unregister_dnaddr_notifier(struct notifier_block *nb);
 
 static inline int dn_dev_islocal(struct net_device *dev, __le16 addr)
 {
index 74004af..f2ca135 100644 (file)
@@ -95,41 +95,38 @@ struct dn_fib_table {
 /*
  * dn_fib.c
  */
-extern void dn_fib_init(void);
-extern void dn_fib_cleanup(void);
-
-extern int dn_fib_ioctl(struct socket *sock, unsigned int cmd, 
-                       unsigned long arg);
-extern struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, 
-                               struct nlattr *attrs[],
-                               const struct nlmsghdr *nlh, int *errp);
-extern int dn_fib_semantic_match(int type, struct dn_fib_info *fi, 
-                       const struct flowidn *fld,
-                       struct dn_fib_res *res);
-extern void dn_fib_release_info(struct dn_fib_info *fi);
-extern void dn_fib_flush(void);
-extern void dn_fib_select_multipath(const struct flowidn *fld,
-                                       struct dn_fib_res *res);
+void dn_fib_init(void);
+void dn_fib_cleanup(void);
+
+int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r,
+                                      struct nlattr *attrs[],
+                                      const struct nlmsghdr *nlh, int *errp);
+int dn_fib_semantic_match(int type, struct dn_fib_info *fi,
+                         const struct flowidn *fld, struct dn_fib_res *res);
+void dn_fib_release_info(struct dn_fib_info *fi);
+void dn_fib_flush(void);
+void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res);
 
 /*
  * dn_tables.c
  */
-extern struct dn_fib_table *dn_fib_get_table(u32 n, int creat);
-extern struct dn_fib_table *dn_fib_empty_table(void);
-extern void dn_fib_table_init(void);
-extern void dn_fib_table_cleanup(void);
+struct dn_fib_table *dn_fib_get_table(u32 n, int creat);
+struct dn_fib_table *dn_fib_empty_table(void);
+void dn_fib_table_init(void);
+void dn_fib_table_cleanup(void);
 
 /*
  * dn_rules.c
  */
-extern void dn_fib_rules_init(void);
-extern void dn_fib_rules_cleanup(void);
-extern unsigned int dnet_addr_type(__le16 addr);
-extern int dn_fib_lookup(struct flowidn *fld, struct dn_fib_res *res);
+void dn_fib_rules_init(void);
+void dn_fib_rules_cleanup(void);
+unsigned int dnet_addr_type(__le16 addr);
+int dn_fib_lookup(struct flowidn *fld, struct dn_fib_res *res);
 
-extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb);
+int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb);
 
-extern void dn_fib_free_info(struct dn_fib_info *fi);
+void dn_fib_free_info(struct dn_fib_info *fi);
 
 static inline void dn_fib_info_put(struct dn_fib_info *fi)
 {
index 4cb4ae7..fac4e3f 100644 (file)
@@ -16,12 +16,12 @@ struct dn_neigh {
        __u8 priority;
 };
 
-extern void dn_neigh_init(void);
-extern void dn_neigh_cleanup(void);
-extern int dn_neigh_router_hello(struct sk_buff *skb);
-extern int dn_neigh_endnode_hello(struct sk_buff *skb);
-extern void dn_neigh_pointopoint_hello(struct sk_buff *skb);
-extern int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n);
+void dn_neigh_init(void);
+void dn_neigh_cleanup(void);
+int dn_neigh_router_hello(struct sk_buff *skb);
+int dn_neigh_endnode_hello(struct sk_buff *skb);
+void dn_neigh_pointopoint_hello(struct sk_buff *skb);
+int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n);
 
 extern struct neigh_table dn_neigh_table;
 
index e43a289..3a3e33d 100644 (file)
 *******************************************************************************/
 /* dn_nsp.c functions prototyping */
 
-extern void dn_nsp_send_data_ack(struct sock *sk);
-extern void dn_nsp_send_oth_ack(struct sock *sk);
-extern void dn_nsp_delayed_ack(struct sock *sk);
-extern void dn_send_conn_ack(struct sock *sk);
-extern void dn_send_conn_conf(struct sock *sk, gfp_t gfp);
-extern void dn_nsp_send_disc(struct sock *sk, unsigned char type, 
-                       unsigned short reason, gfp_t gfp);
-extern void dn_nsp_return_disc(struct sk_buff *skb, unsigned char type,
-                               unsigned short reason);
-extern void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval);
-extern void dn_nsp_send_conninit(struct sock *sk, unsigned char flags);
-
-extern void dn_nsp_output(struct sock *sk);
-extern int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum);
-extern void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp, int oob);
-extern unsigned long dn_nsp_persist(struct sock *sk);
-extern int dn_nsp_xmit_timeout(struct sock *sk);
-
-extern int dn_nsp_rx(struct sk_buff *);
-extern int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
-
-extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
-extern struct sk_buff *dn_alloc_send_skb(struct sock *sk, size_t *size, int noblock, long timeo, int *err);
+void dn_nsp_send_data_ack(struct sock *sk);
+void dn_nsp_send_oth_ack(struct sock *sk);
+void dn_nsp_delayed_ack(struct sock *sk);
+void dn_send_conn_ack(struct sock *sk);
+void dn_send_conn_conf(struct sock *sk, gfp_t gfp);
+void dn_nsp_send_disc(struct sock *sk, unsigned char type,
+                     unsigned short reason, gfp_t gfp);
+void dn_nsp_return_disc(struct sk_buff *skb, unsigned char type,
+                       unsigned short reason);
+void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval);
+void dn_nsp_send_conninit(struct sock *sk, unsigned char flags);
+
+void dn_nsp_output(struct sock *sk);
+int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb,
+                           struct sk_buff_head *q, unsigned short acknum);
+void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp,
+                      int oob);
+unsigned long dn_nsp_persist(struct sock *sk);
+int dn_nsp_xmit_timeout(struct sock *sk);
+
+int dn_nsp_rx(struct sk_buff *);
+int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+
+struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
+struct sk_buff *dn_alloc_send_skb(struct sock *sk, size_t *size, int noblock,
+                                 long timeo, int *err);
 
 #define NSP_REASON_OK 0                /* No error */
 #define NSP_REASON_NR 1                /* No resources */
index 2e9d317..b409ad6 100644 (file)
     GNU General Public License for more details.
 *******************************************************************************/
 
-extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
-extern int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *, struct sock *sk, int flags);
-extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
-extern void dn_rt_cache_flush(int delay);
+struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
+int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *,
+                        struct sock *sk, int flags);
+int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
+void dn_rt_cache_flush(int delay);
 
 /* Masks for flags field */
 #define DN_RT_F_PID 0x07 /* Mask for packet type                      */
@@ -92,8 +93,8 @@ static inline bool dn_is_output_route(struct dn_route *rt)
        return rt->fld.flowidn_iif == 0;
 }
 
-extern void dn_route_init(void);
-extern void dn_route_cleanup(void);
+void dn_route_init(void);
+void dn_route_cleanup(void);
 
 #include <net/sock.h>
 #include <linux/if_arp.h>
index 3bc4865..44995c1 100644 (file)
@@ -106,7 +106,7 @@ struct dst_entry {
        };
 };
 
-extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
+u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
 extern const u32 dst_default_metrics[];
 
 #define DST_METRICS_READ_ONLY  0x1UL
@@ -119,7 +119,7 @@ static inline bool dst_metrics_read_only(const struct dst_entry *dst)
        return dst->_metrics & DST_METRICS_READ_ONLY;
 }
 
-extern void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old);
+void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old);
 
 static inline void dst_destroy_metrics_generic(struct dst_entry *dst)
 {
@@ -262,7 +262,7 @@ static inline struct dst_entry *dst_clone(struct dst_entry *dst)
        return dst;
 }
 
-extern void dst_release(struct dst_entry *dst);
+void dst_release(struct dst_entry *dst);
 
 static inline void refdst_drop(unsigned long refdst)
 {
@@ -362,12 +362,11 @@ static inline struct dst_entry *skb_dst_pop(struct sk_buff *skb)
        return child;
 }
 
-extern int dst_discard(struct sk_buff *skb);
-extern void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
-                      int initial_ref, int initial_obsolete,
-                      unsigned short flags);
-extern void __dst_free(struct dst_entry *dst);
-extern struct dst_entry *dst_destroy(struct dst_entry *dst);
+int dst_discard(struct sk_buff *skb);
+void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref,
+               int initial_obsolete, unsigned short flags);
+void __dst_free(struct dst_entry *dst);
+struct dst_entry *dst_destroy(struct dst_entry *dst);
 
 static inline void dst_free(struct dst_entry *dst)
 {
@@ -463,7 +462,7 @@ static inline struct dst_entry *dst_check(struct dst_entry *dst, u32 cookie)
        return dst;
 }
 
-extern void            dst_init(void);
+void dst_init(void);
 
 /* Flags for xfrm_lookup flags argument. */
 enum {
@@ -479,10 +478,22 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
 {
        return dst_orig;
 } 
+
+static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
+{
+       return NULL;
+}
+
 #else
-extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
-                                    const struct flowi *fl, struct sock *sk,
-                                    int flags);
+struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
+                             const struct flowi *fl, struct sock *sk,
+                             int flags);
+
+/* skb attached with this dst needs transformation if dst->xfrm is valid */
+static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst)
+{
+       return dst->xfrm;
+}
 #endif
 
 #endif /* _NET_DST_H */
index d584513..a43be85 100644 (file)
@@ -3,18 +3,6 @@
 
 #include <linux/skbuff.h>
 
-struct crypto_aead;
-
-struct esp_data {
-       /* 0..255 */
-       int padlen;
-
-       /* Confidentiality & Integrity */
-       struct crypto_aead *aead;
-};
-
-extern void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len);
-
 struct ip_esp_hdr;
 
 static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb)
index 4b2b557..e584de1 100644 (file)
@@ -115,14 +115,13 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla)
        return frh->table;
 }
 
-extern struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, struct net *);
-extern void fib_rules_unregister(struct fib_rules_ops *);
+struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *,
+                                        struct net *);
+void fib_rules_unregister(struct fib_rules_ops *);
 
-extern int                     fib_rules_lookup(struct fib_rules_ops *,
-                                                struct flowi *, int flags,
-                                                struct fib_lookup_arg *);
-extern int                     fib_default_rule_add(struct fib_rules_ops *,
-                                                    u32 pref, u32 table,
-                                                    u32 flags);
-extern u32                     fib_default_rule_pref(struct fib_rules_ops *ops);
+int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags,
+                    struct fib_lookup_arg *);
+int fib_default_rule_add(struct fib_rules_ops *, u32 pref, u32 table,
+                        u32 flags);
+u32 fib_default_rule_pref(struct fib_rules_ops *ops);
 #endif
index 628e11b..65ce471 100644 (file)
@@ -215,12 +215,13 @@ typedef struct flow_cache_object *(*flow_resolve_t)(
                struct net *net, const struct flowi *key, u16 family,
                u8 dir, struct flow_cache_object *oldobj, void *ctx);
 
-extern struct flow_cache_object *flow_cache_lookup(
-               struct net *net, const struct flowi *key, u16 family,
-               u8 dir, flow_resolve_t resolver, void *ctx);
+struct flow_cache_object *flow_cache_lookup(struct net *net,
+                                           const struct flowi *key, u16 family,
+                                           u8 dir, flow_resolve_t resolver,
+                                           void *ctx);
 
-extern void flow_cache_flush(void);
-extern void flow_cache_flush_deferred(void);
+void flow_cache_flush(void);
+void flow_cache_flush_deferred(void);
 extern atomic_t flow_cache_genid;
 
 #endif
index bb8271d..7e64bd8 100644 (file)
@@ -13,5 +13,6 @@ struct flow_keys {
        u8 ip_proto;
 };
 
-extern bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow);
+bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow);
+__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto);
 #endif
index 834d8ad..abf33bb 100644 (file)
@@ -112,19 +112,18 @@ struct garp_port {
        struct rcu_head                 rcu;
 };
 
-extern int     garp_register_application(struct garp_application *app);
-extern void    garp_unregister_application(struct garp_application *app);
-
-extern int     garp_init_applicant(struct net_device *dev,
-                                   struct garp_application *app);
-extern void    garp_uninit_applicant(struct net_device *dev,
-                                     struct garp_application *app);
-
-extern int     garp_request_join(const struct net_device *dev,
-                                 const struct garp_application *app,
-                                 const void *data, u8 len, u8 type);
-extern void    garp_request_leave(const struct net_device *dev,
-                                  const struct garp_application *app,
-                                  const void *data, u8 len, u8 type);
+int garp_register_application(struct garp_application *app);
+void garp_unregister_application(struct garp_application *app);
+
+int garp_init_applicant(struct net_device *dev, struct garp_application *app);
+void garp_uninit_applicant(struct net_device *dev,
+                          struct garp_application *app);
+
+int garp_request_join(const struct net_device *dev,
+                     const struct garp_application *app, const void *data,
+                     u8 len, u8 type);
+void garp_request_leave(const struct net_device *dev,
+                       const struct garp_application *app,
+                       const void *data, u8 len, u8 type);
 
 #endif /* _NET_GARP_H */
index cf8439b..ea4271d 100644 (file)
@@ -19,32 +19,31 @@ struct gnet_dump {
        struct tc_stats   tc_stats;
 };
 
-extern int gnet_stats_start_copy(struct sk_buff *skb, int type,
+int gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock,
+                         struct gnet_dump *d);
+
+int gnet_stats_start_copy_compat(struct sk_buff *skb, int type,
+                                int tc_stats_type, int xstats_type,
                                 spinlock_t *lock, struct gnet_dump *d);
 
-extern int gnet_stats_start_copy_compat(struct sk_buff *skb, int type,
-                                       int tc_stats_type,int xstats_type,
-                                       spinlock_t *lock, struct gnet_dump *d);
-
-extern int gnet_stats_copy_basic(struct gnet_dump *d,
-                                struct gnet_stats_basic_packed *b);
-extern int gnet_stats_copy_rate_est(struct gnet_dump *d,
-                                   const struct gnet_stats_basic_packed *b,
-                                   struct gnet_stats_rate_est64 *r);
-extern int gnet_stats_copy_queue(struct gnet_dump *d,
-                                struct gnet_stats_queue *q);
-extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
-
-extern int gnet_stats_finish_copy(struct gnet_dump *d);
-
-extern int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
-                            struct gnet_stats_rate_est64 *rate_est,
-                            spinlock_t *stats_lock, struct nlattr *opt);
-extern void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
-                              struct gnet_stats_rate_est64 *rate_est);
-extern int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
-                                struct gnet_stats_rate_est64 *rate_est,
-                                spinlock_t *stats_lock, struct nlattr *opt);
-extern bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
-                                const struct gnet_stats_rate_est64 *rate_est);
+int gnet_stats_copy_basic(struct gnet_dump *d,
+                         struct gnet_stats_basic_packed *b);
+int gnet_stats_copy_rate_est(struct gnet_dump *d,
+                            const struct gnet_stats_basic_packed *b,
+                            struct gnet_stats_rate_est64 *r);
+int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q);
+int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
+
+int gnet_stats_finish_copy(struct gnet_dump *d);
+
+int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
+                     struct gnet_stats_rate_est64 *rate_est,
+                     spinlock_t *stats_lock, struct nlattr *opt);
+void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
+                       struct gnet_stats_rate_est64 *rate_est);
+int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
+                         struct gnet_stats_rate_est64 *rate_est,
+                         spinlock_t *stats_lock, struct nlattr *opt);
+bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
+                         const struct gnet_stats_rate_est64 *rate_est);
 #endif
index 8e0b6c8..9b787b6 100644 (file)
@@ -122,7 +122,7 @@ struct genl_ops {
        struct list_head        ops_list;
 };
 
-extern int __genl_register_family(struct genl_family *family);
+int __genl_register_family(struct genl_family *family);
 
 static inline int genl_register_family(struct genl_family *family)
 {
@@ -130,8 +130,8 @@ static inline int genl_register_family(struct genl_family *family)
        return __genl_register_family(family);
 }
 
-extern int __genl_register_family_with_ops(struct genl_family *family,
-       struct genl_ops *ops, size_t n_ops);
+int __genl_register_family_with_ops(struct genl_family *family,
+                                   struct genl_ops *ops, size_t n_ops);
 
 static inline int genl_register_family_with_ops(struct genl_family *family,
        struct genl_ops *ops, size_t n_ops)
@@ -140,18 +140,18 @@ static inline int genl_register_family_with_ops(struct genl_family *family,
        return __genl_register_family_with_ops(family, ops, n_ops);
 }
 
-extern int genl_unregister_family(struct genl_family *family);
-extern int genl_register_ops(struct genl_family *, struct genl_ops *ops);
-extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
-extern int genl_register_mc_group(struct genl_family *family,
-                                 struct genl_multicast_group *grp);
-extern void genl_unregister_mc_group(struct genl_family *family,
-                                    struct genl_multicast_group *grp);
-extern void genl_notify(struct sk_buff *skb, struct net *net, u32 portid,
-                       u32 group, struct nlmsghdr *nlh, gfp_t flags);
+int genl_unregister_family(struct genl_family *family);
+int genl_register_ops(struct genl_family *, struct genl_ops *ops);
+int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
+int genl_register_mc_group(struct genl_family *family,
+                          struct genl_multicast_group *grp);
+void genl_unregister_mc_group(struct genl_family *family,
+                             struct genl_multicast_group *grp);
+void genl_notify(struct sk_buff *skb, struct net *net, u32 portid,
+                u32 group, struct nlmsghdr *nlh, gfp_t flags);
 
 void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
-                               struct genl_family *family, int flags, u8 cmd);
+                 struct genl_family *family, int flags, u8 cmd);
 
 /**
  * genlmsg_nlhdr - Obtain netlink header from user specified header
index 57e4afd..dcd9ae3 100644 (file)
@@ -38,7 +38,13 @@ void gre_offload_exit(void);
 
 void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
                      int hdr_len);
-struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum);
+
+static inline struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
+                                                 bool gre_csum)
+{
+       return iptunnel_handle_offloads(skb, gre_csum, SKB_GSO_GRE);
+}
+
 
 static inline int ip_gre_calc_hlen(__be16 o_flags)
 {
index 081439f..970028e 100644 (file)
@@ -39,10 +39,10 @@ struct net_proto_family;
 struct sk_buff;
 struct net;
 
-extern void    icmp_send(struct sk_buff *skb_in,  int type, int code, __be32 info);
-extern int     icmp_rcv(struct sk_buff *skb);
-extern void    icmp_err(struct sk_buff *, u32 info);
-extern int     icmp_init(void);
-extern void    icmp_out_count(struct net *net, unsigned char type);
+void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info);
+int icmp_rcv(struct sk_buff *skb);
+void icmp_err(struct sk_buff *skb, u32 info);
+int icmp_init(void);
+void icmp_out_count(struct net *net, unsigned char type);
 
 #endif /* _ICMP_H */
index 02ef772..76d5427 100644 (file)
@@ -66,11 +66,10 @@ struct inet6_ifaddr {
        struct hlist_node       addr_lst;
        struct list_head        if_list;
 
-#ifdef CONFIG_IPV6_PRIVACY
        struct list_head        tmp_list;
        struct inet6_ifaddr     *ifpub;
        int                     regen_count;
-#endif
+
        bool                    tokenized;
 
        struct rcu_head         rcu;
@@ -192,11 +191,9 @@ struct inet6_dev {
        __u32                   if_flags;
        int                     dead;
 
-#ifdef CONFIG_IPV6_PRIVACY
        u8                      rndid[8];
        struct timer_list       regen_timer;
        struct list_head        tempaddr_list;
-#endif
 
        struct in6_addr         token;
 
index 04642c9..f981ba7 100644 (file)
@@ -22,27 +22,25 @@ struct sk_buff;
 struct sock;
 struct sockaddr;
 
-extern int inet6_csk_bind_conflict(const struct sock *sk,
-                                  const struct inet_bind_bucket *tb, bool relax);
+int inet6_csk_bind_conflict(const struct sock *sk,
+                           const struct inet_bind_bucket *tb, bool relax);
 
-extern struct dst_entry* inet6_csk_route_req(struct sock *sk,
-                                            struct flowi6 *fl6,
-                                            const struct request_sock *req);
+struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6,
+                                     const struct request_sock *req);
 
-extern struct request_sock *inet6_csk_search_req(const struct sock *sk,
-                                                struct request_sock ***prevp,
-                                                const __be16 rport,
-                                                const struct in6_addr *raddr,
-                                                const struct in6_addr *laddr,
-                                                const int iif);
+struct request_sock *inet6_csk_search_req(const struct sock *sk,
+                                         struct request_sock ***prevp,
+                                         const __be16 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);
+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);
+void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
 
-extern int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl);
+int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl);
 
-extern struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu);
+struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu);
 #endif /* _INET6_CONNECTION_SOCK_H */
index fd4ee01..ae06135 100644 (file)
 
 struct inet_hashinfo;
 
-static inline unsigned int inet6_ehashfn(struct net *net,
-                               const struct in6_addr *laddr, const u16 lport,
-                               const struct in6_addr *faddr, const __be16 fport)
+static inline unsigned int __inet6_ehashfn(const u32 lhash,
+                                   const u16 lport,
+                                   const u32 fhash,
+                                   const __be16 fport,
+                                   const u32 initval)
 {
-       u32 ports = (((u32)lport) << 16) | (__force u32)fport;
-
-       return jhash_3words((__force u32)laddr->s6_addr32[3],
-                           ipv6_addr_jhash(faddr),
-                           ports,
-                           inet_ehash_secret + net_hash_mix(net));
-}
-
-static inline int inet6_sk_ehashfn(const struct sock *sk)
-{
-       const struct inet_sock *inet = inet_sk(sk);
-       const struct ipv6_pinfo *np = inet6_sk(sk);
-       const struct in6_addr *laddr = &np->rcv_saddr;
-       const struct in6_addr *faddr = &np->daddr;
-       const __u16 lport = inet->inet_num;
-       const __be16 fport = inet->inet_dport;
-       struct net *net = sock_net(sk);
-
-       return inet6_ehashfn(net, laddr, lport, faddr, fport);
+       const u32 ports = (((u32)lport) << 16) | (__force u32)fport;
+       return jhash_3words(lhash, fhash, ports, initval);
 }
 
-extern int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp);
+int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp);
 
 /*
  * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
@@ -61,21 +46,19 @@ extern int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp);
  *
  * The sockhash lock must be held as a reader here.
  */
-extern struct sock *__inet6_lookup_established(struct net *net,
-                                          struct inet_hashinfo *hashinfo,
-                                          const struct in6_addr *saddr,
-                                          const __be16 sport,
-                                          const struct in6_addr *daddr,
-                                          const u16 hnum,
-                                          const int dif);
-
-extern struct sock *inet6_lookup_listener(struct net *net,
-                                         struct inet_hashinfo *hashinfo,
-                                         const struct in6_addr *saddr,
-                                         const __be16 sport,
-                                         const struct in6_addr *daddr,
-                                         const unsigned short hnum,
-                                         const int dif);
+struct sock *__inet6_lookup_established(struct net *net,
+                                       struct inet_hashinfo *hashinfo,
+                                       const struct in6_addr *saddr,
+                                       const __be16 sport,
+                                       const struct in6_addr *daddr,
+                                       const u16 hnum, const int dif);
+
+struct sock *inet6_lookup_listener(struct net *net,
+                                  struct inet_hashinfo *hashinfo,
+                                  const struct in6_addr *saddr,
+                                  const __be16 sport,
+                                  const struct in6_addr *daddr,
+                                  const unsigned short hnum, const int dif);
 
 static inline struct sock *__inet6_lookup(struct net *net,
                                          struct inet_hashinfo *hashinfo,
@@ -110,9 +93,9 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo,
                              inet6_iif(skb));
 }
 
-extern struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
-                                const struct in6_addr *saddr, const __be16 sport,
-                                const struct in6_addr *daddr, const __be16 dport,
-                                const int dif);
+struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
+                         const struct in6_addr *saddr, const __be16 sport,
+                         const struct in6_addr *daddr, const __be16 dport,
+                         const int dif);
 #endif /* IS_ENABLED(CONFIG_IPV6) */
 #endif /* _INET6_HASHTABLES_H */
index 2340087..fe7994c 100644 (file)
@@ -13,30 +13,30 @@ struct sock;
 struct sockaddr;
 struct socket;
 
-extern int inet_release(struct socket *sock);
-extern int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
-                              int addr_len, int flags);
-extern int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
-                                int addr_len, int flags);
-extern int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr,
-                             int addr_len, int flags);
-extern int inet_accept(struct socket *sock, struct socket *newsock, int flags);
-extern int inet_sendmsg(struct kiocb *iocb, struct socket *sock,
-                       struct msghdr *msg, size_t size);
-extern ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
-                            size_t size, int flags);
-extern int inet_recvmsg(struct kiocb *iocb, struct socket *sock,
-                       struct msghdr *msg, size_t size, int flags);
-extern int inet_shutdown(struct socket *sock, int how);
-extern int inet_listen(struct socket *sock, int backlog);
-extern void inet_sock_destruct(struct sock *sk);
-extern int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
-extern int inet_getname(struct socket *sock, struct sockaddr *uaddr,
-                       int *uaddr_len, int peer);
-extern int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
-extern int inet_ctl_sock_create(struct sock **sk, unsigned short family,
-                               unsigned short type, unsigned char protocol,
-                               struct net *net);
+int inet_release(struct socket *sock);
+int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
+                       int addr_len, int flags);
+int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
+                         int addr_len, int flags);
+int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr,
+                      int addr_len, int flags);
+int inet_accept(struct socket *sock, struct socket *newsock, int flags);
+int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
+                size_t size);
+ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
+                     size_t size, int flags);
+int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
+                size_t size, int flags);
+int inet_shutdown(struct socket *sock, int how);
+int inet_listen(struct socket *sock, int backlog);
+void inet_sock_destruct(struct sock *sk);
+int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
+int inet_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len,
+                int peer);
+int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+int inet_ctl_sock_create(struct sock **sk, unsigned short family,
+                        unsigned short type, unsigned char protocol,
+                        struct net *net);
 
 static inline void inet_ctl_sock_destroy(struct sock *sk)
 {
index de2c785..c55aeed 100644 (file)
@@ -146,9 +146,9 @@ static inline void *inet_csk_ca(const struct sock *sk)
        return (void *)inet_csk(sk)->icsk_ca_priv;
 }
 
-extern struct sock *inet_csk_clone_lock(const struct sock *sk,
-                                       const struct request_sock *req,
-                                       const gfp_t priority);
+struct sock *inet_csk_clone_lock(const struct sock *sk,
+                                const struct request_sock *req,
+                                const gfp_t priority);
 
 enum inet_csk_ack_state_t {
        ICSK_ACK_SCHED  = 1,
@@ -157,11 +157,11 @@ enum inet_csk_ack_state_t {
        ICSK_ACK_PUSHED2 = 8
 };
 
-extern void inet_csk_init_xmit_timers(struct sock *sk,
-                                     void (*retransmit_handler)(unsigned long),
-                                     void (*delack_handler)(unsigned long),
-                                     void (*keepalive_handler)(unsigned long));
-extern void inet_csk_clear_xmit_timers(struct sock *sk);
+void inet_csk_init_xmit_timers(struct sock *sk,
+                              void (*retransmit_handler)(unsigned long),
+                              void (*delack_handler)(unsigned long),
+                              void (*keepalive_handler)(unsigned long));
+void inet_csk_clear_xmit_timers(struct sock *sk);
 
 static inline void inet_csk_schedule_ack(struct sock *sk)
 {
@@ -178,8 +178,8 @@ static inline void inet_csk_delack_init(struct sock *sk)
        memset(&inet_csk(sk)->icsk_ack, 0, sizeof(inet_csk(sk)->icsk_ack));
 }
 
-extern void inet_csk_delete_keepalive_timer(struct sock *sk);
-extern void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long timeout);
+void inet_csk_delete_keepalive_timer(struct sock *sk);
+void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long timeout);
 
 #ifdef INET_CSK_DEBUG
 extern const char inet_csk_timer_bug_msg[];
@@ -241,23 +241,21 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what,
 #endif
 }
 
-extern struct sock *inet_csk_accept(struct sock *sk, int flags, int *err);
+struct sock *inet_csk_accept(struct sock *sk, int flags, int *err);
 
-extern struct request_sock *inet_csk_search_req(const struct sock *sk,
-                                               struct request_sock ***prevp,
-                                               const __be16 rport,
-                                               const __be32 raddr,
-                                               const __be32 laddr);
-extern int inet_csk_bind_conflict(const struct sock *sk,
-                                 const struct inet_bind_bucket *tb, bool relax);
-extern int inet_csk_get_port(struct sock *sk, unsigned short snum);
+struct request_sock *inet_csk_search_req(const struct sock *sk,
+                                        struct request_sock ***prevp,
+                                        const __be16 rport,
+                                        const __be32 raddr,
+                                        const __be32 laddr);
+int inet_csk_bind_conflict(const struct sock *sk,
+                          const struct inet_bind_bucket *tb, bool relax);
+int inet_csk_get_port(struct sock *sk, unsigned short snum);
 
-extern struct dst_entry* inet_csk_route_req(struct sock *sk,
-                                           struct flowi4 *fl4,
+struct dst_entry *inet_csk_route_req(struct sock *sk, struct flowi4 *fl4,
+                                    const struct request_sock *req);
+struct dst_entry *inet_csk_route_child_sock(struct sock *sk, struct sock *newsk,
                                            const struct request_sock *req);
-extern struct dst_entry* inet_csk_route_child_sock(struct sock *sk,
-                                                  struct sock *newsk,
-                                                  const struct request_sock *req);
 
 static inline void inet_csk_reqsk_queue_add(struct sock *sk,
                                            struct request_sock *req,
@@ -266,9 +264,8 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk,
        reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child);
 }
 
-extern void inet_csk_reqsk_queue_hash_add(struct sock *sk,
-                                         struct request_sock *req,
-                                         unsigned long timeout);
+void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
+                                  unsigned long timeout);
 
 static inline void inet_csk_reqsk_queue_removed(struct sock *sk,
                                                struct request_sock *req)
@@ -315,13 +312,13 @@ static inline void inet_csk_reqsk_queue_drop(struct sock *sk,
        reqsk_free(req);
 }
 
-extern void inet_csk_reqsk_queue_prune(struct sock *parent,
-                                      const unsigned long interval,
-                                      const unsigned long timeout,
-                                      const unsigned long max_rto);
+void inet_csk_reqsk_queue_prune(struct sock *parent,
+                               const unsigned long interval,
+                               const unsigned long timeout,
+                               const unsigned long max_rto);
 
-extern void inet_csk_destroy_sock(struct sock *sk);
-extern void inet_csk_prepare_forced_close(struct sock *sk);
+void inet_csk_destroy_sock(struct sock *sk);
+void inet_csk_prepare_forced_close(struct sock *sk);
 
 /*
  * LISTEN is a special case for poll..
@@ -332,15 +329,15 @@ static inline unsigned int inet_csk_listen_poll(const struct sock *sk)
                        (POLLIN | POLLRDNORM) : 0;
 }
 
-extern int  inet_csk_listen_start(struct sock *sk, const int nr_table_entries);
-extern void inet_csk_listen_stop(struct sock *sk);
+int inet_csk_listen_start(struct sock *sk, const int nr_table_entries);
+void inet_csk_listen_stop(struct sock *sk);
 
-extern void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
+void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
 
-extern int inet_csk_compat_getsockopt(struct sock *sk, int level, int optname,
-                                     char __user *optval, int __user *optlen);
-extern int inet_csk_compat_setsockopt(struct sock *sk, int level, int optname,
-                                     char __user *optval, unsigned int optlen);
+int inet_csk_compat_getsockopt(struct sock *sk, int level, int optname,
+                              char __user *optval, int __user *optlen);
+int inet_csk_compat_setsockopt(struct sock *sk, int level, int optname,
+                              char __user *optval, unsigned int optlen);
 
-extern struct dst_entry *inet_csk_update_pmtu(struct sock *sk, u32 mtu);
+struct dst_entry *inet_csk_update_pmtu(struct sock *sk, u32 mtu);
 #endif /* _INET_CONNECTION_SOCK_H */
index bfcbc00..6f59de9 100644 (file)
@@ -64,6 +64,10 @@ struct inet_frags {
        rwlock_t                lock ____cacheline_aligned_in_smp;
        int                     secret_interval;
        struct timer_list       secret_timer;
+
+       /* The first call to hashfn is responsible to initialize
+        * rnd. This is best done with net_get_random_once.
+        */
        u32                     rnd;
        int                     qsize;
 
index ef83d9e..1bdb477 100644 (file)
 #include <asm/byteorder.h>
 
 /* This is for all connections with a full identity, no wildcards.
- * One chain is dedicated to TIME_WAIT sockets.
- * I'll experiment with dynamic table growth later.
+ * The 'e' prefix stands for Establish, but we really put all sockets
+ * but LISTEN ones.
  */
 struct inet_ehash_bucket {
        struct hlist_nulls_head chain;
-       struct hlist_nulls_head twchain;
 };
 
 /* There are a few simple rules, which allow for local port reuse by
@@ -123,7 +122,6 @@ struct inet_hashinfo {
         *
         *          TCP_ESTABLISHED <= sk->sk_state < TCP_CLOSE
         *
-        * TIME_WAIT sockets use a separate chain (twchain).
         */
        struct inet_ehash_bucket        *ehash;
        spinlock_t                      *ehash_locks;
@@ -218,22 +216,21 @@ static inline void inet_ehash_locks_free(struct inet_hashinfo *hashinfo)
        }
 }
 
-extern struct inet_bind_bucket *
-                   inet_bind_bucket_create(struct kmem_cache *cachep,
-                                           struct net *net,
-                                           struct inet_bind_hashbucket *head,
-                                           const unsigned short snum);
-extern void inet_bind_bucket_destroy(struct kmem_cache *cachep,
-                                    struct inet_bind_bucket *tb);
+struct inet_bind_bucket *
+inet_bind_bucket_create(struct kmem_cache *cachep, struct net *net,
+                       struct inet_bind_hashbucket *head,
+                       const unsigned short snum);
+void inet_bind_bucket_destroy(struct kmem_cache *cachep,
+                             struct inet_bind_bucket *tb);
 
-static inline int inet_bhashfn(struct net *net,
-               const __u16 lport, const int bhash_size)
+static inline int inet_bhashfn(struct net *net, const __u16 lport,
+                              const int bhash_size)
 {
        return (lport + net_hash_mix(net)) & (bhash_size - 1);
 }
 
-extern void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb,
-                          const unsigned short snum);
+void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb,
+                   const unsigned short snum);
 
 /* These can have wildcards, don't try too hard. */
 static inline int inet_lhashfn(struct net *net, const unsigned short num)
@@ -247,23 +244,22 @@ static inline int inet_sk_listen_hashfn(const struct sock *sk)
 }
 
 /* Caller must disable local BH processing. */
-extern int __inet_inherit_port(struct sock *sk, struct sock *child);
+int __inet_inherit_port(struct sock *sk, struct sock *child);
 
-extern void inet_put_port(struct sock *sk);
+void inet_put_port(struct sock *sk);
 
 void inet_hashinfo_init(struct inet_hashinfo *h);
 
-extern int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw);
-extern void inet_hash(struct sock *sk);
-extern void inet_unhash(struct sock *sk);
+int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw);
+void inet_hash(struct sock *sk);
+void inet_unhash(struct sock *sk);
 
-extern struct sock *__inet_lookup_listener(struct net *net,
-                                          struct inet_hashinfo *hashinfo,
-                                          const __be32 saddr,
-                                          const __be16 sport,
-                                          const __be32 daddr,
-                                          const unsigned short hnum,
-                                          const int dif);
+struct sock *__inet_lookup_listener(struct net *net,
+                                   struct inet_hashinfo *hashinfo,
+                                   const __be32 saddr, const __be16 sport,
+                                   const __be32 daddr,
+                                   const unsigned short hnum,
+                                   const int dif);
 
 static inline struct sock *inet_lookup_listener(struct net *net,
                struct inet_hashinfo *hashinfo,
@@ -304,30 +300,17 @@ static inline struct sock *inet_lookup_listener(struct net *net,
                                   ((__force __u64)(__be32)(__saddr)));
 #endif /* __BIG_ENDIAN */
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif)    \
-       ((inet_sk(__sk)->inet_portpair == (__ports))            &&      \
-        (inet_sk(__sk)->inet_addrpair == (__cookie))           &&      \
+       (((__sk)->sk_portpair == (__ports))                     &&      \
+        ((__sk)->sk_addrpair == (__cookie))                    &&      \
         (!(__sk)->sk_bound_dev_if      ||                              \
           ((__sk)->sk_bound_dev_if == (__dif)))                &&      \
         net_eq(sock_net(__sk), (__net)))
-#define INET_TW_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif)\
-       ((inet_twsk(__sk)->tw_portpair == (__ports))    &&              \
-        (inet_twsk(__sk)->tw_addrpair == (__cookie))   &&              \
-        (!(__sk)->sk_bound_dev_if      ||                              \
-          ((__sk)->sk_bound_dev_if == (__dif)))        &&              \
-        net_eq(sock_net(__sk), (__net)))
 #else /* 32-bit arch */
 #define INET_ADDR_COOKIE(__name, __saddr, __daddr)
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \
-       ((inet_sk(__sk)->inet_portpair == (__ports))    &&              \
-        (inet_sk(__sk)->inet_daddr     == (__saddr))   &&              \
-        (inet_sk(__sk)->inet_rcv_saddr == (__daddr))   &&              \
-        (!(__sk)->sk_bound_dev_if      ||                              \
-          ((__sk)->sk_bound_dev_if == (__dif)))        &&              \
-        net_eq(sock_net(__sk), (__net)))
-#define INET_TW_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \
-       ((inet_twsk(__sk)->tw_portpair == (__ports))    &&              \
-        (inet_twsk(__sk)->tw_daddr     == (__saddr))   &&              \
-        (inet_twsk(__sk)->tw_rcv_saddr == (__daddr))   &&              \
+       (((__sk)->sk_portpair == (__ports))             &&              \
+        ((__sk)->sk_daddr      == (__saddr))           &&              \
+        ((__sk)->sk_rcv_saddr  == (__daddr))           &&              \
         (!(__sk)->sk_bound_dev_if      ||                              \
           ((__sk)->sk_bound_dev_if == (__dif)))        &&              \
         net_eq(sock_net(__sk), (__net)))
@@ -339,10 +322,11 @@ static inline struct sock *inet_lookup_listener(struct net *net,
  *
  * Local BH must be disabled here.
  */
-extern struct sock * __inet_lookup_established(struct net *net,
-               struct inet_hashinfo *hashinfo,
-               const __be32 saddr, const __be16 sport,
-               const __be32 daddr, const u16 hnum, const int dif);
+struct sock *__inet_lookup_established(struct net *net,
+                                      struct inet_hashinfo *hashinfo,
+                                      const __be32 saddr, const __be16 sport,
+                                      const __be32 daddr, const u16 hnum,
+                                      const int dif);
 
 static inline struct sock *
        inet_lookup_established(struct net *net, struct inet_hashinfo *hashinfo,
@@ -399,13 +383,14 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,
                                     iph->daddr, dport, inet_iif(skb));
 }
 
-extern int __inet_hash_connect(struct inet_timewait_death_row *death_row,
-               struct sock *sk,
-               u32 port_offset,
-               int (*check_established)(struct inet_timewait_death_row *,
-                       struct sock *, __u16, struct inet_timewait_sock **),
-               int (*hash)(struct sock *sk, struct inet_timewait_sock *twp));
+int __inet_hash_connect(struct inet_timewait_death_row *death_row,
+                       struct sock *sk, u32 port_offset,
+                       int (*check_established)(struct inet_timewait_death_row *,
+                                                struct sock *, __u16,
+                                                struct inet_timewait_sock **),
+                       int (*hash)(struct sock *sk,
+                                   struct inet_timewait_sock *twp));
 
-extern int inet_hash_connect(struct inet_timewait_death_row *death_row,
-                            struct sock *sk);
+int inet_hash_connect(struct inet_timewait_death_row *death_row,
+                     struct sock *sk);
 #endif /* _INET_HASHTABLES_H */
index b21a7f0..1833c3f 100644 (file)
@@ -70,13 +70,14 @@ struct ip_options_data {
 
 struct inet_request_sock {
        struct request_sock     req;
-#if IS_ENABLED(CONFIG_IPV6)
-       u16                     inet6_rsk_offset;
-#endif
-       __be16                  loc_port;
-       __be32                  loc_addr;
-       __be32                  rmt_addr;
-       __be16                  rmt_port;
+#define ir_loc_addr            req.__req_common.skc_rcv_saddr
+#define ir_rmt_addr            req.__req_common.skc_daddr
+#define ir_num                 req.__req_common.skc_num
+#define ir_rmt_port            req.__req_common.skc_dport
+#define ir_v6_rmt_addr         req.__req_common.skc_v6_daddr
+#define ir_v6_loc_addr         req.__req_common.skc_v6_rcv_saddr
+#define ir_iif                 req.__req_common.skc_bound_dev_if
+
        kmemcheck_bitfield_begin(flags);
        u16                     snd_wscale : 4,
                                rcv_wscale : 4,
@@ -88,6 +89,7 @@ struct inet_request_sock {
                                no_srccheck: 1;
        kmemcheck_bitfield_end(flags);
        struct ip_options_rcu   *opt;
+       struct sk_buff          *pktopts;
 };
 
 static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
@@ -103,6 +105,9 @@ struct inet_cork {
        int                     length; /* Total length of all frames */
        struct dst_entry        *dst;
        u8                      tx_flags;
+       __u8                    ttl;
+       __s16                   tos;
+       char                    priority;
 };
 
 struct inet_cork_full {
@@ -143,10 +148,8 @@ struct inet_sock {
        /* Socket demultiplex comparisons on incoming packets. */
 #define inet_daddr             sk.__sk_common.skc_daddr
 #define inet_rcv_saddr         sk.__sk_common.skc_rcv_saddr
-#define inet_addrpair          sk.__sk_common.skc_addrpair
 #define inet_dport             sk.__sk_common.skc_dport
 #define inet_num               sk.__sk_common.skc_num
-#define inet_portpair          sk.__sk_common.skc_portpair
 
        __be32                  inet_saddr;
        __s16                   uc_ttl;
@@ -199,32 +202,18 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to,
 }
 #endif
 
-extern int inet_sk_rebuild_header(struct sock *sk);
-
-extern u32 inet_ehash_secret;
-extern u32 ipv6_hash_secret;
-extern void build_ehash_secret(void);
+int inet_sk_rebuild_header(struct sock *sk);
 
-static inline unsigned int inet_ehashfn(struct net *net,
-                                       const __be32 laddr, const __u16 lport,
-                                       const __be32 faddr, const __be16 fport)
+static inline unsigned int __inet_ehashfn(const __be32 laddr,
+                                         const __u16 lport,
+                                         const __be32 faddr,
+                                         const __be16 fport,
+                                         u32 initval)
 {
        return jhash_3words((__force __u32) laddr,
                            (__force __u32) faddr,
                            ((__u32) lport) << 16 | (__force __u32)fport,
-                           inet_ehash_secret + net_hash_mix(net));
-}
-
-static inline int inet_sk_ehashfn(const struct sock *sk)
-{
-       const struct inet_sock *inet = inet_sk(sk);
-       const __be32 laddr = inet->inet_rcv_saddr;
-       const __u16 lport = inet->inet_num;
-       const __be32 faddr = inet->inet_daddr;
-       const __be16 fport = inet->inet_dport;
-       struct net *net = sock_net(sk);
-
-       return inet_ehashfn(net, laddr, lport, faddr, fport);
+                           initval);
 }
 
 static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops)
index f908dfc..71c6e26 100644 (file)
@@ -58,6 +58,11 @@ struct inet_hashinfo;
 # define INET_TWDR_RECYCLE_TICK (12 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
 #endif
 
+static inline u32 inet_tw_time_stamp(void)
+{
+       return jiffies;
+}
+
 /* TIME_WAIT reaping mechanism. */
 #define INET_TWDR_TWKILL_SLOTS 8 /* Please keep this a power of 2. */
 
@@ -83,9 +88,9 @@ struct inet_timewait_death_row {
        int                     sysctl_max_tw_buckets;
 };
 
-extern void inet_twdr_hangman(unsigned long data);
-extern void inet_twdr_twkill_work(struct work_struct *work);
-extern void inet_twdr_twcal_tick(unsigned long data);
+void inet_twdr_hangman(unsigned long data);
+void inet_twdr_twkill_work(struct work_struct *work);
+void inet_twdr_twcal_tick(unsigned long data);
 
 struct inet_bind_bucket;
 
@@ -111,11 +116,11 @@ struct inet_timewait_sock {
 #define tw_prot                        __tw_common.skc_prot
 #define tw_net                 __tw_common.skc_net
 #define tw_daddr               __tw_common.skc_daddr
+#define tw_v6_daddr            __tw_common.skc_v6_daddr
 #define tw_rcv_saddr           __tw_common.skc_rcv_saddr
-#define tw_addrpair            __tw_common.skc_addrpair
+#define tw_v6_rcv_saddr        __tw_common.skc_v6_rcv_saddr
 #define tw_dport               __tw_common.skc_dport
 #define tw_num                 __tw_common.skc_num
-#define tw_portpair            __tw_common.skc_portpair
 
        int                     tw_timeout;
        volatile unsigned char  tw_substate;
@@ -130,26 +135,14 @@ struct inet_timewait_sock {
                                tw_transparent  : 1,
                                tw_pad          : 6,    /* 6 bits hole */
                                tw_tos          : 8,
-                               tw_ipv6_offset  : 16;
+                               tw_pad2         : 16;   /* 16 bits hole */
        kmemcheck_bitfield_end(flags);
-       unsigned long           tw_ttd;
+       u32                     tw_ttd;
        struct inet_bind_bucket *tw_tb;
        struct hlist_node       tw_death_node;
 };
 #define tw_tclass tw_tos
 
-static inline void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
-                                     struct hlist_nulls_head *list)
-{
-       hlist_nulls_add_head_rcu(&tw->tw_node, list);
-}
-
-static inline void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
-                                          struct hlist_head *list)
-{
-       hlist_add_head(&tw->tw_bind_node, list);
-}
-
 static inline int inet_twsk_dead_hashed(const struct inet_timewait_sock *tw)
 {
        return !hlist_unhashed(&tw->tw_death_node);
@@ -189,34 +182,28 @@ static inline struct inet_timewait_sock *inet_twsk(const struct sock *sk)
        return (struct inet_timewait_sock *)sk;
 }
 
-static inline __be32 sk_rcv_saddr(const struct sock *sk)
-{
-/* both inet_sk() and inet_twsk() store rcv_saddr in skc_rcv_saddr */
-       return sk->__sk_common.skc_rcv_saddr;
-}
-
-extern void inet_twsk_put(struct inet_timewait_sock *tw);
+void inet_twsk_free(struct inet_timewait_sock *tw);
+void inet_twsk_put(struct inet_timewait_sock *tw);
 
-extern int inet_twsk_unhash(struct inet_timewait_sock *tw);
+int inet_twsk_unhash(struct inet_timewait_sock *tw);
 
-extern int inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
-                                struct inet_hashinfo *hashinfo);
+int inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
+                         struct inet_hashinfo *hashinfo);
 
-extern struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk,
-                                                 const int state);
+struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk,
+                                          const int state);
 
-extern void __inet_twsk_hashdance(struct inet_timewait_sock *tw,
-                                 struct sock *sk,
-                                 struct inet_hashinfo *hashinfo);
+void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
+                          struct inet_hashinfo *hashinfo);
 
-extern void inet_twsk_schedule(struct inet_timewait_sock *tw,
-                              struct inet_timewait_death_row *twdr,
-                              const int timeo, const int timewait_len);
-extern void inet_twsk_deschedule(struct inet_timewait_sock *tw,
-                                struct inet_timewait_death_row *twdr);
+void inet_twsk_schedule(struct inet_timewait_sock *tw,
+                       struct inet_timewait_death_row *twdr,
+                       const int timeo, const int timewait_len);
+void inet_twsk_deschedule(struct inet_timewait_sock *tw,
+                         struct inet_timewait_death_row *twdr);
 
-extern void inet_twsk_purge(struct inet_hashinfo *hashinfo,
-                           struct inet_timewait_death_row *twdr, int family);
+void inet_twsk_purge(struct inet_hashinfo *hashinfo,
+                    struct inet_timewait_death_row *twdr, int family);
 
 static inline
 struct net *twsk_net(const struct inet_timewait_sock *twsk)
index 53f464d..f4e127a 100644 (file)
@@ -120,9 +120,9 @@ static inline void inetpeer_transfer_peer(unsigned long *to, unsigned long *from
        }
 }
 
-extern void inet_peer_base_init(struct inet_peer_base *);
+void inet_peer_base_init(struct inet_peer_base *);
 
-void                   inet_initpeers(void) __init;
+void inet_initpeers(void) __init;
 
 #define INETPEER_METRICS_NEW   (~(u32) 0)
 
@@ -159,11 +159,11 @@ static inline struct inet_peer *inet_getpeer_v6(struct inet_peer_base *base,
 }
 
 /* can be called from BH context or outside */
-extern void inet_putpeer(struct inet_peer *p);
-extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout);
+void inet_putpeer(struct inet_peer *p);
+bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout);
 
-extern void inetpeer_invalidate_tree(struct inet_peer_base *);
-extern void inetpeer_invalidate_family(int family);
+void inetpeer_invalidate_tree(struct inet_peer_base *);
+void inetpeer_invalidate_family(int family);
 
 /*
  * temporary check to make sure we dont access rid, ip_id_count, tcp_ts,
index 5e52688..217bc5b 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/skbuff.h>
 
 #include <net/inet_sock.h>
+#include <net/route.h>
 #include <net/snmp.h>
 #include <net/flow.h>
 
@@ -56,6 +57,9 @@ struct ipcm_cookie {
        int                     oif;
        struct ip_options_rcu   *opt;
        __u8                    tx_flags;
+       __u8                    ttl;
+       __s16                   tos;
+       char                    priority;
 };
 
 #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))
@@ -86,64 +90,71 @@ struct packet_type;
 struct rtable;
 struct sockaddr;
 
-extern int             igmp_mc_proc_init(void);
+int igmp_mc_proc_init(void);
 
 /*
  *     Functions provided by ip.c
  */
 
-extern int             ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
-                                             __be32 saddr, __be32 daddr,
-                                             struct ip_options_rcu *opt);
-extern int             ip_rcv(struct sk_buff *skb, struct net_device *dev,
-                              struct packet_type *pt, struct net_device *orig_dev);
-extern int             ip_local_deliver(struct sk_buff *skb);
-extern int             ip_mr_input(struct sk_buff *skb);
-extern int             ip_output(struct sk_buff *skb);
-extern int             ip_mc_output(struct sk_buff *skb);
-extern int             ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
-extern int             ip_do_nat(struct sk_buff *skb);
-extern void            ip_send_check(struct iphdr *ip);
-extern int             __ip_local_out(struct sk_buff *skb);
-extern int             ip_local_out(struct sk_buff *skb);
-extern int             ip_queue_xmit(struct sk_buff *skb, struct flowi *fl);
-extern void            ip_init(void);
-extern int             ip_append_data(struct sock *sk, struct flowi4 *fl4,
-                                      int getfrag(void *from, char *to, int offset, int len,
-                                                  int odd, struct sk_buff *skb),
-                               void *from, int len, int protolen,
-                               struct ipcm_cookie *ipc,
-                               struct rtable **rt,
-                               unsigned int flags);
-extern int             ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb);
-extern ssize_t         ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
-                               int offset, size_t size, int flags);
-extern struct sk_buff  *__ip_make_skb(struct sock *sk,
-                                     struct flowi4 *fl4,
-                                     struct sk_buff_head *queue,
-                                     struct inet_cork *cork);
-extern int             ip_send_skb(struct net *net, struct sk_buff *skb);
-extern int             ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4);
-extern void            ip_flush_pending_frames(struct sock *sk);
-extern struct sk_buff  *ip_make_skb(struct sock *sk,
-                                   struct flowi4 *fl4,
-                                   int getfrag(void *from, char *to, int offset, int len,
-                                               int odd, struct sk_buff *skb),
-                                   void *from, int length, int transhdrlen,
-                                   struct ipcm_cookie *ipc,
-                                   struct rtable **rtp,
-                                   unsigned int flags);
+int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+                         __be32 saddr, __be32 daddr,
+                         struct ip_options_rcu *opt);
+int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
+          struct net_device *orig_dev);
+int ip_local_deliver(struct sk_buff *skb);
+int ip_mr_input(struct sk_buff *skb);
+int ip_output(struct sk_buff *skb);
+int ip_mc_output(struct sk_buff *skb);
+int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
+int ip_do_nat(struct sk_buff *skb);
+void ip_send_check(struct iphdr *ip);
+int __ip_local_out(struct sk_buff *skb);
+int ip_local_out(struct sk_buff *skb);
+int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl);
+void ip_init(void);
+int ip_append_data(struct sock *sk, struct flowi4 *fl4,
+                  int getfrag(void *from, char *to, int offset, int len,
+                              int odd, struct sk_buff *skb),
+                  void *from, int len, int protolen,
+                  struct ipcm_cookie *ipc,
+                  struct rtable **rt,
+                  unsigned int flags);
+int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd,
+                      struct sk_buff *skb);
+ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
+                      int offset, size_t size, int flags);
+struct sk_buff *__ip_make_skb(struct sock *sk, struct flowi4 *fl4,
+                             struct sk_buff_head *queue,
+                             struct inet_cork *cork);
+int ip_send_skb(struct net *net, struct sk_buff *skb);
+int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4);
+void ip_flush_pending_frames(struct sock *sk);
+struct sk_buff *ip_make_skb(struct sock *sk, struct flowi4 *fl4,
+                           int getfrag(void *from, char *to, int offset,
+                                       int len, int odd, struct sk_buff *skb),
+                           void *from, int length, int transhdrlen,
+                           struct ipcm_cookie *ipc, struct rtable **rtp,
+                           unsigned int flags);
 
 static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
 {
        return __ip_make_skb(sk, fl4, &sk->sk_write_queue, &inet_sk(sk)->cork.base);
 }
 
+static inline __u8 get_rttos(struct ipcm_cookie* ipc, struct inet_sock *inet)
+{
+       return (ipc->tos != -1) ? RT_TOS(ipc->tos) : RT_TOS(inet->tos);
+}
+
+static inline __u8 get_rtconn_flags(struct ipcm_cookie* ipc, struct sock* sk)
+{
+       return (ipc->tos != -1) ? RT_CONN_FLAGS_TOS(sk, ipc->tos) : RT_CONN_FLAGS(sk);
+}
+
 /* datagram.c */
-extern int             ip4_datagram_connect(struct sock *sk, 
-                                            struct sockaddr *uaddr, int addr_len);
+int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
 
-extern void ip4_datagram_release_cb(struct sock *sk);
+void ip4_datagram_release_cb(struct sock *sk);
 
 struct ip_reply_arg {
        struct kvec iov[1];   
@@ -184,16 +195,16 @@ extern struct ipv4_config ipv4_config;
 #define NET_ADD_STATS_BH(net, field, adnd) SNMP_ADD_STATS_BH((net)->mib.net_statistics, field, adnd)
 #define NET_ADD_STATS_USER(net, field, adnd) SNMP_ADD_STATS_USER((net)->mib.net_statistics, field, adnd)
 
-extern unsigned long snmp_fold_field(void __percpu *mib[], int offt);
+unsigned long snmp_fold_field(void __percpu *mib[], int offt);
 #if BITS_PER_LONG==32
-extern u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t sync_off);
+u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t sync_off);
 #else
 static inline u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_off)
 {
        return snmp_fold_field(mib, offt);
 }
 #endif
-extern int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align);
+int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align);
 
 static inline void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ])
 {
@@ -206,11 +217,7 @@ static inline void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ])
        }
 }
 
-extern struct local_ports {
-       seqlock_t       lock;
-       int             range[2];
-} sysctl_local_ports;
-extern void inet_get_local_port_range(int *low, int *high);
+void inet_get_local_port_range(struct net *net, int *low, int *high);
 
 extern unsigned long *sysctl_local_reserved_ports;
 static inline int inet_is_reserved_local_port(int port)
@@ -231,9 +238,9 @@ extern int sysctl_ip_early_demux;
 /* From ip_output.c */
 extern int sysctl_ip_dynaddr;
 
-extern void ipfrag_init(void);
+void ipfrag_init(void);
 
-extern void ip_static_sysctl_init(void);
+void ip_static_sysctl_init(void);
 
 static inline bool ip_is_fragment(const struct iphdr *iph)
 {
@@ -262,7 +269,7 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
                 !(dst_metric_locked(dst, RTAX_MTU)));
 }
 
-extern void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more);
+void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more);
 
 static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk)
 {
@@ -367,7 +374,7 @@ static __inline__ void inet_reset_saddr(struct sock *sk)
                struct ipv6_pinfo *np = inet6_sk(sk);
 
                memset(&np->saddr, 0, sizeof(np->saddr));
-               memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr));
+               memset(&sk->sk_v6_rcv_saddr, 0, sizeof(sk->sk_v6_rcv_saddr));
        }
 #endif
 }
@@ -390,7 +397,7 @@ static inline int sk_mc_loop(struct sock *sk)
        return 1;
 }
 
-extern bool ip_call_ra_chain(struct sk_buff *skb);
+bool ip_call_ra_chain(struct sk_buff *skb);
 
 /*
  *     Functions provided by ip_fragment.c
@@ -428,50 +435,52 @@ int ip_frag_nqueues(struct net *net);
  *     Functions provided by ip_forward.c
  */
  
-extern int ip_forward(struct sk_buff *skb);
+int ip_forward(struct sk_buff *skb);
  
 /*
  *     Functions provided by ip_options.c
  */
  
-extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
-                            __be32 daddr, struct rtable *rt, int is_frag);
-extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb);
-extern void ip_options_fragment(struct sk_buff *skb);
-extern int ip_options_compile(struct net *net,
-                             struct ip_options *opt, struct sk_buff *skb);
-extern int ip_options_get(struct net *net, struct ip_options_rcu **optp,
-                         unsigned char *data, int optlen);
-extern int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp,
-                                   unsigned char __user *data, int optlen);
-extern void ip_options_undo(struct ip_options * opt);
-extern void ip_forward_options(struct sk_buff *skb);
-extern int ip_options_rcv_srr(struct sk_buff *skb);
+void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
+                     __be32 daddr, struct rtable *rt, int is_frag);
+int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb);
+void ip_options_fragment(struct sk_buff *skb);
+int ip_options_compile(struct net *net, struct ip_options *opt,
+                      struct sk_buff *skb);
+int ip_options_get(struct net *net, struct ip_options_rcu **optp,
+                  unsigned char *data, int optlen);
+int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp,
+                            unsigned char __user *data, int optlen);
+void ip_options_undo(struct ip_options *opt);
+void ip_forward_options(struct sk_buff *skb);
+int ip_options_rcv_srr(struct sk_buff *skb);
 
 /*
  *     Functions provided by ip_sockglue.c
  */
 
-extern void    ipv4_pktinfo_prepare(struct sk_buff *skb);
-extern void    ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb);
-extern int     ip_cmsg_send(struct net *net,
-                            struct msghdr *msg, struct ipcm_cookie *ipc);
-extern int     ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen);
-extern int     ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen);
-extern int     compat_ip_setsockopt(struct sock *sk, int level,
-                       int optname, char __user *optval, unsigned int optlen);
-extern int     compat_ip_getsockopt(struct sock *sk, int level,
-                       int optname, char __user *optval, int __user *optlen);
-extern int     ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *));
-
-extern int     ip_recv_error(struct sock *sk, struct msghdr *msg, int len);
-extern void    ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
-                             __be16 port, u32 info, u8 *payload);
-extern void    ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
-                              u32 info);
+void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb);
+void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb);
+int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc);
+int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
+                 unsigned int optlen);
+int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
+                 int __user *optlen);
+int compat_ip_setsockopt(struct sock *sk, int level, int optname,
+                        char __user *optval, unsigned int optlen);
+int compat_ip_getsockopt(struct sock *sk, int level, int optname,
+                        char __user *optval, int __user *optlen);
+int ip_ra_control(struct sock *sk, unsigned char on,
+                 void (*destructor)(struct sock *));
+
+int ip_recv_error(struct sock *sk, struct msghdr *msg, int len);
+void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
+                  u32 info, u8 *payload);
+void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
+                   u32 info);
 
 #ifdef CONFIG_PROC_FS
-extern int ip_misc_proc_init(void);
+int ip_misc_proc_init(void);
 #endif
 
 #endif /* _IP_H */
index 7686e3f..9e3c540 100644 (file)
@@ -66,12 +66,14 @@ static inline void __tcp_v6_send_check(struct sk_buff *skb,
        }
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
 static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
 
-       __tcp_v6_send_check(skb, &np->saddr, &np->daddr);
+       __tcp_v6_send_check(skb, &np->saddr, &sk->sk_v6_daddr);
 }
+#endif
 
 int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto);
 #endif
index 48ec25a..2182525 100644 (file)
@@ -165,6 +165,7 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst)
 static inline void rt6_clean_expires(struct rt6_info *rt)
 {
        rt->rt6i_flags &= ~RTF_EXPIRES;
+       rt->dst.expires = 0;
 }
 
 static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires)
@@ -267,48 +268,39 @@ typedef struct rt6_info *(*pol_lookup_t)(struct net *,
  *     exported functions
  */
 
-extern struct fib6_table        *fib6_get_table(struct net *net, u32 id);
-extern struct fib6_table        *fib6_new_table(struct net *net, u32 id);
-extern struct dst_entry         *fib6_rule_lookup(struct net *net,
-                                                 struct flowi6 *fl6, int flags,
-                                                 pol_lookup_t lookup);
+struct fib6_table *fib6_get_table(struct net *net, u32 id);
+struct fib6_table *fib6_new_table(struct net *net, u32 id);
+struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
+                                  int flags, pol_lookup_t lookup);
 
-extern struct fib6_node                *fib6_lookup(struct fib6_node *root,
-                                            const struct in6_addr *daddr,
-                                            const struct in6_addr *saddr);
+struct fib6_node *fib6_lookup(struct fib6_node *root,
+                             const struct in6_addr *daddr,
+                             const struct in6_addr *saddr);
 
-struct fib6_node               *fib6_locate(struct fib6_node *root,
-                                            const struct in6_addr *daddr, int dst_len,
-                                            const struct in6_addr *saddr, int src_len);
+struct fib6_node *fib6_locate(struct fib6_node *root,
+                             const struct in6_addr *daddr, int dst_len,
+                             const struct in6_addr *saddr, int src_len);
 
-extern void                    fib6_clean_all_ro(struct net *net,
-                                              int (*func)(struct rt6_info *, void *arg),
-                                              int prune, void *arg);
+void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
+                   int prune, void *arg);
 
-extern void                    fib6_clean_all(struct net *net,
-                                              int (*func)(struct rt6_info *, void *arg),
-                                              int prune, void *arg);
+int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info);
 
-extern int                     fib6_add(struct fib6_node *root,
-                                        struct rt6_info *rt,
-                                        struct nl_info *info);
+int fib6_del(struct rt6_info *rt, struct nl_info *info);
 
-extern int                     fib6_del(struct rt6_info *rt,
-                                        struct nl_info *info);
+void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info);
 
-extern void                    inet6_rt_notify(int event, struct rt6_info *rt,
-                                               struct nl_info *info);
+void fib6_run_gc(unsigned long expires, struct net *net, bool force);
 
-extern void                    fib6_run_gc(unsigned long expires,
-                                           struct net *net, bool force);
+void fib6_gc_cleanup(void);
 
-extern void                    fib6_gc_cleanup(void);
+int fib6_init(void);
 
-extern int                     fib6_init(void);
+int ipv6_route_open(struct inode *inode, struct file *file);
 
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
-extern int                     fib6_rules_init(void);
-extern void                    fib6_rules_cleanup(void);
+int fib6_rules_init(void);
+void fib6_rules_cleanup(void);
 #else
 static inline int               fib6_rules_init(void)
 {
index f525e70..733747c 100644 (file)
@@ -51,7 +51,7 @@ static inline unsigned int rt6_flags2srcprefs(int flags)
        return (flags >> 3) & 7;
 }
 
-extern void rt6_bind_peer(struct rt6_info *rt, int create);
+void rt6_bind_peer(struct rt6_info *rt, int create);
 
 static inline struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create)
 {
@@ -72,70 +72,58 @@ static inline struct inet_peer *rt6_get_peer_create(struct rt6_info *rt)
        return __rt6_get_peer(rt, 1);
 }
 
-extern void                    ip6_route_input(struct sk_buff *skb);
+void ip6_route_input(struct sk_buff *skb);
 
-extern struct dst_entry *      ip6_route_output(struct net *net,
-                                                const struct sock *sk,
-                                                struct flowi6 *fl6);
-extern struct dst_entry *      ip6_route_lookup(struct net *net,
-                                                struct flowi6 *fl6, int flags);
+struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk,
+                                  struct flowi6 *fl6);
+struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
+                                  int flags);
 
-extern int                     ip6_route_init(void);
-extern void                    ip6_route_cleanup(void);
+int ip6_route_init(void);
+void ip6_route_cleanup(void);
 
-extern int                     ipv6_route_ioctl(struct net *net,
-                                                unsigned int cmd,
-                                                void __user *arg);
+int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg);
 
-extern int                     ip6_route_add(struct fib6_config *cfg);
-extern int                     ip6_ins_rt(struct rt6_info *);
-extern int                     ip6_del_rt(struct rt6_info *);
+int ip6_route_add(struct fib6_config *cfg);
+int ip6_ins_rt(struct rt6_info *);
+int ip6_del_rt(struct rt6_info *);
 
-extern int                     ip6_route_get_saddr(struct net *net,
-                                                   struct rt6_info *rt,
-                                                   const struct in6_addr *daddr,
-                                                   unsigned int prefs,
-                                                   struct in6_addr *saddr);
+int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
+                       const struct in6_addr *daddr, unsigned int prefs,
+                       struct in6_addr *saddr);
 
-extern struct rt6_info         *rt6_lookup(struct net *net,
-                                           const struct in6_addr *daddr,
-                                           const struct in6_addr *saddr,
-                                           int oif, int flags);
+struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
+                           const struct in6_addr *saddr, int oif, int flags);
 
-extern struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
-                                        struct flowi6 *fl6);
-extern int icmp6_dst_gc(void);
+struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6);
+int icmp6_dst_gc(void);
 
-extern void fib6_force_start_gc(struct net *net);
+void fib6_force_start_gc(struct net *net);
 
-extern struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
-                                          const struct in6_addr *addr,
-                                          bool anycast);
+struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
+                                   const struct in6_addr *addr, bool anycast);
 
 /*
  *     support functions for ND
  *
  */
-extern struct rt6_info *       rt6_get_dflt_router(const struct in6_addr *addr,
-                                                   struct net_device *dev);
-extern struct rt6_info *       rt6_add_dflt_router(const struct in6_addr *gwaddr,
-                                                   struct net_device *dev,
-                                                   unsigned int pref);
-
-extern void                    rt6_purge_dflt_routers(struct net *net);
-
-extern int                     rt6_route_rcv(struct net_device *dev,
-                                             u8 *opt, int len,
-                                             const struct in6_addr *gwaddr);
-
-extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
-                           int oif, u32 mark);
-extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk,
-                              __be32 mtu);
-extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
-extern void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
-                                  u32 mark);
-extern void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
+struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr,
+                                    struct net_device *dev);
+struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr,
+                                    struct net_device *dev, unsigned int pref);
+
+void rt6_purge_dflt_routers(struct net *net);
+
+int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
+                 const struct in6_addr *gwaddr);
+
+void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif,
+                    u32 mark);
+void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu);
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
+                           u32 mark);
+void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
 
 struct netlink_callback;
 
@@ -145,10 +133,10 @@ struct rt6_rtnl_dump_arg {
        struct net *net;
 };
 
-extern int rt6_dump_route(struct rt6_info *rt, void *p_arg);
-extern void rt6_ifdown(struct net *net, struct net_device *dev);
-extern void rt6_mtu_change(struct net_device *dev, unsigned int mtu);
-extern void rt6_remove_prefsrc(struct inet6_ifaddr *ifp);
+int rt6_dump_route(struct rt6_info *rt, void *p_arg);
+void rt6_ifdown(struct net *net, struct net_device *dev);
+void rt6_mtu_change(struct net_device *dev, unsigned int mtu);
+void rt6_remove_prefsrc(struct inet6_ifaddr *ifp);
 
 
 /*
@@ -194,11 +182,9 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
               skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
 }
 
-static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr *dest)
+static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt)
 {
-       if (rt->rt6i_flags & RTF_GATEWAY)
-               return &rt->rt6i_gateway;
-       return dest;
+       return &rt->rt6i_gateway;
 }
 
 #endif
index cbf2be3..9922093 100644 (file)
@@ -165,7 +165,7 @@ struct fib_result_nl {
 #define FIB_TABLE_HASHSZ 2
 #endif
 
-extern __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh);
+__be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh);
 
 #define FIB_RES_SADDR(net, res)                                \
        ((FIB_RES_NH(res).nh_saddr_genid ==             \
@@ -187,14 +187,14 @@ struct fib_table {
        unsigned long           tb_data[0];
 };
 
-extern int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
-                           struct fib_result *res, int fib_flags);
-extern int fib_table_insert(struct fib_table *, struct fib_config *);
-extern int fib_table_delete(struct fib_table *, struct fib_config *);
-extern int fib_table_dump(struct fib_table *table, struct sk_buff *skb,
-                         struct netlink_callback *cb);
-extern int fib_table_flush(struct fib_table *table);
-extern void fib_free_table(struct fib_table *tb);
+int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
+                    struct fib_result *res, int fib_flags);
+int fib_table_insert(struct fib_table *, struct fib_config *);
+int fib_table_delete(struct fib_table *, struct fib_config *);
+int fib_table_dump(struct fib_table *table, struct sk_buff *skb,
+                  struct netlink_callback *cb);
+int fib_table_flush(struct fib_table *table);
+void fib_free_table(struct fib_table *tb);
 
 
 
@@ -234,14 +234,13 @@ static inline int fib_lookup(struct net *net, const struct flowi4 *flp,
 }
 
 #else /* CONFIG_IP_MULTIPLE_TABLES */
-extern int __net_init fib4_rules_init(struct net *net);
-extern void __net_exit fib4_rules_exit(struct net *net);
+int __net_init fib4_rules_init(struct net *net);
+void __net_exit fib4_rules_exit(struct net *net);
 
-extern struct fib_table *fib_new_table(struct net *net, u32 id);
-extern struct fib_table *fib_get_table(struct net *net, u32 id);
+struct fib_table *fib_new_table(struct net *net, u32 id);
+struct fib_table *fib_get_table(struct net *net, u32 id);
 
-extern int __fib_lookup(struct net *net, struct flowi4 *flp,
-                       struct fib_result *res);
+int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res);
 
 static inline int fib_lookup(struct net *net, struct flowi4 *flp,
                             struct fib_result *res)
@@ -269,12 +268,12 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp,
 
 /* Exported by fib_frontend.c */
 extern const struct nla_policy rtm_ipv4_policy[];
-extern void            ip_fib_init(void);
-extern __be32 fib_compute_spec_dst(struct sk_buff *skb);
-extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
-                              u8 tos, int oif, struct net_device *dev,
-                              struct in_device *idev, u32 *itag);
-extern void fib_select_default(struct fib_result *res);
+void ip_fib_init(void);
+__be32 fib_compute_spec_dst(struct sk_buff *skb);
+int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
+                       u8 tos, int oif, struct net_device *dev,
+                       struct in_device *idev, u32 *itag);
+void fib_select_default(struct fib_result *res);
 #ifdef CONFIG_IP_ROUTE_CLASSID
 static inline int fib_num_tclassid_users(struct net *net)
 {
@@ -288,15 +287,15 @@ static inline int fib_num_tclassid_users(struct net *net)
 #endif
 
 /* Exported by fib_semantics.c */
-extern int ip_fib_check_default(__be32 gw, struct net_device *dev);
-extern int fib_sync_down_dev(struct net_device *dev, int force);
-extern int fib_sync_down_addr(struct net *net, __be32 local);
-extern int fib_sync_up(struct net_device *dev);
-extern void fib_select_multipath(struct fib_result *res);
+int ip_fib_check_default(__be32 gw, struct net_device *dev);
+int fib_sync_down_dev(struct net_device *dev, int force);
+int fib_sync_down_addr(struct net *net, __be32 local);
+int fib_sync_up(struct net_device *dev);
+void fib_select_multipath(struct fib_result *res);
 
 /* Exported by fib_trie.c */
-extern void fib_trie_init(void);
-extern struct fib_table *fib_trie_table(u32 id);
+void fib_trie_init(void);
+struct fib_table *fib_trie_table(u32 id);
 
 static inline void fib_combine_itag(u32 *itag, const struct fib_result *res)
 {
@@ -314,7 +313,7 @@ static inline void fib_combine_itag(u32 *itag, const struct fib_result *res)
 #endif
 }
 
-extern void free_fib_info(struct fib_info *fi);
+void free_fib_info(struct fib_info *fi);
 
 static inline void fib_info_put(struct fib_info *fi)
 {
@@ -323,8 +322,8 @@ static inline void fib_info_put(struct fib_info *fi)
 }
 
 #ifdef CONFIG_PROC_FS
-extern int __net_init  fib_proc_init(struct net *net);
-extern void __net_exit fib_proc_exit(struct net *net);
+int __net_init fib_proc_init(struct net *net);
+void __net_exit fib_proc_exit(struct net *net);
 #else
 static inline int fib_proc_init(struct net *net)
 {
index a0a4a10..732f8c6 100644 (file)
@@ -150,6 +150,9 @@ int iptunnel_xmit(struct rtable *rt, struct sk_buff *skb,
                  __be32 src, __be32 dst, __u8 proto,
                  __u8 tos, __u8 ttl, __be16 df, bool xnet);
 
+struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum,
+                                        int gso_type_mask);
+
 static inline void iptunnel_xmit_stats(int err,
                                       struct net_device_stats *err_stats,
                                       struct pcpu_tstats __percpu *stats)
index f0d70f0..5679d92 100644 (file)
@@ -109,7 +109,6 @@ extern int ip_vs_conn_tab_size;
 struct ip_vs_iphdr {
        __u32 len;      /* IPv4 simply where L4 starts
                           IPv6 where L4 Transport Header starts */
-       __u32 thoff_reasm; /* Transport Header Offset in nfct_reasm skb */
        __u16 fragoffs; /* IPv6 fragment offset, 0 if first frag (or not frag)*/
        __s16 protocol;
        __s32 flags;
@@ -117,34 +116,12 @@ struct ip_vs_iphdr {
        union nf_inet_addr daddr;
 };
 
-/* Dependency to module: nf_defrag_ipv6 */
-#if defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE)
-static inline struct sk_buff *skb_nfct_reasm(const struct sk_buff *skb)
-{
-       return skb->nfct_reasm;
-}
-static inline void *frag_safe_skb_hp(const struct sk_buff *skb, int offset,
-                                     int len, void *buffer,
-                                     const struct ip_vs_iphdr *ipvsh)
-{
-       if (unlikely(ipvsh->fragoffs && skb_nfct_reasm(skb)))
-               return skb_header_pointer(skb_nfct_reasm(skb),
-                                         ipvsh->thoff_reasm, len, buffer);
-
-       return skb_header_pointer(skb, offset, len, buffer);
-}
-#else
-static inline struct sk_buff *skb_nfct_reasm(const struct sk_buff *skb)
-{
-       return NULL;
-}
 static inline void *frag_safe_skb_hp(const struct sk_buff *skb, int offset,
                                      int len, void *buffer,
                                      const struct ip_vs_iphdr *ipvsh)
 {
        return skb_header_pointer(skb, offset, len, buffer);
 }
-#endif
 
 static inline void
 ip_vs_fill_ip4hdr(const void *nh, struct ip_vs_iphdr *iphdr)
@@ -171,19 +148,12 @@ ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, struct ip_vs_iphdr *iphdr)
                        (struct ipv6hdr *)skb_network_header(skb);
                iphdr->saddr.in6 = iph->saddr;
                iphdr->daddr.in6 = iph->daddr;
-               /* ipv6_find_hdr() updates len, flags, thoff_reasm */
-               iphdr->thoff_reasm = 0;
+               /* ipv6_find_hdr() updates len, flags */
                iphdr->len       = 0;
                iphdr->flags     = 0;
                iphdr->protocol  = ipv6_find_hdr(skb, &iphdr->len, -1,
                                                 &iphdr->fragoffs,
                                                 &iphdr->flags);
-               /* get proto from re-assembled packet and it's offset */
-               if (skb_nfct_reasm(skb))
-                       iphdr->protocol = ipv6_find_hdr(skb_nfct_reasm(skb),
-                                                       &iphdr->thoff_reasm,
-                                                       -1, NULL, NULL);
-
        } else
 #endif
        {
@@ -236,7 +206,7 @@ static inline int ip_vs_addr_equal(int af, const union nf_inet_addr *a,
 #ifdef CONFIG_IP_VS_DEBUG
 #include <linux/net.h>
 
-extern int ip_vs_get_debug_level(void);
+int ip_vs_get_debug_level(void);
 
 static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len,
                                         const union nf_inet_addr *addr,
@@ -532,9 +502,9 @@ struct ip_vs_proto_data {
        struct tcp_states_t     *tcp_state_table;
 };
 
-extern struct ip_vs_protocol   *ip_vs_proto_get(unsigned short proto);
-extern struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net,
-                                                    unsigned short proto);
+struct ip_vs_protocol   *ip_vs_proto_get(unsigned short proto);
+struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net,
+                                             unsigned short proto);
 
 struct ip_vs_conn_param {
        struct net                      *net;
@@ -723,8 +693,6 @@ struct ip_vs_dest_dst {
        struct rcu_head         rcu_head;
 };
 
-/* In grace period after removing */
-#define IP_VS_DEST_STATE_REMOVING      0x01
 /*
  *     The real server destination forwarding entry
  *     with ip address, port number, and so on.
@@ -742,7 +710,7 @@ struct ip_vs_dest {
 
        atomic_t                refcnt;         /* reference counter */
        struct ip_vs_stats      stats;          /* statistics */
-       unsigned long           state;          /* state flags */
+       unsigned long           idle_start;     /* start time, jiffies */
 
        /* connection counters and thresholds */
        atomic_t                activeconns;    /* active connections */
@@ -756,14 +724,13 @@ struct ip_vs_dest {
        struct ip_vs_dest_dst __rcu *dest_dst;  /* cached dst info */
 
        /* for virtual service */
-       struct ip_vs_service    *svc;           /* service it belongs to */
+       struct ip_vs_service __rcu *svc;        /* service it belongs to */
        __u16                   protocol;       /* which protocol (TCP/UDP) */
        __be16                  vport;          /* virtual port number */
        union nf_inet_addr      vaddr;          /* virtual IP address */
        __u32                   vfwmark;        /* firewall mark of service */
 
        struct list_head        t_list;         /* in dest_trash */
-       struct rcu_head         rcu_head;
        unsigned int            in_rs_table:1;  /* we are in rs_table */
 };
 
@@ -1176,8 +1143,8 @@ static inline int sysctl_backup_only(struct netns_ipvs *ipvs)
  *      IPVS core functions
  *      (from ip_vs_core.c)
  */
-extern const char *ip_vs_proto_name(unsigned int proto);
-extern void ip_vs_init_hash_table(struct list_head *table, int rows);
+const char *ip_vs_proto_name(unsigned int proto);
+void ip_vs_init_hash_table(struct list_head *table, int rows);
 #define IP_VS_INIT_HASH_TABLE(t) ip_vs_init_hash_table((t), ARRAY_SIZE((t)))
 
 #define IP_VS_APP_TYPE_FTP     1
@@ -1240,22 +1207,22 @@ static inline void __ip_vs_conn_put(struct ip_vs_conn *cp)
        smp_mb__before_atomic_dec();
        atomic_dec(&cp->refcnt);
 }
-extern void ip_vs_conn_put(struct ip_vs_conn *cp);
-extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport);
+void ip_vs_conn_put(struct ip_vs_conn *cp);
+void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport);
 
 struct ip_vs_conn *ip_vs_conn_new(const struct ip_vs_conn_param *p,
                                  const union nf_inet_addr *daddr,
                                  __be16 dport, unsigned int flags,
                                  struct ip_vs_dest *dest, __u32 fwmark);
-extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp);
+void ip_vs_conn_expire_now(struct ip_vs_conn *cp);
 
-extern const char * ip_vs_state_name(__u16 proto, int state);
+const char *ip_vs_state_name(__u16 proto, int state);
 
-extern void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp);
-extern int ip_vs_check_template(struct ip_vs_conn *ct);
-extern void ip_vs_random_dropentry(struct net *net);
-extern int ip_vs_conn_init(void);
-extern void ip_vs_conn_cleanup(void);
+void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp);
+int ip_vs_check_template(struct ip_vs_conn *ct);
+void ip_vs_random_dropentry(struct net *net);
+int ip_vs_conn_init(void);
+void ip_vs_conn_cleanup(void);
 
 static inline void ip_vs_control_del(struct ip_vs_conn *cp)
 {
@@ -1320,37 +1287,36 @@ ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp)
 /*
  * IPVS netns init & cleanup functions
  */
-extern int ip_vs_estimator_net_init(struct net *net);
-extern int ip_vs_control_net_init(struct net *net);
-extern int ip_vs_protocol_net_init(struct net *net);
-extern int ip_vs_app_net_init(struct net *net);
-extern int ip_vs_conn_net_init(struct net *net);
-extern int ip_vs_sync_net_init(struct net *net);
-extern void ip_vs_conn_net_cleanup(struct net *net);
-extern void ip_vs_app_net_cleanup(struct net *net);
-extern void ip_vs_protocol_net_cleanup(struct net *net);
-extern void ip_vs_control_net_cleanup(struct net *net);
-extern void ip_vs_estimator_net_cleanup(struct net *net);
-extern void ip_vs_sync_net_cleanup(struct net *net);
-extern void ip_vs_service_net_cleanup(struct net *net);
+int ip_vs_estimator_net_init(struct net *net);
+int ip_vs_control_net_init(struct net *net);
+int ip_vs_protocol_net_init(struct net *net);
+int ip_vs_app_net_init(struct net *net);
+int ip_vs_conn_net_init(struct net *net);
+int ip_vs_sync_net_init(struct net *net);
+void ip_vs_conn_net_cleanup(struct net *net);
+void ip_vs_app_net_cleanup(struct net *net);
+void ip_vs_protocol_net_cleanup(struct net *net);
+void ip_vs_control_net_cleanup(struct net *net);
+void ip_vs_estimator_net_cleanup(struct net *net);
+void ip_vs_sync_net_cleanup(struct net *net);
+void ip_vs_service_net_cleanup(struct net *net);
 
 /*
  *      IPVS application functions
  *      (from ip_vs_app.c)
  */
 #define IP_VS_APP_MAX_PORTS  8
-extern struct ip_vs_app *register_ip_vs_app(struct net *net,
-                                           struct ip_vs_app *app);
-extern void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app);
-extern int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
-extern void ip_vs_unbind_app(struct ip_vs_conn *cp);
-extern int register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app,
-                                 __u16 proto, __u16 port);
-extern int ip_vs_app_inc_get(struct ip_vs_app *inc);
-extern void ip_vs_app_inc_put(struct ip_vs_app *inc);
-
-extern int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb);
-extern int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb);
+struct ip_vs_app *register_ip_vs_app(struct net *net, struct ip_vs_app *app);
+void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app);
+int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
+void ip_vs_unbind_app(struct ip_vs_conn *cp);
+int register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto,
+                          __u16 port);
+int ip_vs_app_inc_get(struct ip_vs_app *inc);
+void ip_vs_app_inc_put(struct ip_vs_app *inc);
+
+int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb);
+int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb);
 
 int register_ip_vs_pe(struct ip_vs_pe *pe);
 int unregister_ip_vs_pe(struct ip_vs_pe *pe);
@@ -1371,17 +1337,15 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name);
 /*
  *     IPVS protocol functions (from ip_vs_proto.c)
  */
-extern int ip_vs_protocol_init(void);
-extern void ip_vs_protocol_cleanup(void);
-extern void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags);
-extern int *ip_vs_create_timeout_table(int *table, int size);
-extern int
-ip_vs_set_state_timeout(int *table, int num, const char *const *names,
-                       const char *name, int to);
-extern void
-ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp,
-                         const struct sk_buff *skb,
-                         int offset, const char *msg);
+int ip_vs_protocol_init(void);
+void ip_vs_protocol_cleanup(void);
+void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags);
+int *ip_vs_create_timeout_table(int *table, int size);
+int ip_vs_set_state_timeout(int *table, int num, const char *const *names,
+                           const char *name, int to);
+void ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp,
+                              const struct sk_buff *skb, int offset,
+                              const char *msg);
 
 extern struct ip_vs_protocol ip_vs_protocol_tcp;
 extern struct ip_vs_protocol ip_vs_protocol_udp;
@@ -1394,22 +1358,22 @@ extern struct ip_vs_protocol ip_vs_protocol_sctp;
  *      Registering/unregistering scheduler functions
  *      (from ip_vs_sched.c)
  */
-extern int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
-extern int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
-extern int ip_vs_bind_scheduler(struct ip_vs_service *svc,
-                               struct ip_vs_scheduler *scheduler);
-extern void ip_vs_unbind_scheduler(struct ip_vs_service *svc,
-                                  struct ip_vs_scheduler *sched);
-extern struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name);
-extern void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler);
-extern struct ip_vs_conn *
+int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
+int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
+int ip_vs_bind_scheduler(struct ip_vs_service *svc,
+                        struct ip_vs_scheduler *scheduler);
+void ip_vs_unbind_scheduler(struct ip_vs_service *svc,
+                           struct ip_vs_scheduler *sched);
+struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name);
+void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler);
+struct ip_vs_conn *
 ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
               struct ip_vs_proto_data *pd, int *ignored,
               struct ip_vs_iphdr *iph);
-extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
-                       struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph);
+int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
+               struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph);
 
-extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg);
+void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg);
 
 
 /*
@@ -1418,25 +1382,24 @@ extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg);
 extern struct ip_vs_stats ip_vs_stats;
 extern int sysctl_ip_vs_sync_ver;
 
-extern struct ip_vs_service *
+struct ip_vs_service *
 ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol,
                  const union nf_inet_addr *vaddr, __be16 vport);
 
-extern bool
-ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
-                      const union nf_inet_addr *daddr, __be16 dport);
-
-extern int ip_vs_use_count_inc(void);
-extern void ip_vs_use_count_dec(void);
-extern int ip_vs_register_nl_ioctl(void);
-extern void ip_vs_unregister_nl_ioctl(void);
-extern int ip_vs_control_init(void);
-extern void ip_vs_control_cleanup(void);
-extern struct ip_vs_dest *
+bool ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
+                           const union nf_inet_addr *daddr, __be16 dport);
+
+int ip_vs_use_count_inc(void);
+void ip_vs_use_count_dec(void);
+int ip_vs_register_nl_ioctl(void);
+void ip_vs_unregister_nl_ioctl(void);
+int ip_vs_control_init(void);
+void ip_vs_control_cleanup(void);
+struct ip_vs_dest *
 ip_vs_find_dest(struct net *net, int af, const union nf_inet_addr *daddr,
                __be16 dport, const union nf_inet_addr *vaddr, __be16 vport,
                __u16 protocol, __u32 fwmark, __u32 flags);
-extern void ip_vs_try_bind_dest(struct ip_vs_conn *cp);
+void ip_vs_try_bind_dest(struct ip_vs_conn *cp);
 
 static inline void ip_vs_dest_hold(struct ip_vs_dest *dest)
 {
@@ -1449,60 +1412,59 @@ static inline void ip_vs_dest_put(struct ip_vs_dest *dest)
        atomic_dec(&dest->refcnt);
 }
 
+static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
+{
+       if (atomic_dec_return(&dest->refcnt) < 0)
+               kfree(dest);
+}
+
 /*
  *      IPVS sync daemon data and function prototypes
  *      (from ip_vs_sync.c)
  */
-extern int start_sync_thread(struct net *net, int state, char *mcast_ifn,
-                            __u8 syncid);
-extern int stop_sync_thread(struct net *net, int state);
-extern void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts);
-
+int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid);
+int stop_sync_thread(struct net *net, int state);
+void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts);
 
 /*
  *      IPVS rate estimator prototypes (from ip_vs_est.c)
  */
-extern void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats);
-extern void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats);
-extern void ip_vs_zero_estimator(struct ip_vs_stats *stats);
-extern void ip_vs_read_estimator(struct ip_vs_stats_user *dst,
-                                struct ip_vs_stats *stats);
+void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats);
+void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats);
+void ip_vs_zero_estimator(struct ip_vs_stats *stats);
+void ip_vs_read_estimator(struct ip_vs_stats_user *dst,
+                         struct ip_vs_stats *stats);
 
 /*
  *     Various IPVS packet transmitters (from ip_vs_xmit.c)
  */
-extern int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
-                          struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
-extern int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
-                            struct ip_vs_protocol *pp,
-                            struct ip_vs_iphdr *iph);
-extern int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
-                         struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
-extern int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
-                            struct ip_vs_protocol *pp,
-                            struct ip_vs_iphdr *iph);
-extern int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
-                        struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
-extern int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
-                          struct ip_vs_protocol *pp, int offset,
-                          unsigned int hooknum, struct ip_vs_iphdr *iph);
-extern void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
+int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                     struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                  struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                     struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                 struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
+                   struct ip_vs_protocol *pp, int offset,
+                   unsigned int hooknum, struct ip_vs_iphdr *iph);
+void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
 
 #ifdef CONFIG_IP_VS_IPV6
-extern int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                               struct ip_vs_protocol *pp,
-                               struct ip_vs_iphdr *iph);
-extern int ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                            struct ip_vs_protocol *pp,
-                            struct ip_vs_iphdr *iph);
-extern int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                               struct ip_vs_protocol *pp,
-                               struct ip_vs_iphdr *iph);
-extern int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                           struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
-extern int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
-                             struct ip_vs_protocol *pp, int offset,
-                             unsigned int hooknum, struct ip_vs_iphdr *iph);
+int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                        struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                     struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                        struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                    struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
+int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
+                      struct ip_vs_protocol *pp, int offset,
+                      unsigned int hooknum, struct ip_vs_iphdr *iph);
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -1551,15 +1513,15 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
        return fwd;
 }
 
-extern void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
-                          struct ip_vs_conn *cp, int dir);
+void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                   struct ip_vs_conn *cp, int dir);
 
 #ifdef CONFIG_IP_VS_IPV6
-extern void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
-                             struct ip_vs_conn *cp, int dir);
+void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+                      struct ip_vs_conn *cp, int dir);
 #endif
 
-extern __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
+__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
 
 static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
 {
@@ -1618,13 +1580,13 @@ static inline int ip_vs_conntrack_enabled(struct netns_ipvs *ipvs)
 #endif
 }
 
-extern void ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp,
-                                  int outin);
-extern int ip_vs_confirm_conntrack(struct sk_buff *skb);
-extern void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct,
-                                     struct ip_vs_conn *cp, u_int8_t proto,
-                                     const __be16 port, int from_rs);
-extern void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp);
+void ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp,
+                           int outin);
+int ip_vs_confirm_conntrack(struct sk_buff *skb);
+void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct,
+                              struct ip_vs_conn *cp, u_int8_t proto,
+                              const __be16 port, int from_rs);
+void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp);
 
 #else
 
@@ -1649,7 +1611,7 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp)
 /* CONFIG_IP_VS_NFCT */
 #endif
 
-static inline unsigned int
+static inline int
 ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
 {
        /*
index bbf1c8f..2a5f668 100644 (file)
@@ -244,14 +244,15 @@ struct ipv6_fl_socklist {
        struct rcu_head                 rcu;
 };
 
-extern struct ip6_flowlabel    *fl6_sock_lookup(struct sock *sk, __be32 label);
-extern struct ipv6_txoptions   *fl6_merge_options(struct ipv6_txoptions * opt_space,
-                                                  struct ip6_flowlabel * fl,
-                                                  struct ipv6_txoptions * fopt);
-extern void                    fl6_free_socklist(struct sock *sk);
-extern int                     ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen);
-extern int                     ip6_flowlabel_init(void);
-extern void                    ip6_flowlabel_cleanup(void);
+struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label);
+struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space,
+                                        struct ip6_flowlabel *fl,
+                                        struct ipv6_txoptions *fopt);
+void fl6_free_socklist(struct sock *sk);
+int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen);
+int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq);
+int ip6_flowlabel_init(void);
+void ip6_flowlabel_cleanup(void);
 
 static inline void fl6_sock_release(struct ip6_flowlabel *fl)
 {
@@ -259,7 +260,7 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl)
                atomic_dec(&fl->users);
 }
 
-extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info);
+void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info);
 
 int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
                               struct icmp6hdr *thdr, int len);
@@ -267,19 +268,21 @@ int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
 struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
                                      struct sock *sk, struct flowi6 *fl6);
 
-extern int                     ip6_ra_control(struct sock *sk, int sel);
+int ip6_ra_control(struct sock *sk, int sel);
 
-extern int                     ipv6_parse_hopopts(struct sk_buff *skb);
+int ipv6_parse_hopopts(struct sk_buff *skb);
 
-extern struct ipv6_txoptions *  ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
-extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
-                                                  int newtype,
-                                                  struct ipv6_opt_hdr __user *newopt,
-                                                  int newoptlen);
+struct ipv6_txoptions *ipv6_dup_options(struct sock *sk,
+                                       struct ipv6_txoptions *opt);
+struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
+                                         struct ipv6_txoptions *opt,
+                                         int newtype,
+                                         struct ipv6_opt_hdr __user *newopt,
+                                         int newoptlen);
 struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
                                          struct ipv6_txoptions *opt);
 
-extern bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb);
+bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb);
 
 static inline bool ipv6_accept_ra(struct inet6_dev *idev)
 {
@@ -306,7 +309,7 @@ static inline int ip6_frag_mem(struct net *net)
 #define IPV6_FRAG_LOW_THRESH   (3 * 1024*1024) /* 3145728 */
 #define IPV6_FRAG_TIMEOUT      (60 * HZ)       /* 60 seconds */
 
-extern int __ipv6_addr_type(const struct in6_addr *addr);
+int __ipv6_addr_type(const struct in6_addr *addr);
 static inline int ipv6_addr_type(const struct in6_addr *addr)
 {
        return __ipv6_addr_type(addr) & 0xffff;
@@ -537,14 +540,14 @@ static inline u32 ipv6_addr_hash(const struct in6_addr *a)
 }
 
 /* more secured version of ipv6_addr_hash() */
-static inline u32 ipv6_addr_jhash(const struct in6_addr *a)
+static inline u32 __ipv6_addr_jhash(const struct in6_addr *a, const u32 initval)
 {
        u32 v = (__force u32)a->s6_addr32[0] ^ (__force u32)a->s6_addr32[1];
 
        return jhash_3words(v,
                            (__force u32)a->s6_addr32[2],
                            (__force u32)a->s6_addr32[3],
-                           ipv6_hash_secret);
+                           initval);
 }
 
 static inline bool ipv6_addr_loopback(const struct in6_addr *a)
@@ -656,9 +659,9 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
        return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr));
 }
 
-extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
+void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
 
-extern int ip6_dst_hoplimit(struct dst_entry *dst);
+int ip6_dst_hoplimit(struct dst_entry *dst);
 
 /*
  *     Header manipulation
@@ -682,83 +685,65 @@ static inline __be32 ip6_flowinfo(const struct ipv6hdr *hdr)
  *     rcv function (called from netdevice level)
  */
 
-extern int                     ipv6_rcv(struct sk_buff *skb, 
-                                        struct net_device *dev, 
-                                        struct packet_type *pt,
-                                        struct net_device *orig_dev);
+int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
+            struct packet_type *pt, struct net_device *orig_dev);
 
-extern int                     ip6_rcv_finish(struct sk_buff *skb);
+int ip6_rcv_finish(struct sk_buff *skb);
 
 /*
  *     upper-layer output functions
  */
-extern int                     ip6_xmit(struct sock *sk,
-                                        struct sk_buff *skb,
-                                        struct flowi6 *fl6,
-                                        struct ipv6_txoptions *opt,
-                                        int tclass);
-
-extern int                     ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
-
-extern int                     ip6_append_data(struct sock *sk,
-                                               int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb),
-                                               void *from,
-                                               int length,
-                                               int transhdrlen,
-                                               int hlimit,
-                                               int tclass,
-                                               struct ipv6_txoptions *opt,
-                                               struct flowi6 *fl6,
-                                               struct rt6_info *rt,
-                                               unsigned int flags,
-                                               int dontfrag);
-
-extern int                     ip6_push_pending_frames(struct sock *sk);
-
-extern void                    ip6_flush_pending_frames(struct sock *sk);
-
-extern int                     ip6_dst_lookup(struct sock *sk,
-                                              struct dst_entry **dst,
-                                              struct flowi6 *fl6);
-extern struct dst_entry *      ip6_dst_lookup_flow(struct sock *sk,
-                                                   struct flowi6 *fl6,
-                                                   const struct in6_addr *final_dst,
-                                                   bool can_sleep);
-extern struct dst_entry *      ip6_sk_dst_lookup_flow(struct sock *sk,
-                                                      struct flowi6 *fl6,
-                                                      const struct in6_addr *final_dst,
-                                                      bool can_sleep);
-extern struct dst_entry *      ip6_blackhole_route(struct net *net,
-                                                   struct dst_entry *orig_dst);
+int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+            struct ipv6_txoptions *opt, int tclass);
+
+int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
+
+int ip6_append_data(struct sock *sk,
+                   int getfrag(void *from, char *to, int offset, int len,
+                               int odd, struct sk_buff *skb),
+                   void *from, int length, int transhdrlen, int hlimit,
+                   int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
+                   struct rt6_info *rt, unsigned int flags, int dontfrag);
+
+int ip6_push_pending_frames(struct sock *sk);
+
+void ip6_flush_pending_frames(struct sock *sk);
+
+int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6);
+struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
+                                     const struct in6_addr *final_dst,
+                                     bool can_sleep);
+struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
+                                        const struct in6_addr *final_dst,
+                                        bool can_sleep);
+struct dst_entry *ip6_blackhole_route(struct net *net,
+                                     struct dst_entry *orig_dst);
 
 /*
  *     skb processing functions
  */
 
-extern int                     ip6_output(struct sk_buff *skb);
-extern int                     ip6_forward(struct sk_buff *skb);
-extern int                     ip6_input(struct sk_buff *skb);
-extern int                     ip6_mc_input(struct sk_buff *skb);
+int ip6_output(struct sk_buff *skb);
+int ip6_forward(struct sk_buff *skb);
+int ip6_input(struct sk_buff *skb);
+int ip6_mc_input(struct sk_buff *skb);
 
-extern int                     __ip6_local_out(struct sk_buff *skb);
-extern int                     ip6_local_out(struct sk_buff *skb);
+int __ip6_local_out(struct sk_buff *skb);
+int ip6_local_out(struct sk_buff *skb);
 
 /*
  *     Extension header (options) processing
  */
 
-extern void                    ipv6_push_nfrag_opts(struct sk_buff *skb,
-                                                    struct ipv6_txoptions *opt,
-                                                    u8 *proto,
-                                                    struct in6_addr **daddr_p);
-extern void                    ipv6_push_frag_opts(struct sk_buff *skb,
-                                                   struct ipv6_txoptions *opt,
-                                                   u8 *proto);
+void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
+                         u8 *proto, struct in6_addr **daddr_p);
+void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
+                        u8 *proto);
 
-extern int                     ipv6_skip_exthdr(const struct sk_buff *, int start,
-                                                u8 *nexthdrp, __be16 *frag_offp);
+int ipv6_skip_exthdr(const struct sk_buff *, int start, u8 *nexthdrp,
+                    __be16 *frag_offp);
 
-extern bool                    ipv6_ext_hdr(u8 nexthdr);
+bool ipv6_ext_hdr(u8 nexthdr);
 
 enum {
        IP6_FH_F_FRAG           = (1 << 0),
@@ -767,57 +752,44 @@ enum {
 };
 
 /* find specified header and get offset to it */
-extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
-                        int target, unsigned short *fragoff, int *fragflg);
+int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target,
+                 unsigned short *fragoff, int *fragflg);
 
-extern int ipv6_find_tlv(struct sk_buff *skb, int offset, int type);
+int ipv6_find_tlv(struct sk_buff *skb, int offset, int type);
 
-extern struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
-                                      const struct ipv6_txoptions *opt,
-                                      struct in6_addr *orig);
+struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
+                               const struct ipv6_txoptions *opt,
+                               struct in6_addr *orig);
 
 /*
  *     socket options (ipv6_sockglue.c)
  */
 
-extern int                     ipv6_setsockopt(struct sock *sk, int level, 
-                                               int optname,
-                                               char __user *optval, 
-                                               unsigned int optlen);
-extern int                     ipv6_getsockopt(struct sock *sk, int level, 
-                                               int optname,
-                                               char __user *optval, 
-                                               int __user *optlen);
-extern int                     compat_ipv6_setsockopt(struct sock *sk,
-                                               int level,
-                                               int optname,
-                                               char __user *optval,
-                                               unsigned int optlen);
-extern int                     compat_ipv6_getsockopt(struct sock *sk,
-                                               int level,
-                                               int optname,
-                                               char __user *optval,
-                                               int __user *optlen);
-
-extern int                     ip6_datagram_connect(struct sock *sk, 
-                                                    struct sockaddr *addr, int addr_len);
-
-extern int                     ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
-extern int                     ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len);
-extern void                    ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
-                                               u32 info, u8 *payload);
-extern void                    ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info);
-extern void                    ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu);
-
-extern int inet6_release(struct socket *sock);
-extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, 
-                     int addr_len);
-extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
-                        int *uaddr_len, int peer);
-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,
+int ipv6_setsockopt(struct sock *sk, int level, int optname,
+                   char __user *optval, unsigned int optlen);
+int ipv6_getsockopt(struct sock *sk, int level, int optname,
+                   char __user *optval, int __user *optlen);
+int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
+                          char __user *optval, unsigned int optlen);
+int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
+                          char __user *optval, int __user *optlen);
+
+int ip6_datagram_connect(struct sock *sk, struct sockaddr *addr, int addr_len);
+
+int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
+int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len);
+void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
+                    u32 info, u8 *payload);
+void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info);
+void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu);
+
+int inet6_release(struct socket *sock);
+int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
+int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len,
+                 int peer);
+int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+
+int inet6_hash_connect(struct inet_timewait_death_row *death_row,
                              struct sock *sk);
 
 /*
@@ -829,30 +801,27 @@ 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);
-extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
-extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
-                        struct group_filter __user *optval,
-                        int __user *optlen);
-extern unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
-                                   const struct in6_addr *daddr, u32 rnd);
+int ip6_mc_source(int add, int omode, struct sock *sk,
+                 struct group_source_req *pgsr);
+int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf);
+int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
+                 struct group_filter __user *optval, int __user *optlen);
 
 #ifdef CONFIG_PROC_FS
-extern int  ac6_proc_init(struct net *net);
-extern void ac6_proc_exit(struct net *net);
-extern int  raw6_proc_init(void);
-extern void raw6_proc_exit(void);
-extern int  tcp6_proc_init(struct net *net);
-extern void tcp6_proc_exit(struct net *net);
-extern int  udp6_proc_init(struct net *net);
-extern void udp6_proc_exit(struct net *net);
-extern int  udplite6_proc_init(void);
-extern void udplite6_proc_exit(void);
-extern int  ipv6_misc_proc_init(void);
-extern void ipv6_misc_proc_exit(void);
-extern int snmp6_register_dev(struct inet6_dev *idev);
-extern int snmp6_unregister_dev(struct inet6_dev *idev);
+int ac6_proc_init(struct net *net);
+void ac6_proc_exit(struct net *net);
+int raw6_proc_init(void);
+void raw6_proc_exit(void);
+int tcp6_proc_init(struct net *net);
+void tcp6_proc_exit(struct net *net);
+int udp6_proc_init(struct net *net);
+void udp6_proc_exit(struct net *net);
+int udplite6_proc_init(void);
+void udplite6_proc_exit(void);
+int ipv6_misc_proc_init(void);
+void ipv6_misc_proc_exit(void);
+int snmp6_register_dev(struct inet6_dev *idev);
+int snmp6_unregister_dev(struct inet6_dev *idev);
 
 #else
 static inline int ac6_proc_init(struct net *net) { return 0; }
@@ -865,10 +834,10 @@ static inline int snmp6_unregister_dev(struct inet6_dev *idev) { return 0; }
 extern struct ctl_table ipv6_route_table_template[];
 extern struct ctl_table ipv6_icmp_table_template[];
 
-extern struct ctl_table *ipv6_icmp_sysctl_init(struct net *net);
-extern struct ctl_table *ipv6_route_sysctl_init(struct net *net);
-extern int ipv6_sysctl_register(void);
-extern void ipv6_sysctl_unregister(void);
+struct ctl_table *ipv6_icmp_sysctl_init(struct net *net);
+struct ctl_table *ipv6_route_sysctl_init(struct net *net);
+int ipv6_sysctl_register(void);
+void ipv6_sysctl_unregister(void);
 #endif
 
 #endif /* _NET_IPV6_H */
index c1fec6b..9e9e354 100644 (file)
@@ -123,23 +123,23 @@ extern struct list_head ipx_routes;
 extern rwlock_t ipx_routes_lock;
 
 extern struct list_head ipx_interfaces;
-extern struct ipx_interface *ipx_interfaces_head(void);
+struct ipx_interface *ipx_interfaces_head(void);
 extern spinlock_t ipx_interfaces_lock;
 
 extern struct ipx_interface *ipx_primary_net;
 
-extern int ipx_proc_init(void);
-extern void ipx_proc_exit(void);
+int ipx_proc_init(void);
+void ipx_proc_exit(void);
 
-extern const char *ipx_frame_name(__be16);
-extern const char *ipx_device_name(struct ipx_interface *intrfc);
+const char *ipx_frame_name(__be16);
+const char *ipx_device_name(struct ipx_interface *intrfc);
 
 static __inline__ void ipxitf_hold(struct ipx_interface *intrfc)
 {
        atomic_inc(&intrfc->refcnt);
 }
 
-extern void ipxitf_down(struct ipx_interface *intrfc);
+void ipxitf_down(struct ipx_interface *intrfc);
 
 static __inline__ void ipxitf_put(struct ipx_interface *intrfc)
 {
index 80ffde3..0224402 100644 (file)
@@ -105,13 +105,13 @@ struct ircomm_tty_cb {
 void ircomm_tty_start(struct tty_struct *tty);
 void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self);
 
-extern int ircomm_tty_tiocmget(struct tty_struct *tty);
-extern int ircomm_tty_tiocmset(struct tty_struct *tty,
-                              unsigned int set, unsigned int clear);
-extern int ircomm_tty_ioctl(struct tty_struct *tty, 
-                           unsigned int cmd, unsigned long arg);
-extern void ircomm_tty_set_termios(struct tty_struct *tty, 
-                                  struct ktermios *old_termios);
+int ircomm_tty_tiocmget(struct tty_struct *tty);
+int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set,
+                       unsigned int clear);
+int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
+                    unsigned long arg);
+void ircomm_tty_set_termios(struct tty_struct *tty,
+                           struct ktermios *old_termios);
 
 #endif
 
index 3bed61d..a059465 100644 (file)
@@ -112,20 +112,19 @@ do { if(!(expr)) { \
 struct net_device;
 struct packet_type;
 
-extern void irda_proc_register(void);
-extern void irda_proc_unregister(void);
+void irda_proc_register(void);
+void irda_proc_unregister(void);
 
-extern int irda_sysctl_register(void);
-extern void irda_sysctl_unregister(void);
+int irda_sysctl_register(void);
+void irda_sysctl_unregister(void);
 
-extern int irsock_init(void);
-extern void irsock_cleanup(void);
+int irsock_init(void);
+void irsock_cleanup(void);
 
-extern int irda_nl_register(void);
-extern void irda_nl_unregister(void);
+int irda_nl_register(void);
+void irda_nl_unregister(void);
 
-extern int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
-                           struct packet_type *ptype,
-                           struct net_device *orig_dev);
+int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
+                    struct packet_type *ptype, struct net_device *orig_dev);
 
 #endif /* NET_IRDA_H */
index 94c852d..1141747 100644 (file)
@@ -162,7 +162,7 @@ typedef struct {
         int irq, irq2;        /* Interrupts used */
         int dma, dma2;        /* DMA channel(s) used */
         int fifo_size;        /* FIFO size */
-        int irqflags;         /* interrupt flags (ie, IRQF_SHARED|IRQF_DISABLED) */
+        int irqflags;         /* interrupt flags (ie, IRQF_SHARED) */
        int direction;        /* Link direction, used by some FIR drivers */
        int enabled;          /* Powered on? */
        int suspended;        /* Suspended by APM */
index 4c90824..f9d88da 100644 (file)
@@ -126,6 +126,6 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
                    struct sk_buff *skb, struct irlap_info *info);
 void irlap_print_event(IRLAP_EVENT event);
 
-extern int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb);
+int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb);
 
 #endif
index 6b1dc4f..57173ae 100644 (file)
@@ -163,7 +163,7 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command);
 void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
                         __u8 caddr, int command);
 
-extern int irlap_insert_qos_negotiation_params(struct irlap_cb *self, 
-                                              struct sk_buff *skb);
+int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
+                                       struct sk_buff *skb);
 
 #endif
index 5d5a6a4..a830b01 100644 (file)
@@ -432,44 +432,32 @@ struct iw_public_data {
 /* First : function strictly used inside the kernel */
 
 /* Handle /proc/net/wireless, called in net/code/dev.c */
-extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
-                                int length);
+int dev_get_wireless_info(char *buffer, char **start, off_t offset, int length);
 
 /* Second : functions that may be called by driver modules */
 
 /* Send a single event to user space */
-extern void wireless_send_event(struct net_device *    dev,
-                               unsigned int            cmd,
-                               union iwreq_data *      wrqu,
-                               const char *            extra);
+void wireless_send_event(struct net_device *dev, unsigned int cmd,
+                        union iwreq_data *wrqu, const char *extra);
 
 /* We may need a function to send a stream of events to user space.
  * More on that later... */
 
 /* Standard handler for SIOCSIWSPY */
-extern int iw_handler_set_spy(struct net_device *      dev,
-                             struct iw_request_info *  info,
-                             union iwreq_data *        wrqu,
-                             char *                    extra);
+int iw_handler_set_spy(struct net_device *dev, struct iw_request_info *info,
+                      union iwreq_data *wrqu, char *extra);
 /* Standard handler for SIOCGIWSPY */
-extern int iw_handler_get_spy(struct net_device *      dev,
-                             struct iw_request_info *  info,
-                             union iwreq_data *        wrqu,
-                             char *                    extra);
+int iw_handler_get_spy(struct net_device *dev, struct iw_request_info *info,
+                      union iwreq_data *wrqu, char *extra);
 /* Standard handler for SIOCSIWTHRSPY */
-extern int iw_handler_set_thrspy(struct net_device *   dev,
-                                struct iw_request_info *info,
-                                union iwreq_data *     wrqu,
-                                char *                 extra);
+int iw_handler_set_thrspy(struct net_device *dev, struct iw_request_info *info,
+                         union iwreq_data *wrqu, char *extra);
 /* Standard handler for SIOCGIWTHRSPY */
-extern int iw_handler_get_thrspy(struct net_device *   dev,
-                                struct iw_request_info *info,
-                                union iwreq_data *     wrqu,
-                                char *                 extra);
+int iw_handler_get_thrspy(struct net_device *dev, struct iw_request_info *info,
+                         union iwreq_data *wrqu, char *extra);
 /* Driver call to update spy records */
-extern void wireless_spy_update(struct net_device *    dev,
-                               unsigned char *         address,
-                               struct iw_quality *     wstats);
+void wireless_spy_update(struct net_device *dev, unsigned char *address,
+                        struct iw_quality *wstats);
 
 /************************* INLINE FUNTIONS *************************/
 /*
index df892a9..9510f87 100644 (file)
@@ -105,40 +105,40 @@ struct lapb_cb {
 };
 
 /* lapb_iface.c */
-extern void lapb_connect_confirmation(struct lapb_cb *lapb, int);
-extern void lapb_connect_indication(struct lapb_cb *lapb, int);
-extern void lapb_disconnect_confirmation(struct lapb_cb *lapb, int);
-extern void lapb_disconnect_indication(struct lapb_cb *lapb, int);
-extern int  lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *);
-extern int  lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *);
+void lapb_connect_confirmation(struct lapb_cb *lapb, int);
+void lapb_connect_indication(struct lapb_cb *lapb, int);
+void lapb_disconnect_confirmation(struct lapb_cb *lapb, int);
+void lapb_disconnect_indication(struct lapb_cb *lapb, int);
+int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *);
+int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *);
 
 /* lapb_in.c */
-extern void lapb_data_input(struct lapb_cb *lapb, struct sk_buff *);
+void lapb_data_input(struct lapb_cb *lapb, struct sk_buff *);
 
 /* lapb_out.c */
-extern void lapb_kick(struct lapb_cb *lapb);
-extern void lapb_transmit_buffer(struct lapb_cb *lapb, struct sk_buff *, int);
-extern void lapb_establish_data_link(struct lapb_cb *lapb);
-extern void lapb_enquiry_response(struct lapb_cb *lapb);
-extern void lapb_timeout_response(struct lapb_cb *lapb);
-extern void lapb_check_iframes_acked(struct lapb_cb *lapb, unsigned short);
-extern void lapb_check_need_response(struct lapb_cb *lapb, int, int);
+void lapb_kick(struct lapb_cb *lapb);
+void lapb_transmit_buffer(struct lapb_cb *lapb, struct sk_buff *, int);
+void lapb_establish_data_link(struct lapb_cb *lapb);
+void lapb_enquiry_response(struct lapb_cb *lapb);
+void lapb_timeout_response(struct lapb_cb *lapb);
+void lapb_check_iframes_acked(struct lapb_cb *lapb, unsigned short);
+void lapb_check_need_response(struct lapb_cb *lapb, int, int);
 
 /* lapb_subr.c */
-extern void lapb_clear_queues(struct lapb_cb *lapb);
-extern void lapb_frames_acked(struct lapb_cb *lapb, unsigned short);
-extern void lapb_requeue_frames(struct lapb_cb *lapb);
-extern int  lapb_validate_nr(struct lapb_cb *lapb, unsigned short);
-extern int lapb_decode(struct lapb_cb *lapb, struct sk_buff *, struct lapb_frame *);
-extern void lapb_send_control(struct lapb_cb *lapb, int, int, int);
-extern void lapb_transmit_frmr(struct lapb_cb *lapb);
+void lapb_clear_queues(struct lapb_cb *lapb);
+void lapb_frames_acked(struct lapb_cb *lapb, unsigned short);
+void lapb_requeue_frames(struct lapb_cb *lapb);
+int lapb_validate_nr(struct lapb_cb *lapb, unsigned short);
+int lapb_decode(struct lapb_cb *lapb, struct sk_buff *, struct lapb_frame *);
+void lapb_send_control(struct lapb_cb *lapb, int, int, int);
+void lapb_transmit_frmr(struct lapb_cb *lapb);
 
 /* lapb_timer.c */
-extern void lapb_start_t1timer(struct lapb_cb *lapb);
-extern void lapb_start_t2timer(struct lapb_cb *lapb);
-extern void lapb_stop_t1timer(struct lapb_cb *lapb);
-extern void lapb_stop_t2timer(struct lapb_cb *lapb);
-extern int  lapb_t1timer_running(struct lapb_cb *lapb);
+void lapb_start_t1timer(struct lapb_cb *lapb);
+void lapb_start_t2timer(struct lapb_cb *lapb);
+void lapb_stop_t1timer(struct lapb_cb *lapb);
+void lapb_stop_t2timer(struct lapb_cb *lapb);
+int lapb_t1timer_running(struct lapb_cb *lapb);
 
 /*
  * Debug levels.
index 9e7d7f0..68490cb 100644 (file)
@@ -95,29 +95,29 @@ struct hlist_nulls_head *llc_sk_laddr_hash(struct llc_sap *sap,
 extern struct list_head llc_sap_list;
 extern spinlock_t llc_sap_list_lock;
 
-extern int llc_rcv(struct sk_buff *skb, struct net_device *dev,
-                  struct packet_type *pt, struct net_device *orig_dev);
+int llc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
+           struct net_device *orig_dev);
 
-extern int llc_mac_hdr_init(struct sk_buff *skb,
-                           const unsigned char *sa, const unsigned char *da);
+int llc_mac_hdr_init(struct sk_buff *skb, const unsigned char *sa,
+                    const unsigned char *da);
 
-extern void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
-                                                  struct sk_buff *skb));
-extern void llc_remove_pack(int type);
+void llc_add_pack(int type,
+                 void (*handler)(struct llc_sap *sap, struct sk_buff *skb));
+void llc_remove_pack(int type);
 
-extern void llc_set_station_handler(void (*handler)(struct sk_buff *skb));
+void llc_set_station_handler(void (*handler)(struct sk_buff *skb));
 
-extern struct llc_sap *llc_sap_open(unsigned char lsap,
-                                   int (*rcv)(struct sk_buff *skb,
-                                              struct net_device *dev,
-                                              struct packet_type *pt,
-                                              struct net_device *orig_dev));
+struct llc_sap *llc_sap_open(unsigned char lsap,
+                            int (*rcv)(struct sk_buff *skb,
+                                       struct net_device *dev,
+                                       struct packet_type *pt,
+                                       struct net_device *orig_dev));
 static inline void llc_sap_hold(struct llc_sap *sap)
 {
        atomic_inc(&sap->refcnt);
 }
 
-extern void llc_sap_close(struct llc_sap *sap);
+void llc_sap_close(struct llc_sap *sap);
 
 static inline void llc_sap_put(struct llc_sap *sap)
 {
@@ -125,27 +125,27 @@ static inline void llc_sap_put(struct llc_sap *sap)
                llc_sap_close(sap);
 }
 
-extern struct llc_sap *llc_sap_find(unsigned char sap_value);
+struct llc_sap *llc_sap_find(unsigned char sap_value);
 
-extern int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb,
-                                    unsigned char *dmac, unsigned char dsap);
+int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb,
+                             unsigned char *dmac, unsigned char dsap);
 
-extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb);
-extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb);
+void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb);
+void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb);
 
-extern void llc_station_init(void);
-extern void llc_station_exit(void);
+void llc_station_init(void);
+void llc_station_exit(void);
 
 #ifdef CONFIG_PROC_FS
-extern int llc_proc_init(void);
-extern void llc_proc_exit(void);
+int llc_proc_init(void);
+void llc_proc_exit(void);
 #else
 #define llc_proc_init()        (0)
 #define llc_proc_exit()        do { } while(0)
 #endif /* CONFIG_PROC_FS */
 #ifdef CONFIG_SYSCTL
-extern int llc_sysctl_init(void);
-extern void llc_sysctl_exit(void);
+int llc_sysctl_init(void);
+void llc_sysctl_exit(void);
 
 extern int sysctl_llc2_ack_timeout;
 extern int sysctl_llc2_busy_timeout;
index df83f69..f3be818 100644 (file)
 
 typedef int (*llc_conn_action_t)(struct sock *sk, struct sk_buff *skb);
 
-extern int llc_conn_ac_clear_remote_busy(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ac_conn_confirm(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_data_ind(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_disc_ind(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_rst_ind(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_rst_confirm(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock* sk,
-                                                  struct sk_buff *skb);
-extern int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock* sk,
-                                                     struct sk_buff *skb);
-extern int llc_conn_ac_send_disc_cmd_p_set_x(struct sock* sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ac_send_dm_rsp_f_set_p(struct sock* sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ac_send_dm_rsp_f_set_1(struct sock* sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock* sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock* sk,
-                                              struct sk_buff *skb);
-extern int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock* sk,
-                                              struct sk_buff *skb);
-extern int llc_conn_ac_send_i_cmd_p_set_1(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_send_i_xxx_x_set_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_resend_i_xxx_x_set_0(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock* sk,
-                                                      struct sk_buff *skb);
-extern int llc_conn_ac_resend_i_rsp_f_set_1(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_rej_cmd_p_set_1(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_rej_rsp_f_set_1(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_rej_xxx_x_set_0(struct sock* sk,
+int llc_conn_ac_clear_remote_busy(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_conn_confirm(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_data_ind(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_disc_ind(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_rst_ind(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_rst_confirm(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock *sk,
                                            struct sk_buff *skb);
-extern int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_set_remote_busy(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock* sk,
+int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock *sk,
+                                              struct sk_buff *skb);
+int llc_conn_ac_send_disc_cmd_p_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_dm_rsp_f_set_p(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_dm_rsp_f_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_i_cmd_p_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_i_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_resend_i_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock *sk,
                                                struct sk_buff *skb);
-extern int llc_conn_ac_send_rr_cmd_p_set_1(struct sock* sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ac_send_rr_rsp_f_set_1(struct sock* sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ac_send_ack_rsp_f_set_1(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_rr_xxx_x_set_0(struct sock* sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ac_send_ack_xxx_x_set_0(struct sock* sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock* sk,
-                                             struct sk_buff *skb);
-extern int llc_conn_ac_send_ua_rsp_f_set_p(struct sock* sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ac_set_s_flag_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_s_flag_1(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_start_p_timer(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_start_ack_timer(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_start_rej_timer(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_start_ack_tmr_if_not_running(struct sock* sk,
-                                                   struct sk_buff *skb);
-extern int llc_conn_ac_stop_ack_timer(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_stop_p_timer(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_stop_rej_timer(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_stop_all_timers(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_stop_other_timers(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_upd_nr_received(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_inc_tx_win_size(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_dec_tx_win_size(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_upd_p_flag(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_data_flag_2(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_data_flag_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_data_flag_1(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock* sk,
-                                                        struct sk_buff *skb);
-extern int llc_conn_ac_set_p_flag_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_remote_busy_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_retry_cnt_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_cause_flag_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_cause_flag_1(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_inc_retry_cnt_by_1(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_vr_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_inc_vr_by_1(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_vs_0(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_set_vs_nr(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_rst_vs(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_upd_vs(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_disc(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_reset(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_disc_confirm(struct sock* sk, struct sk_buff *skb);
-extern u8 llc_circular_between(u8 a, u8 b, u8 c);
-extern int llc_conn_ac_send_ack_if_needed(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_adjust_npta_by_rr(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_adjust_npta_by_rnr(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_rst_sendack_flag(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_send_i_rsp_as_ack(struct sock* sk, struct sk_buff *skb);
-extern int llc_conn_ac_send_i_as_ack(struct sock* sk, struct sk_buff *skb);
+int llc_conn_ac_resend_i_rsp_f_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rej_cmd_p_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rej_rsp_f_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rej_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_remote_busy(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rr_cmd_p_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rr_rsp_f_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_ack_rsp_f_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_rr_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_ack_xxx_x_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_ua_rsp_f_set_p(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_s_flag_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_s_flag_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_start_p_timer(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_start_ack_timer(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_start_rej_timer(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_start_ack_tmr_if_not_running(struct sock *sk,
+                                            struct sk_buff *skb);
+int llc_conn_ac_stop_ack_timer(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_stop_p_timer(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_stop_rej_timer(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_stop_all_timers(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_stop_other_timers(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_upd_nr_received(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_inc_tx_win_size(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_dec_tx_win_size(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_upd_p_flag(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_data_flag_2(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_data_flag_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_data_flag_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock *sk,
+                                                 struct sk_buff *skb);
+int llc_conn_ac_set_p_flag_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_remote_busy_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_retry_cnt_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_cause_flag_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_cause_flag_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_inc_retry_cnt_by_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_vr_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_inc_vr_by_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_vs_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_set_vs_nr(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_rst_vs(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_upd_vs(struct sock *sk, struct sk_buff *skb);
+int llc_conn_disc(struct sock *sk, struct sk_buff *skb);
+int llc_conn_reset(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_disc_confirm(struct sock *sk, struct sk_buff *skb);
+u8 llc_circular_between(u8 a, u8 b, u8 c);
+int llc_conn_ac_send_ack_if_needed(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_adjust_npta_by_rr(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_adjust_npta_by_rnr(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_rst_sendack_flag(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_i_rsp_as_ack(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb);
 
-extern void llc_conn_busy_tmr_cb(unsigned long timeout_data);
-extern void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data);
-extern void llc_conn_ack_tmr_cb(unsigned long timeout_data);
-extern void llc_conn_rej_tmr_cb(unsigned long timeout_data);
+void llc_conn_busy_tmr_cb(unsigned long timeout_data);
+void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data);
+void llc_conn_ack_tmr_cb(unsigned long timeout_data);
+void llc_conn_rej_tmr_cb(unsigned long timeout_data);
 
-extern void llc_conn_set_p_flag(struct sock *sk, u8 value);
+void llc_conn_set_p_flag(struct sock *sk, u8 value);
 #endif /* LLC_C_AC_H */
index 6ca3113..3948cf1 100644 (file)
@@ -128,138 +128,93 @@ static __inline__ struct llc_conn_state_ev *llc_conn_ev(struct sk_buff *skb)
 typedef int (*llc_conn_ev_t)(struct sock *sk, struct sk_buff *skb);
 typedef int (*llc_conn_ev_qfyr_t)(struct sock *sk, struct sk_buff *skb);
 
-extern int llc_conn_ev_conn_req(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_data_req(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_disc_req(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_rst_req(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_local_busy_detected(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_local_busy_cleared(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk,
+int llc_conn_ev_conn_req(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_data_req(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_disc_req(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rst_req(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_local_busy_detected(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_local_busy_cleared(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
+                                            struct sk_buff *skb);
+int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
                                              struct sk_buff *skb);
-extern int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk,
-                                             struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
-                                                   struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk,
+int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
+                                            struct sk_buff *skb);
+int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk,
                                               struct sk_buff *skb);
-extern int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk,
-                                                     struct sk_buff *skb);
-extern int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk,
-                                                     struct sk_buff *skb);
-extern int llc_conn_ev_p_tmr_exp(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_sendack_tmr_exp(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk,
+                                              struct sk_buff *skb);
+int llc_conn_ev_p_tmr_exp(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_sendack_tmr_exp(struct sock *sk, struct sk_buff *skb);
 /* NOT_USED functions and their variations */
-extern int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_rx_any_frame(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_tx_buffer_full(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk,
+                                             struct sk_buff *skb);
+int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
+                                             struct sk_buff *skb);
+int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk,
+                                             struct sk_buff *skb);
+int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
+                                             struct sk_buff *skb);
+int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_rx_any_frame(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_tx_buffer_full(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct sk_buff *skb);
 
 /* Available connection action qualifiers */
-extern int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk,
-                                            struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk,
+int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_conn(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_disc(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_failed(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
                                            struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_conn(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_disc(struct sock *sk,
-                                           struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_failed(struct sock *sk,
-                                             struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
-                                                 struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk,
-                                             struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk,
-                                               struct sk_buff *skb);
-extern int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk,
-                                               struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk, struct sk_buff *skb);
+int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk, struct sk_buff *skb);
 
 static __inline__ int llc_conn_space(struct sock *sk, struct sk_buff *skb)
 {
index 2f97d8d..0134681 100644 (file)
@@ -95,28 +95,24 @@ static __inline__ char llc_backlog_type(struct sk_buff *skb)
        return skb->cb[sizeof(skb->cb) - 1];
 }
 
-extern struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority,
-                                struct proto *prot);
-extern void llc_sk_free(struct sock *sk);
+struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority,
+                         struct proto *prot);
+void llc_sk_free(struct sock *sk);
 
-extern void llc_sk_reset(struct sock *sk);
+void llc_sk_reset(struct sock *sk);
 
 /* Access to a connection */
-extern int llc_conn_state_process(struct sock *sk, struct sk_buff *skb);
-extern void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
-extern void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb);
-extern void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr,
-                                        u8 first_p_bit);
-extern void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr,
-                                        u8 first_f_bit);
-extern int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr,
-                                     u16 *how_many_unacked);
-extern struct sock *llc_lookup_established(struct llc_sap *sap,
-                                          struct llc_addr *daddr,
-                                          struct llc_addr *laddr);
-extern void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk);
-extern void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk);
+int llc_conn_state_process(struct sock *sk, struct sk_buff *skb);
+void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
+void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb);
+void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit);
+void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit);
+int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr, u16 *how_many_unacked);
+struct sock *llc_lookup_established(struct llc_sap *sap, struct llc_addr *daddr,
+                                   struct llc_addr *laddr);
+void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk);
+void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk);
 
-extern u8 llc_data_accept_state(u8 state);
-extern void llc_build_offset_table(void);
+u8 llc_data_accept_state(u8 state);
+void llc_build_offset_table(void);
 #endif /* LLC_CONN_H */
index f0cb909..8d5c543 100644 (file)
@@ -62,8 +62,7 @@
 #define LLC_STATUS_CONFLICT    7 /* disconnect conn */
 #define LLC_STATUS_RESET_DONE  8 /*  */
 
-extern int llc_establish_connection(struct sock *sk, u8 *lmac,
-                                   u8 *dmac, u8 dsap);
-extern int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb);
-extern int llc_send_disc(struct sock *sk);
+int llc_establish_connection(struct sock *sk, u8 *lmac, u8 *dmac, u8 dsap);
+int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb);
+int llc_send_disc(struct sock *sk);
 #endif /* LLC_IF_H */
index 5a93d13..31e2de7 100644 (file)
@@ -410,21 +410,20 @@ struct llc_frmr_info {
        u8  ind_bits;           /* indicator bits set with macro */
 } __packed;
 
-extern void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 type);
-extern void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value);
-extern void llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit);
-extern void llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit);
-extern void llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr);
-extern void llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
-extern void llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
-extern void llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
-extern void llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit);
-extern void llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit);
-extern void llc_pdu_init_as_frmr_rsp(struct sk_buff *skb,
-                                    struct llc_pdu_sn *prev_pdu,
-                                    u8 f_bit, u8 vs, u8 vr, u8 vzyxw);
-extern void llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
-extern void llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
-extern void llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
-extern void llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit);
+void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 type);
+void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value);
+void llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit);
+void llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit);
+void llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr);
+void llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
+void llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
+void llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
+void llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit);
+void llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit);
+void llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, struct llc_pdu_sn *prev_pdu,
+                             u8 f_bit, u8 vs, u8 vr, u8 vzyxw);
+void llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
+void llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
+void llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
+void llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit);
 #endif /* LLC_PDU_H */
index 37a3bbd..a61b98c 100644 (file)
 /* All action functions must look like this */
 typedef int (*llc_sap_action_t)(struct llc_sap *sap, struct sk_buff *skb);
 
-extern int llc_sap_action_unitdata_ind(struct llc_sap *sap,
-                                      struct sk_buff *skb);
-extern int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_action_report_status(struct llc_sap *sap,
-                                       struct sk_buff *skb);
-extern int llc_sap_action_xid_ind(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_action_test_ind(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_unitdata_ind(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_report_status(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_xid_ind(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_action_test_ind(struct llc_sap *sap, struct sk_buff *skb);
 #endif /* LLC_S_AC_H */
index e3acb93..84db3a5 100644 (file)
@@ -53,15 +53,14 @@ struct llc_sap;
 
 typedef int (*llc_sap_ev_t)(struct llc_sap *sap, struct sk_buff *skb);
 
-extern int llc_sap_ev_activation_req(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_rx_ui(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_unitdata_req(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_xid_req(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_rx_xid_r(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_test_req(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_rx_test_r(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_sap_ev_deactivation_req(struct llc_sap *sap,
-                                      struct sk_buff *skb);
+int llc_sap_ev_activation_req(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_rx_ui(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_unitdata_req(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_xid_req(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_rx_xid_r(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_test_req(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_rx_test_r(struct llc_sap *sap, struct sk_buff *skb);
+int llc_sap_ev_deactivation_req(struct llc_sap *sap, struct sk_buff *skb);
 #endif /* LLC_S_EV_H */
index ed25bec..1e4df9f 100644 (file)
@@ -19,18 +19,14 @@ struct net_device;
 struct sk_buff;
 struct sock;
 
-extern void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb);
-extern void llc_save_primitive(struct sock *sk, struct sk_buff* skb,
-                              unsigned char prim);
-extern struct sk_buff *llc_alloc_frame(struct sock *sk, struct net_device *dev,
-                                      u8 type, u32 data_size);
+void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb);
+void llc_save_primitive(struct sock *sk, struct sk_buff *skb,
+                       unsigned char prim);
+struct sk_buff *llc_alloc_frame(struct sock *sk, struct net_device *dev,
+                               u8 type, u32 data_size);
 
-extern void llc_build_and_send_test_pkt(struct llc_sap *sap,
-                                       struct sk_buff *skb,
-                                       unsigned char *dmac,
-                                       unsigned char dsap);
-extern void llc_build_and_send_xid_pkt(struct llc_sap *sap,
-                                      struct sk_buff *skb,
-                                      unsigned char *dmac,
-                                      unsigned char dsap);
+void llc_build_and_send_test_pkt(struct llc_sap *sap, struct sk_buff *skb,
+                                unsigned char *dmac, unsigned char dsap);
+void llc_build_and_send_xid_pkt(struct llc_sap *sap, struct sk_buff *skb,
+                               unsigned char *dmac, unsigned char dsap);
 #endif /* LLC_SAP_H */
index cc6035f..7ceed99 100644 (file)
@@ -829,6 +829,15 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
  * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
  * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
+ * @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU
+ *     subframes instead of a one huge frame for performance reasons.
+ *     All, but the last MSDU from an A-MSDU should have this flag set. E.g.
+ *     if an A-MSDU has 3 frames, the first 2 must have the flag set, while
+ *     the 3rd (last) one must not have this flag set. The flag is used to
+ *     deal with retransmission/duplication recovery properly since A-MSDU
+ *     subframes share the same sequence number. Reported subframes can be
+ *     either regular MSDU or singly A-MSDUs. Subframes must not be
+ *     interleaved with other frames.
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -859,6 +868,7 @@ enum mac80211_rx_flags {
        RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
        RX_FLAG_10MHZ                   = BIT(28),
        RX_FLAG_5MHZ                    = BIT(29),
+       RX_FLAG_AMSDU_MORE              = BIT(30),
 };
 
 #define RX_FLAG_STBC_SHIFT             26
@@ -1492,6 +1502,15 @@ struct ieee80211_tx_control {
  *
  * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
  *     only, to allow getting TBTT of a DTIM beacon.
+ *
+ * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates
+ *     and can cope with CCK rates in an aggregation session (e.g. by not
+ *     using aggregation for such frames.)
+ *
+ * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
+ *     for a single active channel while using channel contexts. When support
+ *     is not enabled the default action is to disconnect when getting the
+ *     CSA frame.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -1522,6 +1541,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF              = 1<<25,
        IEEE80211_HW_TIMING_BEACON_ONLY                 = 1<<26,
        IEEE80211_HW_SUPPORTS_HT_CCK_RATES              = 1<<27,
+       IEEE80211_HW_CHANCTX_STA_CSA                    = 1<<28,
 };
 
 /**
@@ -2666,6 +2686,10 @@ enum ieee80211_roc_type {
  *     zero using ieee80211_csa_is_complete() after the beacon has been
  *     transmitted and then call ieee80211_csa_finish().
  *
+ * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
+ *     information in bss_conf is set up and the beacon can be retrieved. A
+ *     channel context is bound before this is called.
+ * @leave_ibss: Leave the IBSS again.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2857,6 +2881,9 @@ struct ieee80211_ops {
        void (*channel_switch_beacon)(struct ieee80211_hw *hw,
                                      struct ieee80211_vif *vif,
                                      struct cfg80211_chan_def *chandef);
+
+       int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+       void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 };
 
 /**
@@ -3919,6 +3946,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
                                                    struct ieee80211_vif *vif),
                                                void *data);
 
+/**
+ * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
+ *
+ * This function iterates over the interfaces associated with a given
+ * hardware that are currently active and calls the callback for them.
+ * This version can only be used while holding the RTNL.
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
+                                             u32 iter_flags,
+                                             void (*iterator)(void *data,
+                                               u8 *mac,
+                                               struct ieee80211_vif *vif),
+                                             void *data);
+
 /**
  * ieee80211_queue_work - add work onto the mac80211 workqueue
  *
@@ -4525,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
                                    struct cfg80211_wowlan_wakeup *wakeup,
                                    gfp_t gfp);
 
+/**
+ * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface
+ * @skb: frame to be sent from within the driver
+ * @band: the band to transmit on
+ * @sta: optional pointer to get the station to send the frame to
+ *
+ * Note: must be called under RCU lock
+ */
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta);
+
 #endif /* MAC80211_H */
index d0d11df..807d6b7 100644 (file)
@@ -133,7 +133,7 @@ struct ieee802154_ops {
 
 /* Basic interface to register ieee802154 device */
 struct ieee802154_dev *
-ieee802154_alloc_device(size_t priv_data_lex, struct ieee802154_ops *ops);
+ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops);
 void ieee802154_free_device(struct ieee802154_dev *dev);
 int ieee802154_register_device(struct ieee802154_dev *dev);
 void ieee802154_unregister_device(struct ieee802154_dev *dev);
index 4fbf02a..31912c3 100644 (file)
@@ -112,6 +112,7 @@ struct mrp_applicant {
        struct mrp_application  *app;
        struct net_device       *dev;
        struct timer_list       join_timer;
+       struct timer_list       periodic_timer;
 
        spinlock_t              lock;
        struct sk_buff_head     queue;
@@ -125,19 +126,17 @@ struct mrp_port {
        struct rcu_head                 rcu;
 };
 
-extern int     mrp_register_application(struct mrp_application *app);
-extern void    mrp_unregister_application(struct mrp_application *app);
+int mrp_register_application(struct mrp_application *app);
+void mrp_unregister_application(struct mrp_application *app);
 
-extern int     mrp_init_applicant(struct net_device *dev,
-                                   struct mrp_application *app);
-extern void    mrp_uninit_applicant(struct net_device *dev,
-                                     struct mrp_application *app);
+int mrp_init_applicant(struct net_device *dev, struct mrp_application *app);
+void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *app);
 
-extern int     mrp_request_join(const struct net_device *dev,
-                                 const struct mrp_application *app,
-                                 const void *value, u8 len, u8 type);
-extern void    mrp_request_leave(const struct net_device *dev,
-                                  const struct mrp_application *app,
-                                  const void *value, u8 len, u8 type);
+int mrp_request_join(const struct net_device *dev,
+                    const struct mrp_application *app,
+                    const void *value, u8 len, u8 type);
+void mrp_request_leave(const struct net_device *dev,
+                      const struct mrp_application *app,
+                      const void *value, u8 len, u8 type);
 
 #endif /* _NET_MRP_H */
index ea0cc26..6bbda34 100644 (file)
@@ -110,8 +110,8 @@ struct ndisc_options {
 
 #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
 
-extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
-                                                struct ndisc_options *ndopts);
+struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
+                                         struct ndisc_options *ndopts);
 
 /*
  * Return the padding between the option length and the start of the
@@ -189,60 +189,51 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
        return n;
 }
 
-extern int                     ndisc_init(void);
-extern int                     ndisc_late_init(void);
+int ndisc_init(void);
+int ndisc_late_init(void);
 
-extern void                    ndisc_late_cleanup(void);
-extern void                    ndisc_cleanup(void);
+void ndisc_late_cleanup(void);
+void ndisc_cleanup(void);
 
-extern int                     ndisc_rcv(struct sk_buff *skb);
+int ndisc_rcv(struct sk_buff *skb);
 
-extern void                    ndisc_send_ns(struct net_device *dev,
-                                             struct neighbour *neigh,
-                                             const struct in6_addr *solicit,
-                                             const struct in6_addr *daddr,
-                                             const struct in6_addr *saddr);
+void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
+                  const struct in6_addr *solicit,
+                  const struct in6_addr *daddr, const struct in6_addr *saddr);
 
-extern void                    ndisc_send_rs(struct net_device *dev,
-                                             const struct in6_addr *saddr,
-                                             const struct in6_addr *daddr);
-extern void                    ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
-                                             const struct in6_addr *daddr,
-                                             const struct in6_addr *solicited_addr,
-                                             bool router, bool solicited, bool override,
-                                             bool inc_opt);
+void ndisc_send_rs(struct net_device *dev,
+                  const struct in6_addr *saddr, const struct in6_addr *daddr);
+void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
+                  const struct in6_addr *daddr,
+                  const struct in6_addr *solicited_addr,
+                  bool router, bool solicited, bool override, bool inc_opt);
 
-extern void                    ndisc_send_redirect(struct sk_buff *skb,
-                                                   const struct in6_addr *target);
+void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target);
 
-extern int                     ndisc_mc_map(const struct in6_addr *addr, char *buf,
-                                            struct net_device *dev, int dir);
+int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev,
+                int dir);
 
 
 /*
  *     IGMP
  */
-extern int                     igmp6_init(void);
+int igmp6_init(void);
 
-extern void                    igmp6_cleanup(void);
+void igmp6_cleanup(void);
 
-extern int                     igmp6_event_query(struct sk_buff *skb);
+int igmp6_event_query(struct sk_buff *skb);
 
-extern int                     igmp6_event_report(struct sk_buff *skb);
+int igmp6_event_report(struct sk_buff *skb);
 
 
 #ifdef CONFIG_SYSCTL
-extern int                     ndisc_ifinfo_sysctl_change(struct ctl_table *ctl,
-                                                          int write,
-                                                          void __user *buffer,
-                                                          size_t *lenp,
-                                                          loff_t *ppos);
+int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write,
+                              void __user *buffer, size_t *lenp, loff_t *ppos);
 int ndisc_ifinfo_sysctl_strategy(struct ctl_table *ctl,
                                 void __user *oldval, size_t __user *oldlenp,
                                 void __user *newval, size_t newlen);
 #endif
 
-extern void                    inet6_ifinfo_notify(int event,
-                                                   struct inet6_dev *idev);
+void inet6_ifinfo_notify(int event, struct inet6_dev *idev);
 
 #endif
index 1313456..da68c9a 100644 (file)
@@ -22,6 +22,7 @@
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 #include <net/netns/conntrack.h>
 #endif
+#include <net/netns/nftables.h>
 #include <net/netns/xfrm.h>
 
 struct user_namespace;
@@ -74,6 +75,7 @@ struct net {
        struct hlist_head       *dev_index_head;
        unsigned int            dev_base_seq;   /* protected by rtnl_mutex */
        int                     ifindex;
+       unsigned int            dev_unreg_count;
 
        /* core fib_rules */
        struct list_head        rules_ops;
@@ -100,6 +102,9 @@ struct net {
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        struct netns_ct         ct;
 #endif
+#if defined(CONFIG_NF_TABLES) || defined(CONFIG_NF_TABLES_MODULE)
+       struct netns_nftables   nft;
+#endif
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
        struct netns_nf_frag    nf_frag;
 #endif
@@ -136,8 +141,8 @@ struct net {
 extern struct net init_net;
 
 #ifdef CONFIG_NET_NS
-extern struct net *copy_net_ns(unsigned long flags,
-       struct user_namespace *user_ns, struct net *old_net);
+struct net *copy_net_ns(unsigned long flags, struct user_namespace *user_ns,
+                       struct net *old_net);
 
 #else /* CONFIG_NET_NS */
 #include <linux/sched.h>
@@ -154,11 +159,11 @@ static inline struct net *copy_net_ns(unsigned long flags,
 
 extern struct list_head net_namespace_list;
 
-extern struct net *get_net_ns_by_pid(pid_t pid);
-extern struct net *get_net_ns_by_fd(int pid);
+struct net *get_net_ns_by_pid(pid_t pid);
+struct net *get_net_ns_by_fd(int pid);
 
 #ifdef CONFIG_NET_NS
-extern void __put_net(struct net *net);
+void __put_net(struct net *net);
 
 static inline struct net *get_net(struct net *net)
 {
@@ -190,7 +195,7 @@ int net_eq(const struct net *net1, const struct net *net2)
        return net1 == net2;
 }
 
-extern void net_drop_ns(void *);
+void net_drop_ns(void *);
 
 #else
 
@@ -307,19 +312,19 @@ struct pernet_operations {
  * device which caused kernel oops, and panics during network
  * namespace cleanup.   So please don't get this wrong.
  */
-extern int register_pernet_subsys(struct pernet_operations *);
-extern void unregister_pernet_subsys(struct pernet_operations *);
-extern int register_pernet_device(struct pernet_operations *);
-extern void unregister_pernet_device(struct pernet_operations *);
+int register_pernet_subsys(struct pernet_operations *);
+void unregister_pernet_subsys(struct pernet_operations *);
+int register_pernet_device(struct pernet_operations *);
+void unregister_pernet_device(struct pernet_operations *);
 
 struct ctl_table;
 struct ctl_table_header;
 
 #ifdef CONFIG_SYSCTL
-extern int net_sysctl_init(void);
-extern struct ctl_table_header *register_net_sysctl(struct net *net,
-       const char *path, struct ctl_table *table);
-extern void unregister_net_sysctl_table(struct ctl_table_header *header);
+int net_sysctl_init(void);
+struct ctl_table_header *register_net_sysctl(struct net *net, const char *path,
+                                            struct ctl_table *table);
+void unregister_net_sysctl_table(struct ctl_table_header *header);
 #else
 static inline int net_sysctl_init(void) { return 0; }
 static inline struct ctl_table_header *register_net_sysctl(struct net *net,
index fe630dd..d8bbb38 100644 (file)
@@ -26,8 +26,8 @@ enum netevent_notif_type {
        NETEVENT_REDIRECT,         /* arg is struct netevent_redirect ptr */
 };
 
-extern int register_netevent_notifier(struct notifier_block *nb);
-extern int unregister_netevent_notifier(struct notifier_block *nb);
-extern int call_netevent_notifiers(unsigned long val, void *v);
+int register_netevent_notifier(struct notifier_block *nb);
+int unregister_netevent_notifier(struct notifier_block *nb);
+int call_netevent_notifiers(unsigned long val, void *v);
 
 #endif
index 7573d52..6c3d12e 100644 (file)
@@ -16,9 +16,9 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4;
 extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4;
 extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp;
 
-extern int nf_conntrack_ipv4_compat_init(void);
-extern void nf_conntrack_ipv4_compat_fini(void);
+int nf_conntrack_ipv4_compat_init(void);
+void nf_conntrack_ipv4_compat_fini(void);
 
-extern void need_ipv4_conntrack(void);
+void need_ipv4_conntrack(void);
 
 #endif /*_NF_CONNTRACK_IPV4_H*/
index 6b00ea3..f01ef20 100644 (file)
@@ -1,6 +1,6 @@
 #ifndef _NF_DEFRAG_IPV4_H
 #define _NF_DEFRAG_IPV4_H
 
-extern void nf_defrag_ipv4_enable(void);
+void nf_defrag_ipv4_enable(void);
 
 #endif /* _NF_DEFRAG_IPV4_H */
index fd79c9a..27666d8 100644 (file)
@@ -1,15 +1,12 @@
 #ifndef _NF_DEFRAG_IPV6_H
 #define _NF_DEFRAG_IPV6_H
 
-extern void nf_defrag_ipv6_enable(void);
+void nf_defrag_ipv6_enable(void);
 
-extern int nf_ct_frag6_init(void);
-extern void nf_ct_frag6_cleanup(void);
-extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user);
-extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
-                              struct net_device *in,
-                              struct net_device *out,
-                              int (*okfn)(struct sk_buff *));
+int nf_ct_frag6_init(void);
+void nf_ct_frag6_cleanup(void);
+struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user);
+void nf_ct_frag6_consume_orig(struct sk_buff *skb);
 
 struct inet_frags_ctl;
 
index 0c1288a..01ea6ee 100644 (file)
@@ -139,15 +139,13 @@ static inline struct net *nf_ct_net(const struct nf_conn *ct)
 }
 
 /* Alter reply tuple (maybe alter helper). */
-extern void
-nf_conntrack_alter_reply(struct nf_conn *ct,
-                        const struct nf_conntrack_tuple *newreply);
+void nf_conntrack_alter_reply(struct nf_conn *ct,
+                             const struct nf_conntrack_tuple *newreply);
 
 /* Is this tuple taken? (ignoring any belonging to the given
    conntrack). */
-extern int
-nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
-                        const struct nf_conn *ignored_conntrack);
+int nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
+                            const struct nf_conn *ignored_conntrack);
 
 /* Return conntrack_info and tuple hash for given skb. */
 static inline struct nf_conn *
@@ -165,37 +163,34 @@ static inline void nf_ct_put(struct nf_conn *ct)
 }
 
 /* Protocol module loading */
-extern int nf_ct_l3proto_try_module_get(unsigned short l3proto);
-extern void nf_ct_l3proto_module_put(unsigned short l3proto);
+int nf_ct_l3proto_try_module_get(unsigned short l3proto);
+void nf_ct_l3proto_module_put(unsigned short l3proto);
 
 /*
  * Allocate a hashtable of hlist_head (if nulls == 0),
  * or hlist_nulls_head (if nulls == 1)
  */
-extern void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls);
+void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls);
 
-extern void nf_ct_free_hashtable(void *hash, unsigned int size);
+void nf_ct_free_hashtable(void *hash, unsigned int size);
 
-extern struct nf_conntrack_tuple_hash *
+struct nf_conntrack_tuple_hash *
 __nf_conntrack_find(struct net *net, u16 zone,
                    const struct nf_conntrack_tuple *tuple);
 
-extern int nf_conntrack_hash_check_insert(struct nf_conn *ct);
+int nf_conntrack_hash_check_insert(struct nf_conn *ct);
 bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report);
 
-extern void nf_conntrack_flush_report(struct net *net, u32 portid, int report);
+void nf_conntrack_flush_report(struct net *net, u32 portid, int report);
 
-extern bool nf_ct_get_tuplepr(const struct sk_buff *skb,
-                             unsigned int nhoff, u_int16_t l3num,
-                             struct nf_conntrack_tuple *tuple);
-extern bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
-                                const struct nf_conntrack_tuple *orig);
+bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
+                      u_int16_t l3num, struct nf_conntrack_tuple *tuple);
+bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
+                         const struct nf_conntrack_tuple *orig);
 
-extern void __nf_ct_refresh_acct(struct nf_conn *ct,
-                                enum ip_conntrack_info ctinfo,
-                                const struct sk_buff *skb,
-                                unsigned long extra_jiffies,
-                                int do_acct);
+void __nf_ct_refresh_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
+                         const struct sk_buff *skb,
+                         unsigned long extra_jiffies, int do_acct);
 
 /* Refresh conntrack for this many jiffies and do accounting */
 static inline void nf_ct_refresh_acct(struct nf_conn *ct,
@@ -214,10 +209,8 @@ static inline void nf_ct_refresh(struct nf_conn *ct,
        __nf_ct_refresh_acct(ct, 0, skb, extra_jiffies, 0);
 }
 
-extern bool __nf_ct_kill_acct(struct nf_conn *ct,
-                             enum ip_conntrack_info ctinfo,
-                             const struct sk_buff *skb,
-                             int do_acct);
+bool __nf_ct_kill_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
+                      const struct sk_buff *skb, int do_acct);
 
 /* kill conntrack and do accounting */
 static inline bool nf_ct_kill_acct(struct nf_conn *ct,
@@ -244,19 +237,17 @@ static inline struct nf_conn *nf_ct_untracked_get(void)
 {
        return &__raw_get_cpu_var(nf_conntrack_untracked);
 }
-extern void nf_ct_untracked_status_or(unsigned long bits);
+void nf_ct_untracked_status_or(unsigned long bits);
 
 /* Iterate over all conntracks: if iter returns true, it's deleted. */
-extern void
-nf_ct_iterate_cleanup(struct net *net,
-                     int (*iter)(struct nf_conn *i, void *data),
-                     void *data, u32 portid, int report);
-extern void nf_conntrack_free(struct nf_conn *ct);
-extern struct nf_conn *
-nf_conntrack_alloc(struct net *net, u16 zone,
-                  const struct nf_conntrack_tuple *orig,
-                  const struct nf_conntrack_tuple *repl,
-                  gfp_t gfp);
+void nf_ct_iterate_cleanup(struct net *net,
+                          int (*iter)(struct nf_conn *i, void *data),
+                          void *data, u32 portid, int report);
+void nf_conntrack_free(struct nf_conn *ct);
+struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone,
+                                  const struct nf_conntrack_tuple *orig,
+                                  const struct nf_conntrack_tuple *repl,
+                                  gfp_t gfp);
 
 static inline int nf_ct_is_template(const struct nf_conn *ct)
 {
@@ -287,7 +278,7 @@ static inline bool nf_is_loopback_packet(const struct sk_buff *skb)
 
 struct kernel_param;
 
-extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
+int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
 extern unsigned int nf_conntrack_htable_size;
 extern unsigned int nf_conntrack_max;
 extern unsigned int nf_conntrack_hash_rnd;
index 2bdb7a1..79d8d16 100644 (file)
@@ -19,17 +19,21 @@ struct nf_conn_counter {
        atomic64_t bytes;
 };
 
+struct nf_conn_acct {
+       struct nf_conn_counter counter[IP_CT_DIR_MAX];
+};
+
 static inline
-struct nf_conn_counter *nf_conn_acct_find(const struct nf_conn *ct)
+struct nf_conn_acct *nf_conn_acct_find(const struct nf_conn *ct)
 {
        return nf_ct_ext_find(ct, NF_CT_EXT_ACCT);
 }
 
 static inline
-struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp)
+struct nf_conn_acct *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp)
 {
        struct net *net = nf_ct_net(ct);
-       struct nf_conn_counter *acct;
+       struct nf_conn_acct *acct;
 
        if (!net->ct.sysctl_acct)
                return NULL;
@@ -42,8 +46,8 @@ struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp)
        return acct;
 };
 
-extern unsigned int
-seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir);
+unsigned int seq_print_acct(struct seq_file *s, const struct nf_conn *ct,
+                           int dir);
 
 /* Check if connection tracking accounting is enabled */
 static inline bool nf_ct_acct_enabled(struct net *net)
@@ -57,9 +61,9 @@ static inline void nf_ct_set_acct(struct net *net, bool enable)
        net->ct.sysctl_acct = enable;
 }
 
-extern int nf_conntrack_acct_pernet_init(struct net *net);
-extern void nf_conntrack_acct_pernet_fini(struct net *net);
+int nf_conntrack_acct_pernet_init(struct net *net);
+void nf_conntrack_acct_pernet_fini(struct net *net);
 
-extern int nf_conntrack_acct_init(void);
-extern void nf_conntrack_acct_fini(void);
+int nf_conntrack_acct_init(void);
+void nf_conntrack_acct_fini(void);
 #endif /* _NF_CONNTRACK_ACCT_H */
index fb2b623..15308b8 100644 (file)
 /* This header is used to share core functionality between the
    standalone connection tracking module, and the compatibility layer's use
    of connection tracking. */
-extern unsigned int nf_conntrack_in(struct net *net,
-                                   u_int8_t pf,
-                                   unsigned int hooknum,
-                                   struct sk_buff *skb);
-
-extern int nf_conntrack_init_net(struct net *net);
-extern void nf_conntrack_cleanup_net(struct net *net);
-extern void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list);
-
-extern int nf_conntrack_proto_pernet_init(struct net *net);
-extern void nf_conntrack_proto_pernet_fini(struct net *net);
-
-extern int nf_conntrack_proto_init(void);
-extern void nf_conntrack_proto_fini(void);
-
-extern int nf_conntrack_init_start(void);
-extern void nf_conntrack_cleanup_start(void);
-
-extern void nf_conntrack_init_end(void);
-extern void nf_conntrack_cleanup_end(void);
-
-extern bool
-nf_ct_get_tuple(const struct sk_buff *skb,
-               unsigned int nhoff,
-               unsigned int dataoff,
-               u_int16_t l3num,
-               u_int8_t protonum,
-               struct nf_conntrack_tuple *tuple,
-               const struct nf_conntrack_l3proto *l3proto,
-               const struct nf_conntrack_l4proto *l4proto);
-
-extern bool
-nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
-                  const struct nf_conntrack_tuple *orig,
-                  const struct nf_conntrack_l3proto *l3proto,
-                  const struct nf_conntrack_l4proto *l4proto);
+unsigned int nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
+                            struct sk_buff *skb);
+
+int nf_conntrack_init_net(struct net *net);
+void nf_conntrack_cleanup_net(struct net *net);
+void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list);
+
+int nf_conntrack_proto_pernet_init(struct net *net);
+void nf_conntrack_proto_pernet_fini(struct net *net);
+
+int nf_conntrack_proto_init(void);
+void nf_conntrack_proto_fini(void);
+
+int nf_conntrack_init_start(void);
+void nf_conntrack_cleanup_start(void);
+
+void nf_conntrack_init_end(void);
+void nf_conntrack_cleanup_end(void);
+
+bool nf_ct_get_tuple(const struct sk_buff *skb, unsigned int nhoff,
+                    unsigned int dataoff, u_int16_t l3num, u_int8_t protonum,
+                    struct nf_conntrack_tuple *tuple,
+                    const struct nf_conntrack_l3proto *l3proto,
+                    const struct nf_conntrack_l4proto *l4proto);
+
+bool nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
+                       const struct nf_conntrack_tuple *orig,
+                       const struct nf_conntrack_l3proto *l3proto,
+                       const struct nf_conntrack_l4proto *l4proto);
 
 /* Find a connection corresponding to a tuple. */
-extern struct nf_conntrack_tuple_hash *
+struct nf_conntrack_tuple_hash *
 nf_conntrack_find_get(struct net *net, u16 zone,
                      const struct nf_conntrack_tuple *tuple);
 
-extern int __nf_conntrack_confirm(struct sk_buff *skb);
+int __nf_conntrack_confirm(struct sk_buff *skb);
 
 /* Confirm a connection: returns NF_DROP if packet must be dropped. */
 static inline int nf_conntrack_confirm(struct sk_buff *skb)
index 092dc65..0e3d08e 100644 (file)
@@ -68,10 +68,12 @@ struct nf_ct_event_notifier {
        int (*fcn)(unsigned int events, struct nf_ct_event *item);
 };
 
-extern int nf_conntrack_register_notifier(struct net *net, struct nf_ct_event_notifier *nb);
-extern void nf_conntrack_unregister_notifier(struct net *net, struct nf_ct_event_notifier *nb);
+int nf_conntrack_register_notifier(struct net *net,
+                                  struct nf_ct_event_notifier *nb);
+void nf_conntrack_unregister_notifier(struct net *net,
+                                     struct nf_ct_event_notifier *nb);
 
-extern void nf_ct_deliver_cached_events(struct nf_conn *ct);
+void nf_ct_deliver_cached_events(struct nf_conn *ct);
 
 static inline void
 nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
@@ -166,8 +168,10 @@ struct nf_exp_event_notifier {
        int (*fcn)(unsigned int events, struct nf_exp_event *item);
 };
 
-extern int nf_ct_expect_register_notifier(struct net *net, struct nf_exp_event_notifier *nb);
-extern void nf_ct_expect_unregister_notifier(struct net *net, struct nf_exp_event_notifier *nb);
+int nf_ct_expect_register_notifier(struct net *net,
+                                  struct nf_exp_event_notifier *nb);
+void nf_ct_expect_unregister_notifier(struct net *net,
+                                     struct nf_exp_event_notifier *nb);
 
 static inline void
 nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
@@ -207,11 +211,11 @@ nf_ct_expect_event(enum ip_conntrack_expect_events event,
        nf_ct_expect_event_report(event, exp, 0, 0);
 }
 
-extern int nf_conntrack_ecache_pernet_init(struct net *net);
-extern void nf_conntrack_ecache_pernet_fini(struct net *net);
+int nf_conntrack_ecache_pernet_init(struct net *net);
+void nf_conntrack_ecache_pernet_fini(struct net *net);
 
-extern int nf_conntrack_ecache_init(void);
-extern void nf_conntrack_ecache_fini(void);
+int nf_conntrack_ecache_init(void);
+void nf_conntrack_ecache_fini(void);
 #else /* CONFIG_NF_CONNTRACK_EVENTS */
 
 static inline void nf_conntrack_event_cache(enum ip_conntrack_events event,
index 88a1d40..956b175 100644 (file)
@@ -36,7 +36,7 @@ enum nf_ct_ext_id {
 #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help
 #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat
 #define NF_CT_EXT_SEQADJ_TYPE struct nf_conn_seqadj
-#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter
+#define NF_CT_EXT_ACCT_TYPE struct nf_conn_acct
 #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache
 #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
 #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp
@@ -73,7 +73,7 @@ static inline void *__nf_ct_ext_find(const struct nf_conn *ct, u8 id)
        ((id##_TYPE *)__nf_ct_ext_find((ext), (id)))
 
 /* Destroy all relationships */
-extern void __nf_ct_ext_destroy(struct nf_conn *ct);
+void __nf_ct_ext_destroy(struct nf_conn *ct);
 static inline void nf_ct_ext_destroy(struct nf_conn *ct)
 {
        if (ct->ext)
index 26c4ae5..6cf614b 100644 (file)
@@ -52,21 +52,24 @@ struct nf_conntrack_helper {
        unsigned int queue_num;         /* For user-space helpers. */
 };
 
-extern struct nf_conntrack_helper *
-__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum);
+struct nf_conntrack_helper *__nf_conntrack_helper_find(const char *name,
+                                                      u16 l3num, u8 protonum);
 
-extern struct nf_conntrack_helper *
-nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum);
+struct nf_conntrack_helper *nf_conntrack_helper_try_module_get(const char *name,
+                                                              u16 l3num,
+                                                              u8 protonum);
 
-extern int nf_conntrack_helper_register(struct nf_conntrack_helper *);
-extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
+int nf_conntrack_helper_register(struct nf_conntrack_helper *);
+void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
 
-extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, struct nf_conntrack_helper *helper, gfp_t gfp);
+struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct,
+                                         struct nf_conntrack_helper *helper,
+                                         gfp_t gfp);
 
-extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
-                                    gfp_t flags);
+int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
+                             gfp_t flags);
 
-extern void nf_ct_helper_destroy(struct nf_conn *ct);
+void nf_ct_helper_destroy(struct nf_conn *ct);
 
 static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct)
 {
@@ -82,17 +85,16 @@ static inline void *nfct_help_data(const struct nf_conn *ct)
        return (void *)help->data;
 }
 
-extern int nf_conntrack_helper_pernet_init(struct net *net);
-extern void nf_conntrack_helper_pernet_fini(struct net *net);
+int nf_conntrack_helper_pernet_init(struct net *net);
+void nf_conntrack_helper_pernet_fini(struct net *net);
 
-extern int nf_conntrack_helper_init(void);
-extern void nf_conntrack_helper_fini(void);
+int nf_conntrack_helper_init(void);
+void nf_conntrack_helper_fini(void);
 
-extern int nf_conntrack_broadcast_help(struct sk_buff *skb,
-                                      unsigned int protoff,
-                                      struct nf_conn *ct,
-                                      enum ip_conntrack_info ctinfo,
-                                      unsigned int timeout);
+int nf_conntrack_broadcast_help(struct sk_buff *skb, unsigned int protoff,
+                               struct nf_conn *ct,
+                               enum ip_conntrack_info ctinfo,
+                               unsigned int timeout);
 
 struct nf_ct_helper_expectfn {
        struct list_head head;
index 3bb89ea..3efab70 100644 (file)
@@ -77,17 +77,17 @@ struct nf_conntrack_l3proto {
 extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX];
 
 /* Protocol pernet registration. */
-extern int nf_ct_l3proto_pernet_register(struct net *net,
-                                        struct nf_conntrack_l3proto *proto);
-extern void nf_ct_l3proto_pernet_unregister(struct net *net,
-                                           struct nf_conntrack_l3proto *proto);
+int nf_ct_l3proto_pernet_register(struct net *net,
+                                 struct nf_conntrack_l3proto *proto);
+void nf_ct_l3proto_pernet_unregister(struct net *net,
+                                    struct nf_conntrack_l3proto *proto);
 
 /* Protocol global registration. */
-extern int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto);
-extern void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto);
+int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto);
+void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto);
 
-extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto);
-extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p);
+struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto);
+void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p);
 
 /* Existing built-in protocols */
 extern struct nf_conntrack_l3proto nf_conntrack_l3proto_generic;
index b411d7b..4c8d573 100644 (file)
@@ -114,22 +114,22 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic;
 
 #define MAX_NF_CT_PROTO 256
 
-extern struct nf_conntrack_l4proto *
-__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto);
+struct nf_conntrack_l4proto *__nf_ct_l4proto_find(u_int16_t l3proto,
+                                                 u_int8_t l4proto);
 
-extern struct nf_conntrack_l4proto *
-nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto);
-extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p);
+struct nf_conntrack_l4proto *nf_ct_l4proto_find_get(u_int16_t l3proto,
+                                                   u_int8_t l4proto);
+void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p);
 
 /* Protocol pernet registration. */
-extern int nf_ct_l4proto_pernet_register(struct net *net,
-                                        struct nf_conntrack_l4proto *proto);
-extern void nf_ct_l4proto_pernet_unregister(struct net *net,
-                                           struct nf_conntrack_l4proto *proto);
+int nf_ct_l4proto_pernet_register(struct net *net,
+                                 struct nf_conntrack_l4proto *proto);
+void nf_ct_l4proto_pernet_unregister(struct net *net,
+                                    struct nf_conntrack_l4proto *proto);
 
 /* Protocol global registration. */
-extern int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto);
-extern void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto);
+int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto);
+void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto);
 
 static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn)
 {
@@ -140,11 +140,11 @@ static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn)
 }
 
 /* Generic netlink helpers */
-extern int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb,
-                                     const struct nf_conntrack_tuple *tuple);
-extern int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],
-                                     struct nf_conntrack_tuple *t);
-extern int nf_ct_port_nlattr_tuple_size(void);
+int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb,
+                              const struct nf_conntrack_tuple *tuple);
+int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],
+                              struct nf_conntrack_tuple *t);
+int nf_ct_port_nlattr_tuple_size(void);
 extern const struct nla_policy nf_ct_port_nla_policy[];
 
 #ifdef CONFIG_SYSCTL
index f6177a5..4b33629 100644 (file)
@@ -30,22 +30,18 @@ static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct)
        return nf_ct_ext_add(ct, NF_CT_EXT_SEQADJ, GFP_ATOMIC);
 }
 
-extern int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
-                            s32 off);
-extern int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
-                           __be32 seq, s32 off);
-extern void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
-                                struct nf_conn *ct,
-                                enum ip_conntrack_info ctinfo,
-                                s32 off);
-
-extern int nf_ct_seq_adjust(struct sk_buff *skb,
-                           struct nf_conn *ct, enum ip_conntrack_info ctinfo,
-                           unsigned int protoff);
-extern s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir,
-                           u32 seq);
-
-extern int nf_conntrack_seqadj_init(void);
-extern void nf_conntrack_seqadj_fini(void);
+int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
+                     s32 off);
+int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
+                    __be32 seq, s32 off);
+void nf_ct_tcp_seqadj_set(struct sk_buff *skb, struct nf_conn *ct,
+                         enum ip_conntrack_info ctinfo, s32 off);
+
+int nf_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
+                    enum ip_conntrack_info ctinfo, unsigned int protoff);
+s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir, u32 seq);
+
+int nf_conntrack_seqadj_init(void);
+void nf_conntrack_seqadj_fini(void);
 
 #endif /* _NF_CONNTRACK_SEQADJ_H */
index 806f54a..6793614 100644 (file)
@@ -56,22 +56,20 @@ struct synproxy_options {
 
 struct tcphdr;
 struct xt_synproxy_info;
-extern void synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
-                                  const struct tcphdr *th,
-                                  struct synproxy_options *opts);
-extern unsigned int synproxy_options_size(const struct synproxy_options *opts);
-extern void synproxy_build_options(struct tcphdr *th,
-                                  const struct synproxy_options *opts);
+bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
+                           const struct tcphdr *th,
+                           struct synproxy_options *opts);
+unsigned int synproxy_options_size(const struct synproxy_options *opts);
+void synproxy_build_options(struct tcphdr *th,
+                           const struct synproxy_options *opts);
 
-extern void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
-                                          struct synproxy_options *opts);
-extern void synproxy_check_timestamp_cookie(struct synproxy_options *opts);
+void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info,
+                                   struct synproxy_options *opts);
+void synproxy_check_timestamp_cookie(struct synproxy_options *opts);
 
-extern unsigned int synproxy_tstamp_adjust(struct sk_buff *skb,
-                                          unsigned int protoff,
-                                          struct tcphdr *th,
-                                          struct nf_conn *ct,
-                                          enum ip_conntrack_info ctinfo,
-                                          const struct nf_conn_synproxy *synproxy);
+unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
+                                   struct tcphdr *th, struct nf_conn *ct,
+                                   enum ip_conntrack_info ctinfo,
+                                   const struct nf_conn_synproxy *synproxy);
 
 #endif /* _NF_CONNTRACK_SYNPROXY_H */
index d23aceb..6230871 100644 (file)
@@ -76,8 +76,8 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct,
 }
 
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-extern int nf_conntrack_timeout_init(void);
-extern void nf_conntrack_timeout_fini(void);
+int nf_conntrack_timeout_init(void);
+void nf_conntrack_timeout_fini(void);
 #else
 static inline int nf_conntrack_timeout_init(void)
 {
index b004614..300ae22 100644 (file)
@@ -48,11 +48,11 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable)
 }
 
 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-extern int nf_conntrack_tstamp_pernet_init(struct net *net);
-extern void nf_conntrack_tstamp_pernet_fini(struct net *net);
+int nf_conntrack_tstamp_pernet_init(struct net *net);
+void nf_conntrack_tstamp_pernet_fini(struct net *net);
 
-extern int nf_conntrack_tstamp_init(void);
-extern void nf_conntrack_tstamp_fini(void);
+int nf_conntrack_tstamp_init(void);
+void nf_conntrack_tstamp_fini(void);
 #else
 static inline int nf_conntrack_tstamp_pernet_init(struct net *net)
 {
index 59a1924..07eaaf6 100644 (file)
@@ -41,13 +41,16 @@ struct nf_conn_nat {
 };
 
 /* Set up the info structure to map into this range. */
-extern unsigned int nf_nat_setup_info(struct nf_conn *ct,
-                                     const struct nf_nat_range *range,
-                                     enum nf_nat_manip_type maniptype);
+unsigned int nf_nat_setup_info(struct nf_conn *ct,
+                              const struct nf_nat_range *range,
+                              enum nf_nat_manip_type maniptype);
+
+extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct,
+                                             unsigned int hooknum);
 
 /* Is this tuple already taken? (not by us)*/
-extern int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
-                            const struct nf_conn *ignored_conntrack);
+int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
+                     const struct nf_conn *ignored_conntrack);
 
 static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct)
 {
index 972e1e4..fbfd1ba 100644 (file)
@@ -7,12 +7,10 @@
 /* This header used to share core functionality between the standalone
    NAT module, and the compatibility layer's use of NAT for masquerading. */
 
-extern unsigned int nf_nat_packet(struct nf_conn *ct,
-                                 enum ip_conntrack_info ctinfo,
-                                 unsigned int hooknum,
-                                 struct sk_buff *skb);
+unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
+                          unsigned int hooknum, struct sk_buff *skb);
 
-extern int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family);
+int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family);
 
 static inline int nf_nat_initialized(struct nf_conn *ct,
                                     enum nf_nat_manip_type manip)
index 404324d..01bcc6b 100644 (file)
@@ -7,14 +7,11 @@
 struct sk_buff;
 
 /* These return true or false. */
-extern int __nf_nat_mangle_tcp_packet(struct sk_buff *skb,
-                                     struct nf_conn *ct,
-                                     enum ip_conntrack_info ctinfo,
-                                     unsigned int protoff,
-                                     unsigned int match_offset,
-                                     unsigned int match_len,
-                                     const char *rep_buffer,
-                                     unsigned int rep_len, bool adjust);
+int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct,
+                              enum ip_conntrack_info ctinfo,
+                              unsigned int protoff, unsigned int match_offset,
+                              unsigned int match_len, const char *rep_buffer,
+                              unsigned int rep_len, bool adjust);
 
 static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb,
                                           struct nf_conn *ct,
@@ -30,18 +27,14 @@ static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb,
                                          rep_buffer, rep_len, true);
 }
 
-extern int nf_nat_mangle_udp_packet(struct sk_buff *skb,
-                                   struct nf_conn *ct,
-                                   enum ip_conntrack_info ctinfo,
-                                   unsigned int protoff,
-                                   unsigned int match_offset,
-                                   unsigned int match_len,
-                                   const char *rep_buffer,
-                                   unsigned int rep_len);
+int nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct,
+                            enum ip_conntrack_info ctinfo,
+                            unsigned int protoff, unsigned int match_offset,
+                            unsigned int match_len, const char *rep_buffer,
+                            unsigned int rep_len);
 
 /* Setup NAT on this expected conntrack so it follows master, but goes
  * to port ct->master->saved_proto. */
-extern void nf_nat_follow_master(struct nf_conn *ct,
-                                struct nf_conntrack_expect *this);
+void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this);
 
 #endif
index bd3b97e..5a2919b 100644 (file)
@@ -35,18 +35,15 @@ struct nf_nat_l3proto {
                                   struct nf_nat_range *range);
 };
 
-extern int nf_nat_l3proto_register(const struct nf_nat_l3proto *);
-extern void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *);
-extern const struct nf_nat_l3proto *__nf_nat_l3proto_find(u8 l3proto);
-
-extern int nf_nat_icmp_reply_translation(struct sk_buff *skb,
-                                        struct nf_conn *ct,
-                                        enum ip_conntrack_info ctinfo,
-                                        unsigned int hooknum);
-extern int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
-                                          struct nf_conn *ct,
-                                          enum ip_conntrack_info ctinfo,
-                                          unsigned int hooknum,
-                                          unsigned int hdrlen);
+int nf_nat_l3proto_register(const struct nf_nat_l3proto *);
+void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *);
+const struct nf_nat_l3proto *__nf_nat_l3proto_find(u8 l3proto);
+
+int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
+                                 enum ip_conntrack_info ctinfo,
+                                 unsigned int hooknum);
+int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
+                                   enum ip_conntrack_info ctinfo,
+                                   unsigned int hooknum, unsigned int hdrlen);
 
 #endif /* _NF_NAT_L3PROTO_H */
index 24feb68..12f4cc8 100644 (file)
@@ -42,10 +42,11 @@ struct nf_nat_l4proto {
 };
 
 /* Protocol registration. */
-extern int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto);
-extern void nf_nat_l4proto_unregister(u8 l3proto, const struct nf_nat_l4proto *l4proto);
+int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto);
+void nf_nat_l4proto_unregister(u8 l3proto,
+                              const struct nf_nat_l4proto *l4proto);
 
-extern const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto);
+const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto);
 
 /* Built-in protocols. */
 extern const struct nf_nat_l4proto nf_nat_l4proto_tcp;
@@ -54,19 +55,18 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_icmp;
 extern const struct nf_nat_l4proto nf_nat_l4proto_icmpv6;
 extern const struct nf_nat_l4proto nf_nat_l4proto_unknown;
 
-extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple,
-                                   enum nf_nat_manip_type maniptype,
-                                   const union nf_conntrack_man_proto *min,
-                                   const union nf_conntrack_man_proto *max);
+bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple,
+                            enum nf_nat_manip_type maniptype,
+                            const union nf_conntrack_man_proto *min,
+                            const union nf_conntrack_man_proto *max);
 
-extern void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
-                                       struct nf_conntrack_tuple *tuple,
-                                       const struct nf_nat_range *range,
-                                       enum nf_nat_manip_type maniptype,
-                                       const struct nf_conn *ct,
-                                       u16 *rover);
+void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
+                                struct nf_conntrack_tuple *tuple,
+                                const struct nf_nat_range *range,
+                                enum nf_nat_manip_type maniptype,
+                                const struct nf_conn *ct, u16 *rover);
 
-extern int nf_nat_l4proto_nlattr_to_range(struct nlattr *tb[],
-                                         struct nf_nat_range *range);
+int nf_nat_l4proto_nlattr_to_range(struct nlattr *tb[],
+                                  struct nf_nat_range *range);
 
 #endif /*_NF_NAT_L4PROTO_H*/
index aaba4bb..c1d5b3e 100644 (file)
@@ -28,7 +28,7 @@ struct nf_queue_handler {
 
 void nf_register_queue_handler(const struct nf_queue_handler *qh);
 void nf_unregister_queue_handler(void);
-extern void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
+void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
 
 bool nf_queue_entry_get_refs(struct nf_queue_entry *entry);
 void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
new file mode 100644 (file)
index 0000000..5a91abf
--- /dev/null
@@ -0,0 +1,519 @@
+#ifndef _NET_NF_TABLES_H
+#define _NET_NF_TABLES_H
+
+#include <linux/list.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netlink.h>
+
+#define NFT_JUMP_STACK_SIZE    16
+
+struct nft_pktinfo {
+       struct sk_buff                  *skb;
+       const struct net_device         *in;
+       const struct net_device         *out;
+       u8                              hooknum;
+       u8                              nhoff;
+       u8                              thoff;
+       /* for x_tables compatibility */
+       struct xt_action_param          xt;
+};
+
+static inline void nft_set_pktinfo(struct nft_pktinfo *pkt,
+                                  const struct nf_hook_ops *ops,
+                                  struct sk_buff *skb,
+                                  const struct net_device *in,
+                                  const struct net_device *out)
+{
+       pkt->skb = skb;
+       pkt->in = pkt->xt.in = in;
+       pkt->out = pkt->xt.out = out;
+       pkt->hooknum = pkt->xt.hooknum = ops->hooknum;
+       pkt->xt.family = ops->pf;
+}
+
+struct nft_data {
+       union {
+               u32                             data[4];
+               struct {
+                       u32                     verdict;
+                       struct nft_chain        *chain;
+               };
+       };
+} __attribute__((aligned(__alignof__(u64))));
+
+static inline int nft_data_cmp(const struct nft_data *d1,
+                              const struct nft_data *d2,
+                              unsigned int len)
+{
+       return memcmp(d1->data, d2->data, len);
+}
+
+static inline void nft_data_copy(struct nft_data *dst,
+                                const struct nft_data *src)
+{
+       BUILD_BUG_ON(__alignof__(*dst) != __alignof__(u64));
+       *(u64 *)&dst->data[0] = *(u64 *)&src->data[0];
+       *(u64 *)&dst->data[2] = *(u64 *)&src->data[2];
+}
+
+static inline void nft_data_debug(const struct nft_data *data)
+{
+       pr_debug("data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n",
+                data->data[0], data->data[1],
+                data->data[2], data->data[3]);
+}
+
+/**
+ *     struct nft_ctx - nf_tables rule/set context
+ *
+ *     @net: net namespace
+ *     @skb: netlink skb
+ *     @nlh: netlink message header
+ *     @afi: address family info
+ *     @table: the table the chain is contained in
+ *     @chain: the chain the rule is contained in
+ *     @nla: netlink attributes
+ */
+struct nft_ctx {
+       struct net                      *net;
+       const struct sk_buff            *skb;
+       const struct nlmsghdr           *nlh;
+       const struct nft_af_info        *afi;
+       const struct nft_table          *table;
+       const struct nft_chain          *chain;
+       const struct nlattr * const     *nla;
+};
+
+struct nft_data_desc {
+       enum nft_data_types             type;
+       unsigned int                    len;
+};
+
+int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
+                 struct nft_data_desc *desc, const struct nlattr *nla);
+void nft_data_uninit(const struct nft_data *data, enum nft_data_types type);
+int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
+                 enum nft_data_types type, unsigned int len);
+
+static inline enum nft_data_types nft_dreg_to_type(enum nft_registers reg)
+{
+       return reg == NFT_REG_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE;
+}
+
+static inline enum nft_registers nft_type_to_reg(enum nft_data_types type)
+{
+       return type == NFT_DATA_VERDICT ? NFT_REG_VERDICT : NFT_REG_1;
+}
+
+int nft_validate_input_register(enum nft_registers reg);
+int nft_validate_output_register(enum nft_registers reg);
+int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
+                          const struct nft_data *data,
+                          enum nft_data_types type);
+
+/**
+ *     struct nft_set_elem - generic representation of set elements
+ *
+ *     @cookie: implementation specific element cookie
+ *     @key: element key
+ *     @data: element data (maps only)
+ *     @flags: element flags (end of interval)
+ *
+ *     The cookie can be used to store a handle to the element for subsequent
+ *     removal.
+ */
+struct nft_set_elem {
+       void                    *cookie;
+       struct nft_data         key;
+       struct nft_data         data;
+       u32                     flags;
+};
+
+struct nft_set;
+struct nft_set_iter {
+       unsigned int    count;
+       unsigned int    skip;
+       int             err;
+       int             (*fn)(const struct nft_ctx *ctx,
+                             const struct nft_set *set,
+                             const struct nft_set_iter *iter,
+                             const struct nft_set_elem *elem);
+};
+
+/**
+ *     struct nft_set_ops - nf_tables set operations
+ *
+ *     @lookup: look up an element within the set
+ *     @insert: insert new element into set
+ *     @remove: remove element from set
+ *     @walk: iterate over all set elemeennts
+ *     @privsize: function to return size of set private data
+ *     @init: initialize private data of new set instance
+ *     @destroy: destroy private data of set instance
+ *     @list: nf_tables_set_ops list node
+ *     @owner: module reference
+ *     @features: features supported by the implementation
+ */
+struct nft_set_ops {
+       bool                            (*lookup)(const struct nft_set *set,
+                                                 const struct nft_data *key,
+                                                 struct nft_data *data);
+       int                             (*get)(const struct nft_set *set,
+                                              struct nft_set_elem *elem);
+       int                             (*insert)(const struct nft_set *set,
+                                                 const struct nft_set_elem *elem);
+       void                            (*remove)(const struct nft_set *set,
+                                                 const struct nft_set_elem *elem);
+       void                            (*walk)(const struct nft_ctx *ctx,
+                                               const struct nft_set *set,
+                                               struct nft_set_iter *iter);
+
+       unsigned int                    (*privsize)(const struct nlattr * const nla[]);
+       int                             (*init)(const struct nft_set *set,
+                                               const struct nlattr * const nla[]);
+       void                            (*destroy)(const struct nft_set *set);
+
+       struct list_head                list;
+       struct module                   *owner;
+       u32                             features;
+};
+
+int nft_register_set(struct nft_set_ops *ops);
+void nft_unregister_set(struct nft_set_ops *ops);
+
+/**
+ *     struct nft_set - nf_tables set instance
+ *
+ *     @list: table set list node
+ *     @bindings: list of set bindings
+ *     @name: name of the set
+ *     @ktype: key type (numeric type defined by userspace, not used in the kernel)
+ *     @dtype: data type (verdict or numeric type defined by userspace)
+ *     @ops: set ops
+ *     @flags: set flags
+ *     @klen: key length
+ *     @dlen: data length
+ *     @data: private set data
+ */
+struct nft_set {
+       struct list_head                list;
+       struct list_head                bindings;
+       char                            name[IFNAMSIZ];
+       u32                             ktype;
+       u32                             dtype;
+       /* runtime data below here */
+       const struct nft_set_ops        *ops ____cacheline_aligned;
+       u16                             flags;
+       u8                              klen;
+       u8                              dlen;
+       unsigned char                   data[]
+               __attribute__((aligned(__alignof__(u64))));
+};
+
+static inline void *nft_set_priv(const struct nft_set *set)
+{
+       return (void *)set->data;
+}
+
+struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
+                                    const struct nlattr *nla);
+
+/**
+ *     struct nft_set_binding - nf_tables set binding
+ *
+ *     @list: set bindings list node
+ *     @chain: chain containing the rule bound to the set
+ *
+ *     A set binding contains all information necessary for validation
+ *     of new elements added to a bound set.
+ */
+struct nft_set_binding {
+       struct list_head                list;
+       const struct nft_chain          *chain;
+};
+
+int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+                      struct nft_set_binding *binding);
+void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+                         struct nft_set_binding *binding);
+
+
+/**
+ *     struct nft_expr_type - nf_tables expression type
+ *
+ *     @select_ops: function to select nft_expr_ops
+ *     @ops: default ops, used when no select_ops functions is present
+ *     @list: used internally
+ *     @name: Identifier
+ *     @owner: module reference
+ *     @policy: netlink attribute policy
+ *     @maxattr: highest netlink attribute number
+ */
+struct nft_expr_type {
+       const struct nft_expr_ops       *(*select_ops)(const struct nft_ctx *,
+                                                      const struct nlattr * const tb[]);
+       const struct nft_expr_ops       *ops;
+       struct list_head                list;
+       const char                      *name;
+       struct module                   *owner;
+       const struct nla_policy         *policy;
+       unsigned int                    maxattr;
+};
+
+/**
+ *     struct nft_expr_ops - nf_tables expression operations
+ *
+ *     @eval: Expression evaluation function
+ *     @size: full expression size, including private data size
+ *     @init: initialization function
+ *     @destroy: destruction function
+ *     @dump: function to dump parameters
+ *     @type: expression type
+ *     @validate: validate expression, called during loop detection
+ *     @data: extra data to attach to this expression operation
+ */
+struct nft_expr;
+struct nft_expr_ops {
+       void                            (*eval)(const struct nft_expr *expr,
+                                               struct nft_data data[NFT_REG_MAX + 1],
+                                               const struct nft_pktinfo *pkt);
+       unsigned int                    size;
+
+       int                             (*init)(const struct nft_ctx *ctx,
+                                               const struct nft_expr *expr,
+                                               const struct nlattr * const tb[]);
+       void                            (*destroy)(const struct nft_expr *expr);
+       int                             (*dump)(struct sk_buff *skb,
+                                               const struct nft_expr *expr);
+       int                             (*validate)(const struct nft_ctx *ctx,
+                                                   const struct nft_expr *expr,
+                                                   const struct nft_data **data);
+       const struct nft_expr_type      *type;
+       void                            *data;
+};
+
+#define NFT_EXPR_MAXATTR               16
+#define NFT_EXPR_SIZE(size)            (sizeof(struct nft_expr) + \
+                                        ALIGN(size, __alignof__(struct nft_expr)))
+
+/**
+ *     struct nft_expr - nf_tables expression
+ *
+ *     @ops: expression ops
+ *     @data: expression private data
+ */
+struct nft_expr {
+       const struct nft_expr_ops       *ops;
+       unsigned char                   data[];
+};
+
+static inline void *nft_expr_priv(const struct nft_expr *expr)
+{
+       return (void *)expr->data;
+}
+
+/**
+ *     struct nft_rule - nf_tables rule
+ *
+ *     @list: used internally
+ *     @rcu_head: used internally for rcu
+ *     @handle: rule handle
+ *     @genmask: generation mask
+ *     @dlen: length of expression data
+ *     @data: expression data
+ */
+struct nft_rule {
+       struct list_head                list;
+       struct rcu_head                 rcu_head;
+       u64                             handle:46,
+                                       genmask:2,
+                                       dlen:16;
+       unsigned char                   data[]
+               __attribute__((aligned(__alignof__(struct nft_expr))));
+};
+
+/**
+ *     struct nft_rule_trans - nf_tables rule update in transaction
+ *
+ *     @list: used internally
+ *     @rule: rule that needs to be updated
+ *     @chain: chain that this rule belongs to
+ *     @table: table for which this chain applies
+ *     @nlh: netlink header of the message that contain this update
+ *     @family: family expressesed as AF_*
+ */
+struct nft_rule_trans {
+       struct list_head                list;
+       struct nft_rule                 *rule;
+       const struct nft_chain          *chain;
+       const struct nft_table          *table;
+       const struct nlmsghdr           *nlh;
+       u8                              family;
+};
+
+static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
+{
+       return (struct nft_expr *)&rule->data[0];
+}
+
+static inline struct nft_expr *nft_expr_next(const struct nft_expr *expr)
+{
+       return ((void *)expr) + expr->ops->size;
+}
+
+static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule)
+{
+       return (struct nft_expr *)&rule->data[rule->dlen];
+}
+
+/*
+ * The last pointer isn't really necessary, but the compiler isn't able to
+ * determine that the result of nft_expr_last() is always the same since it
+ * can't assume that the dlen value wasn't changed within calls in the loop.
+ */
+#define nft_rule_for_each_expr(expr, last, rule) \
+       for ((expr) = nft_expr_first(rule), (last) = nft_expr_last(rule); \
+            (expr) != (last); \
+            (expr) = nft_expr_next(expr))
+
+enum nft_chain_flags {
+       NFT_BASE_CHAIN                  = 0x1,
+};
+
+/**
+ *     struct nft_chain - nf_tables chain
+ *
+ *     @rules: list of rules in the chain
+ *     @list: used internally
+ *     @rcu_head: used internally
+ *     @net: net namespace that this chain belongs to
+ *     @table: table that this chain belongs to
+ *     @handle: chain handle
+ *     @flags: bitmask of enum nft_chain_flags
+ *     @use: number of jump references to this chain
+ *     @level: length of longest path to this chain
+ *     @name: name of the chain
+ */
+struct nft_chain {
+       struct list_head                rules;
+       struct list_head                list;
+       struct rcu_head                 rcu_head;
+       struct net                      *net;
+       struct nft_table                *table;
+       u64                             handle;
+       u8                              flags;
+       u16                             use;
+       u16                             level;
+       char                            name[NFT_CHAIN_MAXNAMELEN];
+};
+
+enum nft_chain_type {
+       NFT_CHAIN_T_DEFAULT = 0,
+       NFT_CHAIN_T_ROUTE,
+       NFT_CHAIN_T_NAT,
+       NFT_CHAIN_T_MAX
+};
+
+struct nft_stats {
+       u64 bytes;
+       u64 pkts;
+};
+
+/**
+ *     struct nft_base_chain - nf_tables base chain
+ *
+ *     @ops: netfilter hook ops
+ *     @type: chain type
+ *     @policy: default policy
+ *     @stats: per-cpu chain stats
+ *     @chain: the chain
+ */
+struct nft_base_chain {
+       struct nf_hook_ops              ops;
+       enum nft_chain_type             type;
+       u8                              policy;
+       struct nft_stats __percpu       *stats;
+       struct nft_chain                chain;
+};
+
+static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chain)
+{
+       return container_of(chain, struct nft_base_chain, chain);
+}
+
+unsigned int nft_do_chain_pktinfo(struct nft_pktinfo *pkt,
+                                 const struct nf_hook_ops *ops);
+
+/**
+ *     struct nft_table - nf_tables table
+ *
+ *     @list: used internally
+ *     @chains: chains in the table
+ *     @sets: sets in the table
+ *     @hgenerator: handle generator state
+ *     @use: number of chain references to this table
+ *     @flags: table flag (see enum nft_table_flags)
+ *     @name: name of the table
+ */
+struct nft_table {
+       struct list_head                list;
+       struct list_head                chains;
+       struct list_head                sets;
+       u64                             hgenerator;
+       u32                             use;
+       u16                             flags;
+       char                            name[];
+};
+
+/**
+ *     struct nft_af_info - nf_tables address family info
+ *
+ *     @list: used internally
+ *     @family: address family
+ *     @nhooks: number of hooks in this family
+ *     @owner: module owner
+ *     @tables: used internally
+ *     @hooks: hookfn overrides for packet validation
+ */
+struct nft_af_info {
+       struct list_head                list;
+       int                             family;
+       unsigned int                    nhooks;
+       struct module                   *owner;
+       struct list_head                tables;
+       nf_hookfn                       *hooks[NF_MAX_HOOKS];
+};
+
+int nft_register_afinfo(struct net *, struct nft_af_info *);
+void nft_unregister_afinfo(struct nft_af_info *);
+
+struct nf_chain_type {
+       unsigned int            hook_mask;
+       const char              *name;
+       enum nft_chain_type     type;
+       nf_hookfn               *fn[NF_MAX_HOOKS];
+       struct module           *me;
+       int                     family;
+};
+
+int nft_register_chain_type(struct nf_chain_type *);
+void nft_unregister_chain_type(struct nf_chain_type *);
+
+int nft_register_expr(struct nft_expr_type *);
+void nft_unregister_expr(struct nft_expr_type *);
+
+#define MODULE_ALIAS_NFT_FAMILY(family)        \
+       MODULE_ALIAS("nft-afinfo-" __stringify(family))
+
+#define MODULE_ALIAS_NFT_CHAIN(family, name) \
+       MODULE_ALIAS("nft-chain-" __stringify(family) "-" name)
+
+#define MODULE_ALIAS_NFT_EXPR(name) \
+       MODULE_ALIAS("nft-expr-" name)
+
+#define MODULE_ALIAS_NFT_SET() \
+       MODULE_ALIAS("nft-set")
+
+#endif /* _NET_NF_TABLES_H */
diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h
new file mode 100644 (file)
index 0000000..cf2b7ae
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _NET_NF_TABLES_CORE_H
+#define _NET_NF_TABLES_CORE_H
+
+int nf_tables_core_module_init(void);
+void nf_tables_core_module_exit(void);
+
+int nft_immediate_module_init(void);
+void nft_immediate_module_exit(void);
+
+struct nft_cmp_fast_expr {
+       u32                     data;
+       enum nft_registers      sreg:8;
+       u8                      len;
+};
+
+extern const struct nft_expr_ops nft_cmp_fast_ops;
+
+int nft_cmp_module_init(void);
+void nft_cmp_module_exit(void);
+
+int nft_lookup_module_init(void);
+void nft_lookup_module_exit(void);
+
+int nft_bitwise_module_init(void);
+void nft_bitwise_module_exit(void);
+
+int nft_byteorder_module_init(void);
+void nft_byteorder_module_exit(void);
+
+struct nft_payload {
+       enum nft_payload_bases  base:8;
+       u8                      offset;
+       u8                      len;
+       enum nft_registers      dreg:8;
+};
+
+extern const struct nft_expr_ops nft_payload_fast_ops;
+
+int nft_payload_module_init(void);
+void nft_payload_module_exit(void);
+
+#endif /* _NET_NF_TABLES_CORE_H */
diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h
new file mode 100644 (file)
index 0000000..1be1c2c
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _NF_TABLES_IPV4_H_
+#define _NF_TABLES_IPV4_H_
+
+#include <net/netfilter/nf_tables.h>
+#include <net/ip.h>
+
+static inline void
+nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
+                    const struct nf_hook_ops *ops,
+                    struct sk_buff *skb,
+                    const struct net_device *in,
+                    const struct net_device *out)
+{
+       struct iphdr *ip;
+
+       nft_set_pktinfo(pkt, ops, skb, in, out);
+
+       pkt->xt.thoff = ip_hdrlen(pkt->skb);
+       ip = ip_hdr(pkt->skb);
+       pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
+}
+
+#endif
diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h
new file mode 100644 (file)
index 0000000..4a9b88a
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _NF_TABLES_IPV6_H_
+#define _NF_TABLES_IPV6_H_
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/ipv6.h>
+
+static inline int
+nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
+                    const struct nf_hook_ops *ops,
+                    struct sk_buff *skb,
+                    const struct net_device *in,
+                    const struct net_device *out)
+{
+       int protohdr, thoff = 0;
+       unsigned short frag_off;
+
+       nft_set_pktinfo(pkt, ops, skb, in, out);
+
+       protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
+       /* If malformed, drop it */
+       if (protohdr < 0)
+               return -1;
+
+       pkt->xt.thoff = thoff;
+       pkt->xt.fragoff = frag_off;
+
+       return 0;
+}
+
+#endif
index 495c71f..79f45e1 100644 (file)
@@ -16,7 +16,7 @@ struct xt_rateest {
        struct rcu_head                 rcu;
 };
 
-extern struct xt_rateest *xt_rateest_lookup(const char *name);
-extern void xt_rateest_put(struct xt_rateest *est);
+struct xt_rateest *xt_rateest_lookup(const char *name);
+void xt_rateest_put(struct xt_rateest *est);
 
 #endif /* _XT_RATEEST_H */
index 9690b0f..2b47eaa 100644 (file)
@@ -225,44 +225,31 @@ struct nl_info {
        u32                     portid;
 };
 
-extern int             netlink_rcv_skb(struct sk_buff *skb,
-                                       int (*cb)(struct sk_buff *,
-                                                 struct nlmsghdr *));
-extern int             nlmsg_notify(struct sock *sk, struct sk_buff *skb,
-                                    u32 portid, unsigned int group, int report,
-                                    gfp_t flags);
-
-extern int             nla_validate(const struct nlattr *head,
-                                    int len, int maxtype,
-                                    const struct nla_policy *policy);
-extern int             nla_parse(struct nlattr **tb, int maxtype,
-                                 const struct nlattr *head, int len,
-                                 const struct nla_policy *policy);
-extern int             nla_policy_len(const struct nla_policy *, int);
-extern struct nlattr * nla_find(const struct nlattr *head,
-                                int len, int attrtype);
-extern size_t          nla_strlcpy(char *dst, const struct nlattr *nla,
-                                   size_t dstsize);
-extern int             nla_memcpy(void *dest, const struct nlattr *src, int count);
-extern int             nla_memcmp(const struct nlattr *nla, const void *data,
-                                  size_t size);
-extern int             nla_strcmp(const struct nlattr *nla, const char *str);
-extern struct nlattr * __nla_reserve(struct sk_buff *skb, int attrtype,
-                                     int attrlen);
-extern void *          __nla_reserve_nohdr(struct sk_buff *skb, int attrlen);
-extern struct nlattr * nla_reserve(struct sk_buff *skb, int attrtype,
-                                   int attrlen);
-extern void *          nla_reserve_nohdr(struct sk_buff *skb, int attrlen);
-extern void            __nla_put(struct sk_buff *skb, int attrtype,
-                                 int attrlen, const void *data);
-extern void            __nla_put_nohdr(struct sk_buff *skb, int attrlen,
-                                       const void *data);
-extern int             nla_put(struct sk_buff *skb, int attrtype,
-                               int attrlen, const void *data);
-extern int             nla_put_nohdr(struct sk_buff *skb, int attrlen,
-                                     const void *data);
-extern int             nla_append(struct sk_buff *skb, int attrlen,
-                                  const void *data);
+int netlink_rcv_skb(struct sk_buff *skb,
+                   int (*cb)(struct sk_buff *, struct nlmsghdr *));
+int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
+                unsigned int group, int report, gfp_t flags);
+
+int nla_validate(const struct nlattr *head, int len, int maxtype,
+                const struct nla_policy *policy);
+int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
+             int len, const struct nla_policy *policy);
+int nla_policy_len(const struct nla_policy *, int);
+struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype);
+size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize);
+int nla_memcpy(void *dest, const struct nlattr *src, int count);
+int nla_memcmp(const struct nlattr *nla, const void *data, size_t size);
+int nla_strcmp(const struct nlattr *nla, const char *str);
+struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen);
+void *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen);
+struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen);
+void *nla_reserve_nohdr(struct sk_buff *skb, int attrlen);
+void __nla_put(struct sk_buff *skb, int attrtype, int attrlen,
+              const void *data);
+void __nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data);
+int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data);
+int nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data);
+int nla_append(struct sk_buff *skb, int attrlen, const void *data);
 
 /**************************************************************************
  * Netlink Messages
index bf2ec22..ee520cb 100644 (file)
@@ -15,6 +15,10 @@ struct fib_rules_ops;
 struct hlist_head;
 struct fib_table;
 struct sock;
+struct local_ports {
+       seqlock_t       lock;
+       int             range[2];
+};
 
 struct netns_ipv4 {
 #ifdef CONFIG_SYSCTL
@@ -62,10 +66,11 @@ struct netns_ipv4 {
        int sysctl_icmp_ratemask;
        int sysctl_icmp_errors_use_inbound_ifaddr;
 
+       struct local_ports sysctl_local_ports;
+
        int sysctl_tcp_ecn;
 
        kgid_t sysctl_ping_group_range[2];
-       long sysctl_tcp_mem[3];
 
        atomic_t dev_addr_genid;
 
diff --git a/include/net/netns/nftables.h b/include/net/netns/nftables.h
new file mode 100644 (file)
index 0000000..15d056d
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef _NETNS_NFTABLES_H_
+#define _NETNS_NFTABLES_H_
+
+#include <linux/list.h>
+
+struct nft_af_info;
+
+struct netns_nftables {
+       struct list_head        af_info;
+       struct list_head        commit_list;
+       struct nft_af_info      *ipv4;
+       struct nft_af_info      *ipv6;
+       struct nft_af_info      *arp;
+       struct nft_af_info      *bridge;
+       u8                      gencursor;
+       u8                      genctr;
+};
+
+#endif
index 121dcf8..110350a 100644 (file)
@@ -183,51 +183,50 @@ extern int  sysctl_netrom_routing_control;
 extern int  sysctl_netrom_link_fails_count;
 extern int  sysctl_netrom_reset_circuit;
 
-extern int  nr_rx_frame(struct sk_buff *, struct net_device *);
-extern void nr_destroy_socket(struct sock *);
+int nr_rx_frame(struct sk_buff *, struct net_device *);
+void nr_destroy_socket(struct sock *);
 
 /* nr_dev.c */
-extern int  nr_rx_ip(struct sk_buff *, struct net_device *);
-extern void nr_setup(struct net_device *);
+int nr_rx_ip(struct sk_buff *, struct net_device *);
+void nr_setup(struct net_device *);
 
 /* nr_in.c */
-extern int  nr_process_rx_frame(struct sock *, struct sk_buff *);
+int nr_process_rx_frame(struct sock *, struct sk_buff *);
 
 /* nr_loopback.c */
-extern void nr_loopback_init(void);
-extern void nr_loopback_clear(void);
-extern int  nr_loopback_queue(struct sk_buff *);
+void nr_loopback_init(void);
+void nr_loopback_clear(void);
+int nr_loopback_queue(struct sk_buff *);
 
 /* nr_out.c */
-extern void nr_output(struct sock *, struct sk_buff *);
-extern void nr_send_nak_frame(struct sock *);
-extern void nr_kick(struct sock *);
-extern void nr_transmit_buffer(struct sock *, struct sk_buff *);
-extern void nr_establish_data_link(struct sock *);
-extern void nr_enquiry_response(struct sock *);
-extern void nr_check_iframes_acked(struct sock *, unsigned short);
+void nr_output(struct sock *, struct sk_buff *);
+void nr_send_nak_frame(struct sock *);
+void nr_kick(struct sock *);
+void nr_transmit_buffer(struct sock *, struct sk_buff *);
+void nr_establish_data_link(struct sock *);
+void nr_enquiry_response(struct sock *);
+void nr_check_iframes_acked(struct sock *, unsigned short);
 
 /* nr_route.c */
-extern void nr_rt_device_down(struct net_device *);
-extern struct net_device *nr_dev_first(void);
-extern struct net_device *nr_dev_get(ax25_address *);
-extern int  nr_rt_ioctl(unsigned int, void __user *);
-extern void nr_link_failed(ax25_cb *, int);
-extern int  nr_route_frame(struct sk_buff *, ax25_cb *);
+void nr_rt_device_down(struct net_device *);
+struct net_device *nr_dev_first(void);
+struct net_device *nr_dev_get(ax25_address *);
+int nr_rt_ioctl(unsigned int, void __user *);
+void nr_link_failed(ax25_cb *, int);
+int nr_route_frame(struct sk_buff *, ax25_cb *);
 extern const struct file_operations nr_nodes_fops;
 extern const struct file_operations nr_neigh_fops;
-extern void nr_rt_free(void);
+void nr_rt_free(void);
 
 /* nr_subr.c */
-extern void nr_clear_queues(struct sock *);
-extern void nr_frames_acked(struct sock *, unsigned short);
-extern void nr_requeue_frames(struct sock *);
-extern int  nr_validate_nr(struct sock *, unsigned short);
-extern int  nr_in_rx_window(struct sock *, unsigned short);
-extern void nr_write_internal(struct sock *, int);
+void nr_clear_queues(struct sock *);
+void nr_frames_acked(struct sock *, unsigned short);
+void nr_requeue_frames(struct sock *);
+int nr_validate_nr(struct sock *, unsigned short);
+int nr_in_rx_window(struct sock *, unsigned short);
+void nr_write_internal(struct sock *, int);
 
-extern void __nr_transmit_reply(struct sk_buff *skb, int mine,
-       unsigned char cmdflags);
+void __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags);
 
 /*
  * This routine is called when a Connect Acknowledge with the Choke Flag
@@ -247,24 +246,24 @@ do {                                                                      \
        __nr_transmit_reply((skb), (mine), NR_RESET);                   \
 } while (0)
 
-extern void nr_disconnect(struct sock *, int);
+void nr_disconnect(struct sock *, int);
 
 /* nr_timer.c */
-extern void nr_init_timers(struct sock *sk);
-extern void nr_start_heartbeat(struct sock *);
-extern void nr_start_t1timer(struct sock *);
-extern void nr_start_t2timer(struct sock *);
-extern void nr_start_t4timer(struct sock *);
-extern void nr_start_idletimer(struct sock *);
-extern void nr_stop_heartbeat(struct sock *);
-extern void nr_stop_t1timer(struct sock *);
-extern void nr_stop_t2timer(struct sock *);
-extern void nr_stop_t4timer(struct sock *);
-extern void nr_stop_idletimer(struct sock *);
-extern int  nr_t1timer_running(struct sock *);
+void nr_init_timers(struct sock *sk);
+void nr_start_heartbeat(struct sock *);
+void nr_start_t1timer(struct sock *);
+void nr_start_t2timer(struct sock *);
+void nr_start_t4timer(struct sock *);
+void nr_start_idletimer(struct sock *);
+void nr_stop_heartbeat(struct sock *);
+void nr_stop_t1timer(struct sock *);
+void nr_stop_t2timer(struct sock *);
+void nr_stop_t4timer(struct sock *);
+void nr_stop_idletimer(struct sock *);
+int nr_t1timer_running(struct sock *);
 
 /* sysctl_net_netrom.c */
-extern void nr_register_sysctl(void);
-extern void nr_unregister_sysctl(void);
+void nr_register_sysctl(void);
+void nr_unregister_sysctl(void);
 
 #endif
diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h
new file mode 100644 (file)
index 0000000..36acecd
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#ifndef __NFC_DIGITAL_H
+#define __NFC_DIGITAL_H
+
+#include <linux/skbuff.h>
+#include <net/nfc/nfc.h>
+
+/**
+ * Configuration types for in_configure_hw and tg_configure_hw.
+ */
+enum {
+       NFC_DIGITAL_CONFIG_RF_TECH = 0,
+       NFC_DIGITAL_CONFIG_FRAMING,
+};
+
+/**
+ * RF technology values passed as param argument to in_configure_hw and
+ * tg_configure_hw for NFC_DIGITAL_CONFIG_RF_TECH configuration type.
+ */
+enum {
+       NFC_DIGITAL_RF_TECH_106A = 0,
+       NFC_DIGITAL_RF_TECH_212F,
+       NFC_DIGITAL_RF_TECH_424F,
+
+       NFC_DIGITAL_RF_TECH_LAST,
+};
+
+/**
+ * Framing configuration passed as param argument to in_configure_hw and
+ * tg_configure_hw for NFC_DIGITAL_CONFIG_FRAMING configuration type.
+ */
+enum {
+       NFC_DIGITAL_FRAMING_NFCA_SHORT = 0,
+       NFC_DIGITAL_FRAMING_NFCA_STANDARD,
+       NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A,
+
+       NFC_DIGITAL_FRAMING_NFCA_T1T,
+       NFC_DIGITAL_FRAMING_NFCA_T2T,
+       NFC_DIGITAL_FRAMING_NFCA_NFC_DEP,
+
+       NFC_DIGITAL_FRAMING_NFCF,
+       NFC_DIGITAL_FRAMING_NFCF_T3T,
+       NFC_DIGITAL_FRAMING_NFCF_NFC_DEP,
+       NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED,
+
+       NFC_DIGITAL_FRAMING_LAST,
+};
+
+#define DIGITAL_MDAA_NFCID1_SIZE 3
+
+struct digital_tg_mdaa_params {
+       u16 sens_res;
+       u8 nfcid1[DIGITAL_MDAA_NFCID1_SIZE];
+       u8 sel_res;
+
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
+       u16 sc;
+};
+
+struct nfc_digital_dev;
+
+/**
+ * nfc_digital_cmd_complete_t - Definition of command result callback
+ *
+ * @ddev: nfc_digital_device ref
+ * @arg: user data
+ * @resp: response data
+ *
+ * resp pointer can be an error code and will be checked with IS_ERR() macro.
+ * The callback is responsible for freeing resp sk_buff.
+ */
+typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev,
+                                          void *arg, struct sk_buff *resp);
+
+/**
+ * Device side NFC Digital operations
+ *
+ * Initiator mode:
+ * @in_configure_hw: Hardware configuration for RF technology and communication
+ *     framing in initiator mode. This is a synchronous function.
+ * @in_send_cmd: Initiator mode data exchange using RF technology and framing
+ *     previously set with in_configure_hw. The peer response is returned
+ *     through callback cb. If an io error occurs or the peer didn't reply
+ *     within the specified timeout (ms), the error code is passed back through
+ *     the resp pointer. This is an asynchronous function.
+ *
+ * Target mode: Only NFC-DEP protocol is supported in target mode.
+ * @tg_configure_hw: Hardware configuration for RF technology and communication
+ *     framing in target mode. This is a synchronous function.
+ * @tg_send_cmd: Target mode data exchange using RF technology and framing
+ *     previously set with tg_configure_hw. The peer next command is returned
+ *     through callback cb. If an io error occurs or the peer didn't reply
+ *     within the specified timeout (ms), the error code is passed back through
+ *     the resp pointer. This is an asynchronous function.
+ * @tg_listen: Put the device in listen mode waiting for data from the peer
+ *     device. This is an asynchronous function.
+ * @tg_listen_mdaa: If supported, put the device in automatic listen mode with
+ *     mode detection and automatic anti-collision. In this mode, the device
+ *     automatically detects the RF technology and executes the anti-collision
+ *     detection using the command responses specified in mdaa_params. The
+ *     mdaa_params structure contains SENS_RES, NFCID1, and SEL_RES for 106A RF
+ *     tech. NFCID2 and system code (sc) for 212F and 424F. The driver returns
+ *     the NFC-DEP ATR_REQ command through cb. The digital stack deducts the RF
+ *     tech by analyzing the SoD of the frame containing the ATR_REQ command.
+ *     This is an asynchronous function.
+ *
+ * @switch_rf: Turns device radio on or off. The stack does not call explicitly
+ *     switch_rf to turn the radio on. A call to in|tg_configure_hw must turn
+ *     the device radio on.
+ * @abort_cmd: Discard the last sent command.
+ */
+struct nfc_digital_ops {
+       int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type,
+                              int param);
+       int (*in_send_cmd)(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+                          u16 timeout, nfc_digital_cmd_complete_t cb,
+                          void *arg);
+
+       int (*tg_configure_hw)(struct nfc_digital_dev *ddev, int type,
+                              int param);
+       int (*tg_send_cmd)(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+                          u16 timeout, nfc_digital_cmd_complete_t cb,
+                          void *arg);
+       int (*tg_listen)(struct nfc_digital_dev *ddev, u16 timeout,
+                        nfc_digital_cmd_complete_t cb, void *arg);
+       int (*tg_listen_mdaa)(struct nfc_digital_dev *ddev,
+                             struct digital_tg_mdaa_params *mdaa_params,
+                             u16 timeout, nfc_digital_cmd_complete_t cb,
+                             void *arg);
+
+       int (*switch_rf)(struct nfc_digital_dev *ddev, bool on);
+       void (*abort_cmd)(struct nfc_digital_dev *ddev);
+};
+
+#define NFC_DIGITAL_POLL_MODE_COUNT_MAX        6 /* 106A, 212F, and 424F in & tg */
+
+typedef int (*digital_poll_t)(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+struct digital_poll_tech {
+       u8 rf_tech;
+       digital_poll_t poll_func;
+};
+
+/**
+ * Driver capabilities - bit mask made of the following values
+ *
+ * @NFC_DIGITAL_DRV_CAPS_IN_CRC: The driver handles CRC calculation in initiator
+ *     mode.
+ * @NFC_DIGITAL_DRV_CAPS_TG_CRC: The driver handles CRC calculation in target
+ *     mode.
+ */
+#define NFC_DIGITAL_DRV_CAPS_IN_CRC    0x0001
+#define NFC_DIGITAL_DRV_CAPS_TG_CRC    0x0002
+
+struct nfc_digital_dev {
+       struct nfc_dev *nfc_dev;
+       struct nfc_digital_ops *ops;
+
+       u32 protocols;
+
+       int tx_headroom;
+       int tx_tailroom;
+
+       u32 driver_capabilities;
+       void *driver_data;
+
+       struct digital_poll_tech poll_techs[NFC_DIGITAL_POLL_MODE_COUNT_MAX];
+       u8 poll_tech_count;
+       u8 poll_tech_index;
+       struct mutex poll_lock;
+
+       struct work_struct cmd_work;
+       struct work_struct cmd_complete_work;
+       struct list_head cmd_queue;
+       struct mutex cmd_lock;
+
+       struct work_struct poll_work;
+
+       u8 curr_protocol;
+       u8 curr_rf_tech;
+       u8 curr_nfc_dep_pni;
+
+       int (*skb_check_crc)(struct sk_buff *skb);
+       void (*skb_add_crc)(struct sk_buff *skb);
+};
+
+struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
+                                                   __u32 supported_protocols,
+                                                   __u32 driver_capabilities,
+                                                   int tx_headroom,
+                                                   int tx_tailroom);
+void nfc_digital_free_device(struct nfc_digital_dev *ndev);
+int nfc_digital_register_device(struct nfc_digital_dev *ndev);
+void nfc_digital_unregister_device(struct nfc_digital_dev *ndev);
+
+static inline void nfc_digital_set_parent_dev(struct nfc_digital_dev *ndev,
+                                             struct device *dev)
+{
+       nfc_set_parent_dev(ndev->nfc_dev, dev);
+}
+
+static inline void nfc_digital_set_drvdata(struct nfc_digital_dev *dev,
+                                          void *data)
+{
+       dev->driver_data = data;
+}
+
+static inline void *nfc_digital_get_drvdata(struct nfc_digital_dev *dev)
+{
+       return dev->driver_data;
+}
+
+#endif /* __NFC_DIGITAL_H */
index b64b7bc..2eca296 100644 (file)
 
 #include <net/nfc/nfc.h>
 
-struct nfc_phy_ops {
-       int (*write)(void *dev_id, struct sk_buff *skb);
-       int (*enable)(void *dev_id);
-       void (*disable)(void *dev_id);
-};
-
 struct nfc_hci_dev;
 
 struct nfc_hci_ops {
index 88785e5..e5aa5ac 100644 (file)
 #define NCI_GID_NFCEE_MGMT                                     0x2
 #define NCI_GID_PROPRIETARY                                    0xf
 
+/* ----- NCI over SPI head/crc(tail) room needed for outgoing frames ----- */
+#define NCI_SPI_HDR_LEN                                                4
+#define NCI_SPI_CRC_LEN                                                2
+
 /* ---- NCI Packet structures ---- */
 #define NCI_CTRL_HDR_SIZE                                      3
 #define NCI_DATA_HDR_SIZE                                      3
index 99fc1f3..6126f1f 100644 (file)
@@ -207,19 +207,9 @@ int nci_to_errno(__u8 code);
 #define NCI_SPI_CRC_ENABLED    0x01
 
 /* ----- NCI SPI structures ----- */
-struct nci_spi_dev;
-
-struct nci_spi_ops {
-       int (*open)(struct nci_spi_dev *ndev);
-       int (*close)(struct nci_spi_dev *ndev);
-       void (*assert_int)(struct nci_spi_dev *ndev);
-       void (*deassert_int)(struct nci_spi_dev *ndev);
-};
-
-struct nci_spi_dev {
-       struct nci_dev          *nci_dev;
+struct nci_spi {
+       struct nci_dev          *ndev;
        struct spi_device       *spi;
-       struct nci_spi_ops      *ops;
 
        unsigned int            xfer_udelay;    /* microseconds delay between
                                                  transactions */
@@ -227,31 +217,15 @@ struct nci_spi_dev {
 
        struct completion       req_completion;
        u8                      req_result;
-
-       void                    *driver_data;
 };
 
-/* ----- NCI SPI Devices ----- */
-struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
-                                               struct nci_spi_ops *ops,
-                                               u32 supported_protocols,
-                                               u32 supported_se,
-                                               u8 acknowledge_mode,
-                                               unsigned int delay);
-void nci_spi_free_device(struct nci_spi_dev *ndev);
-int nci_spi_register_device(struct nci_spi_dev *ndev);
-void nci_spi_unregister_device(struct nci_spi_dev *ndev);
-int nci_spi_recv_frame(struct nci_spi_dev *ndev);
-
-static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev,
-                                           void *data)
-{
-       ndev->driver_data = data;
-}
-
-static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev)
-{
-       return ndev->driver_data;
-}
+/* ----- NCI SPI ----- */
+struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
+                                    u8 acknowledge_mode, unsigned int delay,
+                                    struct nci_dev *ndev);
+int nci_spi_send(struct nci_spi *nspi,
+                struct completion *write_handshake_completion,
+                struct sk_buff *skb);
+struct sk_buff *nci_spi_read(struct nci_spi *nspi);
 
 #endif /* __NCI_CORE_H */
index f68ee68..82fc4e4 100644 (file)
 #include <linux/device.h>
 #include <linux/skbuff.h>
 
-#define nfc_dev_info(dev, fmt, arg...) dev_info((dev), "NFC: " fmt "\n", ## arg)
-#define nfc_dev_err(dev, fmt, arg...) dev_err((dev), "NFC: " fmt "\n", ## arg)
-#define nfc_dev_dbg(dev, fmt, arg...) dev_dbg((dev), fmt "\n", ## arg)
+#define nfc_info(dev, fmt, ...) dev_info((dev), "NFC: " fmt, ##__VA_ARGS__)
+#define nfc_err(dev, fmt, ...) dev_err((dev), "NFC: " fmt, ##__VA_ARGS__)
+
+struct nfc_phy_ops {
+       int (*write)(void *dev_id, struct sk_buff *skb);
+       int (*enable)(void *dev_id);
+       void (*disable)(void *dev_id);
+};
 
 struct nfc_dev;
 
@@ -48,6 +53,8 @@ struct nfc_dev;
 typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb,
                                                                int err);
 
+typedef void (*se_io_cb_t)(void *context, u8 *apdu, size_t apdu_len, int err);
+
 struct nfc_target;
 
 struct nfc_ops {
@@ -74,12 +81,23 @@ struct nfc_ops {
        int (*discover_se)(struct nfc_dev *dev);
        int (*enable_se)(struct nfc_dev *dev, u32 se_idx);
        int (*disable_se)(struct nfc_dev *dev, u32 se_idx);
+       int (*se_io) (struct nfc_dev *dev, u32 se_idx,
+                     u8 *apdu, size_t apdu_length,
+                     se_io_cb_t cb, void *cb_context);
 };
 
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
 #define NFC_ATR_RES_GT_OFFSET 15
 
+/**
+ * struct nfc_target - NFC target descriptiom
+ *
+ * @sens_res: 2 bytes describing the target SENS_RES response, if the target
+ *     is a type A one. The %sens_res most significant byte must be byte 2
+ *     as described by the NFC Forum digital specification (i.e. the platform
+ *     configuration one) while %sens_res least significant byte is byte 1.
+ */
 struct nfc_target {
        u32 idx;
        u32 supported_protocols;
@@ -243,5 +261,6 @@ void nfc_driver_failure(struct nfc_dev *dev, int err);
 
 int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
 int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
+struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
 
 #endif /* __NET_NFC_H */
index 42e9fac..05e4138 100644 (file)
@@ -1,13 +1,13 @@
 #ifndef _NET_P8022_H
 #define _NET_P8022_H
-extern struct datalink_proto *
-       register_8022_client(unsigned char type,
-                            int (*func)(struct sk_buff *skb,
-                                        struct net_device *dev,
-                                        struct packet_type *pt,
-                                        struct net_device *orig_dev));
-extern void unregister_8022_client(struct datalink_proto *proto);
+struct datalink_proto *
+register_8022_client(unsigned char type,
+                    int (*func)(struct sk_buff *skb,
+                                struct net_device *dev,
+                                struct packet_type *pt,
+                                struct net_device *orig_dev));
+void unregister_8022_client(struct datalink_proto *proto);
 
-extern struct datalink_proto *make_8023_client(void);
-extern void destroy_8023_client(struct datalink_proto *dl);
+struct datalink_proto *make_8023_client(void);
+void destroy_8023_client(struct datalink_proto *dl);
 #endif
index 5db0224..3f67704 100644 (file)
@@ -103,8 +103,8 @@ void ping_seq_stop(struct seq_file *seq, void *v);
 int ping_proc_register(struct net *net, struct ping_seq_afinfo *afinfo);
 void ping_proc_unregister(struct net *net, struct ping_seq_afinfo *afinfo);
 
-extern int __init ping_proc_init(void);
-extern void ping_proc_exit(void);
+int __init ping_proc_init(void);
+void ping_proc_exit(void);
 #endif
 
 void __init ping_init(void);
index 047c047..fbf7676 100644 (file)
@@ -96,20 +96,20 @@ extern const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS];
 extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
 #endif
 
-extern int     inet_add_protocol(const struct net_protocol *prot, unsigned char num);
-extern int     inet_del_protocol(const struct net_protocol *prot, unsigned char num);
-extern int     inet_add_offload(const struct net_offload *prot, unsigned char num);
-extern int     inet_del_offload(const struct net_offload *prot, unsigned char num);
-extern void    inet_register_protosw(struct inet_protosw *p);
-extern void    inet_unregister_protosw(struct inet_protosw *p);
+int inet_add_protocol(const struct net_protocol *prot, unsigned char num);
+int inet_del_protocol(const struct net_protocol *prot, unsigned char num);
+int inet_add_offload(const struct net_offload *prot, unsigned char num);
+int inet_del_offload(const struct net_offload *prot, unsigned char num);
+void inet_register_protosw(struct inet_protosw *p);
+void inet_unregister_protosw(struct inet_protosw *p);
 
 #if IS_ENABLED(CONFIG_IPV6)
-extern int     inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num);
-extern int     inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num);
-extern int     inet6_register_protosw(struct inet_protosw *p);
-extern void    inet6_unregister_protosw(struct inet_protosw *p);
+int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num);
+int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num);
+int inet6_register_protosw(struct inet_protosw *p);
+void inet6_unregister_protosw(struct inet_protosw *p);
 #endif
-extern int     inet6_add_offload(const struct net_offload *prot, unsigned char num);
-extern int     inet6_del_offload(const struct net_offload *prot, unsigned char num);
+int inet6_add_offload(const struct net_offload *prot, unsigned char num);
+int inet6_del_offload(const struct net_offload *prot, unsigned char num);
 
 #endif /* _PROTOCOL_H */
index fe456c2..78db4cc 100644 (file)
@@ -1,11 +1,11 @@
 #ifndef _NET_PSNAP_H
 #define _NET_PSNAP_H
 
-extern struct datalink_proto *
+struct datalink_proto *
 register_snap_client(const unsigned char *desc,
                     int (*rcvfunc)(struct sk_buff *, struct net_device *,
                                    struct packet_type *,
                                    struct net_device *orig_dev));
-extern void unregister_snap_client(struct datalink_proto *proto);
+void unregister_snap_client(struct datalink_proto *proto);
 
 #endif
index 42ce6fe..6a40c65 100644 (file)
@@ -26,7 +26,7 @@ extern struct proto raw_prot;
 void raw_icmp_error(struct sk_buff *, int, u32);
 int raw_local_deliver(struct sk_buff *, int);
 
-extern int     raw_rcv(struct sock *, struct sk_buff *);
+int raw_rcv(struct sock *, struct sk_buff *);
 
 #define RAW_HTABLE_SIZE        MAX_INET_PROTOS
 
@@ -36,8 +36,8 @@ struct raw_hashinfo {
 };
 
 #ifdef CONFIG_PROC_FS
-extern int  raw_proc_init(void);
-extern void raw_proc_exit(void);
+int raw_proc_init(void);
+void raw_proc_exit(void);
 
 struct raw_iter_state {
        struct seq_net_private p;
index e7ea660..87783de 100644 (file)
@@ -7,8 +7,7 @@ void raw6_icmp_error(struct sk_buff *, int nexthdr,
                u8 type, u8 code, int inner_offset, __be32);
 bool raw6_local_deliver(struct sk_buff *, int);
 
-extern int                     rawv6_rcv(struct sock *sk,
-                                         struct sk_buff *skb);
+int rawv6_rcv(struct sock *sk, struct sk_buff *skb);
 
 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
 int rawv6_mh_filter_register(int (*filter)(struct sock *sock,
index 59795e4..7f830ff 100644 (file)
@@ -43,11 +43,12 @@ struct request_sock_ops {
                                           struct request_sock *req);
 };
 
-extern int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req);
+int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req);
 
 /* struct request_sock - mini sock to represent a connection request
  */
 struct request_sock {
+       struct sock_common              __req_common;
        struct request_sock             *dl_next;
        u16                             mss;
        u8                              num_retrans; /* number of retransmits */
@@ -162,13 +163,13 @@ struct request_sock_queue {
                                             */
 };
 
-extern int reqsk_queue_alloc(struct request_sock_queue *queue,
-                            unsigned int nr_table_entries);
+int reqsk_queue_alloc(struct request_sock_queue *queue,
+                     unsigned int nr_table_entries);
 
-extern void __reqsk_queue_destroy(struct request_sock_queue *queue);
-extern void reqsk_queue_destroy(struct request_sock_queue *queue);
-extern void reqsk_fastopen_remove(struct sock *sk,
-                                 struct request_sock *req, bool reset);
+void __reqsk_queue_destroy(struct request_sock_queue *queue);
+void reqsk_queue_destroy(struct request_sock_queue *queue);
+void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
+                          bool reset);
 
 static inline struct request_sock *
        reqsk_queue_yank_acceptq(struct request_sock_queue *queue)
index 555dd19..50811fe 100644 (file)
@@ -160,38 +160,42 @@ extern int  sysctl_rose_routing_control;
 extern int  sysctl_rose_link_fail_timeout;
 extern int  sysctl_rose_maximum_vcs;
 extern int  sysctl_rose_window_size;
-extern int  rosecmp(rose_address *, rose_address *);
-extern int  rosecmpm(rose_address *, rose_address *, unsigned short);
-extern char *rose2asc(char *buf, const rose_address *);
-extern struct sock *rose_find_socket(unsigned int, struct rose_neigh *);
-extern void rose_kill_by_neigh(struct rose_neigh *);
-extern unsigned int rose_new_lci(struct rose_neigh *);
-extern int  rose_rx_call_request(struct sk_buff *, struct net_device *, struct rose_neigh *, unsigned int);
-extern void rose_destroy_socket(struct sock *);
+
+int rosecmp(rose_address *, rose_address *);
+int rosecmpm(rose_address *, rose_address *, unsigned short);
+char *rose2asc(char *buf, const rose_address *);
+struct sock *rose_find_socket(unsigned int, struct rose_neigh *);
+void rose_kill_by_neigh(struct rose_neigh *);
+unsigned int rose_new_lci(struct rose_neigh *);
+int rose_rx_call_request(struct sk_buff *, struct net_device *,
+                        struct rose_neigh *, unsigned int);
+void rose_destroy_socket(struct sock *);
 
 /* rose_dev.c */
-extern void  rose_setup(struct net_device *);
+void rose_setup(struct net_device *);
 
 /* rose_in.c */
-extern int  rose_process_rx_frame(struct sock *, struct sk_buff *);
+int rose_process_rx_frame(struct sock *, struct sk_buff *);
 
 /* rose_link.c */
-extern void rose_start_ftimer(struct rose_neigh *);
-extern void rose_stop_ftimer(struct rose_neigh *);
-extern void rose_stop_t0timer(struct rose_neigh *);
-extern int  rose_ftimer_running(struct rose_neigh *);
-extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short);
-extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char);
-extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
+void rose_start_ftimer(struct rose_neigh *);
+void rose_stop_ftimer(struct rose_neigh *);
+void rose_stop_t0timer(struct rose_neigh *);
+int rose_ftimer_running(struct rose_neigh *);
+void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *,
+                         unsigned short);
+void rose_transmit_clear_request(struct rose_neigh *, unsigned int,
+                                unsigned char, unsigned char);
+void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
 
 /* rose_loopback.c */
-extern void rose_loopback_init(void);
-extern void rose_loopback_clear(void);
-extern int  rose_loopback_queue(struct sk_buff *, struct rose_neigh *);
+void rose_loopback_init(void);
+void rose_loopback_clear(void);
+int rose_loopback_queue(struct sk_buff *, struct rose_neigh *);
 
 /* rose_out.c */
-extern void rose_kick(struct sock *);
-extern void rose_enquiry_response(struct sock *);
+void rose_kick(struct sock *);
+void rose_enquiry_response(struct sock *);
 
 /* rose_route.c */
 extern struct rose_neigh *rose_loopback_neigh;
@@ -199,43 +203,45 @@ extern const struct file_operations rose_neigh_fops;
 extern const struct file_operations rose_nodes_fops;
 extern const struct file_operations rose_routes_fops;
 
-extern void rose_add_loopback_neigh(void);
-extern int __must_check rose_add_loopback_node(rose_address *);
-extern void rose_del_loopback_node(rose_address *);
-extern void rose_rt_device_down(struct net_device *);
-extern void rose_link_device_down(struct net_device *);
-extern struct net_device *rose_dev_first(void);
-extern struct net_device *rose_dev_get(rose_address *);
-extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *);
-extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *, int);
-extern int  rose_rt_ioctl(unsigned int, void __user *);
-extern void rose_link_failed(ax25_cb *, int);
-extern int  rose_route_frame(struct sk_buff *, ax25_cb *);
-extern void rose_rt_free(void);
+void rose_add_loopback_neigh(void);
+int __must_check rose_add_loopback_node(rose_address *);
+void rose_del_loopback_node(rose_address *);
+void rose_rt_device_down(struct net_device *);
+void rose_link_device_down(struct net_device *);
+struct net_device *rose_dev_first(void);
+struct net_device *rose_dev_get(rose_address *);
+struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *);
+struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *,
+                                 unsigned char *, int);
+int rose_rt_ioctl(unsigned int, void __user *);
+void rose_link_failed(ax25_cb *, int);
+int rose_route_frame(struct sk_buff *, ax25_cb *);
+void rose_rt_free(void);
 
 /* rose_subr.c */
-extern void rose_clear_queues(struct sock *);
-extern void rose_frames_acked(struct sock *, unsigned short);
-extern void rose_requeue_frames(struct sock *);
-extern int  rose_validate_nr(struct sock *, unsigned short);
-extern void rose_write_internal(struct sock *, int);
-extern int  rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
-extern int  rose_parse_facilities(unsigned char *, unsigned int, struct rose_facilities_struct *);
-extern void rose_disconnect(struct sock *, int, int, int);
+void rose_clear_queues(struct sock *);
+void rose_frames_acked(struct sock *, unsigned short);
+void rose_requeue_frames(struct sock *);
+int rose_validate_nr(struct sock *, unsigned short);
+void rose_write_internal(struct sock *, int);
+int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
+int rose_parse_facilities(unsigned char *, unsigned int,
+                         struct rose_facilities_struct *);
+void rose_disconnect(struct sock *, int, int, int);
 
 /* rose_timer.c */
-extern void rose_start_heartbeat(struct sock *);
-extern void rose_start_t1timer(struct sock *);
-extern void rose_start_t2timer(struct sock *);
-extern void rose_start_t3timer(struct sock *);
-extern void rose_start_hbtimer(struct sock *);
-extern void rose_start_idletimer(struct sock *);
-extern void rose_stop_heartbeat(struct sock *);
-extern void rose_stop_timer(struct sock *);
-extern void rose_stop_idletimer(struct sock *);
+void rose_start_heartbeat(struct sock *);
+void rose_start_t1timer(struct sock *);
+void rose_start_t2timer(struct sock *);
+void rose_start_t3timer(struct sock *);
+void rose_start_hbtimer(struct sock *);
+void rose_start_idletimer(struct sock *);
+void rose_stop_heartbeat(struct sock *);
+void rose_stop_timer(struct sock *);
+void rose_stop_idletimer(struct sock *);
 
 /* sysctl_net_rose.c */
-extern void rose_register_sysctl(void);
-extern void rose_unregister_sysctl(void);
+void rose_register_sysctl(void);
+void rose_unregister_sysctl(void);
 
 #endif
index afdeeb5..f68c167 100644 (file)
@@ -39,6 +39,7 @@
 #define RTO_ONLINK     0x01
 
 #define RT_CONN_FLAGS(sk)   (RT_TOS(inet_sk(sk)->tos) | sock_flag(sk, SOCK_LOCALROUTE))
+#define RT_CONN_FLAGS_TOS(sk,tos)   (RT_TOS(tos) | sock_flag(sk, SOCK_LOCALROUTE))
 
 struct fib_nh;
 struct fib_info;
@@ -87,34 +88,28 @@ struct ip_rt_acct {
 };
 
 struct rt_cache_stat {
-        unsigned int in_hit;
         unsigned int in_slow_tot;
         unsigned int in_slow_mc;
         unsigned int in_no_route;
         unsigned int in_brd;
         unsigned int in_martian_dst;
         unsigned int in_martian_src;
-        unsigned int out_hit;
         unsigned int out_slow_tot;
         unsigned int out_slow_mc;
-        unsigned int gc_total;
-        unsigned int gc_ignored;
-        unsigned int gc_goal_miss;
-        unsigned int gc_dst_overflow;
-        unsigned int in_hlist_search;
-        unsigned int out_hlist_search;
 };
 
 extern struct ip_rt_acct __percpu *ip_rt_acct;
 
 struct in_device;
-extern int             ip_rt_init(void);
-extern void            rt_cache_flush(struct net *net);
-extern void            rt_flush_dev(struct net_device *dev);
-extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
-extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
-                                          struct sock *sk);
-extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig);
+
+int ip_rt_init(void);
+void rt_cache_flush(struct net *net);
+void rt_flush_dev(struct net_device *dev);
+struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
+struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
+                                   struct sock *sk);
+struct dst_entry *ipv4_blackhole_route(struct net *net,
+                                      struct dst_entry *dst_orig);
 
 static inline struct rtable *ip_route_output_key(struct net *net, struct flowi4 *flp)
 {
@@ -162,8 +157,8 @@ static inline struct rtable *ip_route_output_gre(struct net *net, struct flowi4
        return ip_route_output_key(net, fl4);
 }
 
-extern int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 src,
-                               u8 tos, struct net_device *devin);
+int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 src,
+                        u8 tos, struct net_device *devin);
 
 static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src,
                                 u8 tos, struct net_device *devin)
@@ -179,24 +174,25 @@ static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src,
        return err;
 }
 
-extern void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
-                            int oif, u32 mark, u8 protocol, int flow_flags);
-extern void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu);
-extern void ipv4_redirect(struct sk_buff *skb, struct net *net,
-                         int oif, u32 mark, u8 protocol, int flow_flags);
-extern void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk);
-extern void ip_rt_send_redirect(struct sk_buff *skb);
-
-extern unsigned int            inet_addr_type(struct net *net, __be32 addr);
-extern unsigned int            inet_dev_addr_type(struct net *net, const struct net_device *dev, __be32 addr);
-extern void            ip_rt_multicast_event(struct in_device *);
-extern int             ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg);
-extern void            ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt);
-extern int             ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb);
+void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, int oif,
+                     u32 mark, u8 protocol, int flow_flags);
+void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu);
+void ipv4_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+                  u8 protocol, int flow_flags);
+void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk);
+void ip_rt_send_redirect(struct sk_buff *skb);
+
+unsigned int inet_addr_type(struct net *net, __be32 addr);
+unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
+                               __be32 addr);
+void ip_rt_multicast_event(struct in_device *);
+int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg);
+void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt);
+int ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb);
 
 struct in_ifaddr;
-extern void fib_add_ifaddr(struct in_ifaddr *);
-extern void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *);
+void fib_add_ifaddr(struct in_ifaddr *);
+void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *);
 
 static inline void ip_rt_put(struct rtable *rt)
 {
@@ -317,12 +313,20 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
        return hoplimit;
 }
 
-static inline int ip_skb_dst_mtu(struct sk_buff *skb)
+static inline bool ip_sk_accept_pmtu(const struct sock *sk)
+{
+       return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE;
+}
+
+static inline bool ip_sk_use_pmtu(const struct sock *sk)
 {
-       struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
+       return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE;
+}
 
-       return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
-              skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
+static inline int ip_skb_dst_mtu(const struct sk_buff *skb)
+{
+       return (!skb->sk || ip_sk_use_pmtu(skb->sk)) ?
+              dst_mtu(skb_dst(skb)) : skb_dst(skb)->dev->mtu;
 }
 
 #endif /* _ROUTE_H */
index 7026648..bb13a18 100644 (file)
@@ -8,14 +8,12 @@ typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *);
 typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *);
 typedef u16 (*rtnl_calcit_func)(struct sk_buff *, struct nlmsghdr *);
 
-extern int     __rtnl_register(int protocol, int msgtype,
-                               rtnl_doit_func, rtnl_dumpit_func,
-                               rtnl_calcit_func);
-extern void    rtnl_register(int protocol, int msgtype,
-                             rtnl_doit_func, rtnl_dumpit_func,
-                             rtnl_calcit_func);
-extern int     rtnl_unregister(int protocol, int msgtype);
-extern void    rtnl_unregister_all(int protocol);
+int __rtnl_register(int protocol, int msgtype,
+                   rtnl_doit_func, rtnl_dumpit_func, rtnl_calcit_func);
+void rtnl_register(int protocol, int msgtype,
+                  rtnl_doit_func, rtnl_dumpit_func, rtnl_calcit_func);
+int rtnl_unregister(int protocol, int msgtype);
+void rtnl_unregister_all(int protocol);
 
 static inline int rtnl_msg_family(const struct nlmsghdr *nlh)
 {
@@ -83,11 +81,11 @@ struct rtnl_link_ops {
        unsigned int            (*get_num_rx_queues)(void);
 };
 
-extern int     __rtnl_link_register(struct rtnl_link_ops *ops);
-extern void    __rtnl_link_unregister(struct rtnl_link_ops *ops);
+int __rtnl_link_register(struct rtnl_link_ops *ops);
+void __rtnl_link_unregister(struct rtnl_link_ops *ops);
 
-extern int     rtnl_link_register(struct rtnl_link_ops *ops);
-extern void    rtnl_link_unregister(struct rtnl_link_ops *ops);
+int rtnl_link_register(struct rtnl_link_ops *ops);
+void rtnl_link_unregister(struct rtnl_link_ops *ops);
 
 /**
  *     struct rtnl_af_ops - rtnetlink address family operations
@@ -117,18 +115,18 @@ struct rtnl_af_ops {
                                               const struct nlattr *attr);
 };
 
-extern int     __rtnl_af_register(struct rtnl_af_ops *ops);
-extern void    __rtnl_af_unregister(struct rtnl_af_ops *ops);
+int __rtnl_af_register(struct rtnl_af_ops *ops);
+void __rtnl_af_unregister(struct rtnl_af_ops *ops);
 
-extern int     rtnl_af_register(struct rtnl_af_ops *ops);
-extern void    rtnl_af_unregister(struct rtnl_af_ops *ops);
+int rtnl_af_register(struct rtnl_af_ops *ops);
+void rtnl_af_unregister(struct rtnl_af_ops *ops);
 
+struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
+struct net_device *rtnl_create_link(struct net *net, char *ifname,
+                                   const struct rtnl_link_ops *ops,
+                                   struct nlattr *tb[]);
+int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm);
 
-extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
-extern struct net_device *rtnl_create_link(struct net *net,
-       char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]);
-extern int rtnl_configure_link(struct net_device *dev,
-                              const struct ifinfomsg *ifm);
 extern const struct nla_policy ifla_policy[IFLA_MAX+1];
 
 #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
index f4eb365..d0a6321 100644 (file)
@@ -702,13 +702,20 @@ static inline u64 psched_l2t_ns(const struct psched_ratecfg *r,
 }
 
 void psched_ratecfg_precompute(struct psched_ratecfg *r,
-                              const struct tc_ratespec *conf);
+                              const struct tc_ratespec *conf,
+                              u64 rate64);
 
 static inline void psched_ratecfg_getrate(struct tc_ratespec *res,
                                          const struct psched_ratecfg *r)
 {
        memset(res, 0, sizeof(*res));
-       res->rate = r->rate_bytes_ps;
+
+       /* legacy struct tc_ratespec has a 32bit @rate field
+        * Qdisc using 64bit rate should add new attributes
+        * in order to maintain compatibility.
+        */
+       res->rate = min_t(u64, r->rate_bytes_ps, ~0U);
+
        res->overhead = r->overhead;
        res->linklayer = (r->linklayer & TC_LINKLAYER_MASK);
 }
index 8de2d37..262532d 100644 (file)
@@ -33,11 +33,11 @@ struct scm_cookie {
 #endif
 };
 
-extern void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm);
-extern void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm);
-extern int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm);
-extern void __scm_destroy(struct scm_cookie *scm);
-extern struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl);
+void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm);
+void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm);
+int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm);
+void __scm_destroy(struct scm_cookie *scm);
+struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl);
 
 #ifdef CONFIG_SECURITY_NETWORK
 static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_cookie *scm)
index 259924d..6bd44fe 100644 (file)
 #include <linux/types.h>
 #include <net/sctp/sctp.h>
 #include <linux/crc32c.h>
+#include <linux/crc32.h>
 
-static inline __u32 sctp_crc32c(__u32 crc, u8 *buffer, u16 length)
+static inline __wsum sctp_csum_update(const void *buff, int len, __wsum sum)
 {
-       return crc32c(crc, buffer, length);
-}
-
-static inline __u32 sctp_start_cksum(__u8 *buffer, __u16 length)
-{
-       __u32 crc = ~(__u32)0;
-       __u8  zero[sizeof(__u32)] = {0};
-
-       /* Optimize this routine to be SCTP specific, knowing how
-        * to skip the checksum field of the SCTP header.
+       /* This uses the crypto implementation of crc32c, which is either
+        * implemented w/ hardware support or resolves to __crc32c_le().
         */
-
-       /* Calculate CRC up to the checksum. */
-       crc = sctp_crc32c(crc, buffer, sizeof(struct sctphdr) - sizeof(__u32));
-
-       /* Skip checksum field of the header. */
-       crc = sctp_crc32c(crc, zero, sizeof(__u32));
-
-       /* Calculate the rest of the CRC. */
-       crc = sctp_crc32c(crc, &buffer[sizeof(struct sctphdr)],
-                           length - sizeof(struct sctphdr));
-       return crc;
-}
-
-static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
-{
-       return sctp_crc32c(crc32, buffer, length);
+       return crc32c(sum, buff, len);
 }
 
-static inline __le32 sctp_end_cksum(__u32 crc32)
+static inline __wsum sctp_csum_combine(__wsum csum, __wsum csum2,
+                                      int offset, int len)
 {
-       return cpu_to_le32(~crc32);
+       return __crc32c_le_combine(csum, csum2, len);
 }
 
-/* Calculate the CRC32C checksum of an SCTP packet.  */
 static inline __le32 sctp_compute_cksum(const struct sk_buff *skb,
                                        unsigned int offset)
 {
-       const struct sk_buff *iter;
+       struct sctphdr *sh = sctp_hdr(skb);
+        __le32 ret, old = sh->checksum;
+       const struct skb_checksum_ops ops = {
+               .update  = sctp_csum_update,
+               .combine = sctp_csum_combine,
+       };
 
-       __u32 crc32 = sctp_start_cksum(skb->data + offset,
-                                      skb_headlen(skb) - offset);
-       skb_walk_frags(skb, iter)
-               crc32 = sctp_update_cksum((__u8 *) iter->data,
-                                         skb_headlen(iter), crc32);
+       sh->checksum = 0;
+       ret = cpu_to_le32(~__skb_checksum(skb, offset, skb->len - offset,
+                                         ~(__u32)0, &ops));
+       sh->checksum = old;
 
-       return sctp_end_cksum(crc32);
+       return ret;
 }
 
 #endif /* __sctp_checksum_h__ */
index 3794c5a..c5fe806 100644 (file)
 /*
  * sctp/protocol.c
  */
-extern int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *,
-                                    sctp_scope_t, gfp_t gfp,
-                                    int flags);
-extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
-extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
-extern void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int);
+int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *,
+                             sctp_scope_t, gfp_t gfp, int flags);
+struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
+int sctp_register_pf(struct sctp_pf *, sa_family_t);
+void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int);
 
 /*
  * sctp/socket.c
@@ -110,7 +109,7 @@ void sctp_sock_rfree(struct sk_buff *skb);
 void sctp_copy_sock(struct sock *newsk, struct sock *sk,
                    struct sctp_association *asoc);
 extern struct percpu_counter sctp_sockets_allocated;
-extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
+int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
 
 /*
  * sctp/primitive.c
index 6ca975b..f257486 100644 (file)
@@ -3,19 +3,18 @@
 
 #include <linux/types.h>
 
-extern void net_secret_init(void);
-extern __u32 secure_ip_id(__be32 daddr);
-extern __u32 secure_ipv6_id(const __be32 daddr[4]);
-extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
-extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
-                                     __be16 dport);
-extern __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
-                                       __be16 sport, __be16 dport);
-extern __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr,
-                                         __be16 sport, __be16 dport);
-extern u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
-                                      __be16 sport, __be16 dport);
-extern u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
-                                        __be16 sport, __be16 dport);
+__u32 secure_ip_id(__be32 daddr);
+__u32 secure_ipv6_id(const __be32 daddr[4]);
+u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
+u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
+                              __be16 dport);
+__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
+                                __be16 sport, __be16 dport);
+__u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr,
+                                  __be16 sport, __be16 dport);
+u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
+                               __be16 sport, __be16 dport);
+u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
+                                 __be16 sport, __be16 dport);
 
 #endif /* _NET_SECURE_SEQ */
index 6ba2e7b..e3a18ff 100644 (file)
@@ -156,7 +156,7 @@ typedef __u64 __bitwise __addrpair;
  */
 struct sock_common {
        /* skc_daddr and skc_rcv_saddr must be grouped on a 8 bytes aligned
-        * address on 64bit arches : cf INET_MATCH() and INET_TW_MATCH()
+        * address on 64bit arches : cf INET_MATCH()
         */
        union {
                __addrpair      skc_addrpair;
@@ -191,6 +191,12 @@ struct sock_common {
 #ifdef CONFIG_NET_NS
        struct net              *skc_net;
 #endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+       struct in6_addr         skc_v6_daddr;
+       struct in6_addr         skc_v6_rcv_saddr;
+#endif
+
        /*
         * fields between dontcopy_begin/dontcopy_end
         * are not copied in sock_copy()
@@ -218,7 +224,7 @@ struct cg_proto;
   *    @sk_lock:       synchronizer
   *    @sk_rcvbuf: size of receive buffer in bytes
   *    @sk_wq: sock wait queue and async head
-  *    @sk_rx_dst: receive input route used by early tcp demux
+  *    @sk_rx_dst: receive input route used by early demux
   *    @sk_dst_cache: destination cache
   *    @sk_dst_lock: destination cache lock
   *    @sk_policy: flow policy
@@ -233,6 +239,7 @@ struct cg_proto;
   *    @sk_ll_usec: usecs to busypoll when there is no data
   *    @sk_allocation: allocation mode
   *    @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler)
+  *    @sk_max_pacing_rate: Maximum pacing rate (%SO_MAX_PACING_RATE)
   *    @sk_sndbuf: size of send buffer in bytes
   *    @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
   *               %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
@@ -299,6 +306,12 @@ struct sock {
 #define sk_dontcopy_begin      __sk_common.skc_dontcopy_begin
 #define sk_dontcopy_end                __sk_common.skc_dontcopy_end
 #define sk_hash                        __sk_common.skc_hash
+#define sk_portpair            __sk_common.skc_portpair
+#define sk_num                 __sk_common.skc_num
+#define sk_dport               __sk_common.skc_dport
+#define sk_addrpair            __sk_common.skc_addrpair
+#define sk_daddr               __sk_common.skc_daddr
+#define sk_rcv_saddr           __sk_common.skc_rcv_saddr
 #define sk_family              __sk_common.skc_family
 #define sk_state               __sk_common.skc_state
 #define sk_reuse               __sk_common.skc_reuse
@@ -307,6 +320,9 @@ struct sock {
 #define sk_bind_node           __sk_common.skc_bind_node
 #define sk_prot                        __sk_common.skc_prot
 #define sk_net                 __sk_common.skc_net
+#define sk_v6_daddr            __sk_common.skc_v6_daddr
+#define sk_v6_rcv_saddr        __sk_common.skc_v6_rcv_saddr
+
        socket_lock_t           sk_lock;
        struct sk_buff_head     sk_receive_queue;
        /*
@@ -363,6 +379,7 @@ struct sock {
        int                     sk_wmem_queued;
        gfp_t                   sk_allocation;
        u32                     sk_pacing_rate; /* bytes per second */
+       u32                     sk_max_pacing_rate;
        netdev_features_t       sk_route_caps;
        netdev_features_t       sk_route_nocaps;
        int                     sk_gso_type;
@@ -409,6 +426,11 @@ struct sock {
        void                    (*sk_destruct)(struct sock *sk);
 };
 
+#define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data)))
+
+#define rcu_dereference_sk_user_data(sk)       rcu_dereference(__sk_user_data((sk)))
+#define rcu_assign_sk_user_data(sk, ptr)       rcu_assign_pointer(__sk_user_data((sk)), ptr)
+
 /*
  * SK_CAN_REUSE and SK_NO_REUSE on a socket mean that the socket is OK
  * or not whether his port will be reused by someone else. SK_FORCE_REUSE
@@ -746,7 +768,7 @@ static inline int sk_stream_wspace(const struct sock *sk)
        return sk->sk_sndbuf - sk->sk_wmem_queued;
 }
 
-extern void sk_stream_write_space(struct sock *sk);
+void sk_stream_write_space(struct sock *sk);
 
 /* OOB backlog add */
 static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb)
@@ -788,7 +810,7 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s
        return 0;
 }
 
-extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb);
 
 static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 {
@@ -853,15 +875,15 @@ static inline void sock_rps_reset_rxhash(struct sock *sk)
                __rc;                                                   \
        })
 
-extern int sk_stream_wait_connect(struct sock *sk, long *timeo_p);
-extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p);
-extern void sk_stream_wait_close(struct sock *sk, long timeo_p);
-extern int sk_stream_error(struct sock *sk, int flags, int err);
-extern void sk_stream_kill_queues(struct sock *sk);
-extern void sk_set_memalloc(struct sock *sk);
-extern void sk_clear_memalloc(struct sock *sk);
+int sk_stream_wait_connect(struct sock *sk, long *timeo_p);
+int sk_stream_wait_memory(struct sock *sk, long *timeo_p);
+void sk_stream_wait_close(struct sock *sk, long timeo_p);
+int sk_stream_error(struct sock *sk, int flags, int err);
+void sk_stream_kill_queues(struct sock *sk);
+void sk_set_memalloc(struct sock *sk);
+void sk_clear_memalloc(struct sock *sk);
 
-extern int sk_wait_data(struct sock *sk, long *timeo);
+int sk_wait_data(struct sock *sk, long *timeo);
 
 struct request_sock_ops;
 struct timewait_sock_ops;
@@ -1014,10 +1036,10 @@ enum cg_proto_flags {
 
 struct cg_proto {
        void                    (*enter_memory_pressure)(struct sock *sk);
-       struct res_counter      *memory_allocated;      /* Current allocated memory. */
-       struct percpu_counter   *sockets_allocated;     /* Current number of sockets. */
-       int                     *memory_pressure;
-       long                    *sysctl_mem;
+       struct res_counter      memory_allocated;       /* Current allocated memory. */
+       struct percpu_counter   sockets_allocated;      /* Current number of sockets. */
+       int                     memory_pressure;
+       long                    sysctl_mem[3];
        unsigned long           flags;
        /*
         * memcg field is used to find which memcg we belong directly
@@ -1031,8 +1053,8 @@ struct cg_proto {
        struct mem_cgroup       *memcg;
 };
 
-extern int proto_register(struct proto *prot, int alloc_slab);
-extern void proto_unregister(struct proto *prot);
+int proto_register(struct proto *prot, int alloc_slab);
+void proto_unregister(struct proto *prot);
 
 static inline bool memcg_proto_active(struct cg_proto *cg_proto)
 {
@@ -1113,7 +1135,7 @@ static inline bool sk_under_memory_pressure(const struct sock *sk)
                return false;
 
        if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
-               return !!*sk->sk_cgrp->memory_pressure;
+               return !!sk->sk_cgrp->memory_pressure;
 
        return !!*sk->sk_prot->memory_pressure;
 }
@@ -1133,8 +1155,8 @@ static inline void sk_leave_memory_pressure(struct sock *sk)
                struct proto *prot = sk->sk_prot;
 
                for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
-                       if (*cg_proto->memory_pressure)
-                               *cg_proto->memory_pressure = 0;
+                       if (cg_proto->memory_pressure)
+                               cg_proto->memory_pressure = 0;
        }
 
 }
@@ -1170,7 +1192,7 @@ static inline void memcg_memory_allocated_add(struct cg_proto *prot,
        struct res_counter *fail;
        int ret;
 
-       ret = res_counter_charge_nofail(prot->memory_allocated,
+       ret = res_counter_charge_nofail(&prot->memory_allocated,
                                        amt << PAGE_SHIFT, &fail);
        if (ret < 0)
                *parent_status = OVER_LIMIT;
@@ -1179,13 +1201,13 @@ static inline void memcg_memory_allocated_add(struct cg_proto *prot,
 static inline void memcg_memory_allocated_sub(struct cg_proto *prot,
                                              unsigned long amt)
 {
-       res_counter_uncharge(prot->memory_allocated, amt << PAGE_SHIFT);
+       res_counter_uncharge(&prot->memory_allocated, amt << PAGE_SHIFT);
 }
 
 static inline u64 memcg_memory_allocated_read(struct cg_proto *prot)
 {
        u64 ret;
-       ret = res_counter_read_u64(prot->memory_allocated, RES_USAGE);
+       ret = res_counter_read_u64(&prot->memory_allocated, RES_USAGE);
        return ret >> PAGE_SHIFT;
 }
 
@@ -1233,7 +1255,7 @@ static inline void sk_sockets_allocated_dec(struct sock *sk)
                struct cg_proto *cg_proto = sk->sk_cgrp;
 
                for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
-                       percpu_counter_dec(cg_proto->sockets_allocated);
+                       percpu_counter_dec(&cg_proto->sockets_allocated);
        }
 
        percpu_counter_dec(prot->sockets_allocated);
@@ -1247,7 +1269,7 @@ static inline void sk_sockets_allocated_inc(struct sock *sk)
                struct cg_proto *cg_proto = sk->sk_cgrp;
 
                for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
-                       percpu_counter_inc(cg_proto->sockets_allocated);
+                       percpu_counter_inc(&cg_proto->sockets_allocated);
        }
 
        percpu_counter_inc(prot->sockets_allocated);
@@ -1259,7 +1281,7 @@ sk_sockets_allocated_read_positive(struct sock *sk)
        struct proto *prot = sk->sk_prot;
 
        if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
-               return percpu_counter_read_positive(sk->sk_cgrp->sockets_allocated);
+               return percpu_counter_read_positive(&sk->sk_cgrp->sockets_allocated);
 
        return percpu_counter_read_positive(prot->sockets_allocated);
 }
@@ -1287,8 +1309,8 @@ proto_memory_pressure(struct proto *prot)
 
 #ifdef CONFIG_PROC_FS
 /* Called with local bh disabled */
-extern void sock_prot_inuse_add(struct net *net, struct proto *prot, int inc);
-extern int sock_prot_inuse_get(struct net *net, struct proto *proto);
+void sock_prot_inuse_add(struct net *net, struct proto *prot, int inc);
+int sock_prot_inuse_get(struct net *net, struct proto *proto);
 #else
 static inline void sock_prot_inuse_add(struct net *net, struct proto *prot,
                int inc)
@@ -1364,8 +1386,8 @@ static inline struct inode *SOCK_INODE(struct socket *socket)
 /*
  * Functions for memory accounting
  */
-extern int __sk_mem_schedule(struct sock *sk, int size, int kind);
-extern void __sk_mem_reclaim(struct sock *sk);
+int __sk_mem_schedule(struct sock *sk, int size, int kind);
+void __sk_mem_reclaim(struct sock *sk);
 
 #define SK_MEM_QUANTUM ((int)PAGE_SIZE)
 #define SK_MEM_QUANTUM_SHIFT ilog2(SK_MEM_QUANTUM)
@@ -1473,14 +1495,14 @@ do {                                                                    \
        lockdep_init_map(&(sk)->sk_lock.dep_map, (name), (key), 0);     \
 } while (0)
 
-extern void lock_sock_nested(struct sock *sk, int subclass);
+void lock_sock_nested(struct sock *sk, int subclass);
 
 static inline void lock_sock(struct sock *sk)
 {
        lock_sock_nested(sk, 0);
 }
 
-extern void release_sock(struct sock *sk);
+void release_sock(struct sock *sk);
 
 /* BH context may only use the following locking interface. */
 #define bh_lock_sock(__sk)     spin_lock(&((__sk)->sk_lock.slock))
@@ -1489,7 +1511,7 @@ extern void release_sock(struct sock *sk);
                                SINGLE_DEPTH_NESTING)
 #define bh_unlock_sock(__sk)   spin_unlock(&((__sk)->sk_lock.slock))
 
-extern bool lock_sock_fast(struct sock *sk);
+bool lock_sock_fast(struct sock *sk);
 /**
  * unlock_sock_fast - complement of lock_sock_fast
  * @sk: socket
@@ -1507,108 +1529,84 @@ static inline void unlock_sock_fast(struct sock *sk, bool slow)
 }
 
 
-extern struct sock             *sk_alloc(struct net *net, int family,
-                                         gfp_t priority,
-                                         struct proto *prot);
-extern void                    sk_free(struct sock *sk);
-extern void                    sk_release_kernel(struct sock *sk);
-extern struct sock             *sk_clone_lock(const struct sock *sk,
-                                              const gfp_t priority);
-
-extern struct sk_buff          *sock_wmalloc(struct sock *sk,
-                                             unsigned long size, int force,
-                                             gfp_t priority);
-extern struct sk_buff          *sock_rmalloc(struct sock *sk,
-                                             unsigned long size, int force,
-                                             gfp_t priority);
-extern void                    sock_wfree(struct sk_buff *skb);
-extern void                    skb_orphan_partial(struct sk_buff *skb);
-extern void                    sock_rfree(struct sk_buff *skb);
-extern void                    sock_edemux(struct sk_buff *skb);
-
-extern int                     sock_setsockopt(struct socket *sock, int level,
-                                               int op, char __user *optval,
-                                               unsigned int optlen);
-
-extern int                     sock_getsockopt(struct socket *sock, int level,
-                                               int op, char __user *optval,
-                                               int __user *optlen);
-extern struct sk_buff          *sock_alloc_send_skb(struct sock *sk,
-                                                    unsigned long size,
-                                                    int noblock,
-                                                    int *errcode);
-extern struct sk_buff          *sock_alloc_send_pskb(struct sock *sk,
-                                                     unsigned long header_len,
-                                                     unsigned long data_len,
-                                                     int noblock,
-                                                     int *errcode,
-                                                     int max_page_order);
-extern void *sock_kmalloc(struct sock *sk, int size,
-                         gfp_t priority);
-extern void sock_kfree_s(struct sock *sk, void *mem, int size);
-extern void sk_send_sigurg(struct sock *sk);
+struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
+                     struct proto *prot);
+void sk_free(struct sock *sk);
+void sk_release_kernel(struct sock *sk);
+struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority);
+
+struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force,
+                            gfp_t priority);
+struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force,
+                            gfp_t priority);
+void sock_wfree(struct sk_buff *skb);
+void skb_orphan_partial(struct sk_buff *skb);
+void sock_rfree(struct sk_buff *skb);
+void sock_edemux(struct sk_buff *skb);
+
+int sock_setsockopt(struct socket *sock, int level, int op,
+                   char __user *optval, unsigned int optlen);
+
+int sock_getsockopt(struct socket *sock, int level, int op,
+                   char __user *optval, int __user *optlen);
+struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
+                                   int noblock, int *errcode);
+struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
+                                    unsigned long data_len, int noblock,
+                                    int *errcode, int max_page_order);
+void *sock_kmalloc(struct sock *sk, int size, gfp_t priority);
+void sock_kfree_s(struct sock *sk, void *mem, int size);
+void sk_send_sigurg(struct sock *sk);
 
 /*
  * Functions to fill in entries in struct proto_ops when a protocol
  * does not implement a particular function.
  */
-extern int                      sock_no_bind(struct socket *,
-                                            struct sockaddr *, int);
-extern int                      sock_no_connect(struct socket *,
-                                               struct sockaddr *, int, int);
-extern int                      sock_no_socketpair(struct socket *,
-                                                  struct socket *);
-extern int                      sock_no_accept(struct socket *,
-                                              struct socket *, int);
-extern int                      sock_no_getname(struct socket *,
-                                               struct sockaddr *, int *, int);
-extern unsigned int             sock_no_poll(struct file *, struct socket *,
-                                            struct poll_table_struct *);
-extern int                      sock_no_ioctl(struct socket *, unsigned int,
-                                             unsigned long);
-extern int                     sock_no_listen(struct socket *, int);
-extern int                      sock_no_shutdown(struct socket *, int);
-extern int                     sock_no_getsockopt(struct socket *, int , int,
-                                                  char __user *, int __user *);
-extern int                     sock_no_setsockopt(struct socket *, int, int,
-                                                  char __user *, unsigned int);
-extern int                      sock_no_sendmsg(struct kiocb *, struct socket *,
-                                               struct msghdr *, size_t);
-extern int                      sock_no_recvmsg(struct kiocb *, struct socket *,
-                                               struct msghdr *, size_t, int);
-extern int                     sock_no_mmap(struct file *file,
-                                            struct socket *sock,
-                                            struct vm_area_struct *vma);
-extern ssize_t                 sock_no_sendpage(struct socket *sock,
-                                               struct page *page,
-                                               int offset, size_t size,
-                                               int flags);
+int sock_no_bind(struct socket *, struct sockaddr *, int);
+int sock_no_connect(struct socket *, struct sockaddr *, int, int);
+int sock_no_socketpair(struct socket *, struct socket *);
+int sock_no_accept(struct socket *, struct socket *, int);
+int sock_no_getname(struct socket *, struct sockaddr *, int *, int);
+unsigned int sock_no_poll(struct file *, struct socket *,
+                         struct poll_table_struct *);
+int sock_no_ioctl(struct socket *, unsigned int, unsigned long);
+int sock_no_listen(struct socket *, int);
+int sock_no_shutdown(struct socket *, int);
+int sock_no_getsockopt(struct socket *, int , int, char __user *, int __user *);
+int sock_no_setsockopt(struct socket *, int, int, char __user *, unsigned int);
+int sock_no_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t);
+int sock_no_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t,
+                   int);
+int sock_no_mmap(struct file *file, struct socket *sock,
+                struct vm_area_struct *vma);
+ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset,
+                        size_t size, int flags);
 
 /*
  * Functions to fill in entries in struct proto_ops when a protocol
  * uses the inet style.
  */
-extern int sock_common_getsockopt(struct socket *sock, int level, int optname,
+int sock_common_getsockopt(struct socket *sock, int level, int optname,
                                  char __user *optval, int __user *optlen);
-extern int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock,
+int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock,
                               struct msghdr *msg, size_t size, int flags);
-extern int sock_common_setsockopt(struct socket *sock, int level, int optname,
+int sock_common_setsockopt(struct socket *sock, int level, int optname,
                                  char __user *optval, unsigned int optlen);
-extern int compat_sock_common_getsockopt(struct socket *sock, int level,
+int compat_sock_common_getsockopt(struct socket *sock, int level,
                int optname, char __user *optval, int __user *optlen);
-extern int compat_sock_common_setsockopt(struct socket *sock, int level,
+int compat_sock_common_setsockopt(struct socket *sock, int level,
                int optname, char __user *optval, unsigned int optlen);
 
-extern void sk_common_release(struct sock *sk);
+void sk_common_release(struct sock *sk);
 
 /*
  *     Default socket callbacks and setup code
  */
 
 /* Initialise core socket variables */
-extern void sock_init_data(struct socket *sock, struct sock *sk);
+void sock_init_data(struct socket *sock, struct sock *sk);
 
-extern void sk_filter_release_rcu(struct rcu_head *rcu);
+void sk_filter_release_rcu(struct rcu_head *rcu);
 
 /**
  *     sk_filter_release - release a socket filter
@@ -1625,16 +1623,14 @@ static inline void sk_filter_release(struct sk_filter *fp)
 
 static inline void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
 {
-       unsigned int size = sk_filter_len(fp);
-
-       atomic_sub(size, &sk->sk_omem_alloc);
+       atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc);
        sk_filter_release(fp);
 }
 
 static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
 {
        atomic_inc(&fp->refcnt);
-       atomic_add(sk_filter_len(fp), &sk->sk_omem_alloc);
+       atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc);
 }
 
 /*
@@ -1668,9 +1664,12 @@ static inline void sock_put(struct sock *sk)
        if (atomic_dec_and_test(&sk->sk_refcnt))
                sk_free(sk);
 }
+/* Generic version of sock_put(), dealing with all sockets
+ * (TCP_TIMEWAIT, ESTABLISHED...)
+ */
+void sock_gen_put(struct sock *sk);
 
-extern int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
-                         const int nested);
+int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested);
 
 static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
 {
@@ -1724,8 +1723,8 @@ static inline void sock_graft(struct sock *sk, struct socket *parent)
        write_unlock_bh(&sk->sk_callback_lock);
 }
 
-extern kuid_t sock_i_uid(struct sock *sk);
-extern unsigned long sock_i_ino(struct sock *sk);
+kuid_t sock_i_uid(struct sock *sk);
+unsigned long sock_i_ino(struct sock *sk);
 
 static inline struct dst_entry *
 __sk_dst_get(struct sock *sk)
@@ -1747,8 +1746,6 @@ sk_dst_get(struct sock *sk)
        return dst;
 }
 
-extern void sk_reset_txq(struct sock *sk);
-
 static inline void dst_negative_advice(struct sock *sk)
 {
        struct dst_entry *ndst, *dst = __sk_dst_get(sk);
@@ -1758,7 +1755,7 @@ static inline void dst_negative_advice(struct sock *sk)
 
                if (ndst != dst) {
                        rcu_assign_pointer(sk->sk_dst_cache, ndst);
-                       sk_reset_txq(sk);
+                       sk_tx_queue_clear(sk);
                }
        }
 }
@@ -1800,16 +1797,16 @@ sk_dst_reset(struct sock *sk)
        spin_unlock(&sk->sk_dst_lock);
 }
 
-extern struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie);
+struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie);
 
-extern struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie);
+struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie);
 
 static inline bool sk_can_gso(const struct sock *sk)
 {
        return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
 }
 
-extern void sk_setup_caps(struct sock *sk, struct dst_entry *dst);
+void sk_setup_caps(struct sock *sk, struct dst_entry *dst);
 
 static inline void sk_nocaps_add(struct sock *sk, netdev_features_t flags)
 {
@@ -2022,14 +2019,14 @@ static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
        sk_mem_charge(sk, skb->truesize);
 }
 
-extern void sk_reset_timer(struct sock *sk, struct timer_list *timer,
-                          unsigned long expires);
+void sk_reset_timer(struct sock *sk, struct timer_list *timer,
+                   unsigned long expires);
 
-extern void sk_stop_timer(struct sock *sk, struct timer_list *timer);
+void sk_stop_timer(struct sock *sk, struct timer_list *timer);
 
-extern int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
 
-extern int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb);
+int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb);
 
 /*
  *     Recover an error report and clear atomically
@@ -2097,7 +2094,7 @@ static inline struct page_frag *sk_page_frag(struct sock *sk)
        return &sk->sk_frag;
 }
 
-extern bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag);
+bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag);
 
 /*
  *     Default write policy as shown to user space via poll/select/SIGIO
@@ -2135,10 +2132,10 @@ static inline int sock_intr_errno(long timeo)
        return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR;
 }
 
-extern void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
-       struct sk_buff *skb);
-extern void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
-       struct sk_buff *skb);
+void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
+                          struct sk_buff *skb);
+void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
+                            struct sk_buff *skb);
 
 static inline void
 sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
@@ -2171,8 +2168,8 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
                __sock_recv_wifi_status(msg, sk, skb);
 }
 
-extern void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
-                                    struct sk_buff *skb);
+void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
+                             struct sk_buff *skb);
 
 static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
                                          struct sk_buff *skb)
@@ -2197,7 +2194,7 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
  *
  * Currently only depends on SOCK_TIMESTAMPING* flags.
  */
-extern void sock_tx_timestamp(struct sock *sk, __u8 *tx_flags);
+void sock_tx_timestamp(struct sock *sk, __u8 *tx_flags);
 
 /**
  * sk_eat_skb - Release a skb if it is no longer needed
@@ -2261,11 +2258,11 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb)
        return NULL;
 }
 
-extern void sock_enable_timestamp(struct sock *sk, int flag);
-extern int sock_get_timestamp(struct sock *, struct timeval __user *);
-extern int sock_get_timestampns(struct sock *, struct timespec __user *);
-extern int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len,
-                             int level, int type);
+void sock_enable_timestamp(struct sock *sk, int flag);
+int sock_get_timestamp(struct sock *, struct timeval __user *);
+int sock_get_timestampns(struct sock *, struct timespec __user *);
+int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len, int level,
+                      int type);
 
 /*
  *     Enable debug/info messages
index ad447f1..3af174d 100644 (file)
@@ -8,7 +8,7 @@ struct stp_proto {
        void            *data;
 };
 
-extern int stp_proto_register(const struct stp_proto *proto);
-extern void stp_proto_unregister(const struct stp_proto *proto);
+int stp_proto_register(const struct stp_proto *proto);
+void stp_proto_unregister(const struct stp_proto *proto);
 
 #endif /* _NET_STP_H */
index b1aa324..70e55d2 100644 (file)
@@ -50,7 +50,7 @@
 extern struct inet_hashinfo tcp_hashinfo;
 
 extern struct percpu_counter tcp_orphan_count;
-extern void tcp_time_wait(struct sock *sk, int state, int timeo);
+void tcp_time_wait(struct sock *sk, int state, int timeo);
 
 #define MAX_TCP_HEADER (128 + MAX_HEADER)
 #define MAX_TCP_OPTION_SPACE 40
@@ -259,6 +259,7 @@ extern int sysctl_tcp_max_orphans;
 extern int sysctl_tcp_fack;
 extern int sysctl_tcp_reordering;
 extern int sysctl_tcp_dsack;
+extern long sysctl_tcp_mem[3];
 extern int sysctl_tcp_wmem[3];
 extern int sysctl_tcp_rmem[3];
 extern int sysctl_tcp_app_win;
@@ -274,7 +275,6 @@ extern int sysctl_tcp_mtu_probing;
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
-extern int sysctl_tcp_max_ssthresh;
 extern int sysctl_tcp_thin_linear_timeouts;
 extern int sysctl_tcp_thin_dupack;
 extern int sysctl_tcp_early_retrans;
@@ -325,7 +325,7 @@ static inline bool tcp_too_many_orphans(struct sock *sk, int shift)
        return false;
 }
 
-extern bool tcp_check_oom(struct sock *sk, int shift);
+bool tcp_check_oom(struct sock *sk, int shift);
 
 /* syncookies: remember time of last synqueue overflow */
 static inline void tcp_synq_overflow(struct sock *sk)
@@ -348,38 +348,36 @@ extern struct proto tcp_prot;
 #define TCP_ADD_STATS_USER(net, field, val) SNMP_ADD_STATS_USER((net)->mib.tcp_statistics, field, val)
 #define TCP_ADD_STATS(net, field, val) SNMP_ADD_STATS((net)->mib.tcp_statistics, field, val)
 
-extern void tcp_init_mem(struct net *net);
-
-extern void tcp_tasklet_init(void);
-
-extern void tcp_v4_err(struct sk_buff *skb, u32);
-
-extern void tcp_shutdown (struct sock *sk, int how);
-
-extern void tcp_v4_early_demux(struct sk_buff *skb);
-extern int tcp_v4_rcv(struct sk_buff *skb);
-
-extern int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw);
-extern int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-                      size_t size);
-extern int tcp_sendpage(struct sock *sk, struct page *page, int offset,
-                       size_t size, int flags);
-extern void tcp_release_cb(struct sock *sk);
-extern void tcp_wfree(struct sk_buff *skb);
-extern void tcp_write_timer_handler(struct sock *sk);
-extern void tcp_delack_timer_handler(struct sock *sk);
-extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
-                                const struct tcphdr *th, unsigned int len);
-extern void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
-                               const struct tcphdr *th, unsigned int len);
-extern void tcp_rcv_space_adjust(struct sock *sk);
-extern void tcp_cleanup_rbuf(struct sock *sk, int copied);
-extern int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
-extern void tcp_twsk_destructor(struct sock *sk);
-extern ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos,
-                              struct pipe_inode_info *pipe, size_t len,
-                              unsigned int flags);
+void tcp_tasklet_init(void);
+
+void tcp_v4_err(struct sk_buff *skb, u32);
+
+void tcp_shutdown(struct sock *sk, int how);
+
+void tcp_v4_early_demux(struct sk_buff *skb);
+int tcp_v4_rcv(struct sk_buff *skb);
+
+int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw);
+int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+               size_t size);
+int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size,
+                int flags);
+void tcp_release_cb(struct sock *sk);
+void tcp_wfree(struct sk_buff *skb);
+void tcp_write_timer_handler(struct sock *sk);
+void tcp_delack_timer_handler(struct sock *sk);
+int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
+                         const struct tcphdr *th, unsigned int len);
+void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+                        const struct tcphdr *th, unsigned int len);
+void tcp_rcv_space_adjust(struct sock *sk);
+void tcp_cleanup_rbuf(struct sock *sk, int copied);
+int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp);
+void tcp_twsk_destructor(struct sock *sk);
+ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos,
+                       struct pipe_inode_info *pipe, size_t len,
+                       unsigned int flags);
 
 static inline void tcp_dec_quickack_mode(struct sock *sk,
                                         const unsigned int pkts)
@@ -409,66 +407,65 @@ enum tcp_tw_status {
 };
 
 
-extern enum tcp_tw_status tcp_timewait_state_process(struct inet_timewait_sock *tw,
-                                                    struct sk_buff *skb,
-                                                    const struct tcphdr *th);
-extern struct sock * tcp_check_req(struct sock *sk,struct sk_buff *skb,
-                                  struct request_sock *req,
-                                  struct request_sock **prev,
-                                  bool fastopen);
-extern int tcp_child_process(struct sock *parent, struct sock *child,
-                            struct sk_buff *skb);
-extern void tcp_enter_loss(struct sock *sk, int how);
-extern void tcp_clear_retrans(struct tcp_sock *tp);
-extern void tcp_update_metrics(struct sock *sk);
-extern void tcp_init_metrics(struct sock *sk);
-extern void tcp_metrics_init(void);
-extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check);
-extern bool tcp_remember_stamp(struct sock *sk);
-extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw);
-extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst);
-extern void tcp_disable_fack(struct tcp_sock *tp);
-extern void tcp_close(struct sock *sk, long timeout);
-extern void tcp_init_sock(struct sock *sk);
-extern unsigned int tcp_poll(struct file * file, struct socket *sock,
-                            struct poll_table_struct *wait);
-extern int tcp_getsockopt(struct sock *sk, int level, int optname,
+enum tcp_tw_status tcp_timewait_state_process(struct inet_timewait_sock *tw,
+                                             struct sk_buff *skb,
+                                             const struct tcphdr *th);
+struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
+                          struct request_sock *req, struct request_sock **prev,
+                          bool fastopen);
+int tcp_child_process(struct sock *parent, struct sock *child,
+                     struct sk_buff *skb);
+void tcp_enter_loss(struct sock *sk, int how);
+void tcp_clear_retrans(struct tcp_sock *tp);
+void tcp_update_metrics(struct sock *sk);
+void tcp_init_metrics(struct sock *sk);
+void tcp_metrics_init(void);
+bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst,
+                       bool paws_check);
+bool tcp_remember_stamp(struct sock *sk);
+bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw);
+void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst);
+void tcp_disable_fack(struct tcp_sock *tp);
+void tcp_close(struct sock *sk, long timeout);
+void tcp_init_sock(struct sock *sk);
+unsigned int tcp_poll(struct file *file, struct socket *sock,
+                     struct poll_table_struct *wait);
+int tcp_getsockopt(struct sock *sk, int level, int optname,
+                  char __user *optval, int __user *optlen);
+int tcp_setsockopt(struct sock *sk, int level, int optname,
+                  char __user *optval, unsigned int optlen);
+int compat_tcp_getsockopt(struct sock *sk, int level, int optname,
                          char __user *optval, int __user *optlen);
-extern int tcp_setsockopt(struct sock *sk, int level, int optname,
+int compat_tcp_setsockopt(struct sock *sk, int level, int optname,
                          char __user *optval, unsigned int optlen);
-extern int compat_tcp_getsockopt(struct sock *sk, int level, int optname,
-                                char __user *optval, int __user *optlen);
-extern int compat_tcp_setsockopt(struct sock *sk, int level, int optname,
-                                char __user *optval, unsigned int optlen);
-extern void tcp_set_keepalive(struct sock *sk, int val);
-extern void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req);
-extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-                      size_t len, int nonblock, int flags, int *addr_len);
-extern void tcp_parse_options(const struct sk_buff *skb,
-                             struct tcp_options_received *opt_rx,
-                             int estab, struct tcp_fastopen_cookie *foc);
-extern const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
+void tcp_set_keepalive(struct sock *sk, int val);
+void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req);
+int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+               size_t len, int nonblock, int flags, int *addr_len);
+void tcp_parse_options(const struct sk_buff *skb,
+                      struct tcp_options_received *opt_rx,
+                      int estab, struct tcp_fastopen_cookie *foc);
+const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
 
 /*
  *     TCP v4 functions exported for the inet6 API
  */
 
-extern void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb);
-extern int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
-extern struct sock * tcp_create_openreq_child(struct sock *sk,
-                                             struct request_sock *req,
-                                             struct sk_buff *skb);
-extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
-                                         struct request_sock *req,
-                                         struct dst_entry *dst);
-extern int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
-extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
-                         int addr_len);
-extern int tcp_connect(struct sock *sk);
-extern struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst,
-                                       struct request_sock *req,
-                                       struct tcp_fastopen_cookie *foc);
-extern int tcp_disconnect(struct sock *sk, int flags);
+void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb);
+int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
+struct sock *tcp_create_openreq_child(struct sock *sk,
+                                     struct request_sock *req,
+                                     struct sk_buff *skb);
+struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+                                 struct request_sock *req,
+                                 struct dst_entry *dst);
+int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
+int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+int tcp_connect(struct sock *sk);
+struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+                               struct request_sock *req,
+                               struct tcp_fastopen_cookie *foc);
+int tcp_disconnect(struct sock *sk, int flags);
 
 void tcp_connect_init(struct sock *sk);
 void tcp_finish_connect(struct sock *sk, struct sk_buff *skb);
@@ -476,16 +473,32 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size);
 void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb);
 
 /* From syncookies.c */
-extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
-extern int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
-                            u32 cookie);
-extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 
-                                   struct ip_options *opt);
+int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
+                     u32 cookie);
+struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
+                            struct ip_options *opt);
 #ifdef CONFIG_SYN_COOKIES
-extern u32 __cookie_v4_init_sequence(const struct iphdr *iph,
-                                    const struct tcphdr *th, u16 *mssp);
-extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, 
-                                    __u16 *mss);
+#include <linux/ktime.h>
+
+/* Syncookies use a monotonic timer which increments every 64 seconds.
+ * This counter is used both as a hash input and partially encoded into
+ * the cookie value.  A cookie is only validated further if the delta
+ * between the current counter value and the encoded one is less than this,
+ * i.e. a sent cookie is valid only at most for 128 seconds (or less if
+ * the counter advances immediately after a cookie is generated).
+ */
+#define MAX_SYNCOOKIE_AGE 2
+
+static inline u32 tcp_cookie_time(void)
+{
+       struct timespec now;
+       getnstimeofday(&now);
+       return now.tv_sec >> 6; /* 64 seconds granularity */
+}
+
+u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
+                             u16 *mssp);
+__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mss);
 #else
 static inline __u32 cookie_v4_init_sequence(struct sock *sk,
                                            struct sk_buff *skb,
@@ -495,19 +508,19 @@ static inline __u32 cookie_v4_init_sequence(struct sock *sk,
 }
 #endif
 
-extern __u32 cookie_init_timestamp(struct request_sock *req);
-extern bool cookie_check_timestamp(struct tcp_options_received *opt,
-                               struct net *net, bool *ecn_ok);
+__u32 cookie_init_timestamp(struct request_sock *req);
+bool cookie_check_timestamp(struct tcp_options_received *opt, struct net *net,
+                           bool *ecn_ok);
 
 /* From net/ipv6/syncookies.c */
-extern int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th,
-                            u32 cookie);
-extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
+int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th,
+                     u32 cookie);
+struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
 #ifdef CONFIG_SYN_COOKIES
-extern u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
-                                    const struct tcphdr *th, u16 *mssp);
-extern __u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb,
-                                    __u16 *mss);
+u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
+                             const struct tcphdr *th, u16 *mssp);
+__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb,
+                             __u16 *mss);
 #else
 static inline __u32 cookie_v6_init_sequence(struct sock *sk,
                                            struct sk_buff *skb,
@@ -518,47 +531,46 @@ static inline __u32 cookie_v6_init_sequence(struct sock *sk,
 #endif
 /* tcp_output.c */
 
-extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
-                                     int nonagle);
-extern bool tcp_may_send_now(struct sock *sk);
-extern int __tcp_retransmit_skb(struct sock *, struct sk_buff *);
-extern int tcp_retransmit_skb(struct sock *, struct sk_buff *);
-extern void tcp_retransmit_timer(struct sock *sk);
-extern void tcp_xmit_retransmit_queue(struct sock *);
-extern void tcp_simple_retransmit(struct sock *);
-extern int tcp_trim_head(struct sock *, struct sk_buff *, u32);
-extern int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int);
-
-extern void tcp_send_probe0(struct sock *);
-extern void tcp_send_partial(struct sock *);
-extern int tcp_write_wakeup(struct sock *);
-extern void tcp_send_fin(struct sock *sk);
-extern void tcp_send_active_reset(struct sock *sk, gfp_t priority);
-extern int tcp_send_synack(struct sock *);
-extern bool tcp_syn_flood_action(struct sock *sk,
-                                const struct sk_buff *skb,
-                                const char *proto);
-extern void tcp_push_one(struct sock *, unsigned int mss_now);
-extern void tcp_send_ack(struct sock *sk);
-extern void tcp_send_delayed_ack(struct sock *sk);
-extern void tcp_send_loss_probe(struct sock *sk);
-extern bool tcp_schedule_loss_probe(struct sock *sk);
+void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
+                              int nonagle);
+bool tcp_may_send_now(struct sock *sk);
+int __tcp_retransmit_skb(struct sock *, struct sk_buff *);
+int tcp_retransmit_skb(struct sock *, struct sk_buff *);
+void tcp_retransmit_timer(struct sock *sk);
+void tcp_xmit_retransmit_queue(struct sock *);
+void tcp_simple_retransmit(struct sock *);
+int tcp_trim_head(struct sock *, struct sk_buff *, u32);
+int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int);
+
+void tcp_send_probe0(struct sock *);
+void tcp_send_partial(struct sock *);
+int tcp_write_wakeup(struct sock *);
+void tcp_send_fin(struct sock *sk);
+void tcp_send_active_reset(struct sock *sk, gfp_t priority);
+int tcp_send_synack(struct sock *);
+bool tcp_syn_flood_action(struct sock *sk, const struct sk_buff *skb,
+                         const char *proto);
+void tcp_push_one(struct sock *, unsigned int mss_now);
+void tcp_send_ack(struct sock *sk);
+void tcp_send_delayed_ack(struct sock *sk);
+void tcp_send_loss_probe(struct sock *sk);
+bool tcp_schedule_loss_probe(struct sock *sk);
 
 /* tcp_input.c */
-extern void tcp_cwnd_application_limited(struct sock *sk);
-extern void tcp_resume_early_retransmit(struct sock *sk);
-extern void tcp_rearm_rto(struct sock *sk);
-extern void tcp_reset(struct sock *sk);
+void tcp_cwnd_application_limited(struct sock *sk);
+void tcp_resume_early_retransmit(struct sock *sk);
+void tcp_rearm_rto(struct sock *sk);
+void tcp_reset(struct sock *sk);
 
 /* tcp_timer.c */
-extern void tcp_init_xmit_timers(struct sock *);
+void tcp_init_xmit_timers(struct sock *);
 static inline void tcp_clear_xmit_timers(struct sock *sk)
 {
        inet_csk_clear_xmit_timers(sk);
 }
 
-extern unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu);
-extern unsigned int tcp_current_mss(struct sock *sk);
+unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu);
+unsigned int tcp_current_mss(struct sock *sk);
 
 /* Bound MSS / TSO packet size with the half of the window */
 static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
@@ -584,20 +596,20 @@ static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
 }
 
 /* tcp.c */
-extern void tcp_get_info(const struct sock *, struct tcp_info *);
+void tcp_get_info(const struct sock *, struct tcp_info *);
 
 /* Read 'sendfile()'-style from a TCP socket */
 typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *,
                                unsigned int, size_t);
-extern int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
-                        sk_read_actor_t recv_actor);
+int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
+                 sk_read_actor_t recv_actor);
 
-extern void tcp_initialize_rcv_mss(struct sock *sk);
+void tcp_initialize_rcv_mss(struct sock *sk);
 
-extern int tcp_mtu_to_mss(struct sock *sk, int pmtu);
-extern int tcp_mss_to_mtu(struct sock *sk, int mss);
-extern void tcp_mtup_init(struct sock *sk);
-extern void tcp_init_buffer_space(struct sock *sk);
+int tcp_mtu_to_mss(struct sock *sk, int pmtu);
+int tcp_mss_to_mtu(struct sock *sk, int mss);
+void tcp_mtup_init(struct sock *sk);
+void tcp_init_buffer_space(struct sock *sk);
 
 static inline void tcp_bound_rto(const struct sock *sk)
 {
@@ -610,7 +622,7 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
        return (tp->srtt >> 3) + tp->rttvar;
 }
 
-extern void tcp_set_rto(struct sock *sk);
+void tcp_set_rto(struct sock *sk);
 
 static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
 {
@@ -663,7 +675,7 @@ static inline u32 tcp_receive_window(const struct tcp_sock *tp)
  * scaling applied to the result.  The caller does these things
  * if necessary.  This is a "raw" window selection.
  */
-extern u32 __tcp_select_window(struct sock *sk);
+u32 __tcp_select_window(struct sock *sk);
 
 void tcp_send_window_probe(struct sock *sk);
 
@@ -784,7 +796,7 @@ struct tcp_congestion_ops {
        /* lower bound for congestion window (optional) */
        u32 (*min_cwnd)(const struct sock *sk);
        /* do new cwnd calculation (required) */
-       void (*cong_avoid)(struct sock *sk, u32 ack, u32 in_flight);
+       void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
        /* call before changing ca_state (optional) */
        void (*set_state)(struct sock *sk, u8 new_state);
        /* call when cwnd event occurs (optional) */
@@ -800,24 +812,24 @@ struct tcp_congestion_ops {
        struct module   *owner;
 };
 
-extern int tcp_register_congestion_control(struct tcp_congestion_ops *type);
-extern void tcp_unregister_congestion_control(struct tcp_congestion_ops *type);
+int tcp_register_congestion_control(struct tcp_congestion_ops *type);
+void tcp_unregister_congestion_control(struct tcp_congestion_ops *type);
 
-extern void tcp_init_congestion_control(struct sock *sk);
-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 void tcp_get_available_congestion_control(char *buf, size_t len);
-extern void tcp_get_allowed_congestion_control(char *buf, size_t len);
-extern int tcp_set_allowed_congestion_control(char *allowed);
-extern int tcp_set_congestion_control(struct sock *sk, const char *name);
-extern void tcp_slow_start(struct tcp_sock *tp);
-extern void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
+void tcp_init_congestion_control(struct sock *sk);
+void tcp_cleanup_congestion_control(struct sock *sk);
+int tcp_set_default_congestion_control(const char *name);
+void tcp_get_default_congestion_control(char *name);
+void tcp_get_available_congestion_control(char *buf, size_t len);
+void tcp_get_allowed_congestion_control(char *buf, size_t len);
+int tcp_set_allowed_congestion_control(char *allowed);
+int tcp_set_congestion_control(struct sock *sk, const char *name);
+int tcp_slow_start(struct tcp_sock *tp, u32 acked);
+void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
 
 extern struct tcp_congestion_ops tcp_init_congestion_ops;
-extern u32 tcp_reno_ssthresh(struct sock *sk);
-extern void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight);
-extern u32 tcp_reno_min_cwnd(const struct sock *sk);
+u32 tcp_reno_ssthresh(struct sock *sk);
+void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
+u32 tcp_reno_min_cwnd(const struct sock *sk);
 extern struct tcp_congestion_ops tcp_reno;
 
 static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
@@ -936,8 +948,8 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk)
 /* Use define here intentionally to get WARN_ON location shown at the caller */
 #define tcp_verify_left_out(tp)        WARN_ON(tcp_left_out(tp) > tp->packets_out)
 
-extern void tcp_enter_cwr(struct sock *sk, const int set_ssthresh);
-extern __u32 tcp_init_cwnd(const struct tcp_sock *tp, const struct dst_entry *dst);
+void tcp_enter_cwr(struct sock *sk, const int set_ssthresh);
+__u32 tcp_init_cwnd(const struct tcp_sock *tp, const struct dst_entry *dst);
 
 /* The maximum number of MSS of available cwnd for which TSO defers
  * sending if not using sysctl_tcp_tso_win_divisor.
@@ -963,7 +975,7 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp)
 {
        return tp->snd_una + tp->snd_wnd;
 }
-extern bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight);
+bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight);
 
 static inline void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss,
                                       const struct sk_buff *skb)
@@ -1028,7 +1040,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp)
 #endif
 }
 
-extern bool tcp_prequeue(struct sock *sk, struct sk_buff *skb);
+bool tcp_prequeue(struct sock *sk, struct sk_buff *skb);
 
 #undef STATE_TRACE
 
@@ -1039,9 +1051,9 @@ static const char *statename[]={
        "Close Wait","Last ACK","Listen","Closing"
 };
 #endif
-extern void tcp_set_state(struct sock *sk, int state);
+void tcp_set_state(struct sock *sk, int state);
 
-extern void tcp_done(struct sock *sk);
+void tcp_done(struct sock *sk);
 
 static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
 {
@@ -1049,13 +1061,12 @@ static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
        rx_opt->num_sacks = 0;
 }
 
-extern u32 tcp_default_init_rwnd(u32 mss);
+u32 tcp_default_init_rwnd(u32 mss);
 
 /* 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,
-                                     int wscale_ok, __u8 *rcv_wscale,
-                                     __u32 init_rcv_wnd);
+void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd,
+                              __u32 *window_clamp, int wscale_ok,
+                              __u8 *rcv_wscale, __u32 init_rcv_wnd);
 
 static inline int tcp_win_from_space(int space)
 {
@@ -1095,11 +1106,11 @@ static inline void tcp_openreq_init(struct request_sock *req,
        ireq->wscale_ok = rx_opt->wscale_ok;
        ireq->acked = 0;
        ireq->ecn_ok = 0;
-       ireq->rmt_port = tcp_hdr(skb)->source;
-       ireq->loc_port = tcp_hdr(skb)->dest;
+       ireq->ir_rmt_port = tcp_hdr(skb)->source;
+       ireq->ir_num = ntohs(tcp_hdr(skb)->dest);
 }
 
-extern void tcp_enter_memory_pressure(struct sock *sk);
+void tcp_enter_memory_pressure(struct sock *sk);
 
 static inline int keepalive_intvl_when(const struct tcp_sock *tp)
 {
@@ -1252,21 +1263,20 @@ struct tcp_md5sig_pool {
 };
 
 /* - functions */
-extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
-                              const struct sock *sk,
-                              const struct request_sock *req,
-                              const struct sk_buff *skb);
-extern int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
-                         int family, const u8 *newkey,
-                         u8 newkeylen, gfp_t gfp);
-extern int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
-                         int family);
-extern struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
+int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
+                       const struct sock *sk, const struct request_sock *req,
+                       const struct sk_buff *skb);
+int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
+                  int family, const u8 *newkey, u8 newkeylen, gfp_t gfp);
+int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
+                  int family);
+struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
                                         struct sock *addr_sk);
 
 #ifdef CONFIG_TCP_MD5SIG
-extern struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
-                       const union tcp_md5_addr *addr, int family);
+struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
+                                        const union tcp_md5_addr *addr,
+                                        int family);
 #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key)
 #else
 static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
@@ -1278,27 +1288,26 @@ static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
 #define tcp_twsk_md5_key(twsk) NULL
 #endif
 
-extern bool tcp_alloc_md5sig_pool(void);
+bool tcp_alloc_md5sig_pool(void);
 
-extern struct tcp_md5sig_pool  *tcp_get_md5sig_pool(void);
+struct tcp_md5sig_pool *tcp_get_md5sig_pool(void);
 static inline void tcp_put_md5sig_pool(void)
 {
        local_bh_enable();
 }
 
-extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *);
-extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *,
-                                unsigned int header_len);
-extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
-                           const struct tcp_md5sig_key *key);
+int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *);
+int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *,
+                         unsigned int header_len);
+int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
+                    const struct tcp_md5sig_key *key);
 
 /* From tcp_fastopen.c */
-extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
-                                  struct tcp_fastopen_cookie *cookie,
-                                  int *syn_loss, unsigned long *last_syn_loss);
-extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
-                                  struct tcp_fastopen_cookie *cookie,
-                                  bool syn_lost);
+void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
+                           struct tcp_fastopen_cookie *cookie, int *syn_loss,
+                           unsigned long *last_syn_loss);
+void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
+                           struct tcp_fastopen_cookie *cookie, bool syn_lost);
 struct tcp_fastopen_request {
        /* Fast Open cookie. Size 0 means a cookie request */
        struct tcp_fastopen_cookie      cookie;
@@ -1309,9 +1318,9 @@ void tcp_free_fastopen_req(struct tcp_sock *tp);
 
 extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
 int tcp_fastopen_reset_cipher(void *key, unsigned int len);
-extern void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
-                                   struct tcp_fastopen_cookie *foc);
-
+void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
+                            struct tcp_fastopen_cookie *foc);
+void tcp_fastopen_init_key_once(bool publish);
 #define TCP_FASTOPEN_KEY_LENGTH 16
 
 /* Fastopen key context */
@@ -1507,7 +1516,6 @@ enum tcp_seq_states {
        TCP_SEQ_STATE_LISTENING,
        TCP_SEQ_STATE_OPENREQ,
        TCP_SEQ_STATE_ESTABLISHED,
-       TCP_SEQ_STATE_TIME_WAIT,
 };
 
 int tcp_seq_open(struct inode *inode, struct file *file);
@@ -1529,22 +1537,20 @@ struct tcp_iter_state {
        loff_t                  last_pos;
 };
 
-extern int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo);
-extern void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo);
+int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo);
+void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo);
 
 extern struct request_sock_ops tcp_request_sock_ops;
 extern struct request_sock_ops tcp6_request_sock_ops;
 
-extern void tcp_v4_destroy_sock(struct sock *sk);
+void tcp_v4_destroy_sock(struct sock *sk);
 
-extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
-                                      netdev_features_t features);
-extern struct sk_buff **tcp_gro_receive(struct sk_buff **head,
-                                       struct sk_buff *skb);
-extern int tcp_gro_complete(struct sk_buff *skb);
+struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
+                               netdev_features_t features);
+struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb);
+int tcp_gro_complete(struct sk_buff *skb);
 
-extern void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr,
-                               __be32 daddr);
+void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr);
 
 static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp)
 {
@@ -1560,8 +1566,8 @@ static inline bool tcp_stream_memory_free(const struct sock *sk)
 }
 
 #ifdef CONFIG_PROC_FS
-extern int tcp4_proc_init(void);
-extern void tcp4_proc_exit(void);
+int tcp4_proc_init(void);
+void tcp4_proc_exit(void);
 #endif
 
 /* TCP af-specific functions */
@@ -1592,9 +1598,9 @@ struct tcp_request_sock_ops {
 #endif
 };
 
-extern int tcpv4_offload_init(void);
+int tcpv4_offload_init(void);
 
-extern void tcp_v4_init(void);
-extern void tcp_init(void);
+void tcp_v4_init(void);
+void tcp_init(void);
 
 #endif /* _TCP_H */
index 7df18bc..05b94d9 100644 (file)
@@ -1,19 +1,7 @@
 #ifndef _TCP_MEMCG_H
 #define _TCP_MEMCG_H
 
-struct tcp_memcontrol {
-       struct cg_proto cg_proto;
-       /* per-cgroup tcp memory pressure knobs */
-       struct res_counter tcp_memory_allocated;
-       struct percpu_counter tcp_sockets_allocated;
-       /* those two are read-mostly, leave them at the end */
-       long tcp_prot_mem[3];
-       int tcp_memory_pressure;
-};
-
 struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg);
 int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss);
 void tcp_destroy_cgroup(struct mem_cgroup *memcg);
-unsigned long long tcp_max_memory(const struct mem_cgroup *memcg);
-void tcp_prot_mem(struct mem_cgroup *memcg, long val, int idx);
 #endif /* _TCP_MEMCG_H */
index ef2e0b7..a24f0f3 100644 (file)
@@ -79,7 +79,7 @@ struct udp_table {
        unsigned int            log;
 };
 extern struct udp_table udp_table;
-extern void udp_table_init(struct udp_table *, const char *);
+void udp_table_init(struct udp_table *, const char *);
 static inline struct udp_hslot *udp_hashslot(struct udp_table *table,
                                             struct net *net, unsigned int num)
 {
@@ -162,52 +162,53 @@ static inline void udp_lib_hash(struct sock *sk)
        BUG();
 }
 
-extern void udp_lib_unhash(struct sock *sk);
-extern void udp_lib_rehash(struct sock *sk, u16 new_hash);
+void udp_lib_unhash(struct sock *sk);
+void udp_lib_rehash(struct sock *sk, u16 new_hash);
 
 static inline void udp_lib_close(struct sock *sk, long timeout)
 {
        sk_common_release(sk);
 }
 
-extern int udp_lib_get_port(struct sock *sk, unsigned short snum,
-                           int (*)(const struct sock *,const struct sock *),
-                           unsigned int hash2_nulladdr);
+int udp_lib_get_port(struct sock *sk, unsigned short snum,
+                    int (*)(const struct sock *, const struct sock *),
+                    unsigned int hash2_nulladdr);
 
 /* net/ipv4/udp.c */
-extern int udp_get_port(struct sock *sk, unsigned short snum,
-                       int (*saddr_cmp)(const struct sock *,
-                                        const struct sock *));
-extern void udp_err(struct sk_buff *, u32);
-extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
-                           struct msghdr *msg, size_t len);
-extern int udp_push_pending_frames(struct sock *sk);
-extern void udp_flush_pending_frames(struct sock *sk);
-extern void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
-extern int udp_rcv(struct sk_buff *skb);
-extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern int udp_disconnect(struct sock *sk, int flags);
-extern unsigned int udp_poll(struct file *file, struct socket *sock,
-                            poll_table *wait);
-extern struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
-                                             netdev_features_t features);
-extern int udp_lib_getsockopt(struct sock *sk, int level, int optname,
-                             char __user *optval, int __user *optlen);
-extern int udp_lib_setsockopt(struct sock *sk, int level, int optname,
-                             char __user *optval, unsigned int optlen,
-                             int (*push_pending_frames)(struct sock *));
-extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
-                                   __be32 daddr, __be16 dport,
-                                   int dif);
-extern struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
-                                   __be32 daddr, __be16 dport,
-                                   int dif, struct udp_table *tbl);
-extern struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport,
-                                   const struct in6_addr *daddr, __be16 dport,
-                                   int dif);
-extern struct sock *__udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport,
-                                   const struct in6_addr *daddr, __be16 dport,
-                                   int dif, struct udp_table *tbl);
+void udp_v4_early_demux(struct sk_buff *skb);
+int udp_get_port(struct sock *sk, unsigned short snum,
+                int (*saddr_cmp)(const struct sock *,
+                                 const struct sock *));
+void udp_err(struct sk_buff *, u32);
+int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+               size_t len);
+int udp_push_pending_frames(struct sock *sk);
+void udp_flush_pending_frames(struct sock *sk);
+void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
+int udp_rcv(struct sk_buff *skb);
+int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+int udp_disconnect(struct sock *sk, int flags);
+unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
+struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+                                      netdev_features_t features);
+int udp_lib_getsockopt(struct sock *sk, int level, int optname,
+                      char __user *optval, int __user *optlen);
+int udp_lib_setsockopt(struct sock *sk, int level, int optname,
+                      char __user *optval, unsigned int optlen,
+                      int (*push_pending_frames)(struct sock *));
+struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
+                            __be32 daddr, __be16 dport, int dif);
+struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
+                              __be32 daddr, __be16 dport, int dif,
+                              struct udp_table *tbl);
+struct sock *udp6_lib_lookup(struct net *net,
+                            const struct in6_addr *saddr, __be16 sport,
+                            const struct in6_addr *daddr, __be16 dport,
+                            int dif);
+struct sock *__udp6_lib_lookup(struct net *net,
+                              const struct in6_addr *saddr, __be16 sport,
+                              const struct in6_addr *daddr, __be16 dport,
+                              int dif, struct udp_table *tbl);
 
 /*
  *     SNMP statistics for UDP and UDP-Lite
@@ -229,13 +230,13 @@ extern struct sock *__udp6_lib_lookup(struct net *net, const struct in6_addr *sa
 } while(0)
 
 #if IS_ENABLED(CONFIG_IPV6)
-#define UDPX_INC_STATS_BH(sk, field) \
-       do { \
-               if ((sk)->sk_family == AF_INET) \
-                       UDP_INC_STATS_BH(sock_net(sk), field, 0); \
-               else \
-                       UDP6_INC_STATS_BH(sock_net(sk), field, 0); \
-       } while (0);
+#define UDPX_INC_STATS_BH(sk, field)                                   \
+do {                                                                   \
+       if ((sk)->sk_family == AF_INET)                                 \
+               UDP_INC_STATS_BH(sock_net(sk), field, 0);               \
+       else                                                            \
+               UDP6_INC_STATS_BH(sock_net(sk), field, 0);              \
+} while (0)
 #else
 #define UDPX_INC_STATS_BH(sk, field) UDP_INC_STATS_BH(sock_net(sk), field, 0)
 #endif
@@ -259,19 +260,19 @@ struct udp_iter_state {
 };
 
 #ifdef CONFIG_PROC_FS
-extern int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo);
-extern void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo);
+int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo);
+void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo);
 
-extern int udp4_proc_init(void);
-extern void udp4_proc_exit(void);
+int udp4_proc_init(void);
+void udp4_proc_exit(void);
 #endif
 
-extern int udpv4_offload_init(void);
+int udpv4_offload_init(void);
 
-extern void udp_init(void);
+void udp_init(void);
 
-extern void udp_encap_enable(void);
+void udp_encap_enable(void);
 #if IS_ENABLED(CONFIG_IPV6)
-extern void udpv6_encap_enable(void);
+void udpv6_encap_enable(void);
 #endif
 #endif /* _UDP_H */
index 7137545..2caadab 100644 (file)
@@ -126,7 +126,7 @@ static inline __wsum udplite_csum(struct sk_buff *skb)
        return skb_checksum(skb, off, len, 0);
 }
 
-extern void    udplite4_register(void);
-extern int     udplite_get_port(struct sock *sk, unsigned short snum,
-                       int (*scmp)(const struct sock *, const struct sock *));
+void udplite4_register(void);
+int udplite_get_port(struct sock *sk, unsigned short snum,
+                    int (*scmp)(const struct sock *, const struct sock *));
 #endif /* _UDPLITE_H */
index 2d64d3c..6b6d180 100644 (file)
@@ -36,5 +36,16 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
 
 __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb);
 
+/* IP header + UDP + VXLAN + Ethernet header */
+#define VXLAN_HEADROOM (20 + 8 + 8 + 14)
+/* IPv6 header + UDP + VXLAN + Ethernet header */
+#define VXLAN6_HEADROOM (40 + 8 + 8 + 14)
+
+#if IS_ENABLED(CONFIG_VXLAN)
 void vxlan_get_rx_port(struct net_device *netdev);
+#else
+static inline void vxlan_get_rx_port(struct net_device *netdev)
+{
+}
+#endif
 #endif
index 4f6e742..3459119 100644 (file)
@@ -6,13 +6,13 @@
 struct net;
 
 #ifdef CONFIG_WEXT_CORE
-extern int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
-                            void __user *arg);
-extern int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
-                                   unsigned long arg);
+int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
+                     void __user *arg);
+int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
+                            unsigned long arg);
 
-extern struct iw_statistics *get_wireless_stats(struct net_device *dev);
-extern int call_commit_handler(struct net_device *dev);
+struct iw_statistics *get_wireless_stats(struct net_device *dev);
+int call_commit_handler(struct net_device *dev);
 #else
 static inline int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
                                    void __user *arg)
@@ -27,8 +27,8 @@ static inline int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
 #endif
 
 #ifdef CONFIG_WEXT_PROC
-extern int wext_proc_init(struct net *net);
-extern void wext_proc_exit(struct net *net);
+int wext_proc_init(struct net *net);
+void wext_proc_exit(struct net *net);
 #else
 static inline int wext_proc_init(struct net *net)
 {
index bbb74f9..98498e1 100644 (file)
@@ -438,9 +438,9 @@ struct wimax_dev {
  *
  * These functions are not exported to user space.
  */
-extern void wimax_dev_init(struct wimax_dev *);
-extern int wimax_dev_add(struct wimax_dev *, struct net_device *);
-extern void wimax_dev_rm(struct wimax_dev *);
+void wimax_dev_init(struct wimax_dev *);
+int wimax_dev_add(struct wimax_dev *, struct net_device *);
+void wimax_dev_rm(struct wimax_dev *);
 
 static inline
 struct wimax_dev *net_dev_to_wimax(struct net_device *net_dev)
@@ -454,8 +454,8 @@ struct device *wimax_dev_to_dev(struct wimax_dev *wimax_dev)
        return wimax_dev->net_dev->dev.parent;
 }
 
-extern void wimax_state_change(struct wimax_dev *, enum wimax_st);
-extern enum wimax_st wimax_state_get(struct wimax_dev *);
+void wimax_state_change(struct wimax_dev *, enum wimax_st);
+enum wimax_st wimax_state_get(struct wimax_dev *);
 
 /*
  * Radio Switch state reporting.
@@ -463,8 +463,8 @@ extern enum wimax_st wimax_state_get(struct wimax_dev *);
  * enum wimax_rf_state is declared in linux/wimax.h so the exports
  * to user space can use it.
  */
-extern void wimax_report_rfkill_hw(struct wimax_dev *, enum wimax_rf_state);
-extern void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state);
+void wimax_report_rfkill_hw(struct wimax_dev *, enum wimax_rf_state);
+void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state);
 
 
 /*
@@ -490,15 +490,14 @@ extern void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state);
  * send diagnostics information that a device-specific diagnostics
  * tool would be interested in.
  */
-extern struct sk_buff *wimax_msg_alloc(struct wimax_dev *, const char *,
-                                      const void *, size_t, gfp_t);
-extern int wimax_msg_send(struct wimax_dev *, struct sk_buff *);
-extern int wimax_msg(struct wimax_dev *, const char *,
-                    const void *, size_t, gfp_t);
+struct sk_buff *wimax_msg_alloc(struct wimax_dev *, const char *, const void *,
+                               size_t, gfp_t);
+int wimax_msg_send(struct wimax_dev *, struct sk_buff *);
+int wimax_msg(struct wimax_dev *, const char *, const void *, size_t, gfp_t);
 
-extern const void *wimax_msg_data_len(struct sk_buff *, size_t *);
-extern const void *wimax_msg_data(struct sk_buff *);
-extern ssize_t wimax_msg_len(struct sk_buff *);
+const void *wimax_msg_data_len(struct sk_buff *, size_t *);
+const void *wimax_msg_data(struct sk_buff *);
+ssize_t wimax_msg_len(struct sk_buff *);
 
 
 /*
@@ -513,7 +512,7 @@ extern ssize_t wimax_msg_len(struct sk_buff *);
  * device's control structure and (as such) the 'struct wimax_dev' is
  * referenced by the caller.
  */
-extern int wimax_rfkill(struct wimax_dev *, enum wimax_rf_state);
-extern int wimax_reset(struct wimax_dev *);
+int wimax_rfkill(struct wimax_dev *, enum wimax_rf_state);
+int wimax_reset(struct wimax_dev *);
 
 #endif /* #ifndef __NET__WIMAX_H__ */
index b4a8a89..c383aa4 100644 (file)
@@ -187,57 +187,57 @@ extern int  sysctl_x25_clear_request_timeout;
 extern int  sysctl_x25_ack_holdback_timeout;
 extern int  sysctl_x25_forward;
 
-extern int x25_parse_address_block(struct sk_buff *skb,
-               struct x25_address *called_addr,
-               struct x25_address *calling_addr);
-
-extern int  x25_addr_ntoa(unsigned char *, struct x25_address *,
-                         struct x25_address *);
-extern int  x25_addr_aton(unsigned char *, struct x25_address *,
-                         struct x25_address *);
-extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *);
-extern void x25_destroy_socket_from_timer(struct sock *);
-extern int  x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int);
-extern void x25_kill_by_neigh(struct x25_neigh *);
+int x25_parse_address_block(struct sk_buff *skb,
+                           struct x25_address *called_addr,
+                           struct x25_address *calling_addr);
+
+int x25_addr_ntoa(unsigned char *, struct x25_address *, struct x25_address *);
+int x25_addr_aton(unsigned char *, struct x25_address *, struct x25_address *);
+struct sock *x25_find_socket(unsigned int, struct x25_neigh *);
+void x25_destroy_socket_from_timer(struct sock *);
+int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int);
+void x25_kill_by_neigh(struct x25_neigh *);
 
 /* x25_dev.c */
-extern void x25_send_frame(struct sk_buff *, struct x25_neigh *);
-extern int  x25_lapb_receive_frame(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *);
-extern void x25_establish_link(struct x25_neigh *);
-extern void x25_terminate_link(struct x25_neigh *);
+void x25_send_frame(struct sk_buff *, struct x25_neigh *);
+int x25_lapb_receive_frame(struct sk_buff *, struct net_device *,
+                          struct packet_type *, struct net_device *);
+void x25_establish_link(struct x25_neigh *);
+void x25_terminate_link(struct x25_neigh *);
 
 /* x25_facilities.c */
-extern int x25_parse_facilities(struct sk_buff *, struct x25_facilities *,
-                               struct x25_dte_facilities *, unsigned long *);
-extern int x25_create_facilities(unsigned char *, struct x25_facilities *,
-                               struct x25_dte_facilities *, unsigned long);
-extern int x25_negotiate_facilities(struct sk_buff *, struct sock *,
-                               struct x25_facilities *,
-                               struct x25_dte_facilities *);
-extern void x25_limit_facilities(struct x25_facilities *, struct x25_neigh *);
+int x25_parse_facilities(struct sk_buff *, struct x25_facilities *,
+                        struct x25_dte_facilities *, unsigned long *);
+int x25_create_facilities(unsigned char *, struct x25_facilities *,
+                         struct x25_dte_facilities *, unsigned long);
+int x25_negotiate_facilities(struct sk_buff *, struct sock *,
+                            struct x25_facilities *,
+                            struct x25_dte_facilities *);
+void x25_limit_facilities(struct x25_facilities *, struct x25_neigh *);
 
 /* x25_forward.c */
-extern void x25_clear_forward_by_lci(unsigned int lci);
-extern void x25_clear_forward_by_dev(struct net_device *);
-extern int x25_forward_data(int, struct x25_neigh *, struct sk_buff *);
-extern int x25_forward_call(struct x25_address *, struct x25_neigh *,
-                               struct sk_buff *, int);
+void x25_clear_forward_by_lci(unsigned int lci);
+void x25_clear_forward_by_dev(struct net_device *);
+int x25_forward_data(int, struct x25_neigh *, struct sk_buff *);
+int x25_forward_call(struct x25_address *, struct x25_neigh *, struct sk_buff *,
+                    int);
 
 /* x25_in.c */
-extern int  x25_process_rx_frame(struct sock *, struct sk_buff *);
-extern int  x25_backlog_rcv(struct sock *, struct sk_buff *);
+int x25_process_rx_frame(struct sock *, struct sk_buff *);
+int x25_backlog_rcv(struct sock *, struct sk_buff *);
 
 /* x25_link.c */
-extern void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short);
-extern void x25_link_device_up(struct net_device *);
-extern void x25_link_device_down(struct net_device *);
-extern void x25_link_established(struct x25_neigh *);
-extern void x25_link_terminated(struct x25_neigh *);
-extern void x25_transmit_clear_request(struct x25_neigh *, unsigned int, unsigned char);
-extern void x25_transmit_link(struct sk_buff *, struct x25_neigh *);
-extern int  x25_subscr_ioctl(unsigned int, void __user *);
-extern struct x25_neigh *x25_get_neigh(struct net_device *);
-extern void x25_link_free(void);
+void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short);
+void x25_link_device_up(struct net_device *);
+void x25_link_device_down(struct net_device *);
+void x25_link_established(struct x25_neigh *);
+void x25_link_terminated(struct x25_neigh *);
+void x25_transmit_clear_request(struct x25_neigh *, unsigned int,
+                               unsigned char);
+void x25_transmit_link(struct sk_buff *, struct x25_neigh *);
+int x25_subscr_ioctl(unsigned int, void __user *);
+struct x25_neigh *x25_get_neigh(struct net_device *);
+void x25_link_free(void);
 
 /* x25_neigh.c */
 static __inline__ void x25_neigh_hold(struct x25_neigh *nb)
@@ -252,16 +252,16 @@ static __inline__ void x25_neigh_put(struct x25_neigh *nb)
 }
 
 /* x25_out.c */
-extern  int x25_output(struct sock *, struct sk_buff *);
-extern void x25_kick(struct sock *);
-extern void x25_enquiry_response(struct sock *);
+int x25_output(struct sock *, struct sk_buff *);
+void x25_kick(struct sock *);
+void x25_enquiry_response(struct sock *);
 
 /* x25_route.c */
-extern struct x25_route *x25_get_route(struct x25_address *addr);
-extern struct net_device *x25_dev_get(char *);
-extern void x25_route_device_down(struct net_device *dev);
-extern int  x25_route_ioctl(unsigned int, void __user *);
-extern void x25_route_free(void);
+struct x25_route *x25_get_route(struct x25_address *addr);
+struct net_device *x25_dev_get(char *);
+void x25_route_device_down(struct net_device *dev);
+int x25_route_ioctl(unsigned int, void __user *);
+void x25_route_free(void);
 
 static __inline__ void x25_route_hold(struct x25_route *rt)
 {
@@ -275,30 +275,31 @@ static __inline__ void x25_route_put(struct x25_route *rt)
 }
 
 /* x25_subr.c */
-extern void x25_clear_queues(struct sock *);
-extern void x25_frames_acked(struct sock *, unsigned short);
-extern void x25_requeue_frames(struct sock *);
-extern int  x25_validate_nr(struct sock *, unsigned short);
-extern void x25_write_internal(struct sock *, int);
-extern int  x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *);
-extern void x25_disconnect(struct sock *, int, unsigned char, unsigned char);
+void x25_clear_queues(struct sock *);
+void x25_frames_acked(struct sock *, unsigned short);
+void x25_requeue_frames(struct sock *);
+int x25_validate_nr(struct sock *, unsigned short);
+void x25_write_internal(struct sock *, int);
+int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *,
+              int *);
+void x25_disconnect(struct sock *, int, unsigned char, unsigned char);
 
 /* x25_timer.c */
-extern void x25_init_timers(struct sock *sk);
-extern void x25_start_heartbeat(struct sock *);
-extern void x25_start_t2timer(struct sock *);
-extern void x25_start_t21timer(struct sock *);
-extern void x25_start_t22timer(struct sock *);
-extern void x25_start_t23timer(struct sock *);
-extern void x25_stop_heartbeat(struct sock *);
-extern void x25_stop_timer(struct sock *);
-extern unsigned long x25_display_timer(struct sock *);
-extern void x25_check_rbuf(struct sock *);
+void x25_init_timers(struct sock *sk);
+void x25_start_heartbeat(struct sock *);
+void x25_start_t2timer(struct sock *);
+void x25_start_t21timer(struct sock *);
+void x25_start_t22timer(struct sock *);
+void x25_start_t23timer(struct sock *);
+void x25_stop_heartbeat(struct sock *);
+void x25_stop_timer(struct sock *);
+unsigned long x25_display_timer(struct sock *);
+void x25_check_rbuf(struct sock *);
 
 /* sysctl_net_x25.c */
 #ifdef CONFIG_SYSCTL
-extern void x25_register_sysctl(void);
-extern void x25_unregister_sysctl(void);
+void x25_register_sysctl(void);
+void x25_unregister_sysctl(void);
 #else
 static inline void x25_register_sysctl(void) {};
 static inline void x25_unregister_sysctl(void) {};
@@ -318,6 +319,6 @@ extern rwlock_t x25_forward_list_lock;
 extern struct list_head x25_neigh_list;
 extern rwlock_t x25_neigh_list_lock;
 
-extern int x25_proc_init(void);
-extern void x25_proc_exit(void);
+int x25_proc_init(void);
+void x25_proc_exit(void);
 #endif
index e253bf0..6b82fdf 100644 (file)
@@ -307,15 +307,17 @@ struct xfrm_policy_afinfo {
        struct dst_entry        *(*blackhole_route)(struct net *net, struct dst_entry *orig);
 };
 
-extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo);
-extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo);
-extern void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c);
-extern void km_state_notify(struct xfrm_state *x, const struct km_event *c);
+int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo);
+int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo);
+void km_policy_notify(struct xfrm_policy *xp, int dir,
+                     const struct km_event *c);
+void km_state_notify(struct xfrm_state *x, const struct km_event *c);
 
 struct xfrm_tmpl;
-extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
-extern void km_state_expired(struct xfrm_state *x, int hard, u32 portid);
-extern int __xfrm_state_delete(struct xfrm_state *x);
+int km_query(struct xfrm_state *x, struct xfrm_tmpl *t,
+            struct xfrm_policy *pol);
+void km_state_expired(struct xfrm_state *x, int hard, u32 portid);
+int __xfrm_state_delete(struct xfrm_state *x);
 
 struct xfrm_state_afinfo {
        unsigned int            family;
@@ -344,12 +346,12 @@ struct xfrm_state_afinfo {
        void                    (*local_error)(struct sk_buff *skb, u32 mtu);
 };
 
-extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
-extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
-extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
-extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
+int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
+int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
+struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
+void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
 
-extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
+void xfrm_state_delete_tunnel(struct xfrm_state *x);
 
 struct xfrm_type {
        char                    *description;
@@ -372,8 +374,8 @@ struct xfrm_type {
        u32                     (*get_mtu)(struct xfrm_state *, int size);
 };
 
-extern int xfrm_register_type(const struct xfrm_type *type, unsigned short family);
-extern int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family);
+int xfrm_register_type(const struct xfrm_type *type, unsigned short family);
+int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family);
 
 struct xfrm_mode {
        /*
@@ -434,8 +436,8 @@ enum {
        XFRM_MODE_FLAG_TUNNEL = 1,
 };
 
-extern int xfrm_register_mode(struct xfrm_mode *mode, int family);
-extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family);
+int xfrm_register_mode(struct xfrm_mode *mode, int family);
+int xfrm_unregister_mode(struct xfrm_mode *mode, int family);
 
 static inline int xfrm_af2proto(unsigned int family)
 {
@@ -595,8 +597,8 @@ struct xfrm_mgr {
                                           const struct xfrm_kmaddress *k);
 };
 
-extern int xfrm_register_km(struct xfrm_mgr *km);
-extern int xfrm_unregister_km(struct xfrm_mgr *km);
+int xfrm_register_km(struct xfrm_mgr *km);
+int xfrm_unregister_km(struct xfrm_mgr *km);
 
 /*
  * This structure is used for the duration where packets are being
@@ -713,23 +715,23 @@ static inline void xfrm_audit_helper_usrinfo(kuid_t auid, u32 ses, u32 secid,
                audit_log_task_context(audit_buf);
 }
 
-extern void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
-                                 kuid_t auid, u32 ses, u32 secid);
-extern void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
-                                 kuid_t auid, u32 ses, u32 secid);
-extern void xfrm_audit_state_add(struct xfrm_state *x, int result,
-                                kuid_t auid, u32 ses, u32 secid);
-extern void xfrm_audit_state_delete(struct xfrm_state *x, int result,
-                                   kuid_t auid, u32 ses, u32 secid);
-extern void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
-                                            struct sk_buff *skb);
-extern void xfrm_audit_state_replay(struct xfrm_state *x,
-                                   struct sk_buff *skb, __be32 net_seq);
-extern void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family);
-extern void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family,
-                                     __be32 net_spi, __be32 net_seq);
-extern void xfrm_audit_state_icvfail(struct xfrm_state *x,
-                                    struct sk_buff *skb, u8 proto);
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid,
+                          u32 ses, u32 secid);
+void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid,
+                             u32 ses, u32 secid);
+void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid,
+                         u32 ses, u32 secid);
+void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid,
+                            u32 ses, u32 secid);
+void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
+                                     struct sk_buff *skb);
+void xfrm_audit_state_replay(struct xfrm_state *x, struct sk_buff *skb,
+                            __be32 net_seq);
+void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family);
+void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, __be32 net_spi,
+                              __be32 net_seq);
+void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb,
+                             u8 proto);
 #else
 
 static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
@@ -784,7 +786,7 @@ static inline void xfrm_pol_hold(struct xfrm_policy *policy)
                atomic_inc(&policy->refcnt);
 }
 
-extern void xfrm_policy_destroy(struct xfrm_policy *policy);
+void xfrm_policy_destroy(struct xfrm_policy *policy);
 
 static inline void xfrm_pol_put(struct xfrm_policy *policy)
 {
@@ -799,7 +801,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols)
                xfrm_pol_put(pols[i]);
 }
 
-extern void __xfrm_state_destroy(struct xfrm_state *);
+void __xfrm_state_destroy(struct xfrm_state *);
 
 static inline void __xfrm_state_put(struct xfrm_state *x)
 {
@@ -903,9 +905,8 @@ __be16 xfrm_flowi_dport(const struct flowi *fl, const union flowi_uli *uli)
        return port;
 }
 
-extern bool xfrm_selector_match(const struct xfrm_selector *sel,
-                               const struct flowi *fl,
-                               unsigned short family);
+bool xfrm_selector_match(const struct xfrm_selector *sel,
+                        const struct flowi *fl, unsigned short family);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 /*     If neither has a context --> match
@@ -975,7 +976,7 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
 }
 #endif
 
-extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
+void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
 
 struct sec_path {
        atomic_t                refcnt;
@@ -1000,7 +1001,7 @@ secpath_get(struct sec_path *sp)
        return sp;
 }
 
-extern void __secpath_destroy(struct sec_path *sp);
+void __secpath_destroy(struct sec_path *sp);
 
 static inline void
 secpath_put(struct sec_path *sp)
@@ -1009,7 +1010,7 @@ secpath_put(struct sec_path *sp)
                __secpath_destroy(sp);
 }
 
-extern struct sec_path *secpath_dup(struct sec_path *src);
+struct sec_path *secpath_dup(struct sec_path *src);
 
 static inline void
 secpath_reset(struct sk_buff *skb)
@@ -1059,7 +1060,8 @@ xfrm_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, un
 }
 
 #ifdef CONFIG_XFRM
-extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family);
+int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb,
+                       unsigned short family);
 
 static inline int __xfrm_policy_check2(struct sock *sk, int dir,
                                       struct sk_buff *skb,
@@ -1103,8 +1105,8 @@ static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir,
        return __xfrm_policy_check2(sk, dir, skb, AF_INET6, 1);
 }
 
-extern int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
-                                unsigned int family, int reverse);
+int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
+                         unsigned int family, int reverse);
 
 static inline int xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
                                      unsigned int family)
@@ -1119,7 +1121,7 @@ static inline int xfrm_decode_session_reverse(struct sk_buff *skb,
        return __xfrm_decode_session(skb, fl, family, 1);
 }
 
-extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
+int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
 
 static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
@@ -1140,7 +1142,7 @@ static inline int xfrm6_route_forward(struct sk_buff *skb)
        return xfrm_route_forward(skb, AF_INET6);
 }
 
-extern int __xfrm_sk_clone_policy(struct sock *sk);
+int __xfrm_sk_clone_policy(struct sock *sk);
 
 static inline int xfrm_sk_clone_policy(struct sock *sk)
 {
@@ -1149,7 +1151,7 @@ static inline int xfrm_sk_clone_policy(struct sock *sk)
        return 0;
 }
 
-extern int xfrm_policy_delete(struct xfrm_policy *pol, int dir);
+int xfrm_policy_delete(struct xfrm_policy *pol, int dir);
 
 static inline void xfrm_sk_free_policy(struct sock *sk)
 {
@@ -1163,7 +1165,7 @@ static inline void xfrm_sk_free_policy(struct sock *sk)
        }
 }
 
-extern void xfrm_garbage_collect(struct net *net);
+void xfrm_garbage_collect(struct net *net);
 
 #else
 
@@ -1355,6 +1357,12 @@ struct xfrm_tunnel {
        int priority;
 };
 
+struct xfrm_tunnel_notifier {
+       int (*handler)(struct sk_buff *skb);
+       struct xfrm_tunnel_notifier __rcu *next;
+       int priority;
+};
+
 struct xfrm6_tunnel {
        int (*handler)(struct sk_buff *skb);
        int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
@@ -1363,16 +1371,16 @@ struct xfrm6_tunnel {
        int priority;
 };
 
-extern void xfrm_init(void);
-extern void xfrm4_init(void);
-extern int xfrm_state_init(struct net *net);
-extern void xfrm_state_fini(struct net *net);
-extern void xfrm4_state_init(void);
+void xfrm_init(void);
+void xfrm4_init(void);
+int xfrm_state_init(struct net *net);
+void xfrm_state_fini(struct net *net);
+void xfrm4_state_init(void);
 #ifdef CONFIG_XFRM
-extern int xfrm6_init(void);
-extern void xfrm6_fini(void);
-extern int xfrm6_state_init(void);
-extern void xfrm6_state_fini(void);
+int xfrm6_init(void);
+void xfrm6_fini(void);
+int xfrm6_state_init(void);
+void xfrm6_state_fini(void);
 #else
 static inline int xfrm6_init(void)
 {
@@ -1385,52 +1393,52 @@ static inline void xfrm6_fini(void)
 #endif
 
 #ifdef CONFIG_XFRM_STATISTICS
-extern int xfrm_proc_init(struct net *net);
-extern void xfrm_proc_fini(struct net *net);
+int xfrm_proc_init(struct net *net);
+void xfrm_proc_fini(struct net *net);
 #endif
 
-extern int xfrm_sysctl_init(struct net *net);
+int xfrm_sysctl_init(struct net *net);
 #ifdef CONFIG_SYSCTL
-extern void xfrm_sysctl_fini(struct net *net);
+void xfrm_sysctl_fini(struct net *net);
 #else
 static inline void xfrm_sysctl_fini(struct net *net)
 {
 }
 #endif
 
-extern void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto);
-extern int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
-                          int (*func)(struct xfrm_state *, int, void*), void *);
-extern void xfrm_state_walk_done(struct xfrm_state_walk *walk);
-extern struct xfrm_state *xfrm_state_alloc(struct net *net);
-extern struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
-                                         const xfrm_address_t *saddr,
-                                         const struct flowi *fl,
-                                         struct xfrm_tmpl *tmpl,
-                                         struct xfrm_policy *pol, int *err,
-                                         unsigned short family);
-extern struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark,
-                                              xfrm_address_t *daddr,
-                                              xfrm_address_t *saddr,
-                                              unsigned short family,
-                                              u8 mode, u8 proto, u32 reqid);
-extern int xfrm_state_check_expire(struct xfrm_state *x);
-extern void xfrm_state_insert(struct xfrm_state *x);
-extern int xfrm_state_add(struct xfrm_state *x);
-extern int xfrm_state_update(struct xfrm_state *x);
-extern struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark,
-                                           const xfrm_address_t *daddr, __be32 spi,
-                                           u8 proto, unsigned short family);
-extern struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
-                                                  const xfrm_address_t *daddr,
-                                                  const xfrm_address_t *saddr,
-                                                  u8 proto,
-                                                  unsigned short family);
+void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto);
+int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
+                   int (*func)(struct xfrm_state *, int, void*), void *);
+void xfrm_state_walk_done(struct xfrm_state_walk *walk);
+struct xfrm_state *xfrm_state_alloc(struct net *net);
+struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
+                                  const xfrm_address_t *saddr,
+                                  const struct flowi *fl,
+                                  struct xfrm_tmpl *tmpl,
+                                  struct xfrm_policy *pol, int *err,
+                                  unsigned short family);
+struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark,
+                                      xfrm_address_t *daddr,
+                                      xfrm_address_t *saddr,
+                                      unsigned short family,
+                                      u8 mode, u8 proto, u32 reqid);
+int xfrm_state_check_expire(struct xfrm_state *x);
+void xfrm_state_insert(struct xfrm_state *x);
+int xfrm_state_add(struct xfrm_state *x);
+int xfrm_state_update(struct xfrm_state *x);
+struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark,
+                                    const xfrm_address_t *daddr, __be32 spi,
+                                    u8 proto, unsigned short family);
+struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
+                                           const xfrm_address_t *daddr,
+                                           const xfrm_address_t *saddr,
+                                           u8 proto,
+                                           unsigned short family);
 #ifdef CONFIG_XFRM_SUB_POLICY
-extern int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src,
-                         int n, unsigned short family);
-extern int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src,
-                          int n, unsigned short family);
+int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
+                  unsigned short family);
+int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
+                   unsigned short family);
 #else
 static inline int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src,
                                 int n, unsigned short family)
@@ -1462,68 +1470,69 @@ struct xfrmk_spdinfo {
        u32 spdhmcnt;
 };
 
-extern struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark,
-                                             u32 seq);
-extern int xfrm_state_delete(struct xfrm_state *x);
-extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
-extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
-extern void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
-extern u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
-extern int xfrm_init_replay(struct xfrm_state *x);
-extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
-extern int __xfrm_init_state(struct xfrm_state *x, bool init_replay);
-extern int xfrm_init_state(struct xfrm_state *x);
-extern int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi,
-                     int encap_type);
-extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
-extern int xfrm_output_resume(struct sk_buff *skb, int err);
-extern int xfrm_output(struct sk_buff *skb);
-extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
-extern void xfrm_local_error(struct sk_buff *skb, int mtu);
-extern int xfrm4_extract_header(struct sk_buff *skb);
-extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
-                          int encap_type);
-extern int xfrm4_transport_finish(struct sk_buff *skb, int async);
-extern int xfrm4_rcv(struct sk_buff *skb);
+struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
+int xfrm_state_delete(struct xfrm_state *x);
+int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
+void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
+void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
+int xfrm_init_replay(struct xfrm_state *x);
+int xfrm_state_mtu(struct xfrm_state *x, int mtu);
+int __xfrm_init_state(struct xfrm_state *x, bool init_replay);
+int xfrm_init_state(struct xfrm_state *x);
+int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
+int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
+int xfrm_output_resume(struct sk_buff *skb, int err);
+int xfrm_output(struct sk_buff *skb);
+int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
+void xfrm_local_error(struct sk_buff *skb, int mtu);
+int xfrm4_extract_header(struct sk_buff *skb);
+int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
+                   int encap_type);
+int xfrm4_transport_finish(struct sk_buff *skb, int async);
+int xfrm4_rcv(struct sk_buff *skb);
 
 static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
 {
        return xfrm4_rcv_encap(skb, nexthdr, spi, 0);
 }
 
-extern int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm4_output(struct sk_buff *skb);
-extern int xfrm4_output_finish(struct sk_buff *skb);
-extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
-extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
-extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler);
-extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler);
-extern void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
-extern int xfrm6_extract_header(struct sk_buff *skb);
-extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
-extern int xfrm6_transport_finish(struct sk_buff *skb, int async);
-extern int xfrm6_rcv(struct sk_buff *skb);
-extern int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
-                           xfrm_address_t *saddr, u8 proto);
-extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family);
-extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family);
-extern __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr);
-extern __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr);
-extern int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
-extern int xfrm6_output(struct sk_buff *skb);
-extern int xfrm6_output_finish(struct sk_buff *skb);
-extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
-                                u8 **prevhdr);
-extern void xfrm6_local_error(struct sk_buff *skb, u32 mtu);
+int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm4_output(struct sk_buff *skb);
+int xfrm4_output_finish(struct sk_buff *skb);
+int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
+int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
+void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
+int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
+int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
+int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
+int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
+int xfrm6_extract_header(struct sk_buff *skb);
+int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
+int xfrm6_transport_finish(struct sk_buff *skb, int async);
+int xfrm6_rcv(struct sk_buff *skb);
+int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
+                    xfrm_address_t *saddr, u8 proto);
+void xfrm6_local_error(struct sk_buff *skb, u32 mtu);
+int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family);
+int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family);
+__be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr);
+__be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr);
+int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm6_output(struct sk_buff *skb);
+int xfrm6_output_finish(struct sk_buff *skb);
+int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
+                         u8 **prevhdr);
 
 #ifdef CONFIG_XFRM
-extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
-extern int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen);
+int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
+int xfrm_user_policy(struct sock *sk, int optname,
+                    u8 __user *optval, int optlen);
 #else
 static inline int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
 {
@@ -1540,59 +1549,62 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
 
 struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp);
 
-extern void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
-extern int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
-       int (*func)(struct xfrm_policy *, int, int, void*), void *);
-extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk);
+void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
+int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
+                    int (*func)(struct xfrm_policy *, int, int, void*),
+                    void *);
+void xfrm_policy_walk_done(struct xfrm_policy_walk *walk);
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
 struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
                                          u8 type, int dir,
                                          struct xfrm_selector *sel,
                                          struct xfrm_sec_ctx *ctx, int delete,
                                          int *err);
-struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 id, int delete, int *err);
+struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir,
+                                    u32 id, int delete, int *err);
 int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info);
 u32 xfrm_get_acqseq(void);
-extern int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
+int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
 struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark,
                                 u8 mode, u32 reqid, u8 proto,
                                 const xfrm_address_t *daddr,
                                 const xfrm_address_t *saddr, int create,
                                 unsigned short family);
-extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
+int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
 
 #ifdef CONFIG_XFRM_MIGRATE
-extern int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
-                     const struct xfrm_migrate *m, int num_bundles,
-                     const struct xfrm_kmaddress *k);
-extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m);
-extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
-                                             struct xfrm_migrate *m);
-extern int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
-                       struct xfrm_migrate *m, int num_bundles,
-                       struct xfrm_kmaddress *k);
+int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
+              const struct xfrm_migrate *m, int num_bundles,
+              const struct xfrm_kmaddress *k);
+struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m);
+struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
+                                     struct xfrm_migrate *m);
+int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
+                struct xfrm_migrate *m, int num_bundles,
+                struct xfrm_kmaddress *k);
 #endif
 
-extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
-extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid);
-extern int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
-
-extern void xfrm_input_init(void);
-extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq);
-
-extern void xfrm_probe_algs(void);
-extern int xfrm_count_pfkey_auth_supported(void);
-extern int xfrm_count_pfkey_enc_supported(void);
-extern struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx);
-extern struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx);
-extern struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id);
-extern struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id);
-extern struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id);
-extern struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe);
-extern struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe);
-extern struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe);
-extern struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len,
-                                                  int probe);
+int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
+void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid);
+int km_report(struct net *net, u8 proto, struct xfrm_selector *sel,
+             xfrm_address_t *addr);
+
+void xfrm_input_init(void);
+int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq);
+
+void xfrm_probe_algs(void);
+int xfrm_count_pfkey_auth_supported(void);
+int xfrm_count_pfkey_enc_supported(void);
+struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx);
+struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx);
+struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id);
+struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id);
+struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id);
+struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe);
+struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe);
+struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe);
+struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len,
+                                           int probe);
 
 static inline bool xfrm6_addr_equal(const xfrm_address_t *a,
                                    const xfrm_address_t *b)
index fe66533..fb0a312 100644 (file)
@@ -68,6 +68,7 @@ struct rsnd_scu_platform_info {
  *
  * A : generation
  */
+#define RSND_GEN_MASK  (0xF << 0)
 #define RSND_GEN1      (1 << 0) /* fixme */
 #define RSND_GEN2      (2 << 0) /* fixme */
 
index 60ae7c3..4c2301d 100644 (file)
@@ -618,6 +618,7 @@ TRACE_EVENT(block_rq_remap,
                __field( unsigned int,  nr_sector       )
                __field( dev_t,         old_dev         )
                __field( sector_t,      old_sector      )
+               __field( unsigned int,  nr_bios         )
                __array( char,          rwbs,   RWBS_LEN)
        ),
 
@@ -627,15 +628,16 @@ TRACE_EVENT(block_rq_remap,
                __entry->nr_sector      = blk_rq_sectors(rq);
                __entry->old_dev        = dev;
                __entry->old_sector     = from;
+               __entry->nr_bios        = blk_rq_count_bios(rq);
                blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq));
        ),
 
-       TP_printk("%d,%d %s %llu + %u <- (%d,%d) %llu",
+       TP_printk("%d,%d %s %llu + %u <- (%d,%d) %llu %u",
                  MAJOR(__entry->dev), MINOR(__entry->dev), __entry->rwbs,
                  (unsigned long long)__entry->sector,
                  __entry->nr_sector,
                  MAJOR(__entry->old_dev), MINOR(__entry->old_dev),
-                 (unsigned long long)__entry->old_sector)
+                 (unsigned long long)__entry->old_sector, __entry->nr_bios)
 );
 
 #endif /* _TRACE_BLOCK_H */
index 45702c3..f18b3b7 100644 (file)
@@ -42,6 +42,7 @@ struct extent_buffer;
                { BTRFS_TREE_LOG_OBJECTID,      "TREE_LOG"      },      \
                { BTRFS_QUOTA_TREE_OBJECTID,    "QUOTA_TREE"    },      \
                { BTRFS_TREE_RELOC_OBJECTID,    "TREE_RELOC"    },      \
+               { BTRFS_UUID_TREE_OBJECTID,     "UUID_RELOC"    },      \
                { BTRFS_DATA_RELOC_TREE_OBJECTID, "DATA_RELOC_TREE" })
 
 #define show_root_type(obj)                                            \
index aef8fc3..da9cc0f 100644 (file)
@@ -144,7 +144,7 @@ TRACE_EVENT(target_sequencer_start,
        ),
 
        TP_fast_assign(
-               __entry->unpacked_lun   = cmd->se_lun->unpacked_lun;
+               __entry->unpacked_lun   = cmd->orig_fe_lun;
                __entry->opcode         = cmd->t_task_cdb[0];
                __entry->data_length    = cmd->data_length;
                __entry->task_attribute = cmd->sam_task_attr;
@@ -182,7 +182,7 @@ TRACE_EVENT(target_cmd_complete,
        ),
 
        TP_fast_assign(
-               __entry->unpacked_lun   = cmd->se_lun->unpacked_lun;
+               __entry->unpacked_lun   = cmd->orig_fe_lun;
                __entry->opcode         = cmd->t_task_cdb[0];
                __entry->data_length    = cmd->data_length;
                __entry->task_attribute = cmd->sam_task_attr;
index f04b69b..38f14d0 100644 (file)
@@ -78,4 +78,6 @@
 
 #define SO_BUSY_POLL           46
 
+#define SO_MAX_PACING_RATE     47
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 5508117..28acbaf 100644 (file)
@@ -223,6 +223,8 @@ struct drm_mode_get_connector {
        __u32 connection;
        __u32 mm_width, mm_height; /**< HxW in millimeters */
        __u32 subpixel;
+
+       __u32 pad;
 };
 
 #define DRM_MODE_PROP_PENDING  (1<<0)
index fa8b3ad..46d41e8 100644 (file)
@@ -1007,4 +1007,6 @@ struct drm_radeon_info {
 #define SI_TILE_MODE_DEPTH_STENCIL_2D_4AA      3
 #define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA      2
 
+#define CIK_TILE_MODE_DEPTH_STENCIL_1D         5
+
 #endif
index 3ebe387..382251a 100644 (file)
@@ -7,6 +7,38 @@
  * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
  * All rights reserved.
  *
+ * 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, 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.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
  */
 
 #ifndef CAN_BCM_H
index 7b7148b..b632045 100644 (file)
@@ -7,6 +7,38 @@
  * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
  * All rights reserved.
  *
+ * 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, 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.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
  */
 
 #ifndef CAN_ERROR_H
index 4e27c82..844c896 100644 (file)
@@ -7,6 +7,38 @@
  * Copyright (c) 2011 Volkswagen Group Electronic Research
  * All rights reserved.
  *
+ * 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, 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.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
  */
 
 #ifndef CAN_GW_H
index 14966dd..df944ed 100644 (file)
@@ -5,6 +5,14 @@
  *
  * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
  *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * 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.
  */
 
 #ifndef CAN_NETLINK_H
index a814062..c7d8c33 100644 (file)
@@ -8,6 +8,38 @@
  * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
  * All rights reserved.
  *
+ * 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, 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.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
  */
 
 #ifndef CAN_RAW_H
diff --git a/include/uapi/linux/hsr_netlink.h b/include/uapi/linux/hsr_netlink.h
new file mode 100644 (file)
index 0000000..2475cb8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __UAPI_HSR_NETLINK_H
+#define __UAPI_HSR_NETLINK_H
+
+/* Generic Netlink HSR family definition
+ */
+
+/* attributes */
+enum {
+       HSR_A_UNSPEC,
+       HSR_A_NODE_ADDR,
+       HSR_A_IFINDEX,
+       HSR_A_IF1_AGE,
+       HSR_A_IF2_AGE,
+       HSR_A_NODE_ADDR_B,
+       HSR_A_IF1_SEQ,
+       HSR_A_IF2_SEQ,
+       HSR_A_IF1_IFINDEX,
+       HSR_A_IF2_IFINDEX,
+       HSR_A_ADDR_B_IFINDEX,
+       __HSR_A_MAX,
+};
+#define HSR_A_MAX (__HSR_A_MAX - 1)
+
+
+/* commands */
+enum {
+       HSR_C_UNSPEC,
+       HSR_C_RING_ERROR,
+       HSR_C_NODE_DOWN,
+       HSR_C_GET_NODE_STATUS,
+       HSR_C_SET_NODE_STATUS,
+       HSR_C_GET_NODE_LIST,
+       HSR_C_SET_NODE_LIST,
+       __HSR_C_MAX,
+};
+#define HSR_C_MAX (__HSR_C_MAX - 1)
+
+#endif /* __UAPI_HSR_NETLINK_H */
index 1ec407b..d758163 100644 (file)
@@ -83,6 +83,7 @@
 #define IFF_SUPP_NOFCS 0x80000         /* device supports sending custom FCS */
 #define IFF_LIVE_ADDR_CHANGE 0x100000  /* device supports hardware address
                                         * change when it's running */
+#define IFF_MACVLAN 0x200000           /* Macvlan device */
 
 
 #define IF_GET_IFACE   0x0001          /* for querying only */
index a17edda..9635a62 100644 (file)
@@ -91,6 +91,8 @@
 #define BOND_XMIT_POLICY_LAYER2                0 /* layer 2 (MAC only), default */
 #define BOND_XMIT_POLICY_LAYER34       1 /* layer 3+4 (IP ^ (TCP || UDP)) */
 #define BOND_XMIT_POLICY_LAYER23       2 /* layer 2+3 (IP ^ MAC) */
+#define BOND_XMIT_POLICY_ENCAP23       3 /* encapsulated layer 2+3 */
+#define BOND_XMIT_POLICY_ENCAP34       4 /* encapsulated layer 3+4 */
 
 typedef struct ifbond {
        __s32 bond_mode;
index ade07f1..2ce0f6a 100644 (file)
@@ -85,6 +85,7 @@
 #define ETH_P_8021AH   0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_MVRP     0x88F5          /* 802.1Q MVRP                  */
 #define ETH_P_1588     0x88F7          /* IEEE 1588 Timesync */
+#define ETH_P_PRP      0x88FB          /* IEC 62439-3 PRP/HSRv0        */
 #define ETH_P_FCOE     0x8906          /* Fibre Channel over Ethernet  */
 #define ETH_P_TDLS     0x890D          /* TDLS */
 #define ETH_P_FIP      0x8914          /* FCoE Initialization Protocol */
index 80394e8..b78566f 100644 (file)
@@ -325,6 +325,17 @@ struct ifla_vxlan_port_range {
        __be16  high;
 };
 
+/* Bonding section */
+
+enum {
+       IFLA_BOND_UNSPEC,
+       IFLA_BOND_MODE,
+       IFLA_BOND_ACTIVE_SLAVE,
+       __IFLA_BOND_MAX,
+};
+
+#define IFLA_BOND_MAX  (__IFLA_BOND_MAX - 1)
+
 /* SR-IOV virtual function management section */
 
 enum {
@@ -470,4 +481,17 @@ enum {
 
 #define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
 
+
+/* HSR section */
+
+enum {
+       IFLA_HSR_UNSPEC,
+       IFLA_HSR_SLAVE1,
+       IFLA_HSR_SLAVE2,
+       IFLA_HSR_MULTICAST_SPEC,
+       __IFLA_HSR_MAX,
+};
+
+#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
+
 #endif /* _UAPI_LINUX_IF_LINK_H */
index f9e8e49..393c5de 100644 (file)
@@ -115,6 +115,11 @@ struct in_addr {
 #define IP_PMTUDISC_WANT               1       /* Use per route hints  */
 #define IP_PMTUDISC_DO                 2       /* Always DF            */
 #define IP_PMTUDISC_PROBE              3       /* Ignore dst pmtu      */
+/* Always use interface mtu (ignores dst pmtu) but don't set DF flag.
+ * Also incoming ICMP frag_needed notifications will be ignored on
+ * this socket to prevent accepting spoofed ones.
+ */
+#define IP_PMTUDISC_INTERFACE          4
 
 #define IP_MULTICAST_IF                        32
 #define IP_MULTICAST_TTL               33
index 2945822..fbcffe8 100644 (file)
@@ -334,7 +334,7 @@ enum {
        __IPVS_CMD_ATTR_MAX,
 };
 
-#define IPVS_CMD_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1)
+#define IPVS_CMD_ATTR_MAX (__IPVS_CMD_ATTR_MAX - 1)
 
 /*
  * Attributes used to describe a service
index 1749154..17c3af2 100644 (file)
@@ -5,6 +5,8 @@ header-y += nf_conntrack_ftp.h
 header-y += nf_conntrack_sctp.h
 header-y += nf_conntrack_tcp.h
 header-y += nf_conntrack_tuple_common.h
+header-y += nf_tables.h
+header-y += nf_tables_compat.h
 header-y += nf_nat.h
 header-y += nfnetlink.h
 header-y += nfnetlink_acct.h
index 8024cdf..25d3b2f 100644 (file)
 #ifndef _UAPI_IP_SET_H
 #define _UAPI_IP_SET_H
 
-
 #include <linux/types.h>
 
 /* The protocol version */
 #define IPSET_PROTOCOL         6
 
+/* The maximum permissible comment length we will accept over netlink */
+#define IPSET_MAX_COMMENT_SIZE 255
+
 /* The max length of strings including NUL: set and type identifiers */
 #define IPSET_MAXNAMELEN       32
 
@@ -110,6 +112,7 @@ enum {
        IPSET_ATTR_IFACE,
        IPSET_ATTR_BYTES,
        IPSET_ATTR_PACKETS,
+       IPSET_ATTR_COMMENT,
        __IPSET_ATTR_ADT_MAX,
 };
 #define IPSET_ATTR_ADT_MAX     (__IPSET_ATTR_ADT_MAX - 1)
@@ -140,6 +143,7 @@ enum ipset_errno {
        IPSET_ERR_IPADDR_IPV4,
        IPSET_ERR_IPADDR_IPV6,
        IPSET_ERR_COUNTER,
+       IPSET_ERR_COMMENT,
 
        /* Type specific error codes */
        IPSET_ERR_TYPE_SPECIFIC = 4352,
@@ -176,6 +180,8 @@ enum ipset_cadt_flags {
        IPSET_FLAG_NOMATCH      = (1 << IPSET_FLAG_BIT_NOMATCH),
        IPSET_FLAG_BIT_WITH_COUNTERS = 3,
        IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
+       IPSET_FLAG_BIT_WITH_COMMENT = 4,
+       IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT),
        IPSET_FLAG_CADT_MAX     = 15,
 };
 
@@ -250,6 +256,14 @@ struct ip_set_req_get_set {
 #define IP_SET_OP_GET_BYINDEX  0x00000007      /* Get set name by index */
 /* Uses ip_set_req_get_set */
 
+#define IP_SET_OP_GET_FNAME    0x00000008      /* Get set index and family */
+struct ip_set_req_get_set_family {
+       unsigned int op;
+       unsigned int version;
+       unsigned int family;
+       union ip_set_name_index set;
+};
+
 #define IP_SET_OP_VERSION      0x00000100      /* Ask kernel version */
 struct ip_set_req_version {
        unsigned int op;
index 8dd8038..319f471 100644 (file)
@@ -25,6 +25,10 @@ enum ip_conntrack_info {
        IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
 };
 
+#define NF_CT_STATE_INVALID_BIT                        (1 << 0)
+#define NF_CT_STATE_BIT(ctinfo)                        (1 << ((ctinfo) % IP_CT_IS_REPLY + 1))
+#define NF_CT_STATE_UNTRACKED_BIT              (1 << (IP_CT_NUMBER + 1))
+
 /* Bitset representing status of connection. */
 enum ip_conntrack_status {
        /* It's an expected connection: bit 0 set.  This bit never changed */
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
new file mode 100644 (file)
index 0000000..fbfd229
--- /dev/null
@@ -0,0 +1,718 @@
+#ifndef _LINUX_NF_TABLES_H
+#define _LINUX_NF_TABLES_H
+
+#define NFT_CHAIN_MAXNAMELEN 32
+
+enum nft_registers {
+       NFT_REG_VERDICT,
+       NFT_REG_1,
+       NFT_REG_2,
+       NFT_REG_3,
+       NFT_REG_4,
+       __NFT_REG_MAX
+};
+#define NFT_REG_MAX    (__NFT_REG_MAX - 1)
+
+/**
+ * enum nft_verdicts - nf_tables internal verdicts
+ *
+ * @NFT_CONTINUE: continue evaluation of the current rule
+ * @NFT_BREAK: terminate evaluation of the current rule
+ * @NFT_JUMP: push the current chain on the jump stack and jump to a chain
+ * @NFT_GOTO: jump to a chain without pushing the current chain on the jump stack
+ * @NFT_RETURN: return to the topmost chain on the jump stack
+ *
+ * The nf_tables verdicts share their numeric space with the netfilter verdicts.
+ */
+enum nft_verdicts {
+       NFT_CONTINUE    = -1,
+       NFT_BREAK       = -2,
+       NFT_JUMP        = -3,
+       NFT_GOTO        = -4,
+       NFT_RETURN      = -5,
+};
+
+/**
+ * enum nf_tables_msg_types - nf_tables netlink message types
+ *
+ * @NFT_MSG_NEWTABLE: create a new table (enum nft_table_attributes)
+ * @NFT_MSG_GETTABLE: get a table (enum nft_table_attributes)
+ * @NFT_MSG_DELTABLE: delete a table (enum nft_table_attributes)
+ * @NFT_MSG_NEWCHAIN: create a new chain (enum nft_chain_attributes)
+ * @NFT_MSG_GETCHAIN: get a chain (enum nft_chain_attributes)
+ * @NFT_MSG_DELCHAIN: delete a chain (enum nft_chain_attributes)
+ * @NFT_MSG_NEWRULE: create a new rule (enum nft_rule_attributes)
+ * @NFT_MSG_GETRULE: get a rule (enum nft_rule_attributes)
+ * @NFT_MSG_DELRULE: delete a rule (enum nft_rule_attributes)
+ * @NFT_MSG_NEWSET: create a new set (enum nft_set_attributes)
+ * @NFT_MSG_GETSET: get a set (enum nft_set_attributes)
+ * @NFT_MSG_DELSET: delete a set (enum nft_set_attributes)
+ * @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes)
+ */
+enum nf_tables_msg_types {
+       NFT_MSG_NEWTABLE,
+       NFT_MSG_GETTABLE,
+       NFT_MSG_DELTABLE,
+       NFT_MSG_NEWCHAIN,
+       NFT_MSG_GETCHAIN,
+       NFT_MSG_DELCHAIN,
+       NFT_MSG_NEWRULE,
+       NFT_MSG_GETRULE,
+       NFT_MSG_DELRULE,
+       NFT_MSG_NEWSET,
+       NFT_MSG_GETSET,
+       NFT_MSG_DELSET,
+       NFT_MSG_NEWSETELEM,
+       NFT_MSG_GETSETELEM,
+       NFT_MSG_DELSETELEM,
+       NFT_MSG_MAX,
+};
+
+/**
+ * enum nft_list_attributes - nf_tables generic list netlink attributes
+ *
+ * @NFTA_LIST_ELEM: list element (NLA_NESTED)
+ */
+enum nft_list_attributes {
+       NFTA_LIST_UNPEC,
+       NFTA_LIST_ELEM,
+       __NFTA_LIST_MAX
+};
+#define NFTA_LIST_MAX          (__NFTA_LIST_MAX - 1)
+
+/**
+ * enum nft_hook_attributes - nf_tables netfilter hook netlink attributes
+ *
+ * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
+ * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
+ */
+enum nft_hook_attributes {
+       NFTA_HOOK_UNSPEC,
+       NFTA_HOOK_HOOKNUM,
+       NFTA_HOOK_PRIORITY,
+       __NFTA_HOOK_MAX
+};
+#define NFTA_HOOK_MAX          (__NFTA_HOOK_MAX - 1)
+
+/**
+ * enum nft_table_flags - nf_tables table flags
+ *
+ * @NFT_TABLE_F_DORMANT: this table is not active
+ */
+enum nft_table_flags {
+       NFT_TABLE_F_DORMANT     = 0x1,
+};
+
+/**
+ * enum nft_table_attributes - nf_tables table netlink attributes
+ *
+ * @NFTA_TABLE_NAME: name of the table (NLA_STRING)
+ * @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
+ */
+enum nft_table_attributes {
+       NFTA_TABLE_UNSPEC,
+       NFTA_TABLE_NAME,
+       NFTA_TABLE_FLAGS,
+       __NFTA_TABLE_MAX
+};
+#define NFTA_TABLE_MAX         (__NFTA_TABLE_MAX - 1)
+
+/**
+ * enum nft_chain_attributes - nf_tables chain netlink attributes
+ *
+ * @NFTA_CHAIN_TABLE: name of the table containing the chain (NLA_STRING)
+ * @NFTA_CHAIN_HANDLE: numeric handle of the chain (NLA_U64)
+ * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING)
+ * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes)
+ * @NFTA_CHAIN_POLICY: numeric policy of the chain (NLA_U32)
+ * @NFTA_CHAIN_USE: number of references to this chain (NLA_U32)
+ * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
+ * @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
+ */
+enum nft_chain_attributes {
+       NFTA_CHAIN_UNSPEC,
+       NFTA_CHAIN_TABLE,
+       NFTA_CHAIN_HANDLE,
+       NFTA_CHAIN_NAME,
+       NFTA_CHAIN_HOOK,
+       NFTA_CHAIN_POLICY,
+       NFTA_CHAIN_USE,
+       NFTA_CHAIN_TYPE,
+       NFTA_CHAIN_COUNTERS,
+       __NFTA_CHAIN_MAX
+};
+#define NFTA_CHAIN_MAX         (__NFTA_CHAIN_MAX - 1)
+
+/**
+ * enum nft_rule_attributes - nf_tables rule netlink attributes
+ *
+ * @NFTA_RULE_TABLE: name of the table containing the rule (NLA_STRING)
+ * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING)
+ * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U64)
+ * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes)
+ * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes)
+ * @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64)
+ */
+enum nft_rule_attributes {
+       NFTA_RULE_UNSPEC,
+       NFTA_RULE_TABLE,
+       NFTA_RULE_CHAIN,
+       NFTA_RULE_HANDLE,
+       NFTA_RULE_EXPRESSIONS,
+       NFTA_RULE_COMPAT,
+       NFTA_RULE_POSITION,
+       __NFTA_RULE_MAX
+};
+#define NFTA_RULE_MAX          (__NFTA_RULE_MAX - 1)
+
+/**
+ * enum nft_rule_compat_flags - nf_tables rule compat flags
+ *
+ * @NFT_RULE_COMPAT_F_INV: invert the check result
+ */
+enum nft_rule_compat_flags {
+       NFT_RULE_COMPAT_F_INV   = (1 << 1),
+       NFT_RULE_COMPAT_F_MASK  = NFT_RULE_COMPAT_F_INV,
+};
+
+/**
+ * enum nft_rule_compat_attributes - nf_tables rule compat attributes
+ *
+ * @NFTA_RULE_COMPAT_PROTO: numerice value of handled protocol (NLA_U32)
+ * @NFTA_RULE_COMPAT_FLAGS: bitmask of enum nft_rule_compat_flags (NLA_U32)
+ */
+enum nft_rule_compat_attributes {
+       NFTA_RULE_COMPAT_UNSPEC,
+       NFTA_RULE_COMPAT_PROTO,
+       NFTA_RULE_COMPAT_FLAGS,
+       __NFTA_RULE_COMPAT_MAX
+};
+#define NFTA_RULE_COMPAT_MAX   (__NFTA_RULE_COMPAT_MAX - 1)
+
+/**
+ * enum nft_set_flags - nf_tables set flags
+ *
+ * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink
+ * @NFT_SET_CONSTANT: set contents may not change while bound
+ * @NFT_SET_INTERVAL: set contains intervals
+ * @NFT_SET_MAP: set is used as a dictionary
+ */
+enum nft_set_flags {
+       NFT_SET_ANONYMOUS               = 0x1,
+       NFT_SET_CONSTANT                = 0x2,
+       NFT_SET_INTERVAL                = 0x4,
+       NFT_SET_MAP                     = 0x8,
+};
+
+/**
+ * enum nft_set_attributes - nf_tables set netlink attributes
+ *
+ * @NFTA_SET_TABLE: table name (NLA_STRING)
+ * @NFTA_SET_NAME: set name (NLA_STRING)
+ * @NFTA_SET_FLAGS: bitmask of enum nft_set_flags (NLA_U32)
+ * @NFTA_SET_KEY_TYPE: key data type, informational purpose only (NLA_U32)
+ * @NFTA_SET_KEY_LEN: key data length (NLA_U32)
+ * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32)
+ * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32)
+ */
+enum nft_set_attributes {
+       NFTA_SET_UNSPEC,
+       NFTA_SET_TABLE,
+       NFTA_SET_NAME,
+       NFTA_SET_FLAGS,
+       NFTA_SET_KEY_TYPE,
+       NFTA_SET_KEY_LEN,
+       NFTA_SET_DATA_TYPE,
+       NFTA_SET_DATA_LEN,
+       __NFTA_SET_MAX
+};
+#define NFTA_SET_MAX           (__NFTA_SET_MAX - 1)
+
+/**
+ * enum nft_set_elem_flags - nf_tables set element flags
+ *
+ * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval
+ */
+enum nft_set_elem_flags {
+       NFT_SET_ELEM_INTERVAL_END       = 0x1,
+};
+
+/**
+ * enum nft_set_elem_attributes - nf_tables set element netlink attributes
+ *
+ * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
+ * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
+ * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
+ */
+enum nft_set_elem_attributes {
+       NFTA_SET_ELEM_UNSPEC,
+       NFTA_SET_ELEM_KEY,
+       NFTA_SET_ELEM_DATA,
+       NFTA_SET_ELEM_FLAGS,
+       __NFTA_SET_ELEM_MAX
+};
+#define NFTA_SET_ELEM_MAX      (__NFTA_SET_ELEM_MAX - 1)
+
+/**
+ * enum nft_set_elem_list_attributes - nf_tables set element list netlink attributes
+ *
+ * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING)
+ * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING)
+ * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes)
+ */
+enum nft_set_elem_list_attributes {
+       NFTA_SET_ELEM_LIST_UNSPEC,
+       NFTA_SET_ELEM_LIST_TABLE,
+       NFTA_SET_ELEM_LIST_SET,
+       NFTA_SET_ELEM_LIST_ELEMENTS,
+       __NFTA_SET_ELEM_LIST_MAX
+};
+#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
+
+/**
+ * enum nft_data_types - nf_tables data types
+ *
+ * @NFT_DATA_VALUE: generic data
+ * @NFT_DATA_VERDICT: netfilter verdict
+ *
+ * The type of data is usually determined by the kernel directly and is not
+ * explicitly specified by userspace. The only difference are sets, where
+ * userspace specifies the key and mapping data types.
+ *
+ * The values 0xffffff00-0xffffffff are reserved for internally used types.
+ * The remaining range can be freely used by userspace to encode types, all
+ * values are equivalent to NFT_DATA_VALUE.
+ */
+enum nft_data_types {
+       NFT_DATA_VALUE,
+       NFT_DATA_VERDICT        = 0xffffff00U,
+};
+
+#define NFT_DATA_RESERVED_MASK 0xffffff00U
+
+/**
+ * enum nft_data_attributes - nf_tables data netlink attributes
+ *
+ * @NFTA_DATA_VALUE: generic data (NLA_BINARY)
+ * @NFTA_DATA_VERDICT: nf_tables verdict (NLA_NESTED: nft_verdict_attributes)
+ */
+enum nft_data_attributes {
+       NFTA_DATA_UNSPEC,
+       NFTA_DATA_VALUE,
+       NFTA_DATA_VERDICT,
+       __NFTA_DATA_MAX
+};
+#define NFTA_DATA_MAX          (__NFTA_DATA_MAX - 1)
+
+/**
+ * enum nft_verdict_attributes - nf_tables verdict netlink attributes
+ *
+ * @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts)
+ * @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING)
+ */
+enum nft_verdict_attributes {
+       NFTA_VERDICT_UNSPEC,
+       NFTA_VERDICT_CODE,
+       NFTA_VERDICT_CHAIN,
+       __NFTA_VERDICT_MAX
+};
+#define NFTA_VERDICT_MAX       (__NFTA_VERDICT_MAX - 1)
+
+/**
+ * enum nft_expr_attributes - nf_tables expression netlink attributes
+ *
+ * @NFTA_EXPR_NAME: name of the expression type (NLA_STRING)
+ * @NFTA_EXPR_DATA: type specific data (NLA_NESTED)
+ */
+enum nft_expr_attributes {
+       NFTA_EXPR_UNSPEC,
+       NFTA_EXPR_NAME,
+       NFTA_EXPR_DATA,
+       __NFTA_EXPR_MAX
+};
+#define NFTA_EXPR_MAX          (__NFTA_EXPR_MAX - 1)
+
+/**
+ * enum nft_immediate_attributes - nf_tables immediate expression netlink attributes
+ *
+ * @NFTA_IMMEDIATE_DREG: destination register to load data into (NLA_U32)
+ * @NFTA_IMMEDIATE_DATA: data to load (NLA_NESTED: nft_data_attributes)
+ */
+enum nft_immediate_attributes {
+       NFTA_IMMEDIATE_UNSPEC,
+       NFTA_IMMEDIATE_DREG,
+       NFTA_IMMEDIATE_DATA,
+       __NFTA_IMMEDIATE_MAX
+};
+#define NFTA_IMMEDIATE_MAX     (__NFTA_IMMEDIATE_MAX - 1)
+
+/**
+ * enum nft_bitwise_attributes - nf_tables bitwise expression netlink attributes
+ *
+ * @NFTA_BITWISE_SREG: source register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_LEN: length of operands (NLA_U32)
+ * @NFTA_BITWISE_MASK: mask value (NLA_NESTED: nft_data_attributes)
+ * @NFTA_BITWISE_XOR: xor value (NLA_NESTED: nft_data_attributes)
+ *
+ * The bitwise expression performs the following operation:
+ *
+ * dreg = (sreg & mask) ^ xor
+ *
+ * which allow to express all bitwise operations:
+ *
+ *             mask    xor
+ * NOT:                1       1
+ * OR:         0       x
+ * XOR:                1       x
+ * AND:                x       0
+ */
+enum nft_bitwise_attributes {
+       NFTA_BITWISE_UNSPEC,
+       NFTA_BITWISE_SREG,
+       NFTA_BITWISE_DREG,
+       NFTA_BITWISE_LEN,
+       NFTA_BITWISE_MASK,
+       NFTA_BITWISE_XOR,
+       __NFTA_BITWISE_MAX
+};
+#define NFTA_BITWISE_MAX       (__NFTA_BITWISE_MAX - 1)
+
+/**
+ * enum nft_byteorder_ops - nf_tables byteorder operators
+ *
+ * @NFT_BYTEORDER_NTOH: network to host operator
+ * @NFT_BYTEORDER_HTON: host to network opertaor
+ */
+enum nft_byteorder_ops {
+       NFT_BYTEORDER_NTOH,
+       NFT_BYTEORDER_HTON,
+};
+
+/**
+ * enum nft_byteorder_attributes - nf_tables byteorder expression netlink attributes
+ *
+ * @NFTA_BYTEORDER_SREG: source register (NLA_U32: nft_registers)
+ * @NFTA_BYTEORDER_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_BYTEORDER_OP: operator (NLA_U32: enum nft_byteorder_ops)
+ * @NFTA_BYTEORDER_LEN: length of the data (NLA_U32)
+ * @NFTA_BYTEORDER_SIZE: data size in bytes (NLA_U32: 2 or 4)
+ */
+enum nft_byteorder_attributes {
+       NFTA_BYTEORDER_UNSPEC,
+       NFTA_BYTEORDER_SREG,
+       NFTA_BYTEORDER_DREG,
+       NFTA_BYTEORDER_OP,
+       NFTA_BYTEORDER_LEN,
+       NFTA_BYTEORDER_SIZE,
+       __NFTA_BYTEORDER_MAX
+};
+#define NFTA_BYTEORDER_MAX     (__NFTA_BYTEORDER_MAX - 1)
+
+/**
+ * enum nft_cmp_ops - nf_tables relational operator
+ *
+ * @NFT_CMP_EQ: equal
+ * @NFT_CMP_NEQ: not equal
+ * @NFT_CMP_LT: less than
+ * @NFT_CMP_LTE: less than or equal to
+ * @NFT_CMP_GT: greater than
+ * @NFT_CMP_GTE: greater than or equal to
+ */
+enum nft_cmp_ops {
+       NFT_CMP_EQ,
+       NFT_CMP_NEQ,
+       NFT_CMP_LT,
+       NFT_CMP_LTE,
+       NFT_CMP_GT,
+       NFT_CMP_GTE,
+};
+
+/**
+ * enum nft_cmp_attributes - nf_tables cmp expression netlink attributes
+ *
+ * @NFTA_CMP_SREG: source register of data to compare (NLA_U32: nft_registers)
+ * @NFTA_CMP_OP: cmp operation (NLA_U32: nft_cmp_ops)
+ * @NFTA_CMP_DATA: data to compare against (NLA_NESTED: nft_data_attributes)
+ */
+enum nft_cmp_attributes {
+       NFTA_CMP_UNSPEC,
+       NFTA_CMP_SREG,
+       NFTA_CMP_OP,
+       NFTA_CMP_DATA,
+       __NFTA_CMP_MAX
+};
+#define NFTA_CMP_MAX           (__NFTA_CMP_MAX - 1)
+
+/**
+ * enum nft_lookup_attributes - nf_tables set lookup expression netlink attributes
+ *
+ * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING)
+ * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers)
+ * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers)
+ */
+enum nft_lookup_attributes {
+       NFTA_LOOKUP_UNSPEC,
+       NFTA_LOOKUP_SET,
+       NFTA_LOOKUP_SREG,
+       NFTA_LOOKUP_DREG,
+       __NFTA_LOOKUP_MAX
+};
+#define NFTA_LOOKUP_MAX                (__NFTA_LOOKUP_MAX - 1)
+
+/**
+ * enum nft_payload_bases - nf_tables payload expression offset bases
+ *
+ * @NFT_PAYLOAD_LL_HEADER: link layer header
+ * @NFT_PAYLOAD_NETWORK_HEADER: network header
+ * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
+ */
+enum nft_payload_bases {
+       NFT_PAYLOAD_LL_HEADER,
+       NFT_PAYLOAD_NETWORK_HEADER,
+       NFT_PAYLOAD_TRANSPORT_HEADER,
+};
+
+/**
+ * enum nft_payload_attributes - nf_tables payload expression netlink attributes
+ *
+ * @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers)
+ * @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases)
+ * @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32)
+ * @NFTA_PAYLOAD_LEN: payload length (NLA_U32)
+ */
+enum nft_payload_attributes {
+       NFTA_PAYLOAD_UNSPEC,
+       NFTA_PAYLOAD_DREG,
+       NFTA_PAYLOAD_BASE,
+       NFTA_PAYLOAD_OFFSET,
+       NFTA_PAYLOAD_LEN,
+       __NFTA_PAYLOAD_MAX
+};
+#define NFTA_PAYLOAD_MAX       (__NFTA_PAYLOAD_MAX - 1)
+
+/**
+ * enum nft_exthdr_attributes - nf_tables IPv6 extension header expression netlink attributes
+ *
+ * @NFTA_EXTHDR_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_EXTHDR_TYPE: extension header type (NLA_U8)
+ * @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32)
+ * @NFTA_EXTHDR_LEN: extension header length (NLA_U32)
+ */
+enum nft_exthdr_attributes {
+       NFTA_EXTHDR_UNSPEC,
+       NFTA_EXTHDR_DREG,
+       NFTA_EXTHDR_TYPE,
+       NFTA_EXTHDR_OFFSET,
+       NFTA_EXTHDR_LEN,
+       __NFTA_EXTHDR_MAX
+};
+#define NFTA_EXTHDR_MAX                (__NFTA_EXTHDR_MAX - 1)
+
+/**
+ * enum nft_meta_keys - nf_tables meta expression keys
+ *
+ * @NFT_META_LEN: packet length (skb->len)
+ * @NFT_META_PROTOCOL: packet ethertype protocol (skb->protocol), invalid in OUTPUT
+ * @NFT_META_PRIORITY: packet priority (skb->priority)
+ * @NFT_META_MARK: packet mark (skb->mark)
+ * @NFT_META_IIF: packet input interface index (dev->ifindex)
+ * @NFT_META_OIF: packet output interface index (dev->ifindex)
+ * @NFT_META_IIFNAME: packet input interface name (dev->name)
+ * @NFT_META_OIFNAME: packet output interface name (dev->name)
+ * @NFT_META_IIFTYPE: packet input interface type (dev->type)
+ * @NFT_META_OIFTYPE: packet output interface type (dev->type)
+ * @NFT_META_SKUID: originating socket UID (fsuid)
+ * @NFT_META_SKGID: originating socket GID (fsgid)
+ * @NFT_META_NFTRACE: packet nftrace bit
+ * @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid)
+ * @NFT_META_SECMARK: packet secmark (skb->secmark)
+ */
+enum nft_meta_keys {
+       NFT_META_LEN,
+       NFT_META_PROTOCOL,
+       NFT_META_PRIORITY,
+       NFT_META_MARK,
+       NFT_META_IIF,
+       NFT_META_OIF,
+       NFT_META_IIFNAME,
+       NFT_META_OIFNAME,
+       NFT_META_IIFTYPE,
+       NFT_META_OIFTYPE,
+       NFT_META_SKUID,
+       NFT_META_SKGID,
+       NFT_META_NFTRACE,
+       NFT_META_RTCLASSID,
+       NFT_META_SECMARK,
+};
+
+/**
+ * enum nft_meta_attributes - nf_tables meta expression netlink attributes
+ *
+ * @NFTA_META_DREG: destination register (NLA_U32)
+ * @NFTA_META_KEY: meta data item to load (NLA_U32: nft_meta_keys)
+ */
+enum nft_meta_attributes {
+       NFTA_META_UNSPEC,
+       NFTA_META_DREG,
+       NFTA_META_KEY,
+       __NFTA_META_MAX
+};
+#define NFTA_META_MAX          (__NFTA_META_MAX - 1)
+
+/**
+ * enum nft_ct_keys - nf_tables ct expression keys
+ *
+ * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info)
+ * @NFT_CT_DIRECTION: conntrack direction (enum ip_conntrack_dir)
+ * @NFT_CT_STATUS: conntrack status (bitmask of enum ip_conntrack_status)
+ * @NFT_CT_MARK: conntrack mark value
+ * @NFT_CT_SECMARK: conntrack secmark value
+ * @NFT_CT_EXPIRATION: relative conntrack expiration time in ms
+ * @NFT_CT_HELPER: connection tracking helper assigned to conntrack
+ * @NFT_CT_L3PROTOCOL: conntrack layer 3 protocol
+ * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address)
+ * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address)
+ * @NFT_CT_PROTOCOL: conntrack layer 4 protocol
+ * @NFT_CT_PROTO_SRC: conntrack layer 4 protocol source
+ * @NFT_CT_PROTO_DST: conntrack layer 4 protocol destination
+ */
+enum nft_ct_keys {
+       NFT_CT_STATE,
+       NFT_CT_DIRECTION,
+       NFT_CT_STATUS,
+       NFT_CT_MARK,
+       NFT_CT_SECMARK,
+       NFT_CT_EXPIRATION,
+       NFT_CT_HELPER,
+       NFT_CT_L3PROTOCOL,
+       NFT_CT_SRC,
+       NFT_CT_DST,
+       NFT_CT_PROTOCOL,
+       NFT_CT_PROTO_SRC,
+       NFT_CT_PROTO_DST,
+};
+
+/**
+ * enum nft_ct_attributes - nf_tables ct expression netlink attributes
+ *
+ * @NFTA_CT_DREG: destination register (NLA_U32)
+ * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys)
+ * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8)
+ */
+enum nft_ct_attributes {
+       NFTA_CT_UNSPEC,
+       NFTA_CT_DREG,
+       NFTA_CT_KEY,
+       NFTA_CT_DIRECTION,
+       __NFTA_CT_MAX
+};
+#define NFTA_CT_MAX            (__NFTA_CT_MAX - 1)
+
+/**
+ * enum nft_limit_attributes - nf_tables limit expression netlink attributes
+ *
+ * @NFTA_LIMIT_RATE: refill rate (NLA_U64)
+ * @NFTA_LIMIT_UNIT: refill unit (NLA_U64)
+ */
+enum nft_limit_attributes {
+       NFTA_LIMIT_UNSPEC,
+       NFTA_LIMIT_RATE,
+       NFTA_LIMIT_UNIT,
+       __NFTA_LIMIT_MAX
+};
+#define NFTA_LIMIT_MAX         (__NFTA_LIMIT_MAX - 1)
+
+/**
+ * enum nft_counter_attributes - nf_tables counter expression netlink attributes
+ *
+ * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64)
+ * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64)
+ */
+enum nft_counter_attributes {
+       NFTA_COUNTER_UNSPEC,
+       NFTA_COUNTER_BYTES,
+       NFTA_COUNTER_PACKETS,
+       __NFTA_COUNTER_MAX
+};
+#define NFTA_COUNTER_MAX       (__NFTA_COUNTER_MAX - 1)
+
+/**
+ * enum nft_log_attributes - nf_tables log expression netlink attributes
+ *
+ * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
+ * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING)
+ * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32)
+ * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32)
+ */
+enum nft_log_attributes {
+       NFTA_LOG_UNSPEC,
+       NFTA_LOG_GROUP,
+       NFTA_LOG_PREFIX,
+       NFTA_LOG_SNAPLEN,
+       NFTA_LOG_QTHRESHOLD,
+       __NFTA_LOG_MAX
+};
+#define NFTA_LOG_MAX           (__NFTA_LOG_MAX - 1)
+
+/**
+ * enum nft_reject_types - nf_tables reject expression reject types
+ *
+ * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable
+ * @NFT_REJECT_TCP_RST: reject using TCP RST
+ */
+enum nft_reject_types {
+       NFT_REJECT_ICMP_UNREACH,
+       NFT_REJECT_TCP_RST,
+};
+
+/**
+ * enum nft_reject_attributes - nf_tables reject expression netlink attributes
+ *
+ * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types)
+ * @NFTA_REJECT_ICMP_CODE: ICMP code to use (NLA_U8)
+ */
+enum nft_reject_attributes {
+       NFTA_REJECT_UNSPEC,
+       NFTA_REJECT_TYPE,
+       NFTA_REJECT_ICMP_CODE,
+       __NFTA_REJECT_MAX
+};
+#define NFTA_REJECT_MAX                (__NFTA_REJECT_MAX - 1)
+
+/**
+ * enum nft_nat_types - nf_tables nat expression NAT types
+ *
+ * @NFT_NAT_SNAT: source NAT
+ * @NFT_NAT_DNAT: destination NAT
+ */
+enum nft_nat_types {
+       NFT_NAT_SNAT,
+       NFT_NAT_DNAT,
+};
+
+/**
+ * enum nft_nat_attributes - nf_tables nat expression netlink attributes
+ *
+ * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types)
+ * @NFTA_NAT_FAMILY: NAT family (NLA_U32)
+ * @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers)
+ * @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers)
+ * @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
+ * @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
+ */
+enum nft_nat_attributes {
+       NFTA_NAT_UNSPEC,
+       NFTA_NAT_TYPE,
+       NFTA_NAT_FAMILY,
+       NFTA_NAT_REG_ADDR_MIN,
+       NFTA_NAT_REG_ADDR_MAX,
+       NFTA_NAT_REG_PROTO_MIN,
+       NFTA_NAT_REG_PROTO_MAX,
+       __NFTA_NAT_MAX
+};
+#define NFTA_NAT_MAX           (__NFTA_NAT_MAX - 1)
+
+#endif /* _LINUX_NF_TABLES_H */
diff --git a/include/uapi/linux/netfilter/nf_tables_compat.h b/include/uapi/linux/netfilter/nf_tables_compat.h
new file mode 100644 (file)
index 0000000..8310f5f
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _NFT_COMPAT_NFNETLINK_H_
+#define _NFT_COMPAT_NFNETLINK_H_
+
+enum nft_target_attributes {
+       NFTA_TARGET_UNSPEC,
+       NFTA_TARGET_NAME,
+       NFTA_TARGET_REV,
+       NFTA_TARGET_INFO,
+       __NFTA_TARGET_MAX
+};
+#define NFTA_TARGET_MAX                (__NFTA_TARGET_MAX - 1)
+
+enum nft_match_attributes {
+       NFTA_MATCH_UNSPEC,
+       NFTA_MATCH_NAME,
+       NFTA_MATCH_REV,
+       NFTA_MATCH_INFO,
+       __NFTA_MATCH_MAX
+};
+#define NFTA_MATCH_MAX         (__NFTA_MATCH_MAX - 1)
+
+#define NFT_COMPAT_NAME_MAX    32
+
+enum {
+       NFNL_MSG_COMPAT_GET,
+       NFNL_MSG_COMPAT_MAX
+};
+
+enum {
+       NFTA_COMPAT_UNSPEC = 0,
+       NFTA_COMPAT_NAME,
+       NFTA_COMPAT_REV,
+       NFTA_COMPAT_TYPE,
+       __NFTA_COMPAT_MAX,
+};
+#define NFTA_COMPAT_MAX (__NFTA_COMPAT_MAX - 1)
+
+#endif
index 4a4efaf..596ddd4 100644 (file)
@@ -18,6 +18,8 @@ enum nfnetlink_groups {
 #define NFNLGRP_CONNTRACK_EXP_UPDATE   NFNLGRP_CONNTRACK_EXP_UPDATE
        NFNLGRP_CONNTRACK_EXP_DESTROY,
 #define NFNLGRP_CONNTRACK_EXP_DESTROY  NFNLGRP_CONNTRACK_EXP_DESTROY
+       NFNLGRP_NFTABLES,
+#define NFNLGRP_NFTABLES                NFNLGRP_NFTABLES
        __NFNLGRP_MAX,
 };
 #define NFNLGRP_MAX    (__NFNLGRP_MAX - 1)
@@ -51,6 +53,12 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_ACCT               7
 #define NFNL_SUBSYS_CTNETLINK_TIMEOUT  8
 #define NFNL_SUBSYS_CTHELPER           9
-#define NFNL_SUBSYS_COUNT              10
+#define NFNL_SUBSYS_NFTABLES           10
+#define NFNL_SUBSYS_NFT_COMPAT         11
+#define NFNL_SUBSYS_COUNT              12
+
+/* Reserved control nfnetlink messages */
+#define NFNL_MSG_BATCH_BEGIN           NLMSG_MIN_TYPE
+#define NFNL_MSG_BATCH_END             NLMSG_MIN_TYPE+1
 
 #endif /* _UAPI_NFNETLINK_H */
index a2810a7..1ab0b97 100644 (file)
@@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types {
        IPCTNL_MSG_TIMEOUT_NEW,
        IPCTNL_MSG_TIMEOUT_GET,
        IPCTNL_MSG_TIMEOUT_DELETE,
+       IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
+       IPCTNL_MSG_TIMEOUT_DEFAULT_GET,
 
        IPCTNL_MSG_TIMEOUT_MAX
 };
index 29bed72..6ad6cc0 100644 (file)
@@ -85,6 +85,7 @@
  *     a specific SE notifies us about the end of a transaction. The parameter
  *     for this event is the application ID (AID).
  * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
+ * @NFC_CMD_SE_IO: Send/Receive APDUs to/from the selected secure element.
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -114,6 +115,7 @@ enum nfc_commands {
        NFC_EVENT_SE_CONNECTIVITY,
        NFC_EVENT_SE_TRANSACTION,
        NFC_CMD_GET_SE,
+       NFC_CMD_SE_IO,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -147,6 +149,7 @@ enum nfc_commands {
  * @NFC_ATTR_SE_INDEX: Secure element index
  * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
  * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status
+ * @NFC_ATTR_APDU: Secure element APDU
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -174,6 +177,7 @@ enum nfc_attrs {
        NFC_ATTR_SE_TYPE,
        NFC_ATTR_SE_AID,
        NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS,
+       NFC_ATTR_SE_APDU,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
index fde2c02..f752e98 100644 (file)
@@ -988,7 +988,7 @@ enum nl80211_commands {
  *     to query the CRDA to retrieve one regulatory domain. This attribute can
  *     also be used by userspace to query the kernel for the currently set
  *     regulatory domain. We chose an alpha2 as that is also used by the
- *     IEEE-802.11d country information element to identify a country.
+ *     IEEE-802.11 country information element to identify a country.
  *     Users can also simply ask the wireless core to set regulatory domain
  *     to a specific alpha2.
  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
@@ -1496,6 +1496,18 @@ enum nl80211_commands {
  * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
  *     As specified in the &enum nl80211_rxmgmt_flags.
  *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *     controls DFS operation in IBSS mode. If the flag is included in
+ *     %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *     channels and reports radar events to userspace. Userspace is required
+ *     to react to radar events, e.g. initiate a channel switch or leave the
+ *     IBSS network.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1806,6 +1818,12 @@ enum nl80211_attrs {
 
        NL80211_ATTR_RXMGMT_FLAGS,
 
+       NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+       NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+       NL80211_ATTR_HANDLE_DFS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3860,13 +3878,12 @@ enum nl80211_radar_event {
  *
  * Channel states used by the DFS code.
  *
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
  *     check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
  *     is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
  */
-
 enum nl80211_dfs_state {
        NL80211_DFS_USABLE,
        NL80211_DFS_UNAVAILABLE,
index a74d375..d120f9f 100644 (file)
@@ -63,15 +63,18 @@ enum ovs_datapath_cmd {
  * not be sent.
  * @OVS_DP_ATTR_STATS: Statistics about packets that have passed through the
  * datapath.  Always present in notifications.
+ * @OVS_DP_ATTR_MEGAFLOW_STATS: Statistics about mega flow masks usage for the
+ * datapath. Always present in notifications.
  *
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_DP_* commands.
  */
 enum ovs_datapath_attr {
        OVS_DP_ATTR_UNSPEC,
-       OVS_DP_ATTR_NAME,       /* name of dp_ifindex netdev */
-       OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */
-       OVS_DP_ATTR_STATS,      /* struct ovs_dp_stats */
+       OVS_DP_ATTR_NAME,               /* name of dp_ifindex netdev */
+       OVS_DP_ATTR_UPCALL_PID,         /* Netlink PID to receive upcalls */
+       OVS_DP_ATTR_STATS,              /* struct ovs_dp_stats */
+       OVS_DP_ATTR_MEGAFLOW_STATS,     /* struct ovs_dp_megaflow_stats */
        __OVS_DP_ATTR_MAX
 };
 
@@ -84,6 +87,14 @@ struct ovs_dp_stats {
        __u64 n_flows;           /* Number of flows present */
 };
 
+struct ovs_dp_megaflow_stats {
+       __u64 n_mask_hit;        /* Number of masks used for flow lookups. */
+       __u32 n_masks;           /* Number of masks for the datapath. */
+       __u32 pad0;              /* Pad for future expension. */
+       __u64 pad1;              /* Pad for future expension. */
+       __u64 pad2;              /* Pad for future expension. */
+};
+
 struct ovs_vport_stats {
        __u64   rx_packets;             /* total packets received       */
        __u64   tx_packets;             /* total packets transmitted    */
@@ -260,6 +271,7 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
        OVS_KEY_ATTR_TUNNEL,    /* Nested set of ovs_tunnel attributes */
        OVS_KEY_ATTR_SCTP,      /* struct ovs_key_sctp */
+       OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */
 
 #ifdef __KERNEL__
        OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
index 40a1fb8..2fc1602 100644 (file)
@@ -380,10 +380,13 @@ struct perf_event_mmap_page {
        union {
                __u64   capabilities;
                struct {
-                       __u64   cap_usr_time            : 1,
-                               cap_usr_rdpmc           : 1,
-                               cap_usr_time_zero       : 1,
-                               cap_____res             : 61;
+                       __u64   cap_bit0                : 1, /* Always 0, deprecated, see commit 860f085b74e9 */
+                               cap_bit0_is_deprecated  : 1, /* Always 1, signals that bit 0 is zero */
+
+                               cap_user_rdpmc          : 1, /* The RDPMC instruction can be used to read counts */
+                               cap_user_time           : 1, /* The time_* fields are used */
+                               cap_user_time_zero      : 1, /* The time_zero field is used */
+                               cap_____res             : 59;
                };
        };
 
@@ -442,23 +445,26 @@ struct perf_event_mmap_page {
         *               ((rem * time_mult) >> time_shift);
         */
        __u64   time_zero;
+       __u32   size;                   /* Header size up to __reserved[] fields. */
 
                /*
                 * Hole for extension of the self monitor capabilities
                 */
 
-       __u64   __reserved[119];        /* align to 1k */
+       __u8    __reserved[118*8+4];    /* align to 1k. */
 
        /*
         * Control data for the mmap() data buffer.
         *
-        * User-space reading the @data_head value should issue an rmb(), on
-        * SMP capable platforms, after reading this value -- see
-        * perf_event_wakeup().
+        * User-space reading the @data_head value should issue an smp_rmb(),
+        * after reading this value.
         *
         * When the mapping is PROT_WRITE the @data_tail value should be
-        * written by userspace to reflect the last read data. In this case
-        * the kernel will not over-write unread data.
+        * written by userspace to reflect the last read data, after issueing
+        * an smp_mb() to separate the data read from the ->data_tail store.
+        * In this case the kernel will not over-write unread data.
+        *
+        * See perf_output_put_handle() for the data ordering.
         */
        __u64   data_head;              /* head in the data section */
        __u64   data_tail;              /* user-space written tail */
@@ -528,6 +534,7 @@ enum perf_event_type {
         *      u64                             len;
         *      u64                             pgoff;
         *      char                            filename[];
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_MMAP                        = 1,
index 082eafa..25731df 100644 (file)
@@ -388,6 +388,20 @@ enum {
 
 #define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
 
+/* BPF classifier */
+
+enum {
+       TCA_BPF_UNSPEC,
+       TCA_BPF_ACT,
+       TCA_BPF_POLICE,
+       TCA_BPF_CLASSID,
+       TCA_BPF_OPS_LEN,
+       TCA_BPF_OPS,
+       __TCA_BPF_MAX,
+};
+
+#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
+
 /* Extended Matches */
 
 struct tcf_ematch_tree_hdr {
index 9b82913..307f293 100644 (file)
@@ -171,6 +171,8 @@ enum {
        TCA_TBF_PARMS,
        TCA_TBF_RTAB,
        TCA_TBF_PTAB,
+       TCA_TBF_RATE64,
+       TCA_TBF_PRATE64,
        __TCA_TBF_MAX,
 };
 
@@ -357,6 +359,8 @@ enum {
        TCA_HTB_CTAB,
        TCA_HTB_RTAB,
        TCA_HTB_DIRECT_QLEN,
+       TCA_HTB_RATE64,
+       TCA_HTB_CEIL64,
        __TCA_HTB_MAX,
 };
 
index 0623ec4..56f1216 100644 (file)
@@ -1,5 +1,6 @@
 # UAPI Header export list
 header-y += tc_csum.h
+header-y += tc_defact.h
 header-y += tc_gact.h
 header-y += tc_ipt.h
 header-y += tc_mirred.h
diff --git a/include/uapi/linux/tc_act/tc_defact.h b/include/uapi/linux/tc_act/tc_defact.h
new file mode 100644 (file)
index 0000000..17dddb4
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __LINUX_TC_DEF_H
+#define __LINUX_TC_DEF_H
+
+#include <linux/pkt_cls.h>
+
+struct tc_defact {
+       tc_gen;
+};
+
+enum {
+       TCA_DEF_UNSPEC,
+       TCA_DEF_TM,
+       TCA_DEF_PARMS,
+       TCA_DEF_DATA,
+       __TCA_DEF_MAX
+};
+#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
+
+#endif
index 0b233c5..e3ddd86 100644 (file)
@@ -87,8 +87,10 @@ enum {
        IB_USER_VERBS_CMD_CLOSE_XRCD,
        IB_USER_VERBS_CMD_CREATE_XSRQ,
        IB_USER_VERBS_CMD_OPEN_QP,
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
        IB_USER_VERBS_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD,
        IB_USER_VERBS_CMD_DESTROY_FLOW
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 };
 
 /*
@@ -126,6 +128,7 @@ struct ib_uverbs_cmd_hdr {
        __u16 out_words;
 };
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 struct ib_uverbs_cmd_hdr_ex {
        __u32 command;
        __u16 in_words;
@@ -134,6 +137,7 @@ struct ib_uverbs_cmd_hdr_ex {
        __u16 provider_out_words;
        __u32 cmd_hdr_reserved;
 };
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 struct ib_uverbs_get_context {
        __u64 response;
@@ -696,6 +700,7 @@ struct ib_uverbs_detach_mcast {
        __u64 driver_data[0];
 };
 
+#ifdef CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING
 struct ib_kern_eth_filter {
        __u8  dst_mac[6];
        __u8  src_mac[6];
@@ -780,6 +785,7 @@ struct ib_uverbs_destroy_flow  {
        __u32 comp_mask;
        __u32 flow_handle;
 };
+#endif /* CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING */
 
 struct ib_uverbs_create_srq {
        __u64 response;
index eb262e3..c50061d 100644 (file)
  * node as before.
  */
 
+/*
+ * "feature-no-csum-offload" should be used to turn IPv4 TCP/UDP checksum
+ * offload off or on. If it is missing then the feature is assumed to be on.
+ * "feature-ipv6-csum-offload" should be used to turn IPv6 TCP/UDP checksum
+ * offload on or off. If it is missing then the feature is assumed to be off.
+ */
+
+/*
+ * "feature-gso-tcpv4" and "feature-gso-tcpv6" advertise the capability to
+ * handle large TCP packets (in IPv4 or IPv6 form respectively). Neither
+ * frontends nor backends are assumed to be capable unless the flags are
+ * present.
+ */
+
 /*
  * This is the 'wire' format for packets:
  *  Request 1: xen_netif_tx_request  -- XEN_NETTXF_* (any flags)
@@ -95,8 +109,10 @@ struct xen_netif_tx_request {
 #define _XEN_NETIF_EXTRA_FLAG_MORE     (0)
 #define  XEN_NETIF_EXTRA_FLAG_MORE     (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
 
-/* GSO types - only TCPv4 currently supported. */
+/* GSO types */
+#define XEN_NETIF_GSO_TYPE_NONE                (0)
 #define XEN_NETIF_GSO_TYPE_TCPV4       (1)
+#define XEN_NETIF_GSO_TYPE_TCPV6       (2)
 
 /*
  * This structure needs to fit within both netif_tx_request and
index af310af..edee99f 100644 (file)
@@ -76,6 +76,7 @@
 #include <linux/elevator.h>
 #include <linux/sched_clock.h>
 #include <linux/context_tracking.h>
+#include <linux/random.h>
 
 #include <asm/io.h>
 #include <asm/bugs.h>
@@ -135,6 +136,13 @@ static char *static_command_line;
 static char *execute_command;
 static char *ramdisk_execute_command;
 
+/*
+ * Used to generate warnings if static_key manipulation functions are used
+ * before jump_label_init is called.
+ */
+bool static_key_initialized __read_mostly = false;
+EXPORT_SYMBOL_GPL(static_key_initialized);
+
 /*
  * If set, this is an indication to the drivers that reset the underlying
  * device before going ahead with the initialization otherwise driver might
@@ -780,6 +788,7 @@ static void __init do_basic_setup(void)
        do_ctors();
        usermodehelper_enable();
        do_initcalls();
+       random_int_secret_init();
 }
 
 static void __init do_pre_smp_initcalls(void)
index 130dfec..b0e99de 100644 (file)
@@ -62,7 +62,7 @@ static int proc_ipc_dointvec_minmax_orphans(ctl_table *table, int write,
        return err;
 }
 
-static int proc_ipc_callback_dointvec(ctl_table *table, int write,
+static int proc_ipc_callback_dointvec_minmax(ctl_table *table, int write,
        void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table ipc_table;
@@ -72,7 +72,7 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write,
        memcpy(&ipc_table, table, sizeof(ipc_table));
        ipc_table.data = get_ipc(table);
 
-       rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
+       rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 
        if (write && !rc && lenp_bef == *lenp)
                /*
@@ -152,15 +152,13 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
 #define proc_ipc_dointvec         NULL
 #define proc_ipc_dointvec_minmax   NULL
 #define proc_ipc_dointvec_minmax_orphans   NULL
-#define proc_ipc_callback_dointvec NULL
+#define proc_ipc_callback_dointvec_minmax  NULL
 #define proc_ipcauto_dointvec_minmax NULL
 #endif
 
 static int zero;
 static int one = 1;
-#ifdef CONFIG_CHECKPOINT_RESTORE
 static int int_max = INT_MAX;
-#endif
 
 static struct ctl_table ipc_kern_table[] = {
        {
@@ -198,21 +196,27 @@ static struct ctl_table ipc_kern_table[] = {
                .data           = &init_ipc_ns.msg_ctlmax,
                .maxlen         = sizeof (init_ipc_ns.msg_ctlmax),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec,
+               .proc_handler   = proc_ipc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &int_max,
        },
        {
                .procname       = "msgmni",
                .data           = &init_ipc_ns.msg_ctlmni,
                .maxlen         = sizeof (init_ipc_ns.msg_ctlmni),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_callback_dointvec,
+               .proc_handler   = proc_ipc_callback_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &int_max,
        },
        {
                .procname       =  "msgmnb",
                .data           = &init_ipc_ns.msg_ctlmnb,
                .maxlen         = sizeof (init_ipc_ns.msg_ctlmnb),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec,
+               .proc_handler   = proc_ipc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &int_max,
        },
        {
                .procname       = "sem",
index b0d541d..558aa91 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -165,6 +165,15 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
        ipc_rmid(&msg_ids(ns), &s->q_perm);
 }
 
+static void msg_rcu_free(struct rcu_head *head)
+{
+       struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+       struct msg_queue *msq = ipc_rcu_to_struct(p);
+
+       security_msg_queue_free(msq);
+       ipc_rcu_free(head);
+}
+
 /**
  * newque - Create a new msg queue
  * @ns: namespace
@@ -189,15 +198,14 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
        msq->q_perm.security = NULL;
        retval = security_msg_queue_alloc(msq);
        if (retval) {
-               ipc_rcu_putref(msq);
+               ipc_rcu_putref(msq, ipc_rcu_free);
                return retval;
        }
 
        /* ipc_addid() locks msq upon success. */
        id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
        if (id < 0) {
-               security_msg_queue_free(msq);
-               ipc_rcu_putref(msq);
+               ipc_rcu_putref(msq, msg_rcu_free);
                return id;
        }
 
@@ -276,8 +284,7 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
                free_msg(msg);
        }
        atomic_sub(msq->q_cbytes, &ns->msg_bytes);
-       security_msg_queue_free(msq);
-       ipc_rcu_putref(msq);
+       ipc_rcu_putref(msq, msg_rcu_free);
 }
 
 /*
@@ -688,6 +695,12 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                if (ipcperms(ns, &msq->q_perm, S_IWUGO))
                        goto out_unlock0;
 
+               /* raced with RMID? */
+               if (msq->q_perm.deleted) {
+                       err = -EIDRM;
+                       goto out_unlock0;
+               }
+
                err = security_msg_queue_msgsnd(msq, msg, msgflg);
                if (err)
                        goto out_unlock0;
@@ -717,7 +730,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                rcu_read_lock();
                ipc_lock_object(&msq->q_perm);
 
-               ipc_rcu_putref(msq);
+               ipc_rcu_putref(msq, ipc_rcu_free);
                if (msq->q_perm.deleted) {
                        err = -EIDRM;
                        goto out_unlock0;
@@ -894,6 +907,13 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
                        goto out_unlock1;
 
                ipc_lock_object(&msq->q_perm);
+
+               /* raced with RMID? */
+               if (msq->q_perm.deleted) {
+                       msg = ERR_PTR(-EIDRM);
+                       goto out_unlock0;
+               }
+
                msg = find_msg(msq, &msgtyp, mode);
                if (!IS_ERR(msg)) {
                        /*
index 69b6a21..db9d241 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -243,71 +243,122 @@ static void merge_queues(struct sem_array *sma)
        }
 }
 
+static void sem_rcu_free(struct rcu_head *head)
+{
+       struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+       struct sem_array *sma = ipc_rcu_to_struct(p);
+
+       security_sem_free(sma);
+       ipc_rcu_free(head);
+}
+
+/*
+ * Wait until all currently ongoing simple ops have completed.
+ * Caller must own sem_perm.lock.
+ * New simple ops cannot start, because simple ops first check
+ * that sem_perm.lock is free.
+ * that a) sem_perm.lock is free and b) complex_count is 0.
+ */
+static void sem_wait_array(struct sem_array *sma)
+{
+       int i;
+       struct sem *sem;
+
+       if (sma->complex_count)  {
+               /* The thread that increased sma->complex_count waited on
+                * all sem->lock locks. Thus we don't need to wait again.
+                */
+               return;
+       }
+
+       for (i = 0; i < sma->sem_nsems; i++) {
+               sem = sma->sem_base + i;
+               spin_unlock_wait(&sem->lock);
+       }
+}
+
 /*
  * If the request contains only one semaphore operation, and there are
  * no complex transactions pending, lock only the semaphore involved.
  * Otherwise, lock the entire semaphore array, since we either have
  * multiple semaphores in our own semops, or we need to look at
  * semaphores from other pending complex operations.
- *
- * Carefully guard against sma->complex_count changing between zero
- * and non-zero while we are spinning for the lock. The value of
- * sma->complex_count cannot change while we are holding the lock,
- * so sem_unlock should be fine.
- *
- * The global lock path checks that all the local locks have been released,
- * checking each local lock once. This means that the local lock paths
- * cannot start their critical sections while the global lock is held.
  */
 static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
                              int nsops)
 {
-       int locknum;
- again:
-       if (nsops == 1 && !sma->complex_count) {
-               struct sem *sem = sma->sem_base + sops->sem_num;
+       struct sem *sem;
 
-               /* Lock just the semaphore we are interested in. */
-               spin_lock(&sem->lock);
+       if (nsops != 1) {
+               /* Complex operation - acquire a full lock */
+               ipc_lock_object(&sma->sem_perm);
 
-               /*
-                * If sma->complex_count was set while we were spinning,
-                * we may need to look at things we did not lock here.
+               /* And wait until all simple ops that are processed
+                * right now have dropped their locks.
                 */
-               if (unlikely(sma->complex_count)) {
-                       spin_unlock(&sem->lock);
-                       goto lock_array;
-               }
+               sem_wait_array(sma);
+               return -1;
+       }
+
+       /*
+        * Only one semaphore affected - try to optimize locking.
+        * The rules are:
+        * - optimized locking is possible if no complex operation
+        *   is either enqueued or processed right now.
+        * - The test for enqueued complex ops is simple:
+        *      sma->complex_count != 0
+        * - Testing for complex ops that are processed right now is
+        *   a bit more difficult. Complex ops acquire the full lock
+        *   and first wait that the running simple ops have completed.
+        *   (see above)
+        *   Thus: If we own a simple lock and the global lock is free
+        *      and complex_count is now 0, then it will stay 0 and
+        *      thus just locking sem->lock is sufficient.
+        */
+       sem = sma->sem_base + sops->sem_num;
 
+       if (sma->complex_count == 0) {
                /*
-                * Another process is holding the global lock on the
-                * sem_array; we cannot enter our critical section,
-                * but have to wait for the global lock to be released.
+                * It appears that no complex operation is around.
+                * Acquire the per-semaphore lock.
                 */
-               if (unlikely(spin_is_locked(&sma->sem_perm.lock))) {
-                       spin_unlock(&sem->lock);
-                       spin_unlock_wait(&sma->sem_perm.lock);
-                       goto again;
+               spin_lock(&sem->lock);
+
+               /* Then check that the global lock is free */
+               if (!spin_is_locked(&sma->sem_perm.lock)) {
+                       /* spin_is_locked() is not a memory barrier */
+                       smp_mb();
+
+                       /* Now repeat the test of complex_count:
+                        * It can't change anymore until we drop sem->lock.
+                        * Thus: if is now 0, then it will stay 0.
+                        */
+                       if (sma->complex_count == 0) {
+                               /* fast path successful! */
+                               return sops->sem_num;
+                       }
                }
+               spin_unlock(&sem->lock);
+       }
 
-               locknum = sops->sem_num;
+       /* slow path: acquire the full lock */
+       ipc_lock_object(&sma->sem_perm);
+
+       if (sma->complex_count == 0) {
+               /* False alarm:
+                * There is no complex operation, thus we can switch
+                * back to the fast path.
+                */
+               spin_lock(&sem->lock);
+               ipc_unlock_object(&sma->sem_perm);
+               return sops->sem_num;
        } else {
-               int i;
-               /*
-                * Lock the semaphore array, and wait for all of the
-                * individual semaphore locks to go away.  The code
-                * above ensures no new single-lock holders will enter
-                * their critical section while the array lock is held.
+               /* Not a false alarm, thus complete the sequence for a
+                * full lock.
                 */
- lock_array:
-               ipc_lock_object(&sma->sem_perm);
-               for (i = 0; i < sma->sem_nsems; i++) {
-                       struct sem *sem = sma->sem_base + i;
-                       spin_unlock_wait(&sem->lock);
-               }
-               locknum = -1;
+               sem_wait_array(sma);
+               return -1;
        }
-       return locknum;
 }
 
 static inline void sem_unlock(struct sem_array *sma, int locknum)
@@ -374,12 +425,7 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns
 static inline void sem_lock_and_putref(struct sem_array *sma)
 {
        sem_lock(sma, NULL, -1);
-       ipc_rcu_putref(sma);
-}
-
-static inline void sem_putref(struct sem_array *sma)
-{
-       ipc_rcu_putref(sma);
+       ipc_rcu_putref(sma, ipc_rcu_free);
 }
 
 static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -458,14 +504,13 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
        sma->sem_perm.security = NULL;
        retval = security_sem_alloc(sma);
        if (retval) {
-               ipc_rcu_putref(sma);
+               ipc_rcu_putref(sma, ipc_rcu_free);
                return retval;
        }
 
        id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
        if (id < 0) {
-               security_sem_free(sma);
-               ipc_rcu_putref(sma);
+               ipc_rcu_putref(sma, sem_rcu_free);
                return id;
        }
        ns->used_sems += nsems;
@@ -872,6 +917,24 @@ again:
        return semop_completed;
 }
 
+/**
+ * set_semotime(sma, sops) - set sem_otime
+ * @sma: semaphore array
+ * @sops: operations that modified the array, may be NULL
+ *
+ * sem_otime is replicated to avoid cache line trashing.
+ * This function sets one instance to the current time.
+ */
+static void set_semotime(struct sem_array *sma, struct sembuf *sops)
+{
+       if (sops == NULL) {
+               sma->sem_base[0].sem_otime = get_seconds();
+       } else {
+               sma->sem_base[sops[0].sem_num].sem_otime =
+                                                       get_seconds();
+       }
+}
+
 /**
  * do_smart_update(sma, sops, nsops, otime, pt) - optimized update_queue
  * @sma: semaphore array
@@ -922,17 +985,10 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop
                        }
                }
        }
-       if (otime) {
-               if (sops == NULL) {
-                       sma->sem_base[0].sem_otime = get_seconds();
-               } else {
-                       sma->sem_base[sops[0].sem_num].sem_otime =
-                                                               get_seconds();
-               }
-       }
+       if (otime)
+               set_semotime(sma, sops);
 }
 
-
 /* The following counts are associated to each semaphore:
  *   semncnt        number of tasks waiting on semval being nonzero
  *   semzcnt        number of tasks waiting on semval being zero
@@ -1047,8 +1103,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
 
        wake_up_sem_queue_do(&tasks);
        ns->used_sems -= sma->sem_nsems;
-       security_sem_free(sma);
-       ipc_rcu_putref(sma);
+       ipc_rcu_putref(sma, sem_rcu_free);
 }
 
 static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
@@ -1227,6 +1282,12 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
 
        sem_lock(sma, NULL, -1);
 
+       if (sma->sem_perm.deleted) {
+               sem_unlock(sma, -1);
+               rcu_read_unlock();
+               return -EIDRM;
+       }
+
        curr = &sma->sem_base[semnum];
 
        ipc_assert_locked_object(&sma->sem_perm);
@@ -1281,28 +1342,28 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                int i;
 
                sem_lock(sma, NULL, -1);
+               if (sma->sem_perm.deleted) {
+                       err = -EIDRM;
+                       goto out_unlock;
+               }
                if(nsems > SEMMSL_FAST) {
                        if (!ipc_rcu_getref(sma)) {
-                               sem_unlock(sma, -1);
-                               rcu_read_unlock();
                                err = -EIDRM;
-                               goto out_free;
+                               goto out_unlock;
                        }
                        sem_unlock(sma, -1);
                        rcu_read_unlock();
                        sem_io = ipc_alloc(sizeof(ushort)*nsems);
                        if(sem_io == NULL) {
-                               sem_putref(sma);
+                               ipc_rcu_putref(sma, ipc_rcu_free);
                                return -ENOMEM;
                        }
 
                        rcu_read_lock();
                        sem_lock_and_putref(sma);
                        if (sma->sem_perm.deleted) {
-                               sem_unlock(sma, -1);
-                               rcu_read_unlock();
                                err = -EIDRM;
-                               goto out_free;
+                               goto out_unlock;
                        }
                }
                for (i = 0; i < sma->sem_nsems; i++)
@@ -1320,28 +1381,28 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                struct sem_undo *un;
 
                if (!ipc_rcu_getref(sma)) {
-                       rcu_read_unlock();
-                       return -EIDRM;
+                       err = -EIDRM;
+                       goto out_rcu_wakeup;
                }
                rcu_read_unlock();
 
                if(nsems > SEMMSL_FAST) {
                        sem_io = ipc_alloc(sizeof(ushort)*nsems);
                        if(sem_io == NULL) {
-                               sem_putref(sma);
+                               ipc_rcu_putref(sma, ipc_rcu_free);
                                return -ENOMEM;
                        }
                }
 
                if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) {
-                       sem_putref(sma);
+                       ipc_rcu_putref(sma, ipc_rcu_free);
                        err = -EFAULT;
                        goto out_free;
                }
 
                for (i = 0; i < nsems; i++) {
                        if (sem_io[i] > SEMVMX) {
-                               sem_putref(sma);
+                               ipc_rcu_putref(sma, ipc_rcu_free);
                                err = -ERANGE;
                                goto out_free;
                        }
@@ -1349,10 +1410,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                rcu_read_lock();
                sem_lock_and_putref(sma);
                if (sma->sem_perm.deleted) {
-                       sem_unlock(sma, -1);
-                       rcu_read_unlock();
                        err = -EIDRM;
-                       goto out_free;
+                       goto out_unlock;
                }
 
                for (i = 0; i < nsems; i++)
@@ -1376,6 +1435,10 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                goto out_rcu_wakeup;
 
        sem_lock(sma, NULL, -1);
+       if (sma->sem_perm.deleted) {
+               err = -EIDRM;
+               goto out_unlock;
+       }
        curr = &sma->sem_base[semnum];
 
        switch (cmd) {
@@ -1629,7 +1692,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
        /* step 2: allocate new undo structure */
        new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
        if (!new) {
-               sem_putref(sma);
+               ipc_rcu_putref(sma, ipc_rcu_free);
                return ERR_PTR(-ENOMEM);
        }
 
@@ -1781,6 +1844,10 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
        if (error)
                goto out_rcu_wakeup;
 
+       error = -EIDRM;
+       locknum = sem_lock(sma, sops, nsops);
+       if (sma->sem_perm.deleted)
+               goto out_unlock_free;
        /*
         * semid identifiers are not unique - find_alloc_undo may have
         * allocated an undo structure, it was invalidated by an RMID
@@ -1788,19 +1855,22 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
         * This case can be detected checking un->semid. The existence of
         * "un" itself is guaranteed by rcu.
         */
-       error = -EIDRM;
-       locknum = sem_lock(sma, sops, nsops);
        if (un && un->semid == -1)
                goto out_unlock_free;
 
        error = perform_atomic_semop(sma, sops, nsops, un,
                                        task_tgid_vnr(current));
-       if (error <= 0) {
-               if (alter && error == 0)
+       if (error == 0) {
+               /* If the operation was successful, then do
+                * the required updates.
+                */
+               if (alter)
                        do_smart_update(sma, sops, nsops, 1, &tasks);
-
-               goto out_unlock_free;
+               else
+                       set_semotime(sma, sops);
        }
+       if (error <= 0)
+               goto out_unlock_free;
 
        /* We need to sleep on this operation, so we put the current
         * task into the pending queue and go to sleep.
@@ -1997,6 +2067,12 @@ void exit_sem(struct task_struct *tsk)
                }
 
                sem_lock(sma, NULL, -1);
+               /* exit_sem raced with IPC_RMID, nothing to do */
+               if (sma->sem_perm.deleted) {
+                       sem_unlock(sma, -1);
+                       rcu_read_unlock();
+                       continue;
+               }
                un = __lookup_undo(ulp, semid);
                if (un == NULL) {
                        /* exit_sem raced with IPC_RMID+semget() that created
@@ -2059,6 +2135,14 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
        struct sem_array *sma = it;
        time_t sem_otime;
 
+       /*
+        * The proc interface isn't aware of sem_lock(), it calls
+        * ipc_lock_object() directly (in sysvipc_find_ipc).
+        * In order to stay compatible with sem_lock(), we must wait until
+        * all simple semop() calls have left their critical regions.
+        */
+       sem_wait_array(sma);
+
        sem_otime = get_semotime(sma);
 
        return seq_printf(s,
index 2821cdf..d697396 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -167,6 +167,15 @@ static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp)
        ipc_lock_object(&ipcp->shm_perm);
 }
 
+static void shm_rcu_free(struct rcu_head *head)
+{
+       struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+       struct shmid_kernel *shp = ipc_rcu_to_struct(p);
+
+       security_shm_free(shp);
+       ipc_rcu_free(head);
+}
+
 static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
 {
        ipc_rmid(&shm_ids(ns), &s->shm_perm);
@@ -208,8 +217,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
                user_shm_unlock(file_inode(shp->shm_file)->i_size,
                                                shp->mlock_user);
        fput (shp->shm_file);
-       security_shm_free(shp);
-       ipc_rcu_putref(shp);
+       ipc_rcu_putref(shp, shm_rcu_free);
 }
 
 /*
@@ -497,7 +505,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        shp->shm_perm.security = NULL;
        error = security_shm_alloc(shp);
        if (error) {
-               ipc_rcu_putref(shp);
+               ipc_rcu_putref(shp, ipc_rcu_free);
                return error;
        }
 
@@ -566,8 +574,7 @@ no_id:
                user_shm_unlock(size, shp->mlock_user);
        fput(file);
 no_file:
-       security_shm_free(shp);
-       ipc_rcu_putref(shp);
+       ipc_rcu_putref(shp, shm_rcu_free);
        return error;
 }
 
index e829da9..7684f41 100644 (file)
  *            Pavel Emelianov <xemul@openvz.org>
  *
  * General sysv ipc locking scheme:
- *  when doing ipc id lookups, take the ids->rwsem
- *      rcu_read_lock()
- *          obtain the ipc object (kern_ipc_perm)
- *          perform security, capabilities, auditing and permission checks, etc.
- *          acquire the ipc lock (kern_ipc_perm.lock) throught ipc_lock_object()
- *             perform data updates (ie: SET, RMID, LOCK/UNLOCK commands)
+ *     rcu_read_lock()
+ *          obtain the ipc object (kern_ipc_perm) by looking up the id in an idr
+ *         tree.
+ *         - perform initial checks (capabilities, auditing and permission,
+ *           etc).
+ *         - perform read-only operations, such as STAT, INFO commands.
+ *           acquire the ipc lock (kern_ipc_perm.lock) through
+ *           ipc_lock_object()
+ *             - perform data updates, such as SET, RMID commands and
+ *               mechanism-specific operations (semop/semtimedop,
+ *               msgsnd/msgrcv, shmat/shmdt).
+ *         drop the ipc lock, through ipc_unlock_object().
+ *     rcu_read_unlock()
+ *
+ *  The ids->rwsem must be taken when:
+ *     - creating, removing and iterating the existing entries in ipc
+ *       identifier sets.
+ *     - iterating through files under /proc/sysvipc/
+ *
+ *  Note that sems have a special fast path that avoids kern_ipc_perm.lock -
+ *  see sem_lock().
  */
 
 #include <linux/mm.h>
@@ -474,11 +489,6 @@ void ipc_free(void* ptr, int size)
                kfree(ptr);
 }
 
-struct ipc_rcu {
-       struct rcu_head rcu;
-       atomic_t refcount;
-} ____cacheline_aligned_in_smp;
-
 /**
  *     ipc_rcu_alloc   -       allocate ipc and rcu space 
  *     @size: size desired
@@ -505,27 +515,24 @@ int ipc_rcu_getref(void *ptr)
        return atomic_inc_not_zero(&p->refcount);
 }
 
-/**
- * ipc_schedule_free - free ipc + rcu space
- * @head: RCU callback structure for queued work
- */
-static void ipc_schedule_free(struct rcu_head *head)
-{
-       vfree(container_of(head, struct ipc_rcu, rcu));
-}
-
-void ipc_rcu_putref(void *ptr)
+void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head))
 {
        struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
 
        if (!atomic_dec_and_test(&p->refcount))
                return;
 
-       if (is_vmalloc_addr(ptr)) {
-               call_rcu(&p->rcu, ipc_schedule_free);
-       } else {
-               kfree_rcu(p, rcu);
-       }
+       call_rcu(&p->rcu, func);
+}
+
+void ipc_rcu_free(struct rcu_head *head)
+{
+       struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
+
+       if (is_vmalloc_addr(p))
+               vfree(p);
+       else
+               kfree(p);
 }
 
 /**
index c5f3338..f2f5036 100644 (file)
@@ -47,6 +47,13 @@ static inline void msg_exit_ns(struct ipc_namespace *ns) { }
 static inline void shm_exit_ns(struct ipc_namespace *ns) { }
 #endif
 
+struct ipc_rcu {
+       struct rcu_head rcu;
+       atomic_t refcount;
+} ____cacheline_aligned_in_smp;
+
+#define ipc_rcu_to_struct(p)  ((void *)(p+1))
+
 /*
  * Structure that holds the parameters needed by the ipc operations
  * (see after)
@@ -120,7 +127,8 @@ void ipc_free(void* ptr, int size);
  */
 void* ipc_rcu_alloc(int size);
 int ipc_rcu_getref(void *ptr);
-void ipc_rcu_putref(void *ptr);
+void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head));
+void ipc_rcu_free(struct rcu_head *head);
 
 struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
 struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id);
index 91e53d0..7b0e23a 100644 (file)
@@ -1117,9 +1117,10 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
 
                        sleep_time = timeout_start + audit_backlog_wait_time -
                                        jiffies;
-                       if ((long)sleep_time > 0)
+                       if ((long)sleep_time > 0) {
                                wait_for_auditd(sleep_time);
-                       continue;
+                               continue;
+                       }
                }
                if (audit_rate_check() && printk_ratelimit())
                        printk(KERN_WARNING
index 2418b6e..8bd9cfd 100644 (file)
@@ -2039,7 +2039,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
 
                /* @tsk either already exited or can't exit until the end */
                if (tsk->flags & PF_EXITING)
-                       continue;
+                       goto next;
 
                /* as per above, nr_threads may decrease, but not increase. */
                BUG_ON(i >= group_size);
@@ -2047,7 +2047,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
                ent.cgrp = task_cgroup_from_root(tsk, root);
                /* nothing to do if this task is already in the cgroup */
                if (ent.cgrp == cgrp)
-                       continue;
+                       goto next;
                /*
                 * saying GFP_ATOMIC has no effect here because we did prealloc
                 * earlier, but it's good form to communicate our expectations.
@@ -2055,7 +2055,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk,
                retval = flex_array_put(group, i, &ent, GFP_ATOMIC);
                BUG_ON(retval != 0);
                i++;
-
+       next:
                if (!threadgroup)
                        break;
        } while_each_thread(leader, tsk);
@@ -3188,11 +3188,9 @@ css_next_descendant_post(struct cgroup_subsys_state *pos,
 
        WARN_ON_ONCE(!rcu_read_lock_held());
 
-       /* if first iteration, visit the leftmost descendant */
-       if (!pos) {
-               next = css_leftmost_descendant(root);
-               return next != root ? next : NULL;
-       }
+       /* if first iteration, visit leftmost descendant which may be @root */
+       if (!pos)
+               return css_leftmost_descendant(root);
 
        /* if we visited @root, we're done */
        if (pos == root)
index 247091b..859c8df 100644 (file)
@@ -50,6 +50,15 @@ void context_tracking_user_enter(void)
 {
        unsigned long flags;
 
+       /*
+        * Repeat the user_enter() check here because some archs may be calling
+        * this from asm and if no CPU needs context tracking, they shouldn't
+        * go further. Repeat the check here until they support the static key
+        * check.
+        */
+       if (!static_key_false(&context_tracking_enabled))
+               return;
+
        /*
         * Some contexts may involve an exception occuring in an irq,
         * leading to that nesting:
@@ -151,6 +160,9 @@ void context_tracking_user_exit(void)
 {
        unsigned long flags;
 
+       if (!static_key_false(&context_tracking_enabled))
+               return;
+
        if (in_interrupt())
                return;
 
index dd236b6..953c143 100644 (file)
@@ -3660,6 +3660,26 @@ static void calc_timer_values(struct perf_event *event,
        *running = ctx_time - event->tstamp_running;
 }
 
+static void perf_event_init_userpage(struct perf_event *event)
+{
+       struct perf_event_mmap_page *userpg;
+       struct ring_buffer *rb;
+
+       rcu_read_lock();
+       rb = rcu_dereference(event->rb);
+       if (!rb)
+               goto unlock;
+
+       userpg = rb->user_page;
+
+       /* Allow new userspace to detect that bit 0 is deprecated */
+       userpg->cap_bit0_is_deprecated = 1;
+       userpg->size = offsetof(struct perf_event_mmap_page, __reserved);
+
+unlock:
+       rcu_read_unlock();
+}
+
 void __weak arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now)
 {
 }
@@ -4044,6 +4064,7 @@ again:
        ring_buffer_attach(event, rb);
        rcu_assign_pointer(event->rb, rb);
 
+       perf_event_init_userpage(event);
        perf_event_update_userpage(event);
 
 unlock:
@@ -6746,6 +6767,10 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
        if (ret)
                return -EFAULT;
 
+       /* disabled for now */
+       if (attr->mmap2)
+               return -EINVAL;
+
        if (attr->__reserved_1)
                return -EINVAL;
 
@@ -7213,15 +7238,15 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
                perf_remove_from_context(event);
                unaccount_event_cpu(event, src_cpu);
                put_ctx(src_ctx);
-               list_add(&event->event_entry, &events);
+               list_add(&event->migrate_entry, &events);
        }
        mutex_unlock(&src_ctx->mutex);
 
        synchronize_rcu();
 
        mutex_lock(&dst_ctx->mutex);
-       list_for_each_entry_safe(event, tmp, &events, event_entry) {
-               list_del(&event->event_entry);
+       list_for_each_entry_safe(event, tmp, &events, migrate_entry) {
+               list_del(&event->migrate_entry);
                if (event->state >= PERF_EVENT_STATE_OFF)
                        event->state = PERF_EVENT_STATE_INACTIVE;
                account_event_cpu(event, dst_cpu);
index cd55144..9c2ddfb 100644 (file)
@@ -87,10 +87,31 @@ again:
                goto out;
 
        /*
-        * Publish the known good head. Rely on the full barrier implied
-        * by atomic_dec_and_test() order the rb->head read and this
-        * write.
+        * Since the mmap() consumer (userspace) can run on a different CPU:
+        *
+        *   kernel                             user
+        *
+        *   READ ->data_tail                   READ ->data_head
+        *   smp_mb()   (A)                     smp_rmb()       (C)
+        *   WRITE $data                        READ $data
+        *   smp_wmb()  (B)                     smp_mb()        (D)
+        *   STORE ->data_head                  WRITE ->data_tail
+        *
+        * Where A pairs with D, and B pairs with C.
+        *
+        * I don't think A needs to be a full barrier because we won't in fact
+        * write data until we see the store from userspace. So we simply don't
+        * issue the data WRITE until we observe it. Be conservative for now.
+        *
+        * OTOH, D needs to be a full barrier since it separates the data READ
+        * from the tail WRITE.
+        *
+        * For B a WMB is sufficient since it separates two WRITEs, and for C
+        * an RMB is sufficient since it separates two READs.
+        *
+        * See perf_output_begin().
         */
+       smp_wmb();
        rb->user_page->data_head = head;
 
        /*
@@ -154,9 +175,11 @@ int perf_output_begin(struct perf_output_handle *handle,
                 * Userspace could choose to issue a mb() before updating the
                 * tail pointer. So that all reads will be completed before the
                 * write is issued.
+                *
+                * See perf_output_put_handle().
                 */
                tail = ACCESS_ONCE(rb->user_page->data_tail);
-               smp_rmb();
+               smp_mb();
                offset = head = local_read(&rb->head);
                head += size;
                if (unlikely(!perf_output_space(rb, tail, offset, head)))
index 297a924..9019f15 100644 (file)
@@ -58,6 +58,7 @@ static void jump_label_update(struct static_key *key, int enable);
 
 void static_key_slow_inc(struct static_key *key)
 {
+       STATIC_KEY_CHECK_USE();
        if (atomic_inc_not_zero(&key->enabled))
                return;
 
@@ -103,12 +104,14 @@ static void jump_label_update_timeout(struct work_struct *work)
 
 void static_key_slow_dec(struct static_key *key)
 {
+       STATIC_KEY_CHECK_USE();
        __static_key_slow_dec(key, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(static_key_slow_dec);
 
 void static_key_slow_dec_deferred(struct static_key_deferred *key)
 {
+       STATIC_KEY_CHECK_USE();
        __static_key_slow_dec(&key->key, key->timeout, &key->work);
 }
 EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
@@ -116,6 +119,7 @@ EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
 void jump_label_rate_limit(struct static_key_deferred *key,
                unsigned long rl)
 {
+       STATIC_KEY_CHECK_USE();
        key->timeout = rl;
        INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
 }
@@ -212,6 +216,7 @@ void __init jump_label_init(void)
                key->next = NULL;
 #endif
        }
+       static_key_initialized = true;
        jump_label_unlock();
 }
 
index fb32636..b086006 100644 (file)
@@ -571,6 +571,10 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
        DECLARE_COMPLETION_ONSTACK(done);
        int retval = 0;
 
+       if (!sub_info->path) {
+               call_usermodehelper_freeinfo(sub_info);
+               return -EINVAL;
+       }
        helper_lock();
        if (!khelper_wq || usermodehelper_disabled) {
                retval = -EBUSY;
index 6d647ae..d24105b 100644 (file)
@@ -410,7 +410,7 @@ ww_mutex_set_context_fastpath(struct ww_mutex *lock,
 static __always_inline int __sched
 __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                    struct lockdep_map *nest_lock, unsigned long ip,
-                   struct ww_acquire_ctx *ww_ctx)
+                   struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
 {
        struct task_struct *task = current;
        struct mutex_waiter waiter;
@@ -450,7 +450,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                struct task_struct *owner;
                struct mspin_node  node;
 
-               if (!__builtin_constant_p(ww_ctx == NULL) && ww_ctx->acquired > 0) {
+               if (use_ww_ctx && ww_ctx->acquired > 0) {
                        struct ww_mutex *ww;
 
                        ww = container_of(lock, struct ww_mutex, base);
@@ -480,7 +480,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                if ((atomic_read(&lock->count) == 1) &&
                    (atomic_cmpxchg(&lock->count, 1, 0) == 1)) {
                        lock_acquired(&lock->dep_map, ip);
-                       if (!__builtin_constant_p(ww_ctx == NULL)) {
+                       if (use_ww_ctx) {
                                struct ww_mutex *ww;
                                ww = container_of(lock, struct ww_mutex, base);
 
@@ -551,7 +551,7 @@ slowpath:
                        goto err;
                }
 
-               if (!__builtin_constant_p(ww_ctx == NULL) && ww_ctx->acquired > 0) {
+               if (use_ww_ctx && ww_ctx->acquired > 0) {
                        ret = __mutex_lock_check_stamp(lock, ww_ctx);
                        if (ret)
                                goto err;
@@ -575,7 +575,7 @@ skip_wait:
        lock_acquired(&lock->dep_map, ip);
        mutex_set_owner(lock);
 
-       if (!__builtin_constant_p(ww_ctx == NULL)) {
+       if (use_ww_ctx) {
                struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
                struct mutex_waiter *cur;
 
@@ -615,7 +615,7 @@ mutex_lock_nested(struct mutex *lock, unsigned int subclass)
 {
        might_sleep();
        __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
-                           subclass, NULL, _RET_IP_, NULL);
+                           subclass, NULL, _RET_IP_, NULL, 0);
 }
 
 EXPORT_SYMBOL_GPL(mutex_lock_nested);
@@ -625,7 +625,7 @@ _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest)
 {
        might_sleep();
        __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
-                           0, nest, _RET_IP_, NULL);
+                           0, nest, _RET_IP_, NULL, 0);
 }
 
 EXPORT_SYMBOL_GPL(_mutex_lock_nest_lock);
@@ -635,7 +635,7 @@ mutex_lock_killable_nested(struct mutex *lock, unsigned int subclass)
 {
        might_sleep();
        return __mutex_lock_common(lock, TASK_KILLABLE,
-                                  subclass, NULL, _RET_IP_, NULL);
+                                  subclass, NULL, _RET_IP_, NULL, 0);
 }
 EXPORT_SYMBOL_GPL(mutex_lock_killable_nested);
 
@@ -644,7 +644,7 @@ mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass)
 {
        might_sleep();
        return __mutex_lock_common(lock, TASK_INTERRUPTIBLE,
-                                  subclass, NULL, _RET_IP_, NULL);
+                                  subclass, NULL, _RET_IP_, NULL, 0);
 }
 
 EXPORT_SYMBOL_GPL(mutex_lock_interruptible_nested);
@@ -682,7 +682,7 @@ __ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
 
        might_sleep();
        ret =  __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE,
-                                  0, &ctx->dep_map, _RET_IP_, ctx);
+                                  0, &ctx->dep_map, _RET_IP_, ctx, 1);
        if (!ret && ctx->acquired > 1)
                return ww_mutex_deadlock_injection(lock, ctx);
 
@@ -697,7 +697,7 @@ __ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
 
        might_sleep();
        ret = __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE,
-                                 0, &ctx->dep_map, _RET_IP_, ctx);
+                                 0, &ctx->dep_map, _RET_IP_, ctx, 1);
 
        if (!ret && ctx->acquired > 1)
                return ww_mutex_deadlock_injection(lock, ctx);
@@ -809,28 +809,28 @@ __mutex_lock_slowpath(atomic_t *lock_count)
        struct mutex *lock = container_of(lock_count, struct mutex, count);
 
        __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0,
-                           NULL, _RET_IP_, NULL);
+                           NULL, _RET_IP_, NULL, 0);
 }
 
 static noinline int __sched
 __mutex_lock_killable_slowpath(struct mutex *lock)
 {
        return __mutex_lock_common(lock, TASK_KILLABLE, 0,
-                                  NULL, _RET_IP_, NULL);
+                                  NULL, _RET_IP_, NULL, 0);
 }
 
 static noinline int __sched
 __mutex_lock_interruptible_slowpath(struct mutex *lock)
 {
        return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, 0,
-                                  NULL, _RET_IP_, NULL);
+                                  NULL, _RET_IP_, NULL, 0);
 }
 
 static noinline int __sched
 __ww_mutex_lock_slowpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
 {
        return __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE, 0,
-                                  NULL, _RET_IP_, ctx);
+                                  NULL, _RET_IP_, ctx, 1);
 }
 
 static noinline int __sched
@@ -838,7 +838,7 @@ __ww_mutex_lock_interruptible_slowpath(struct ww_mutex *lock,
                                            struct ww_acquire_ctx *ctx)
 {
        return __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE, 0,
-                                  NULL, _RET_IP_, ctx);
+                                  NULL, _RET_IP_, ctx, 1);
 }
 
 #endif
index 81c4e78..c00d5b5 100644 (file)
@@ -254,11 +254,11 @@ int parse_args(const char *doing,
 
 
 STANDARD_PARAM_DEF(byte, unsigned char, "%hhu", unsigned long, kstrtoul);
-STANDARD_PARAM_DEF(short, short, "%hi", long, kstrtoul);
+STANDARD_PARAM_DEF(short, short, "%hi", long, kstrtol);
 STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", unsigned long, kstrtoul);
-STANDARD_PARAM_DEF(int, int, "%i", long, kstrtoul);
+STANDARD_PARAM_DEF(int, int, "%i", long, kstrtol);
 STANDARD_PARAM_DEF(uint, unsigned int, "%u", unsigned long, kstrtoul);
-STANDARD_PARAM_DEF(long, long, "%li", long, kstrtoul);
+STANDARD_PARAM_DEF(long, long, "%li", long, kstrtol);
 STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", unsigned long, kstrtoul);
 
 int param_set_charp(const char *val, const struct kernel_param *kp)
index ebe5e80..9b9a266 100644 (file)
@@ -273,6 +273,11 @@ void free_pid(struct pid *pid)
                         */
                        wake_up_process(ns->child_reaper);
                        break;
+               case PIDNS_HASH_ADDING:
+                       /* Handle a fork failure of the first process */
+                       WARN_ON(ns->child_reaper);
+                       ns->nr_hashed = 0;
+                       /* fall through */
                case 0:
                        schedule_work(&ns->proc_work);
                        break;
index c9c759d..0121dab 100644 (file)
@@ -846,7 +846,7 @@ static int software_resume(void)
        goto Finish;
 }
 
-late_initcall(software_resume);
+late_initcall_sync(software_resume);
 
 
 static const char * const hibernation_modes[] = {
index 358a146..98c3b34 100644 (file)
@@ -743,7 +743,10 @@ int create_basic_memory_bitmaps(void)
        struct memory_bitmap *bm1, *bm2;
        int error = 0;
 
-       BUG_ON(forbidden_pages_map || free_pages_map);
+       if (forbidden_pages_map && free_pages_map)
+               return 0;
+       else
+               BUG_ON(forbidden_pages_map || free_pages_map);
 
        bm1 = kzalloc(sizeof(struct memory_bitmap), GFP_KERNEL);
        if (!bm1)
index 72e8f4f..957f061 100644 (file)
@@ -39,6 +39,7 @@ static struct snapshot_data {
        char frozen;
        char ready;
        char platform_support;
+       bool free_bitmaps;
 } snapshot_state;
 
 atomic_t snapshot_device_available = ATOMIC_INIT(1);
@@ -82,6 +83,10 @@ static int snapshot_open(struct inode *inode, struct file *filp)
                data->swap = -1;
                data->mode = O_WRONLY;
                error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
+               if (!error) {
+                       error = create_basic_memory_bitmaps();
+                       data->free_bitmaps = !error;
+               }
                if (error)
                        pm_notifier_call_chain(PM_POST_RESTORE);
        }
@@ -111,6 +116,8 @@ static int snapshot_release(struct inode *inode, struct file *filp)
                pm_restore_gfp_mask();
                free_basic_memory_bitmaps();
                thaw_processes();
+       } else if (data->free_bitmaps) {
+               free_basic_memory_bitmaps();
        }
        pm_notifier_call_chain(data->mode == O_RDONLY ?
                        PM_POST_HIBERNATION : PM_POST_RESTORE);
@@ -231,6 +238,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
                        break;
                pm_restore_gfp_mask();
                free_basic_memory_bitmaps();
+               data->free_bitmaps = false;
                thaw_processes();
                data->frozen = 0;
                break;
index 269ed93..f813b34 100644 (file)
@@ -32,7 +32,14 @@ EXPORT_SYMBOL(cad_pid);
 #endif
 enum reboot_mode reboot_mode DEFAULT_REBOOT_MODE;
 
-int reboot_default;
+/*
+ * This variable is used privately to keep track of whether or not
+ * reboot_type is still set to its default value (i.e., reboot= hasn't
+ * been set on the command line).  This is needed so that we can
+ * suppress DMI scanning for reboot quirks.  Without it, it's
+ * impossible to override a faulty reboot quirk without recompiling.
+ */
+int reboot_default = 1;
 int reboot_cpu;
 enum reboot_type reboot_type = BOOT_ACPI;
 int reboot_force;
index 11cd136..7c70201 100644 (file)
@@ -4242,7 +4242,7 @@ static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq)
        }
 
        if (!se) {
-               cfs_rq->h_load = rq->avg.load_avg_contrib;
+               cfs_rq->h_load = cfs_rq->runnable_load_avg;
                cfs_rq->last_h_load_update = now;
        }
 
@@ -4823,8 +4823,8 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
                (busiest->load_per_task * SCHED_POWER_SCALE) /
                busiest->group_power;
 
-       if (busiest->avg_load - local->avg_load + scaled_busy_load_per_task >=
-           (scaled_busy_load_per_task * imbn)) {
+       if (busiest->avg_load + scaled_busy_load_per_task >=
+           local->avg_load + (scaled_busy_load_per_task * imbn)) {
                env->imbalance = busiest->load_per_task;
                return;
        }
@@ -4896,7 +4896,8 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
         * max load less than avg load(as we skip the groups at or below
         * its cpu_power, while calculating max_load..)
         */
-       if (busiest->avg_load < sds->avg_load) {
+       if (busiest->avg_load <= sds->avg_load ||
+           local->avg_load >= sds->avg_load) {
                env->imbalance = 0;
                return fix_small_imbalance(env, sds);
        }
index 53cc09c..d7d498d 100644 (file)
@@ -328,10 +328,19 @@ void irq_enter(void)
 
 static inline void invoke_softirq(void)
 {
-       if (!force_irqthreads)
-               __do_softirq();
-       else
+       if (!force_irqthreads) {
+               /*
+                * We can safely execute softirq on the current stack if
+                * it is the irq stack, because it should be near empty
+                * at this stage. But we have no way to know if the arch
+                * calls irq_exit() on the irq stack. So call softirq
+                * in its own stack to prevent from any overrun on top
+                * of a potentially deep task stack.
+                */
+               do_softirq();
+       } else {
                wakeup_softirqd();
+       }
 }
 
 static inline void tick_irq_exit(void)
index 38959c8..662c579 100644 (file)
@@ -33,29 +33,64 @@ struct ce_unbind {
        int res;
 };
 
-/**
- * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
- * @latch:     value to convert
- * @evt:       pointer to clock event device descriptor
- *
- * Math helper, returns latch value converted to nanoseconds (bound checked)
- */
-u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
+                       bool ismax)
 {
        u64 clc = (u64) latch << evt->shift;
+       u64 rnd;
 
        if (unlikely(!evt->mult)) {
                evt->mult = 1;
                WARN_ON(1);
        }
+       rnd = (u64) evt->mult - 1;
+
+       /*
+        * Upper bound sanity check. If the backwards conversion is
+        * not equal latch, we know that the above shift overflowed.
+        */
+       if ((clc >> evt->shift) != (u64)latch)
+               clc = ~0ULL;
+
+       /*
+        * Scaled math oddities:
+        *
+        * For mult <= (1 << shift) we can safely add mult - 1 to
+        * prevent integer rounding loss. So the backwards conversion
+        * from nsec to device ticks will be correct.
+        *
+        * For mult > (1 << shift), i.e. device frequency is > 1GHz we
+        * need to be careful. Adding mult - 1 will result in a value
+        * which when converted back to device ticks can be larger
+        * than latch by up to (mult - 1) >> shift. For the min_delta
+        * calculation we still want to apply this in order to stay
+        * above the minimum device ticks limit. For the upper limit
+        * we would end up with a latch value larger than the upper
+        * limit of the device, so we omit the add to stay below the
+        * device upper boundary.
+        *
+        * Also omit the add if it would overflow the u64 boundary.
+        */
+       if ((~0ULL - clc > rnd) &&
+           (!ismax || evt->mult <= (1U << evt->shift)))
+               clc += rnd;
 
        do_div(clc, evt->mult);
-       if (clc < 1000)
-               clc = 1000;
-       if (clc > KTIME_MAX)
-               clc = KTIME_MAX;
 
-       return clc;
+       /* Deltas less than 1usec are pointless noise */
+       return clc > 1000 ? clc : 1000;
+}
+
+/**
+ * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
+ * @latch:     value to convert
+ * @evt:       pointer to clock event device descriptor
+ *
+ * Math helper, returns latch value converted to nanoseconds (bound checked)
+ */
+u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+{
+       return cev_delta2ns(latch, evt, false);
 }
 EXPORT_SYMBOL_GPL(clockevent_delta2ns);
 
@@ -380,8 +415,8 @@ void clockevents_config(struct clock_event_device *dev, u32 freq)
                sec = 600;
 
        clockevents_calc_mult_shift(dev, freq, sec);
-       dev->min_delta_ns = clockevent_delta2ns(dev->min_delta_ticks, dev);
-       dev->max_delta_ns = clockevent_delta2ns(dev->max_delta_ticks, dev);
+       dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
+       dev->max_delta_ns = cev_delta2ns(dev->max_delta_ticks, dev, true);
 }
 
 /**
index 51c4f34..4431610 100644 (file)
@@ -486,7 +486,52 @@ static struct smp_hotplug_thread watchdog_threads = {
        .unpark                 = watchdog_enable,
 };
 
-static int watchdog_enable_all_cpus(void)
+static void restart_watchdog_hrtimer(void *info)
+{
+       struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);
+       int ret;
+
+       /*
+        * No need to cancel and restart hrtimer if it is currently executing
+        * because it will reprogram itself with the new period now.
+        * We should never see it unqueued here because we are running per-cpu
+        * with interrupts disabled.
+        */
+       ret = hrtimer_try_to_cancel(hrtimer);
+       if (ret == 1)
+               hrtimer_start(hrtimer, ns_to_ktime(sample_period),
+                               HRTIMER_MODE_REL_PINNED);
+}
+
+static void update_timers(int cpu)
+{
+       struct call_single_data data = {.func = restart_watchdog_hrtimer};
+       /*
+        * Make sure that perf event counter will adopt to a new
+        * sampling period. Updating the sampling period directly would
+        * be much nicer but we do not have an API for that now so
+        * let's use a big hammer.
+        * Hrtimer will adopt the new period on the next tick but this
+        * might be late already so we have to restart the timer as well.
+        */
+       watchdog_nmi_disable(cpu);
+       __smp_call_function_single(cpu, &data, 1);
+       watchdog_nmi_enable(cpu);
+}
+
+static void update_timers_all_cpus(void)
+{
+       int cpu;
+
+       get_online_cpus();
+       preempt_disable();
+       for_each_online_cpu(cpu)
+               update_timers(cpu);
+       preempt_enable();
+       put_online_cpus();
+}
+
+static int watchdog_enable_all_cpus(bool sample_period_changed)
 {
        int err = 0;
 
@@ -496,6 +541,8 @@ static int watchdog_enable_all_cpus(void)
                        pr_err("Failed to create watchdog threads, disabled\n");
                else
                        watchdog_running = 1;
+       } else if (sample_period_changed) {
+               update_timers_all_cpus();
        }
 
        return err;
@@ -520,13 +567,15 @@ int proc_dowatchdog(struct ctl_table *table, int write,
                    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int err, old_thresh, old_enabled;
+       static DEFINE_MUTEX(watchdog_proc_mutex);
 
+       mutex_lock(&watchdog_proc_mutex);
        old_thresh = ACCESS_ONCE(watchdog_thresh);
        old_enabled = ACCESS_ONCE(watchdog_user_enabled);
 
        err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
        if (err || !write)
-               return err;
+               goto out;
 
        set_sample_period();
        /*
@@ -535,7 +584,7 @@ int proc_dowatchdog(struct ctl_table *table, int write,
         * watchdog_*_all_cpus() function takes care of this.
         */
        if (watchdog_user_enabled && watchdog_thresh)
-               err = watchdog_enable_all_cpus();
+               err = watchdog_enable_all_cpus(old_thresh != watchdog_thresh);
        else
                watchdog_disable_all_cpus();
 
@@ -544,7 +593,8 @@ int proc_dowatchdog(struct ctl_table *table, int write,
                watchdog_thresh = old_thresh;
                watchdog_user_enabled = old_enabled;
        }
-
+out:
+       mutex_unlock(&watchdog_proc_mutex);
        return err;
 }
 #endif /* CONFIG_SYSCTL */
@@ -554,5 +604,5 @@ void __init lockup_detector_init(void)
        set_sample_period();
 
        if (watchdog_user_enabled)
-               watchdog_enable_all_cpus();
+               watchdog_enable_all_cpus(false);
 }
index 06344d9..094f315 100644 (file)
@@ -983,7 +983,7 @@ config DEBUG_KOBJECT
 
 config DEBUG_KOBJECT_RELEASE
        bool "kobject release debugging"
-       depends on DEBUG_KERNEL
+       depends on DEBUG_OBJECTS_TIMERS
        help
          kobjects are reference counted objects.  This means that their
          last reference count put is not predictable, and the kobject can
index 410093d..70f00ca 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/crc32.h>
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/sched.h>
 #include "crc32defs.h"
 
 #if CRC_LE_BITS > 8
@@ -49,6 +50,30 @@ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@dell.com>");
 MODULE_DESCRIPTION("Various CRC32 calculations");
 MODULE_LICENSE("GPL");
 
+#define GF2_DIM                32
+
+static u32 gf2_matrix_times(u32 *mat, u32 vec)
+{
+       u32 sum = 0;
+
+       while (vec) {
+               if (vec & 1)
+                       sum ^= *mat;
+               vec >>= 1;
+               mat++;
+       }
+
+       return sum;
+}
+
+static void gf2_matrix_square(u32 *square, u32 *mat)
+{
+       int i;
+
+       for (i = 0; i < GF2_DIM; i++)
+               square[i] = gf2_matrix_times(mat, mat[i]);
+}
+
 #if CRC_LE_BITS > 8 || CRC_BE_BITS > 8
 
 /* implements slicing-by-4 or slicing-by-8 algorithm */
@@ -130,6 +155,52 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 (*tab)[256])
 }
 #endif
 
+/* For conditions of distribution and use, see copyright notice in zlib.h */
+static u32 crc32_generic_combine(u32 crc1, u32 crc2, size_t len2,
+                                u32 polynomial)
+{
+       u32 even[GF2_DIM]; /* Even-power-of-two zeros operator */
+       u32 odd[GF2_DIM];  /* Odd-power-of-two zeros operator  */
+       u32 row;
+       int i;
+
+       if (len2 <= 0)
+               return crc1;
+
+       /* Put operator for one zero bit in odd */
+       odd[0] = polynomial;
+       row = 1;
+       for (i = 1; i < GF2_DIM; i++) {
+               odd[i] = row;
+               row <<= 1;
+       }
+
+       gf2_matrix_square(even, odd); /* Put operator for two zero bits in even */
+       gf2_matrix_square(odd, even); /* Put operator for four zero bits in odd */
+
+       /* Apply len2 zeros to crc1 (first square will put the operator for one
+        * zero byte, eight zero bits, in even).
+        */
+       do {
+               /* Apply zeros operator for this bit of len2 */
+               gf2_matrix_square(even, odd);
+               if (len2 & 1)
+                       crc1 = gf2_matrix_times(even, crc1);
+               len2 >>= 1;
+               /* If no more bits set, then done */
+               if (len2 == 0)
+                       break;
+               /* Another iteration of the loop with odd and even swapped */
+               gf2_matrix_square(odd, even);
+               if (len2 & 1)
+                       crc1 = gf2_matrix_times(odd, crc1);
+               len2 >>= 1;
+       } while (len2 != 0);
+
+       crc1 ^= crc2;
+       return crc1;
+}
+
 /**
  * crc32_le_generic() - Calculate bitwise little-endian Ethernet AUTODIN II
  *                     CRC32/CRC32C
@@ -200,8 +271,19 @@ u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len)
                        (const u32 (*)[256])crc32ctable_le, CRC32C_POLY_LE);
 }
 #endif
+u32 __pure crc32_le_combine(u32 crc1, u32 crc2, size_t len2)
+{
+       return crc32_generic_combine(crc1, crc2, len2, CRCPOLY_LE);
+}
+
+u32 __pure __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2)
+{
+       return crc32_generic_combine(crc1, crc2, len2, CRC32C_POLY_LE);
+}
 EXPORT_SYMBOL(crc32_le);
+EXPORT_SYMBOL(crc32_le_combine);
 EXPORT_SYMBOL(__crc32c_le);
+EXPORT_SYMBOL(__crc32c_le_combine);
 
 /**
  * crc32_be_generic() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
@@ -795,206 +877,106 @@ static struct crc_test {
        u32 crc32c_le;  /* expected crc32c_le result */
 } test[] =
 {
-       {0x674bf11d, 0x00000038, 0x00000542, 0x0af6d466, 0xd8b6e4c1,
-        0xf6e93d6c},
-       {0x35c672c6, 0x0000003a, 0x000001aa, 0xc6d3dfba, 0x28aaf3ad,
-        0x0fe92aca},
-       {0x496da28e, 0x00000039, 0x000005af, 0xd933660f, 0x5d57e81f,
-        0x52e1ebb8},
-       {0x09a9b90e, 0x00000027, 0x000001f8, 0xb45fe007, 0xf45fca9a,
-        0x0798af9a},
-       {0xdc97e5a9, 0x00000025, 0x000003b6, 0xf81a3562, 0xe0126ba2,
-        0x18eb3152},
-       {0x47c58900, 0x0000000a, 0x000000b9, 0x8e58eccf, 0xf3afc793,
-        0xd00d08c7},
-       {0x292561e8, 0x0000000c, 0x00000403, 0xa2ba8aaf, 0x0b797aed,
-        0x8ba966bc},
-       {0x415037f6, 0x00000003, 0x00000676, 0xa17d52e8, 0x7f0fdf35,
-        0x11d694a2},
-       {0x3466e707, 0x00000026, 0x00000042, 0x258319be, 0x75c484a2,
-        0x6ab3208d},
-       {0xafd1281b, 0x00000023, 0x000002ee, 0x4428eaf8, 0x06c7ad10,
-        0xba4603c5},
-       {0xd3857b18, 0x00000028, 0x000004a2, 0x5c430821, 0xb062b7cb,
-        0xe6071c6f},
-       {0x1d825a8f, 0x0000002b, 0x0000050b, 0xd2c45f0c, 0xd68634e0,
-        0x179ec30a},
-       {0x5033e3bc, 0x0000000b, 0x00000078, 0xa3ea4113, 0xac6d31fb,
-        0x0903beb8},
-       {0x94f1fb5e, 0x0000000f, 0x000003a2, 0xfbfc50b1, 0x3cfe50ed,
-        0x6a7cb4fa},
-       {0xc9a0fe14, 0x00000009, 0x00000473, 0x5fb61894, 0x87070591,
-        0xdb535801},
-       {0x88a034b1, 0x0000001c, 0x000005ad, 0xc1b16053, 0x46f95c67,
-        0x92bed597},
-       {0xf0f72239, 0x00000020, 0x0000026d, 0xa6fa58f3, 0xf8c2c1dd,
-        0x192a3f1b},
-       {0xcc20a5e3, 0x0000003b, 0x0000067a, 0x7740185a, 0x308b979a,
-        0xccbaec1a},
-       {0xce589c95, 0x0000002b, 0x00000641, 0xd055e987, 0x40aae25b,
-        0x7eabae4d},
-       {0x78edc885, 0x00000035, 0x000005be, 0xa39cb14b, 0x035b0d1f,
-        0x28c72982},
-       {0x9d40a377, 0x0000003b, 0x00000038, 0x1f47ccd2, 0x197fbc9d,
-        0xc3cd4d18},
-       {0x703d0e01, 0x0000003c, 0x000006f1, 0x88735e7c, 0xfed57c5a,
-        0xbca8f0e7},
-       {0x776bf505, 0x0000000f, 0x000005b2, 0x5cc4fc01, 0xf32efb97,
-        0x713f60b3},
-       {0x4a3e7854, 0x00000027, 0x000004b8, 0x8d923c82, 0x0cbfb4a2,
-        0xebd08fd5},
-       {0x209172dd, 0x0000003b, 0x00000356, 0xb89e9c2b, 0xd7868138,
-        0x64406c59},
-       {0x3ba4cc5b, 0x0000002f, 0x00000203, 0xe51601a9, 0x5b2a1032,
-        0x7421890e},
-       {0xfc62f297, 0x00000000, 0x00000079, 0x71a8e1a2, 0x5d88685f,
-        0xe9347603},
-       {0x64280b8b, 0x00000016, 0x000007ab, 0x0fa7a30c, 0xda3a455f,
-        0x1bef9060},
-       {0x97dd724b, 0x00000033, 0x000007ad, 0x5788b2f4, 0xd7326d32,
-        0x34720072},
-       {0x61394b52, 0x00000035, 0x00000571, 0xc66525f1, 0xcabe7fef,
-        0x48310f59},
-       {0x29b4faff, 0x00000024, 0x0000006e, 0xca13751e, 0x993648e0,
-        0x783a4213},
-       {0x29bfb1dc, 0x0000000b, 0x00000244, 0x436c43f7, 0x429f7a59,
-        0x9e8efd41},
-       {0x86ae934b, 0x00000035, 0x00000104, 0x0760ec93, 0x9cf7d0f4,
-        0xfc3d34a5},
-       {0xc4c1024e, 0x0000002e, 0x000006b1, 0x6516a3ec, 0x19321f9c,
-        0x17a52ae2},
-       {0x3287a80a, 0x00000026, 0x00000496, 0x0b257eb1, 0x754ebd51,
-        0x886d935a},
-       {0xa4db423e, 0x00000023, 0x0000045d, 0x9b3a66dc, 0x873e9f11,
-        0xeaaeaeb2},
-       {0x7a1078df, 0x00000015, 0x0000014a, 0x8c2484c5, 0x6a628659,
-        0x8e900a4b},
-       {0x6048bd5b, 0x00000006, 0x0000006a, 0x897e3559, 0xac9961af,
-        0xd74662b1},
-       {0xd8f9ea20, 0x0000003d, 0x00000277, 0x60eb905b, 0xed2aaf99,
-        0xd26752ba},
-       {0xea5ec3b4, 0x0000002a, 0x000004fe, 0x869965dc, 0x6c1f833b,
-        0x8b1fcd62},
-       {0x2dfb005d, 0x00000016, 0x00000345, 0x6a3b117e, 0xf05e8521,
-        0xf54342fe},
-       {0x5a214ade, 0x00000020, 0x000005b6, 0x467f70be, 0xcb22ccd3,
-        0x5b95b988},
-       {0xf0ab9cca, 0x00000032, 0x00000515, 0xed223df3, 0x7f3ef01d,
-        0x2e1176be},
-       {0x91b444f9, 0x0000002e, 0x000007f8, 0x84e9a983, 0x5676756f,
-        0x66120546},
-       {0x1b5d2ddb, 0x0000002e, 0x0000012c, 0xba638c4c, 0x3f42047b,
-        0xf256a5cc},
-       {0xd824d1bb, 0x0000003a, 0x000007b5, 0x6288653b, 0x3a3ebea0,
-        0x4af1dd69},
-       {0x0470180c, 0x00000034, 0x000001f0, 0x9d5b80d6, 0x3de08195,
-        0x56f0a04a},
-       {0xffaa3a3f, 0x00000036, 0x00000299, 0xf3a82ab8, 0x53e0c13d,
-        0x74f6b6b2},
-       {0x6406cfeb, 0x00000023, 0x00000600, 0xa920b8e8, 0xe4e2acf4,
-        0x085951fd},
-       {0xb24aaa38, 0x0000003e, 0x000004a1, 0x657cc328, 0x5077b2c3,
-        0xc65387eb},
-       {0x58b2ab7c, 0x00000039, 0x000002b4, 0x3a17ee7e, 0x9dcb3643,
-        0x1ca9257b},
-       {0x3db85970, 0x00000006, 0x000002b6, 0x95268b59, 0xb9812c10,
-        0xfd196d76},
-       {0x857830c5, 0x00000003, 0x00000590, 0x4ef439d5, 0xf042161d,
-        0x5ef88339},
-       {0xe1fcd978, 0x0000003e, 0x000007d8, 0xae8d8699, 0xce0a1ef5,
-        0x2c3714d9},
-       {0xb982a768, 0x00000016, 0x000006e0, 0x62fad3df, 0x5f8a067b,
-        0x58576548},
-       {0x1d581ce8, 0x0000001e, 0x0000058b, 0xf0f5da53, 0x26e39eee,
-        0xfd7c57de},
-       {0x2456719b, 0x00000025, 0x00000503, 0x4296ac64, 0xd50e4c14,
-        0xd5fedd59},
-       {0xfae6d8f2, 0x00000000, 0x0000055d, 0x057fdf2e, 0x2a31391a,
-        0x1cc3b17b},
-       {0xcba828e3, 0x00000039, 0x000002ce, 0xe3f22351, 0x8f00877b,
-        0x270eed73},
-       {0x13d25952, 0x0000000a, 0x0000072d, 0x76d4b4cc, 0x5eb67ec3,
-        0x91ecbb11},
-       {0x0342be3f, 0x00000015, 0x00000599, 0xec75d9f1, 0x9d4d2826,
-        0x05ed8d0c},
-       {0xeaa344e0, 0x00000014, 0x000004d8, 0x72a4c981, 0x2064ea06,
-        0x0b09ad5b},
-       {0xbbb52021, 0x0000003b, 0x00000272, 0x04af99fc, 0xaf042d35,
-        0xf8d511fb},
-       {0xb66384dc, 0x0000001d, 0x000007fc, 0xd7629116, 0x782bd801,
-        0x5ad832cc},
-       {0x616c01b6, 0x00000022, 0x000002c8, 0x5b1dab30, 0x783ce7d2,
-        0x1214d196},
-       {0xce2bdaad, 0x00000016, 0x0000062a, 0x932535c8, 0x3f02926d,
-        0x5747218a},
-       {0x00fe84d7, 0x00000005, 0x00000205, 0x850e50aa, 0x753d649c,
-        0xde8f14de},
-       {0xbebdcb4c, 0x00000006, 0x0000055d, 0xbeaa37a2, 0x2d8c9eba,
-        0x3563b7b9},
-       {0xd8b1a02a, 0x00000010, 0x00000387, 0x5017d2fc, 0x503541a5,
-        0x071475d0},
-       {0x3b96cad2, 0x00000036, 0x00000347, 0x1d2372ae, 0x926cd90b,
-        0x54c79d60},
-       {0xc94c1ed7, 0x00000005, 0x0000038b, 0x9e9fdb22, 0x144a9178,
-        0x4c53eee6},
-       {0x1aad454e, 0x00000025, 0x000002b2, 0xc3f6315c, 0x5c7a35b3,
-        0x10137a3c},
-       {0xa4fec9a6, 0x00000000, 0x000006d6, 0x90be5080, 0xa4107605,
-        0xaa9d6c73},
-       {0x1bbe71e2, 0x0000001f, 0x000002fd, 0x4e504c3b, 0x284ccaf1,
-        0xb63d23e7},
-       {0x4201c7e4, 0x00000002, 0x000002b7, 0x7822e3f9, 0x0cc912a9,
-        0x7f53e9cf},
-       {0x23fddc96, 0x00000003, 0x00000627, 0x8a385125, 0x07767e78,
-        0x13c1cd83},
-       {0xd82ba25c, 0x00000016, 0x0000063e, 0x98e4148a, 0x283330c9,
-        0x49ff5867},
-       {0x786f2032, 0x0000002d, 0x0000060f, 0xf201600a, 0xf561bfcd,
-        0x8467f211},
-       {0xfebe4e1f, 0x0000002a, 0x000004f2, 0x95e51961, 0xfd80dcab,
-        0x3f9683b2},
-       {0x1a6e0a39, 0x00000008, 0x00000672, 0x8af6c2a5, 0x78dd84cb,
-        0x76a3f874},
-       {0x56000ab8, 0x0000000e, 0x000000e5, 0x36bacb8f, 0x22ee1f77,
-        0x863b702f},
-       {0x4717fe0c, 0x00000000, 0x000006ec, 0x8439f342, 0x5c8e03da,
-        0xdc6c58ff},
-       {0xd5d5d68e, 0x0000003c, 0x000003a3, 0x46fff083, 0x177d1b39,
-        0x0622cc95},
-       {0xc25dd6c6, 0x00000024, 0x000006c0, 0x5ceb8eb4, 0x892b0d16,
-        0xe85605cd},
-       {0xe9b11300, 0x00000023, 0x00000683, 0x07a5d59a, 0x6c6a3208,
-        0x31da5f06},
-       {0x95cd285e, 0x00000001, 0x00000047, 0x7b3a4368, 0x0202c07e,
-        0xa1f2e784},
-       {0xd9245a25, 0x0000001e, 0x000003a6, 0xd33c1841, 0x1936c0d5,
-        0xb07cc616},
-       {0x103279db, 0x00000006, 0x0000039b, 0xca09b8a0, 0x77d62892,
-        0xbf943b6c},
-       {0x1cba3172, 0x00000027, 0x000001c8, 0xcb377194, 0xebe682db,
-        0x2c01af1c},
-       {0x8f613739, 0x0000000c, 0x000001df, 0xb4b0bc87, 0x7710bd43,
-        0x0fe5f56d},
-       {0x1c6aa90d, 0x0000001b, 0x0000053c, 0x70559245, 0xda7894ac,
-        0xf8943b2d},
-       {0xaabe5b93, 0x0000003d, 0x00000715, 0xcdbf42fa, 0x0c3b99e7,
-        0xe4d89272},
-       {0xf15dd038, 0x00000006, 0x000006db, 0x6e104aea, 0x8d5967f2,
-        0x7c2f6bbb},
-       {0x584dd49c, 0x00000020, 0x000007bc, 0x36b6cfd6, 0xad4e23b2,
-        0xabbf388b},
-       {0x5d8c9506, 0x00000020, 0x00000470, 0x4c62378e, 0x31d92640,
-        0x1dca1f4e},
-       {0xb80d17b0, 0x00000032, 0x00000346, 0x22a5bb88, 0x9a7ec89f,
-        0x5c170e23},
-       {0xdaf0592e, 0x00000023, 0x000007b0, 0x3cab3f99, 0x9b1fdd99,
-        0xc0e9d672},
-       {0x4793cc85, 0x0000000d, 0x00000706, 0xe82e04f6, 0xed3db6b7,
-        0xc18bdc86},
-       {0x82ebf64e, 0x00000009, 0x000007c3, 0x69d590a9, 0x9efa8499,
-        0xa874fcdd},
-       {0xb18a0319, 0x00000026, 0x000007db, 0x1cf98dcc, 0x8fa9ad6a,
-        0x9dc0bb48},
+       {0x674bf11d, 0x00000038, 0x00000542, 0x0af6d466, 0xd8b6e4c1, 0xf6e93d6c},
+       {0x35c672c6, 0x0000003a, 0x000001aa, 0xc6d3dfba, 0x28aaf3ad, 0x0fe92aca},
+       {0x496da28e, 0x00000039, 0x000005af, 0xd933660f, 0x5d57e81f, 0x52e1ebb8},
+       {0x09a9b90e, 0x00000027, 0x000001f8, 0xb45fe007, 0xf45fca9a, 0x0798af9a},
+       {0xdc97e5a9, 0x00000025, 0x000003b6, 0xf81a3562, 0xe0126ba2, 0x18eb3152},
+       {0x47c58900, 0x0000000a, 0x000000b9, 0x8e58eccf, 0xf3afc793, 0xd00d08c7},
+       {0x292561e8, 0x0000000c, 0x00000403, 0xa2ba8aaf, 0x0b797aed, 0x8ba966bc},
+       {0x415037f6, 0x00000003, 0x00000676, 0xa17d52e8, 0x7f0fdf35, 0x11d694a2},
+       {0x3466e707, 0x00000026, 0x00000042, 0x258319be, 0x75c484a2, 0x6ab3208d},
+       {0xafd1281b, 0x00000023, 0x000002ee, 0x4428eaf8, 0x06c7ad10, 0xba4603c5},
+       {0xd3857b18, 0x00000028, 0x000004a2, 0x5c430821, 0xb062b7cb, 0xe6071c6f},
+       {0x1d825a8f, 0x0000002b, 0x0000050b, 0xd2c45f0c, 0xd68634e0, 0x179ec30a},
+       {0x5033e3bc, 0x0000000b, 0x00000078, 0xa3ea4113, 0xac6d31fb, 0x0903beb8},
+       {0x94f1fb5e, 0x0000000f, 0x000003a2, 0xfbfc50b1, 0x3cfe50ed, 0x6a7cb4fa},
+       {0xc9a0fe14, 0x00000009, 0x00000473, 0x5fb61894, 0x87070591, 0xdb535801},
+       {0x88a034b1, 0x0000001c, 0x000005ad, 0xc1b16053, 0x46f95c67, 0x92bed597},
+       {0xf0f72239, 0x00000020, 0x0000026d, 0xa6fa58f3, 0xf8c2c1dd, 0x192a3f1b},
+       {0xcc20a5e3, 0x0000003b, 0x0000067a, 0x7740185a, 0x308b979a, 0xccbaec1a},
+       {0xce589c95, 0x0000002b, 0x00000641, 0xd055e987, 0x40aae25b, 0x7eabae4d},
+       {0x78edc885, 0x00000035, 0x000005be, 0xa39cb14b, 0x035b0d1f, 0x28c72982},
+       {0x9d40a377, 0x0000003b, 0x00000038, 0x1f47ccd2, 0x197fbc9d, 0xc3cd4d18},
+       {0x703d0e01, 0x0000003c, 0x000006f1, 0x88735e7c, 0xfed57c5a, 0xbca8f0e7},
+       {0x776bf505, 0x0000000f, 0x000005b2, 0x5cc4fc01, 0xf32efb97, 0x713f60b3},
+       {0x4a3e7854, 0x00000027, 0x000004b8, 0x8d923c82, 0x0cbfb4a2, 0xebd08fd5},
+       {0x209172dd, 0x0000003b, 0x00000356, 0xb89e9c2b, 0xd7868138, 0x64406c59},
+       {0x3ba4cc5b, 0x0000002f, 0x00000203, 0xe51601a9, 0x5b2a1032, 0x7421890e},
+       {0xfc62f297, 0x00000000, 0x00000079, 0x71a8e1a2, 0x5d88685f, 0xe9347603},
+       {0x64280b8b, 0x00000016, 0x000007ab, 0x0fa7a30c, 0xda3a455f, 0x1bef9060},
+       {0x97dd724b, 0x00000033, 0x000007ad, 0x5788b2f4, 0xd7326d32, 0x34720072},
+       {0x61394b52, 0x00000035, 0x00000571, 0xc66525f1, 0xcabe7fef, 0x48310f59},
+       {0x29b4faff, 0x00000024, 0x0000006e, 0xca13751e, 0x993648e0, 0x783a4213},
+       {0x29bfb1dc, 0x0000000b, 0x00000244, 0x436c43f7, 0x429f7a59, 0x9e8efd41},
+       {0x86ae934b, 0x00000035, 0x00000104, 0x0760ec93, 0x9cf7d0f4, 0xfc3d34a5},
+       {0xc4c1024e, 0x0000002e, 0x000006b1, 0x6516a3ec, 0x19321f9c, 0x17a52ae2},
+       {0x3287a80a, 0x00000026, 0x00000496, 0x0b257eb1, 0x754ebd51, 0x886d935a},
+       {0xa4db423e, 0x00000023, 0x0000045d, 0x9b3a66dc, 0x873e9f11, 0xeaaeaeb2},
+       {0x7a1078df, 0x00000015, 0x0000014a, 0x8c2484c5, 0x6a628659, 0x8e900a4b},
+       {0x6048bd5b, 0x00000006, 0x0000006a, 0x897e3559, 0xac9961af, 0xd74662b1},
+       {0xd8f9ea20, 0x0000003d, 0x00000277, 0x60eb905b, 0xed2aaf99, 0xd26752ba},
+       {0xea5ec3b4, 0x0000002a, 0x000004fe, 0x869965dc, 0x6c1f833b, 0x8b1fcd62},
+       {0x2dfb005d, 0x00000016, 0x00000345, 0x6a3b117e, 0xf05e8521, 0xf54342fe},
+       {0x5a214ade, 0x00000020, 0x000005b6, 0x467f70be, 0xcb22ccd3, 0x5b95b988},
+       {0xf0ab9cca, 0x00000032, 0x00000515, 0xed223df3, 0x7f3ef01d, 0x2e1176be},
+       {0x91b444f9, 0x0000002e, 0x000007f8, 0x84e9a983, 0x5676756f, 0x66120546},
+       {0x1b5d2ddb, 0x0000002e, 0x0000012c, 0xba638c4c, 0x3f42047b, 0xf256a5cc},
+       {0xd824d1bb, 0x0000003a, 0x000007b5, 0x6288653b, 0x3a3ebea0, 0x4af1dd69},
+       {0x0470180c, 0x00000034, 0x000001f0, 0x9d5b80d6, 0x3de08195, 0x56f0a04a},
+       {0xffaa3a3f, 0x00000036, 0x00000299, 0xf3a82ab8, 0x53e0c13d, 0x74f6b6b2},
+       {0x6406cfeb, 0x00000023, 0x00000600, 0xa920b8e8, 0xe4e2acf4, 0x085951fd},
+       {0xb24aaa38, 0x0000003e, 0x000004a1, 0x657cc328, 0x5077b2c3, 0xc65387eb},
+       {0x58b2ab7c, 0x00000039, 0x000002b4, 0x3a17ee7e, 0x9dcb3643, 0x1ca9257b},
+       {0x3db85970, 0x00000006, 0x000002b6, 0x95268b59, 0xb9812c10, 0xfd196d76},
+       {0x857830c5, 0x00000003, 0x00000590, 0x4ef439d5, 0xf042161d, 0x5ef88339},
+       {0xe1fcd978, 0x0000003e, 0x000007d8, 0xae8d8699, 0xce0a1ef5, 0x2c3714d9},
+       {0xb982a768, 0x00000016, 0x000006e0, 0x62fad3df, 0x5f8a067b, 0x58576548},
+       {0x1d581ce8, 0x0000001e, 0x0000058b, 0xf0f5da53, 0x26e39eee, 0xfd7c57de},
+       {0x2456719b, 0x00000025, 0x00000503, 0x4296ac64, 0xd50e4c14, 0xd5fedd59},
+       {0xfae6d8f2, 0x00000000, 0x0000055d, 0x057fdf2e, 0x2a31391a, 0x1cc3b17b},
+       {0xcba828e3, 0x00000039, 0x000002ce, 0xe3f22351, 0x8f00877b, 0x270eed73},
+       {0x13d25952, 0x0000000a, 0x0000072d, 0x76d4b4cc, 0x5eb67ec3, 0x91ecbb11},
+       {0x0342be3f, 0x00000015, 0x00000599, 0xec75d9f1, 0x9d4d2826, 0x05ed8d0c},
+       {0xeaa344e0, 0x00000014, 0x000004d8, 0x72a4c981, 0x2064ea06, 0x0b09ad5b},
+       {0xbbb52021, 0x0000003b, 0x00000272, 0x04af99fc, 0xaf042d35, 0xf8d511fb},
+       {0xb66384dc, 0x0000001d, 0x000007fc, 0xd7629116, 0x782bd801, 0x5ad832cc},
+       {0x616c01b6, 0x00000022, 0x000002c8, 0x5b1dab30, 0x783ce7d2, 0x1214d196},
+       {0xce2bdaad, 0x00000016, 0x0000062a, 0x932535c8, 0x3f02926d, 0x5747218a},
+       {0x00fe84d7, 0x00000005, 0x00000205, 0x850e50aa, 0x753d649c, 0xde8f14de},
+       {0xbebdcb4c, 0x00000006, 0x0000055d, 0xbeaa37a2, 0x2d8c9eba, 0x3563b7b9},
+       {0xd8b1a02a, 0x00000010, 0x00000387, 0x5017d2fc, 0x503541a5, 0x071475d0},
+       {0x3b96cad2, 0x00000036, 0x00000347, 0x1d2372ae, 0x926cd90b, 0x54c79d60},
+       {0xc94c1ed7, 0x00000005, 0x0000038b, 0x9e9fdb22, 0x144a9178, 0x4c53eee6},
+       {0x1aad454e, 0x00000025, 0x000002b2, 0xc3f6315c, 0x5c7a35b3, 0x10137a3c},
+       {0xa4fec9a6, 0x00000000, 0x000006d6, 0x90be5080, 0xa4107605, 0xaa9d6c73},
+       {0x1bbe71e2, 0x0000001f, 0x000002fd, 0x4e504c3b, 0x284ccaf1, 0xb63d23e7},
+       {0x4201c7e4, 0x00000002, 0x000002b7, 0x7822e3f9, 0x0cc912a9, 0x7f53e9cf},
+       {0x23fddc96, 0x00000003, 0x00000627, 0x8a385125, 0x07767e78, 0x13c1cd83},
+       {0xd82ba25c, 0x00000016, 0x0000063e, 0x98e4148a, 0x283330c9, 0x49ff5867},
+       {0x786f2032, 0x0000002d, 0x0000060f, 0xf201600a, 0xf561bfcd, 0x8467f211},
+       {0xfebe4e1f, 0x0000002a, 0x000004f2, 0x95e51961, 0xfd80dcab, 0x3f9683b2},
+       {0x1a6e0a39, 0x00000008, 0x00000672, 0x8af6c2a5, 0x78dd84cb, 0x76a3f874},
+       {0x56000ab8, 0x0000000e, 0x000000e5, 0x36bacb8f, 0x22ee1f77, 0x863b702f},
+       {0x4717fe0c, 0x00000000, 0x000006ec, 0x8439f342, 0x5c8e03da, 0xdc6c58ff},
+       {0xd5d5d68e, 0x0000003c, 0x000003a3, 0x46fff083, 0x177d1b39, 0x0622cc95},
+       {0xc25dd6c6, 0x00000024, 0x000006c0, 0x5ceb8eb4, 0x892b0d16, 0xe85605cd},
+       {0xe9b11300, 0x00000023, 0x00000683, 0x07a5d59a, 0x6c6a3208, 0x31da5f06},
+       {0x95cd285e, 0x00000001, 0x00000047, 0x7b3a4368, 0x0202c07e, 0xa1f2e784},
+       {0xd9245a25, 0x0000001e, 0x000003a6, 0xd33c1841, 0x1936c0d5, 0xb07cc616},
+       {0x103279db, 0x00000006, 0x0000039b, 0xca09b8a0, 0x77d62892, 0xbf943b6c},
+       {0x1cba3172, 0x00000027, 0x000001c8, 0xcb377194, 0xebe682db, 0x2c01af1c},
+       {0x8f613739, 0x0000000c, 0x000001df, 0xb4b0bc87, 0x7710bd43, 0x0fe5f56d},
+       {0x1c6aa90d, 0x0000001b, 0x0000053c, 0x70559245, 0xda7894ac, 0xf8943b2d},
+       {0xaabe5b93, 0x0000003d, 0x00000715, 0xcdbf42fa, 0x0c3b99e7, 0xe4d89272},
+       {0xf15dd038, 0x00000006, 0x000006db, 0x6e104aea, 0x8d5967f2, 0x7c2f6bbb},
+       {0x584dd49c, 0x00000020, 0x000007bc, 0x36b6cfd6, 0xad4e23b2, 0xabbf388b},
+       {0x5d8c9506, 0x00000020, 0x00000470, 0x4c62378e, 0x31d92640, 0x1dca1f4e},
+       {0xb80d17b0, 0x00000032, 0x00000346, 0x22a5bb88, 0x9a7ec89f, 0x5c170e23},
+       {0xdaf0592e, 0x00000023, 0x000007b0, 0x3cab3f99, 0x9b1fdd99, 0xc0e9d672},
+       {0x4793cc85, 0x0000000d, 0x00000706, 0xe82e04f6, 0xed3db6b7, 0xc18bdc86},
+       {0x82ebf64e, 0x00000009, 0x000007c3, 0x69d590a9, 0x9efa8499, 0xa874fcdd},
+       {0xb18a0319, 0x00000026, 0x000007db, 0x1cf98dcc, 0x8fa9ad6a, 0x9dc0bb48},
 };
 
 #include <linux/time.h>
@@ -1050,6 +1032,41 @@ static int __init crc32c_test(void)
        return 0;
 }
 
+static int __init crc32c_combine_test(void)
+{
+       int i, j;
+       int errors = 0, runs = 0;
+
+       for (i = 0; i < 10; i++) {
+               u32 crc_full;
+
+               crc_full = __crc32c_le(test[i].crc, test_buf + test[i].start,
+                                      test[i].length);
+               for (j = 0; j <= test[i].length; ++j) {
+                       u32 crc1, crc2;
+                       u32 len1 = j, len2 = test[i].length - j;
+
+                       crc1 = __crc32c_le(test[i].crc, test_buf +
+                                          test[i].start, len1);
+                       crc2 = __crc32c_le(0, test_buf + test[i].start +
+                                          len1, len2);
+
+                       if (!(crc_full == __crc32c_le_combine(crc1, crc2, len2) &&
+                             crc_full == test[i].crc32c_le))
+                               errors++;
+                       runs++;
+                       cond_resched();
+               }
+       }
+
+       if (errors)
+               pr_warn("crc32c_combine: %d/%d self tests failed\n", errors, runs);
+       else
+               pr_info("crc32c_combine: %d self tests passed\n", runs);
+
+       return 0;
+}
+
 static int __init crc32_test(void)
 {
        int i;
@@ -1109,10 +1126,49 @@ static int __init crc32_test(void)
        return 0;
 }
 
+static int __init crc32_combine_test(void)
+{
+       int i, j;
+       int errors = 0, runs = 0;
+
+       for (i = 0; i < 10; i++) {
+               u32 crc_full;
+
+               crc_full = crc32_le(test[i].crc, test_buf + test[i].start,
+                                   test[i].length);
+               for (j = 0; j <= test[i].length; ++j) {
+                       u32 crc1, crc2;
+                       u32 len1 = j, len2 = test[i].length - j;
+
+                       crc1 = crc32_le(test[i].crc, test_buf +
+                                       test[i].start, len1);
+                       crc2 = crc32_le(0, test_buf + test[i].start +
+                                       len1, len2);
+
+                       if (!(crc_full == crc32_le_combine(crc1, crc2, len2) &&
+                             crc_full == test[i].crc_le))
+                               errors++;
+                       runs++;
+                       cond_resched();
+               }
+       }
+
+       if (errors)
+               pr_warn("crc32_combine: %d/%d self tests failed\n", errors, runs);
+       else
+               pr_info("crc32_combine: %d self tests passed\n", runs);
+
+       return 0;
+}
+
 static int __init crc32test_init(void)
 {
        crc32_test();
        crc32c_test();
+
+       crc32_combine_test();
+       crc32c_combine_test();
+
        return 0;
 }
 
index 3f0494c..8499c81 100644 (file)
@@ -14,6 +14,8 @@
 
 const char hex_asc[] = "0123456789abcdef";
 EXPORT_SYMBOL(hex_asc);
+const char hex_asc_upper[] = "0123456789ABCDEF";
+EXPORT_SYMBOL(hex_asc_upper);
 
 /**
  * hex_to_bin - convert a hex digit to its real value
index 9621751..084f7b1 100644 (file)
@@ -592,7 +592,7 @@ static void kobject_release(struct kref *kref)
 {
        struct kobject *kobj = container_of(kref, struct kobject, kref);
 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
-       pr_debug("kobject: '%s' (%p): %s, parent %p (delayed)\n",
+       pr_info("kobject: '%s' (%p): %s, parent %p (delayed)\n",
                 kobject_name(kobj), kobj, __func__, kobj->parent);
        INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
        schedule_delayed_work(&kobj->release, HZ);
@@ -933,10 +933,7 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj)
 
 bool kobj_ns_current_may_mount(enum kobj_ns_type type)
 {
-       bool may_mount = false;
-
-       if (type == KOBJ_NS_TYPE_NONE)
-               return true;
+       bool may_mount = true;
 
        spin_lock(&kobj_ns_type_lock);
        if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
index e2cd2c0..6f9d434 100644 (file)
@@ -3,6 +3,22 @@
 
 #ifdef CONFIG_CMPXCHG_LOCKREF
 
+/*
+ * Allow weakly-ordered memory architectures to provide barrier-less
+ * cmpxchg semantics for lockref updates.
+ */
+#ifndef cmpxchg64_relaxed
+# define cmpxchg64_relaxed cmpxchg64
+#endif
+
+/*
+ * Allow architectures to override the default cpu_relax() within CMPXCHG_LOOP.
+ * This is useful for architectures with an expensive cpu_relax().
+ */
+#ifndef arch_mutex_cpu_relax
+# define arch_mutex_cpu_relax() cpu_relax()
+#endif
+
 /*
  * Note that the "cmpxchg()" reloads the "old" value for the
  * failure case.
        while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) {     \
                struct lockref new = old, prev = old;                           \
                CODE                                                            \
-               old.lock_count = cmpxchg(&lockref->lock_count,                  \
-                                        old.lock_count, new.lock_count);       \
+               old.lock_count = cmpxchg64_relaxed(&lockref->lock_count,        \
+                                                  old.lock_count,              \
+                                                  new.lock_count);             \
                if (likely(old.lock_count == prev.lock_count)) {                \
                        SUCCESS;                                                \
                }                                                               \
-               cpu_relax();                                                    \
+               arch_mutex_cpu_relax();                                         \
        }                                                                       \
 } while (0)
 
index 7deeb62..1a53d49 100644 (file)
@@ -53,6 +53,7 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release)
        ref->release = release;
        return 0;
 }
+EXPORT_SYMBOL_GPL(percpu_ref_init);
 
 /**
  * percpu_ref_cancel_init - cancel percpu_ref_init()
@@ -84,6 +85,7 @@ void percpu_ref_cancel_init(struct percpu_ref *ref)
                free_percpu(ref->pcpu_count);
        }
 }
+EXPORT_SYMBOL_GPL(percpu_ref_cancel_init);
 
 static void percpu_ref_kill_rcu(struct rcu_head *rcu)
 {
@@ -156,3 +158,4 @@ void percpu_ref_kill_and_confirm(struct percpu_ref *ref,
 
        call_rcu_sched(&ref->rcu, percpu_ref_kill_rcu);
 }
+EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm);
index a685c8a..d16fa29 100644 (file)
@@ -577,7 +577,8 @@ void sg_miter_stop(struct sg_mapping_iter *miter)
                miter->__offset += miter->consumed;
                miter->__remaining -= miter->consumed;
 
-               if (miter->__flags & SG_MITER_TO_SG)
+               if ((miter->__flags & SG_MITER_TO_SG) &&
+                   !PageSlab(miter->page))
                        flush_kernel_dcache_page(miter->page);
 
                if (miter->__flags & SG_MITER_ATOMIC) {
index 026771a..394838f 100644 (file)
@@ -183,7 +183,7 @@ config MEMORY_HOTPLUG_SPARSE
 config MEMORY_HOTREMOVE
        bool "Allow for memory hot remove"
        select MEMORY_ISOLATION
-       select HAVE_BOOTMEM_INFO_NODE if X86_64
+       select HAVE_BOOTMEM_INFO_NODE if (X86_64 || PPC64)
        depends on MEMORY_HOTPLUG && ARCH_ENABLE_MEMORY_HOTREMOVE
        depends on MIGRATION
 
index c9f0a43..5a7d58f 100644 (file)
@@ -204,6 +204,8 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
        struct bio_vec *to, *from;
        unsigned i;
 
+       if (force)
+               goto bounce;
        bio_for_each_segment(from, *bio_orig, i)
                if (page_to_pfn(from->bv_page) > queue_bounce_pfn(q))
                        goto bounce;
index c437893..b5326b1 100644 (file)
@@ -677,6 +677,13 @@ static void isolate_freepages(struct zone *zone,
                                        pfn -= pageblock_nr_pages) {
                unsigned long isolated;
 
+               /*
+                * This can iterate a massively long zone without finding any
+                * suitable migration targets, so periodically check if we need
+                * to schedule.
+                */
+               cond_resched();
+
                if (!pfn_valid(pfn))
                        continue;
 
index 1e6aec4..ae4846f 100644 (file)
@@ -1616,7 +1616,6 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        struct inode *inode = mapping->host;
        pgoff_t offset = vmf->pgoff;
        struct page *page;
-       bool memcg_oom;
        pgoff_t size;
        int ret = 0;
 
@@ -1625,11 +1624,7 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                return VM_FAULT_SIGBUS;
 
        /*
-        * Do we have something in the page cache already?  Either
-        * way, try readahead, but disable the memcg OOM killer for it
-        * as readahead is optional and no errors are propagated up
-        * the fault stack.  The OOM killer is enabled while trying to
-        * instantiate the faulting page individually below.
+        * Do we have something in the page cache already?
         */
        page = find_get_page(mapping, offset);
        if (likely(page) && !(vmf->flags & FAULT_FLAG_TRIED)) {
@@ -1637,14 +1632,10 @@ int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                 * We found the page, so try async readahead before
                 * waiting for the lock.
                 */
-               memcg_oom = mem_cgroup_toggle_oom(false);
                do_async_mmap_readahead(vma, ra, file, page, offset);
-               mem_cgroup_toggle_oom(memcg_oom);
        } else if (!page) {
                /* No page in the page cache at all */
-               memcg_oom = mem_cgroup_toggle_oom(false);
                do_sync_mmap_readahead(vma, ra, file, offset);
-               mem_cgroup_toggle_oom(memcg_oom);
                count_vm_event(PGMAJFAULT);
                mem_cgroup_count_vm_event(vma->vm_mm, PGMAJFAULT);
                ret = VM_FAULT_MAJOR;
index 7489884..cca80d9 100644 (file)
@@ -1278,64 +1278,90 @@ out:
 int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                                unsigned long addr, pmd_t pmd, pmd_t *pmdp)
 {
+       struct anon_vma *anon_vma = NULL;
        struct page *page;
        unsigned long haddr = addr & HPAGE_PMD_MASK;
+       int page_nid = -1, this_nid = numa_node_id();
        int target_nid;
-       int current_nid = -1;
-       bool migrated;
+       bool page_locked;
+       bool migrated = false;
 
        spin_lock(&mm->page_table_lock);
        if (unlikely(!pmd_same(pmd, *pmdp)))
                goto out_unlock;
 
        page = pmd_page(pmd);
-       get_page(page);
-       current_nid = page_to_nid(page);
+       page_nid = page_to_nid(page);
        count_vm_numa_event(NUMA_HINT_FAULTS);
-       if (current_nid == numa_node_id())
+       if (page_nid == this_nid)
                count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);
 
+       /*
+        * Acquire the page lock to serialise THP migrations but avoid dropping
+        * page_table_lock if at all possible
+        */
+       page_locked = trylock_page(page);
        target_nid = mpol_misplaced(page, vma, haddr);
        if (target_nid == -1) {
-               put_page(page);
-               goto clear_pmdnuma;
+               /* If the page was locked, there are no parallel migrations */
+               if (page_locked)
+                       goto clear_pmdnuma;
+
+               /*
+                * Otherwise wait for potential migrations and retry. We do
+                * relock and check_same as the page may no longer be mapped.
+                * As the fault is being retried, do not account for it.
+                */
+               spin_unlock(&mm->page_table_lock);
+               wait_on_page_locked(page);
+               page_nid = -1;
+               goto out;
        }
 
-       /* Acquire the page lock to serialise THP migrations */
+       /* Page is misplaced, serialise migrations and parallel THP splits */
+       get_page(page);
        spin_unlock(&mm->page_table_lock);
-       lock_page(page);
+       if (!page_locked)
+               lock_page(page);
+       anon_vma = page_lock_anon_vma_read(page);
 
        /* Confirm the PTE did not while locked */
        spin_lock(&mm->page_table_lock);
        if (unlikely(!pmd_same(pmd, *pmdp))) {
                unlock_page(page);
                put_page(page);
+               page_nid = -1;
                goto out_unlock;
        }
-       spin_unlock(&mm->page_table_lock);
 
-       /* Migrate the THP to the requested node */
+       /*
+        * Migrate the THP to the requested node, returns with page unlocked
+        * and pmd_numa cleared.
+        */
+       spin_unlock(&mm->page_table_lock);
        migrated = migrate_misplaced_transhuge_page(mm, vma,
                                pmdp, pmd, addr, page, target_nid);
-       if (!migrated)
-               goto check_same;
+       if (migrated)
+               page_nid = target_nid;
 
-       task_numa_fault(target_nid, HPAGE_PMD_NR, true);
-       return 0;
-
-check_same:
-       spin_lock(&mm->page_table_lock);
-       if (unlikely(!pmd_same(pmd, *pmdp)))
-               goto out_unlock;
+       goto out;
 clear_pmdnuma:
+       BUG_ON(!PageLocked(page));
        pmd = pmd_mknonnuma(pmd);
        set_pmd_at(mm, haddr, pmdp, pmd);
        VM_BUG_ON(pmd_numa(*pmdp));
        update_mmu_cache_pmd(vma, addr, pmdp);
+       unlock_page(page);
 out_unlock:
        spin_unlock(&mm->page_table_lock);
-       if (current_nid != -1)
-               task_numa_fault(current_nid, HPAGE_PMD_NR, false);
+
+out:
+       if (anon_vma)
+               page_unlock_anon_vma_read(anon_vma);
+
+       if (page_nid != -1)
+               task_numa_fault(page_nid, HPAGE_PMD_NR, migrated);
+
        return 0;
 }
 
@@ -2697,6 +2723,7 @@ void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address,
 
        mmun_start = haddr;
        mmun_end   = haddr + HPAGE_PMD_SIZE;
+again:
        mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
        spin_lock(&mm->page_table_lock);
        if (unlikely(!pmd_trans_huge(*pmd))) {
@@ -2719,7 +2746,14 @@ void __split_huge_page_pmd(struct vm_area_struct *vma, unsigned long address,
        split_huge_page(page);
 
        put_page(page);
-       BUG_ON(pmd_trans_huge(*pmd));
+
+       /*
+        * We don't always have down_write of mmap_sem here: a racing
+        * do_huge_pmd_wp_page() might have copied-on-write to another
+        * huge page before our split_huge_page() got the anon_vma lock.
+        */
+       if (unlikely(pmd_trans_huge(*pmd)))
+               goto again;
 }
 
 void split_huge_page_pmd_mm(struct mm_struct *mm, unsigned long address,
index b49579c..0b7656e 100644 (file)
@@ -653,6 +653,7 @@ static void free_huge_page(struct page *page)
        BUG_ON(page_count(page));
        BUG_ON(page_mapcount(page));
        restore_reserve = PagePrivate(page);
+       ClearPagePrivate(page);
 
        spin_lock(&hugetlb_lock);
        hugetlb_cgroup_uncharge_page(hstate_index(h),
@@ -695,8 +696,22 @@ static void prep_compound_gigantic_page(struct page *page, unsigned long order)
        /* we rely on prep_new_huge_page to set the destructor */
        set_compound_order(page, order);
        __SetPageHead(page);
+       __ClearPageReserved(page);
        for (i = 1; i < nr_pages; i++, p = mem_map_next(p, page, i)) {
                __SetPageTail(p);
+               /*
+                * For gigantic hugepages allocated through bootmem at
+                * boot, it's safer to be consistent with the not-gigantic
+                * hugepages and clear the PG_reserved bit from all tail pages
+                * too.  Otherwse drivers using get_user_pages() to access tail
+                * pages may get the reference counting wrong if they see
+                * PG_reserved set on a tail page (despite the head page not
+                * having PG_reserved set).  Enforcing this consistency between
+                * head and tail pages allows drivers to optimize away a check
+                * on the head page when they need know if put_page() is needed
+                * after get_user_pages().
+                */
+               __ClearPageReserved(p);
                set_page_count(p, 0);
                p->first_page = page;
        }
@@ -1329,9 +1344,9 @@ static void __init gather_bootmem_prealloc(void)
 #else
                page = virt_to_page(m);
 #endif
-               __ClearPageReserved(page);
                WARN_ON(page_count(page) != 1);
                prep_compound_huge_page(page, h->order);
+               WARN_ON(PageReserved(page));
                prep_new_huge_page(h, page, page_to_nid(page));
                /*
                 * If we had gigantic hugepages allocated at boot time, we need
index afc2daa..4c84678 100644 (file)
@@ -20,8 +20,6 @@ static int hwpoison_inject(void *data, u64 val)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       if (!hwpoison_filter_enable)
-               goto inject;
        if (!pfn_valid(pfn))
                return -ENXIO;
 
@@ -33,6 +31,9 @@ static int hwpoison_inject(void *data, u64 val)
        if (!get_page_unless_zero(hpage))
                return 0;
 
+       if (!hwpoison_filter_enable)
+               goto inject;
+
        if (!PageLRU(p) && !PageHuge(p))
                shake_page(p, 0);
        /*
index 7246791..72f9dec 100644 (file)
@@ -81,8 +81,9 @@ restart:
                 * decrement nr_to_walk first so that we don't livelock if we
                 * get stuck on large numbesr of LRU_RETRY items
                 */
-               if (--(*nr_to_walk) == 0)
+               if (!*nr_to_walk)
                        break;
+               --*nr_to_walk;
 
                ret = isolate(item, &nlru->lock, cb_arg);
                switch (ret) {
index 6975bc8..539eeb9 100644 (file)
@@ -343,10 +343,11 @@ static long madvise_remove(struct vm_area_struct *vma,
  */
 static int madvise_hwpoison(int bhv, unsigned long start, unsigned long end)
 {
+       struct page *p;
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
-       for (; start < end; start += PAGE_SIZE) {
-               struct page *p;
+       for (; start < end; start += PAGE_SIZE <<
+                               compound_order(compound_head(p))) {
                int ret;
 
                ret = get_user_pages_fast(start, 1, 0, &p);
index d5ff3ce..665dcd7 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/limits.h>
 #include <linux/export.h>
 #include <linux/mutex.h>
+#include <linux/rbtree.h>
 #include <linux/slab.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
@@ -53,6 +54,7 @@
 #include <linux/page_cgroup.h>
 #include <linux/cpu.h>
 #include <linux/oom.h>
+#include <linux/lockdep.h>
 #include "internal.h"
 #include <net/sock.h>
 #include <net/ip.h>
@@ -160,6 +162,10 @@ struct mem_cgroup_per_zone {
 
        struct mem_cgroup_reclaim_iter reclaim_iter[DEF_PRIORITY + 1];
 
+       struct rb_node          tree_node;      /* RB tree node */
+       unsigned long long      usage_in_excess;/* Set to the value by which */
+                                               /* the soft limit is exceeded*/
+       bool                    on_tree;
        struct mem_cgroup       *memcg;         /* Back pointer, we cannot */
                                                /* use container_of        */
 };
@@ -168,6 +174,26 @@ struct mem_cgroup_per_node {
        struct mem_cgroup_per_zone zoneinfo[MAX_NR_ZONES];
 };
 
+/*
+ * Cgroups above their limits are maintained in a RB-Tree, independent of
+ * their hierarchy representation
+ */
+
+struct mem_cgroup_tree_per_zone {
+       struct rb_root rb_root;
+       spinlock_t lock;
+};
+
+struct mem_cgroup_tree_per_node {
+       struct mem_cgroup_tree_per_zone rb_tree_per_zone[MAX_NR_ZONES];
+};
+
+struct mem_cgroup_tree {
+       struct mem_cgroup_tree_per_node *rb_tree_per_node[MAX_NUMNODES];
+};
+
+static struct mem_cgroup_tree soft_limit_tree __read_mostly;
+
 struct mem_cgroup_threshold {
        struct eventfd_ctx *eventfd;
        u64 threshold;
@@ -286,7 +312,7 @@ struct mem_cgroup {
 
        atomic_t        dead_count;
 #if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_INET)
-       struct tcp_memcontrol tcp_mem;
+       struct cg_proto tcp_mem;
 #endif
 #if defined(CONFIG_MEMCG_KMEM)
        /* analogous to slab_common's slab_caches list. per-memcg */
@@ -303,22 +329,6 @@ struct mem_cgroup {
        atomic_t        numainfo_events;
        atomic_t        numainfo_updating;
 #endif
-       /*
-        * Protects soft_contributed transitions.
-        * See mem_cgroup_update_soft_limit
-        */
-       spinlock_t soft_lock;
-
-       /*
-        * If true then this group has increased parents' children_in_excess
-        * when it got over the soft limit.
-        * When a group falls bellow the soft limit, parents' children_in_excess
-        * is decreased and soft_contributed changed to false.
-        */
-       bool soft_contributed;
-
-       /* Number of children that are in soft limit excess */
-       atomic_t children_in_excess;
 
        struct mem_cgroup_per_node *nodeinfo[0];
        /* WARNING: nodeinfo must be the last member here */
@@ -422,6 +432,7 @@ static bool move_file(void)
  * limit reclaim to prevent infinite loops, if they ever occur.
  */
 #define        MEM_CGROUP_MAX_RECLAIM_LOOPS            100
+#define        MEM_CGROUP_MAX_SOFT_LIMIT_RECLAIM_LOOPS 2
 
 enum charge_type {
        MEM_CGROUP_CHARGE_TYPE_CACHE = 0,
@@ -540,13 +551,13 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
        if (!memcg || mem_cgroup_is_root(memcg))
                return NULL;
 
-       return &memcg->tcp_mem.cg_proto;
+       return &memcg->tcp_mem;
 }
 EXPORT_SYMBOL(tcp_proto_cgroup);
 
 static void disarm_sock_keys(struct mem_cgroup *memcg)
 {
-       if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto))
+       if (!memcg_proto_activated(&memcg->tcp_mem))
                return;
        static_key_slow_dec(&memcg_socket_limit_enabled);
 }
@@ -648,6 +659,164 @@ page_cgroup_zoneinfo(struct mem_cgroup *memcg, struct page *page)
        return mem_cgroup_zoneinfo(memcg, nid, zid);
 }
 
+static struct mem_cgroup_tree_per_zone *
+soft_limit_tree_node_zone(int nid, int zid)
+{
+       return &soft_limit_tree.rb_tree_per_node[nid]->rb_tree_per_zone[zid];
+}
+
+static struct mem_cgroup_tree_per_zone *
+soft_limit_tree_from_page(struct page *page)
+{
+       int nid = page_to_nid(page);
+       int zid = page_zonenum(page);
+
+       return &soft_limit_tree.rb_tree_per_node[nid]->rb_tree_per_zone[zid];
+}
+
+static void
+__mem_cgroup_insert_exceeded(struct mem_cgroup *memcg,
+                               struct mem_cgroup_per_zone *mz,
+                               struct mem_cgroup_tree_per_zone *mctz,
+                               unsigned long long new_usage_in_excess)
+{
+       struct rb_node **p = &mctz->rb_root.rb_node;
+       struct rb_node *parent = NULL;
+       struct mem_cgroup_per_zone *mz_node;
+
+       if (mz->on_tree)
+               return;
+
+       mz->usage_in_excess = new_usage_in_excess;
+       if (!mz->usage_in_excess)
+               return;
+       while (*p) {
+               parent = *p;
+               mz_node = rb_entry(parent, struct mem_cgroup_per_zone,
+                                       tree_node);
+               if (mz->usage_in_excess < mz_node->usage_in_excess)
+                       p = &(*p)->rb_left;
+               /*
+                * We can't avoid mem cgroups that are over their soft
+                * limit by the same amount
+                */
+               else if (mz->usage_in_excess >= mz_node->usage_in_excess)
+                       p = &(*p)->rb_right;
+       }
+       rb_link_node(&mz->tree_node, parent, p);
+       rb_insert_color(&mz->tree_node, &mctz->rb_root);
+       mz->on_tree = true;
+}
+
+static void
+__mem_cgroup_remove_exceeded(struct mem_cgroup *memcg,
+                               struct mem_cgroup_per_zone *mz,
+                               struct mem_cgroup_tree_per_zone *mctz)
+{
+       if (!mz->on_tree)
+               return;
+       rb_erase(&mz->tree_node, &mctz->rb_root);
+       mz->on_tree = false;
+}
+
+static void
+mem_cgroup_remove_exceeded(struct mem_cgroup *memcg,
+                               struct mem_cgroup_per_zone *mz,
+                               struct mem_cgroup_tree_per_zone *mctz)
+{
+       spin_lock(&mctz->lock);
+       __mem_cgroup_remove_exceeded(memcg, mz, mctz);
+       spin_unlock(&mctz->lock);
+}
+
+
+static void mem_cgroup_update_tree(struct mem_cgroup *memcg, struct page *page)
+{
+       unsigned long long excess;
+       struct mem_cgroup_per_zone *mz;
+       struct mem_cgroup_tree_per_zone *mctz;
+       int nid = page_to_nid(page);
+       int zid = page_zonenum(page);
+       mctz = soft_limit_tree_from_page(page);
+
+       /*
+        * Necessary to update all ancestors when hierarchy is used.
+        * because their event counter is not touched.
+        */
+       for (; memcg; memcg = parent_mem_cgroup(memcg)) {
+               mz = mem_cgroup_zoneinfo(memcg, nid, zid);
+               excess = res_counter_soft_limit_excess(&memcg->res);
+               /*
+                * We have to update the tree if mz is on RB-tree or
+                * mem is over its softlimit.
+                */
+               if (excess || mz->on_tree) {
+                       spin_lock(&mctz->lock);
+                       /* if on-tree, remove it */
+                       if (mz->on_tree)
+                               __mem_cgroup_remove_exceeded(memcg, mz, mctz);
+                       /*
+                        * Insert again. mz->usage_in_excess will be updated.
+                        * If excess is 0, no tree ops.
+                        */
+                       __mem_cgroup_insert_exceeded(memcg, mz, mctz, excess);
+                       spin_unlock(&mctz->lock);
+               }
+       }
+}
+
+static void mem_cgroup_remove_from_trees(struct mem_cgroup *memcg)
+{
+       int node, zone;
+       struct mem_cgroup_per_zone *mz;
+       struct mem_cgroup_tree_per_zone *mctz;
+
+       for_each_node(node) {
+               for (zone = 0; zone < MAX_NR_ZONES; zone++) {
+                       mz = mem_cgroup_zoneinfo(memcg, node, zone);
+                       mctz = soft_limit_tree_node_zone(node, zone);
+                       mem_cgroup_remove_exceeded(memcg, mz, mctz);
+               }
+       }
+}
+
+static struct mem_cgroup_per_zone *
+__mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_zone *mctz)
+{
+       struct rb_node *rightmost = NULL;
+       struct mem_cgroup_per_zone *mz;
+
+retry:
+       mz = NULL;
+       rightmost = rb_last(&mctz->rb_root);
+       if (!rightmost)
+               goto done;              /* Nothing to reclaim from */
+
+       mz = rb_entry(rightmost, struct mem_cgroup_per_zone, tree_node);
+       /*
+        * Remove the node now but someone else can add it back,
+        * we will to add it back at the end of reclaim to its correct
+        * position in the tree.
+        */
+       __mem_cgroup_remove_exceeded(mz->memcg, mz, mctz);
+       if (!res_counter_soft_limit_excess(&mz->memcg->res) ||
+               !css_tryget(&mz->memcg->css))
+               goto retry;
+done:
+       return mz;
+}
+
+static struct mem_cgroup_per_zone *
+mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_zone *mctz)
+{
+       struct mem_cgroup_per_zone *mz;
+
+       spin_lock(&mctz->lock);
+       mz = __mem_cgroup_largest_soft_limit_node(mctz);
+       spin_unlock(&mctz->lock);
+       return mz;
+}
+
 /*
  * Implementation Note: reading percpu statistics for memcg.
  *
@@ -698,6 +867,7 @@ static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
        unsigned long val = 0;
        int cpu;
 
+       get_online_cpus();
        for_each_online_cpu(cpu)
                val += per_cpu(memcg->stat->events[idx], cpu);
 #ifdef CONFIG_HOTPLUG_CPU
@@ -705,6 +875,7 @@ static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
        val += memcg->nocpu_base.events[idx];
        spin_unlock(&memcg->pcp_counter_lock);
 #endif
+       put_online_cpus();
        return val;
 }
 
@@ -821,48 +992,6 @@ static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg,
        return false;
 }
 
-/*
- * Called from rate-limited memcg_check_events when enough
- * MEM_CGROUP_TARGET_SOFTLIMIT events are accumulated and it makes sure
- * that all the parents up the hierarchy will be notified that this group
- * is in excess or that it is not in excess anymore. mmecg->soft_contributed
- * makes the transition a single action whenever the state flips from one to
- * the other.
- */
-static void mem_cgroup_update_soft_limit(struct mem_cgroup *memcg)
-{
-       unsigned long long excess = res_counter_soft_limit_excess(&memcg->res);
-       struct mem_cgroup *parent = memcg;
-       int delta = 0;
-
-       spin_lock(&memcg->soft_lock);
-       if (excess) {
-               if (!memcg->soft_contributed) {
-                       delta = 1;
-                       memcg->soft_contributed = true;
-               }
-       } else {
-               if (memcg->soft_contributed) {
-                       delta = -1;
-                       memcg->soft_contributed = false;
-               }
-       }
-
-       /*
-        * Necessary to update all ancestors when hierarchy is used
-        * because their event counter is not touched.
-        * We track children even outside the hierarchy for the root
-        * cgroup because tree walk starting at root should visit
-        * all cgroups and we want to prevent from pointless tree
-        * walk if no children is below the limit.
-        */
-       while (delta && (parent = parent_mem_cgroup(parent)))
-               atomic_add(delta, &parent->children_in_excess);
-       if (memcg != root_mem_cgroup && !root_mem_cgroup->use_hierarchy)
-               atomic_add(delta, &root_mem_cgroup->children_in_excess);
-       spin_unlock(&memcg->soft_lock);
-}
-
 /*
  * Check events in order.
  *
@@ -886,7 +1015,7 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page)
 
                mem_cgroup_threshold(memcg);
                if (unlikely(do_softlimit))
-                       mem_cgroup_update_soft_limit(memcg);
+                       mem_cgroup_update_tree(memcg, page);
 #if MAX_NUMNODES > 1
                if (unlikely(do_numainfo))
                        atomic_inc(&memcg->numainfo_events);
@@ -929,15 +1058,6 @@ struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm)
        return memcg;
 }
 
-static enum mem_cgroup_filter_t
-mem_cgroup_filter(struct mem_cgroup *memcg, struct mem_cgroup *root,
-               mem_cgroup_iter_filter cond)
-{
-       if (!cond)
-               return VISIT;
-       return cond(memcg, root);
-}
-
 /*
  * Returns a next (in a pre-order walk) alive memcg (with elevated css
  * ref. count) or NULL if the whole root's subtree has been visited.
@@ -945,7 +1065,7 @@ mem_cgroup_filter(struct mem_cgroup *memcg, struct mem_cgroup *root,
  * helper function to be used by mem_cgroup_iter
  */
 static struct mem_cgroup *__mem_cgroup_iter_next(struct mem_cgroup *root,
-               struct mem_cgroup *last_visited, mem_cgroup_iter_filter cond)
+               struct mem_cgroup *last_visited)
 {
        struct cgroup_subsys_state *prev_css, *next_css;
 
@@ -963,31 +1083,11 @@ skip_node:
        if (next_css) {
                struct mem_cgroup *mem = mem_cgroup_from_css(next_css);
 
-               switch (mem_cgroup_filter(mem, root, cond)) {
-               case SKIP:
+               if (css_tryget(&mem->css))
+                       return mem;
+               else {
                        prev_css = next_css;
                        goto skip_node;
-               case SKIP_TREE:
-                       if (mem == root)
-                               return NULL;
-                       /*
-                        * css_rightmost_descendant is not an optimal way to
-                        * skip through a subtree (especially for imbalanced
-                        * trees leaning to right) but that's what we have right
-                        * now. More effective solution would be traversing
-                        * right-up for first non-NULL without calling
-                        * css_next_descendant_pre afterwards.
-                        */
-                       prev_css = css_rightmost_descendant(next_css);
-                       goto skip_node;
-               case VISIT:
-                       if (css_tryget(&mem->css))
-                               return mem;
-                       else {
-                               prev_css = next_css;
-                               goto skip_node;
-                       }
-                       break;
                }
        }
 
@@ -1051,7 +1151,6 @@ static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter,
  * @root: hierarchy root
  * @prev: previously returned memcg, NULL on first invocation
  * @reclaim: cookie for shared reclaim walks, NULL for full walks
- * @cond: filter for visited nodes, NULL for no filter
  *
  * Returns references to children of the hierarchy below @root, or
  * @root itself, or %NULL after a full round-trip.
@@ -1064,18 +1163,15 @@ static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter,
  * divide up the memcgs in the hierarchy among all concurrent
  * reclaimers operating on the same zone and priority.
  */
-struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root,
+struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
                                   struct mem_cgroup *prev,
-                                  struct mem_cgroup_reclaim_cookie *reclaim,
-                                  mem_cgroup_iter_filter cond)
+                                  struct mem_cgroup_reclaim_cookie *reclaim)
 {
        struct mem_cgroup *memcg = NULL;
        struct mem_cgroup *last_visited = NULL;
 
-       if (mem_cgroup_disabled()) {
-               /* first call must return non-NULL, second return NULL */
-               return (struct mem_cgroup *)(unsigned long)!prev;
-       }
+       if (mem_cgroup_disabled())
+               return NULL;
 
        if (!root)
                root = root_mem_cgroup;
@@ -1086,9 +1182,7 @@ struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root,
        if (!root->use_hierarchy && root != root_mem_cgroup) {
                if (prev)
                        goto out_css_put;
-               if (mem_cgroup_filter(root, root, cond) == VISIT)
-                       return root;
-               return NULL;
+               return root;
        }
 
        rcu_read_lock();
@@ -1111,7 +1205,7 @@ struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root,
                        last_visited = mem_cgroup_iter_load(iter, root, &seq);
                }
 
-               memcg = __mem_cgroup_iter_next(root, last_visited, cond);
+               memcg = __mem_cgroup_iter_next(root, last_visited);
 
                if (reclaim) {
                        mem_cgroup_iter_update(iter, last_visited, memcg, seq);
@@ -1122,11 +1216,7 @@ struct mem_cgroup *mem_cgroup_iter_cond(struct mem_cgroup *root,
                                reclaim->generation = iter->generation;
                }
 
-               /*
-                * We have finished the whole tree walk or no group has been
-                * visited because filter told us to skip the root node.
-                */
-               if (!memcg && (prev || (cond && !last_visited)))
+               if (prev && !memcg)
                        goto out_unlock;
        }
 out_unlock:
@@ -1767,7 +1857,6 @@ static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg,
        return total;
 }
 
-#if MAX_NUMNODES > 1
 /**
  * test_mem_cgroup_node_reclaimable
  * @memcg: the target memcg
@@ -1790,6 +1879,7 @@ static bool test_mem_cgroup_node_reclaimable(struct mem_cgroup *memcg,
        return false;
 
 }
+#if MAX_NUMNODES > 1
 
 /*
  * Always updating the nodemask is not very good - even if we have an empty
@@ -1857,52 +1947,112 @@ int mem_cgroup_select_victim_node(struct mem_cgroup *memcg)
        return node;
 }
 
+/*
+ * Check all nodes whether it contains reclaimable pages or not.
+ * For quick scan, we make use of scan_nodes. This will allow us to skip
+ * unused nodes. But scan_nodes is lazily updated and may not cotain
+ * enough new information. We need to do double check.
+ */
+static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
+{
+       int nid;
+
+       /*
+        * quick check...making use of scan_node.
+        * We can skip unused nodes.
+        */
+       if (!nodes_empty(memcg->scan_nodes)) {
+               for (nid = first_node(memcg->scan_nodes);
+                    nid < MAX_NUMNODES;
+                    nid = next_node(nid, memcg->scan_nodes)) {
+
+                       if (test_mem_cgroup_node_reclaimable(memcg, nid, noswap))
+                               return true;
+               }
+       }
+       /*
+        * Check rest of nodes.
+        */
+       for_each_node_state(nid, N_MEMORY) {
+               if (node_isset(nid, memcg->scan_nodes))
+                       continue;
+               if (test_mem_cgroup_node_reclaimable(memcg, nid, noswap))
+                       return true;
+       }
+       return false;
+}
+
 #else
 int mem_cgroup_select_victim_node(struct mem_cgroup *memcg)
 {
        return 0;
 }
 
-#endif
-
-/*
- * A group is eligible for the soft limit reclaim under the given root
- * hierarchy if
- *     a) it is over its soft limit
- *     b) any parent up the hierarchy is over its soft limit
- *
- * If the given group doesn't have any children over the limit then it
- * doesn't make any sense to iterate its subtree.
- */
-enum mem_cgroup_filter_t
-mem_cgroup_soft_reclaim_eligible(struct mem_cgroup *memcg,
-               struct mem_cgroup *root)
+static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
 {
-       struct mem_cgroup *parent;
-
-       if (!memcg)
-               memcg = root_mem_cgroup;
-       parent = memcg;
-
-       if (res_counter_soft_limit_excess(&memcg->res))
-               return VISIT;
+       return test_mem_cgroup_node_reclaimable(memcg, 0, noswap);
+}
+#endif
 
-       /*
-        * If any parent up to the root in the hierarchy is over its soft limit
-        * then we have to obey and reclaim from this group as well.
-        */
-       while ((parent = parent_mem_cgroup(parent))) {
-               if (res_counter_soft_limit_excess(&parent->res))
-                       return VISIT;
-               if (parent == root)
+static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,
+                                  struct zone *zone,
+                                  gfp_t gfp_mask,
+                                  unsigned long *total_scanned)
+{
+       struct mem_cgroup *victim = NULL;
+       int total = 0;
+       int loop = 0;
+       unsigned long excess;
+       unsigned long nr_scanned;
+       struct mem_cgroup_reclaim_cookie reclaim = {
+               .zone = zone,
+               .priority = 0,
+       };
+
+       excess = res_counter_soft_limit_excess(&root_memcg->res) >> PAGE_SHIFT;
+
+       while (1) {
+               victim = mem_cgroup_iter(root_memcg, victim, &reclaim);
+               if (!victim) {
+                       loop++;
+                       if (loop >= 2) {
+                               /*
+                                * If we have not been able to reclaim
+                                * anything, it might because there are
+                                * no reclaimable pages under this hierarchy
+                                */
+                               if (!total)
+                                       break;
+                               /*
+                                * We want to do more targeted reclaim.
+                                * excess >> 2 is not to excessive so as to
+                                * reclaim too much, nor too less that we keep
+                                * coming back to reclaim from this cgroup
+                                */
+                               if (total >= (excess >> 2) ||
+                                       (loop > MEM_CGROUP_MAX_RECLAIM_LOOPS))
+                                       break;
+                       }
+                       continue;
+               }
+               if (!mem_cgroup_reclaimable(victim, false))
+                       continue;
+               total += mem_cgroup_shrink_node_zone(victim, gfp_mask, false,
+                                                    zone, &nr_scanned);
+               *total_scanned += nr_scanned;
+               if (!res_counter_soft_limit_excess(&root_memcg->res))
                        break;
        }
-
-       if (!atomic_read(&memcg->children_in_excess))
-               return SKIP_TREE;
-       return SKIP;
+       mem_cgroup_iter_break(root_memcg, victim);
+       return total;
 }
 
+#ifdef CONFIG_LOCKDEP
+static struct lockdep_map memcg_oom_lock_dep_map = {
+       .name = "memcg_oom_lock",
+};
+#endif
+
 static DEFINE_SPINLOCK(memcg_oom_lock);
 
 /*
@@ -1940,7 +2090,8 @@ static bool mem_cgroup_oom_trylock(struct mem_cgroup *memcg)
                        }
                        iter->oom_lock = false;
                }
-       }
+       } else
+               mutex_acquire(&memcg_oom_lock_dep_map, 0, 1, _RET_IP_);
 
        spin_unlock(&memcg_oom_lock);
 
@@ -1952,6 +2103,7 @@ static void mem_cgroup_oom_unlock(struct mem_cgroup *memcg)
        struct mem_cgroup *iter;
 
        spin_lock(&memcg_oom_lock);
+       mutex_release(&memcg_oom_lock_dep_map, 1, _RET_IP_);
        for_each_mem_cgroup_tree(iter, memcg)
                iter->oom_lock = false;
        spin_unlock(&memcg_oom_lock);
@@ -2018,110 +2170,59 @@ static void memcg_oom_recover(struct mem_cgroup *memcg)
                memcg_wakeup_oom(memcg);
 }
 
-/*
- * try to call OOM killer
- */
 static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order)
 {
-       bool locked;
-       int wakeups;
-
        if (!current->memcg_oom.may_oom)
                return;
-
-       current->memcg_oom.in_memcg_oom = 1;
-
        /*
-        * As with any blocking lock, a contender needs to start
-        * listening for wakeups before attempting the trylock,
-        * otherwise it can miss the wakeup from the unlock and sleep
-        * indefinitely.  This is just open-coded because our locking
-        * is so particular to memcg hierarchies.
+        * We are in the middle of the charge context here, so we
+        * don't want to block when potentially sitting on a callstack
+        * that holds all kinds of filesystem and mm locks.
+        *
+        * Also, the caller may handle a failed allocation gracefully
+        * (like optional page cache readahead) and so an OOM killer
+        * invocation might not even be necessary.
+        *
+        * That's why we don't do anything here except remember the
+        * OOM context and then deal with it at the end of the page
+        * fault when the stack is unwound, the locks are released,
+        * and when we know whether the fault was overall successful.
         */
-       wakeups = atomic_read(&memcg->oom_wakeups);
-       mem_cgroup_mark_under_oom(memcg);
-
-       locked = mem_cgroup_oom_trylock(memcg);
-
-       if (locked)
-               mem_cgroup_oom_notify(memcg);
-
-       if (locked && !memcg->oom_kill_disable) {
-               mem_cgroup_unmark_under_oom(memcg);
-               mem_cgroup_out_of_memory(memcg, mask, order);
-               mem_cgroup_oom_unlock(memcg);
-               /*
-                * There is no guarantee that an OOM-lock contender
-                * sees the wakeups triggered by the OOM kill
-                * uncharges.  Wake any sleepers explicitely.
-                */
-               memcg_oom_recover(memcg);
-       } else {
-               /*
-                * A system call can just return -ENOMEM, but if this
-                * is a page fault and somebody else is handling the
-                * OOM already, we need to sleep on the OOM waitqueue
-                * for this memcg until the situation is resolved.
-                * Which can take some time because it might be
-                * handled by a userspace task.
-                *
-                * However, this is the charge context, which means
-                * that we may sit on a large call stack and hold
-                * various filesystem locks, the mmap_sem etc. and we
-                * don't want the OOM handler to deadlock on them
-                * while we sit here and wait.  Store the current OOM
-                * context in the task_struct, then return -ENOMEM.
-                * At the end of the page fault handler, with the
-                * stack unwound, pagefault_out_of_memory() will check
-                * back with us by calling
-                * mem_cgroup_oom_synchronize(), possibly putting the
-                * task to sleep.
-                */
-               current->memcg_oom.oom_locked = locked;
-               current->memcg_oom.wakeups = wakeups;
-               css_get(&memcg->css);
-               current->memcg_oom.wait_on_memcg = memcg;
-       }
+       css_get(&memcg->css);
+       current->memcg_oom.memcg = memcg;
+       current->memcg_oom.gfp_mask = mask;
+       current->memcg_oom.order = order;
 }
 
 /**
  * mem_cgroup_oom_synchronize - complete memcg OOM handling
+ * @handle: actually kill/wait or just clean up the OOM state
  *
- * This has to be called at the end of a page fault if the the memcg
- * OOM handler was enabled and the fault is returning %VM_FAULT_OOM.
+ * This has to be called at the end of a page fault if the memcg OOM
+ * handler was enabled.
  *
- * Memcg supports userspace OOM handling, so failed allocations must
+ * Memcg supports userspace OOM handling where failed allocations must
  * sleep on a waitqueue until the userspace task resolves the
  * situation.  Sleeping directly in the charge context with all kinds
  * of locks held is not a good idea, instead we remember an OOM state
  * in the task and mem_cgroup_oom_synchronize() has to be called at
- * the end of the page fault to put the task to sleep and clean up the
- * OOM state.
+ * the end of the page fault to complete the OOM handling.
  *
  * Returns %true if an ongoing memcg OOM situation was detected and
- * finalized, %false otherwise.
+ * completed, %false otherwise.
  */
-bool mem_cgroup_oom_synchronize(void)
+bool mem_cgroup_oom_synchronize(bool handle)
 {
+       struct mem_cgroup *memcg = current->memcg_oom.memcg;
        struct oom_wait_info owait;
-       struct mem_cgroup *memcg;
+       bool locked;
 
        /* OOM is global, do not handle */
-       if (!current->memcg_oom.in_memcg_oom)
-               return false;
-
-       /*
-        * We invoked the OOM killer but there is a chance that a kill
-        * did not free up any charges.  Everybody else might already
-        * be sleeping, so restart the fault and keep the rampage
-        * going until some charges are released.
-        */
-       memcg = current->memcg_oom.wait_on_memcg;
        if (!memcg)
-               goto out;
+               return false;
 
-       if (test_thread_flag(TIF_MEMDIE) || fatal_signal_pending(current))
-               goto out_memcg;
+       if (!handle)
+               goto cleanup;
 
        owait.memcg = memcg;
        owait.wait.flags = 0;
@@ -2130,13 +2231,25 @@ bool mem_cgroup_oom_synchronize(void)
        INIT_LIST_HEAD(&owait.wait.task_list);
 
        prepare_to_wait(&memcg_oom_waitq, &owait.wait, TASK_KILLABLE);
-       /* Only sleep if we didn't miss any wakeups since OOM */
-       if (atomic_read(&memcg->oom_wakeups) == current->memcg_oom.wakeups)
+       mem_cgroup_mark_under_oom(memcg);
+
+       locked = mem_cgroup_oom_trylock(memcg);
+
+       if (locked)
+               mem_cgroup_oom_notify(memcg);
+
+       if (locked && !memcg->oom_kill_disable) {
+               mem_cgroup_unmark_under_oom(memcg);
+               finish_wait(&memcg_oom_waitq, &owait.wait);
+               mem_cgroup_out_of_memory(memcg, current->memcg_oom.gfp_mask,
+                                        current->memcg_oom.order);
+       } else {
                schedule();
-       finish_wait(&memcg_oom_waitq, &owait.wait);
-out_memcg:
-       mem_cgroup_unmark_under_oom(memcg);
-       if (current->memcg_oom.oom_locked) {
+               mem_cgroup_unmark_under_oom(memcg);
+               finish_wait(&memcg_oom_waitq, &owait.wait);
+       }
+
+       if (locked) {
                mem_cgroup_oom_unlock(memcg);
                /*
                 * There is no guarantee that an OOM-lock contender
@@ -2145,10 +2258,9 @@ out_memcg:
                 */
                memcg_oom_recover(memcg);
        }
+cleanup:
+       current->memcg_oom.memcg = NULL;
        css_put(&memcg->css);
-       current->memcg_oom.wait_on_memcg = NULL;
-out:
-       current->memcg_oom.in_memcg_oom = 0;
        return true;
 }
 
@@ -2562,6 +2674,9 @@ static int __mem_cgroup_try_charge(struct mm_struct *mm,
                     || fatal_signal_pending(current)))
                goto bypass;
 
+       if (unlikely(task_in_memcg_oom(current)))
+               goto bypass;
+
        /*
         * We always charge the cgroup the mm_struct belongs to.
         * The mm_struct's mem_cgroup changes on task migration if the
@@ -2659,8 +2774,10 @@ done:
        *ptr = memcg;
        return 0;
 nomem:
-       *ptr = NULL;
-       return -ENOMEM;
+       if (!(gfp_mask & __GFP_NOFAIL)) {
+               *ptr = NULL;
+               return -ENOMEM;
+       }
 bypass:
        *ptr = root_mem_cgroup;
        return -EINTR;
@@ -2812,7 +2929,9 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
        unlock_page_cgroup(pc);
 
        /*
-        * "charge_statistics" updated event counter.
+        * "charge_statistics" updated event counter. Then, check it.
+        * Insert ancestor (and ancestor's ancestors), to softlimit RB-tree.
+        * if they exceeds softlimit.
         */
        memcg_check_events(memcg, page);
 }
@@ -3663,8 +3782,7 @@ void mem_cgroup_move_account_page_stat(struct mem_cgroup *from,
 {
        /* Update stat data for mem_cgroup */
        preempt_disable();
-       WARN_ON_ONCE(from->stat->count[idx] < nr_pages);
-       __this_cpu_add(from->stat->count[idx], -nr_pages);
+       __this_cpu_sub(from->stat->count[idx], nr_pages);
        __this_cpu_add(to->stat->count[idx], nr_pages);
        preempt_enable();
 }
@@ -4647,6 +4765,98 @@ static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
        return ret;
 }
 
+unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
+                                           gfp_t gfp_mask,
+                                           unsigned long *total_scanned)
+{
+       unsigned long nr_reclaimed = 0;
+       struct mem_cgroup_per_zone *mz, *next_mz = NULL;
+       unsigned long reclaimed;
+       int loop = 0;
+       struct mem_cgroup_tree_per_zone *mctz;
+       unsigned long long excess;
+       unsigned long nr_scanned;
+
+       if (order > 0)
+               return 0;
+
+       mctz = soft_limit_tree_node_zone(zone_to_nid(zone), zone_idx(zone));
+       /*
+        * This loop can run a while, specially if mem_cgroup's continuously
+        * keep exceeding their soft limit and putting the system under
+        * pressure
+        */
+       do {
+               if (next_mz)
+                       mz = next_mz;
+               else
+                       mz = mem_cgroup_largest_soft_limit_node(mctz);
+               if (!mz)
+                       break;
+
+               nr_scanned = 0;
+               reclaimed = mem_cgroup_soft_reclaim(mz->memcg, zone,
+                                                   gfp_mask, &nr_scanned);
+               nr_reclaimed += reclaimed;
+               *total_scanned += nr_scanned;
+               spin_lock(&mctz->lock);
+
+               /*
+                * If we failed to reclaim anything from this memory cgroup
+                * it is time to move on to the next cgroup
+                */
+               next_mz = NULL;
+               if (!reclaimed) {
+                       do {
+                               /*
+                                * Loop until we find yet another one.
+                                *
+                                * By the time we get the soft_limit lock
+                                * again, someone might have aded the
+                                * group back on the RB tree. Iterate to
+                                * make sure we get a different mem.
+                                * mem_cgroup_largest_soft_limit_node returns
+                                * NULL if no other cgroup is present on
+                                * the tree
+                                */
+                               next_mz =
+                               __mem_cgroup_largest_soft_limit_node(mctz);
+                               if (next_mz == mz)
+                                       css_put(&next_mz->memcg->css);
+                               else /* next_mz == NULL or other memcg */
+                                       break;
+                       } while (1);
+               }
+               __mem_cgroup_remove_exceeded(mz->memcg, mz, mctz);
+               excess = res_counter_soft_limit_excess(&mz->memcg->res);
+               /*
+                * One school of thought says that we should not add
+                * back the node to the tree if reclaim returns 0.
+                * But our reclaim could return 0, simply because due
+                * to priority we are exposing a smaller subset of
+                * memory to reclaim from. Consider this as a longer
+                * term TODO.
+                */
+               /* If excess == 0, no tree ops */
+               __mem_cgroup_insert_exceeded(mz->memcg, mz, mctz, excess);
+               spin_unlock(&mctz->lock);
+               css_put(&mz->memcg->css);
+               loop++;
+               /*
+                * Could not reclaim anything and there are no more
+                * mem cgroups to try or we seem to be looping without
+                * reclaiming anything.
+                */
+               if (!nr_reclaimed &&
+                       (next_mz == NULL ||
+                       loop > MEM_CGROUP_MAX_SOFT_LIMIT_RECLAIM_LOOPS))
+                       break;
+       } while (!nr_reclaimed);
+       if (next_mz)
+               css_put(&next_mz->memcg->css);
+       return nr_reclaimed;
+}
+
 /**
  * mem_cgroup_force_empty_list - clears LRU of a group
  * @memcg: group to clear
@@ -4748,31 +4958,18 @@ static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg)
        } while (usage > 0);
 }
 
-/*
- * This mainly exists for tests during the setting of set of use_hierarchy.
- * Since this is the very setting we are changing, the current hierarchy value
- * is meaningless
- */
-static inline bool __memcg_has_children(struct mem_cgroup *memcg)
-{
-       struct cgroup_subsys_state *pos;
-
-       /* bounce at first found */
-       css_for_each_child(pos, &memcg->css)
-               return true;
-       return false;
-}
-
-/*
- * Must be called with memcg_create_mutex held, unless the cgroup is guaranteed
- * to be already dead (as in mem_cgroup_force_empty, for instance).  This is
- * from mem_cgroup_count_children(), in the sense that we don't really care how
- * many children we have; we only need to know if we have any.  It also counts
- * any memcg without hierarchy as infertile.
- */
 static inline bool memcg_has_children(struct mem_cgroup *memcg)
 {
-       return memcg->use_hierarchy && __memcg_has_children(memcg);
+       lockdep_assert_held(&memcg_create_mutex);
+       /*
+        * The lock does not prevent addition or deletion to the list
+        * of children, but it prevents a new child from being
+        * initialized based on this parent in css_online(), so it's
+        * enough to decide whether hierarchically inherited
+        * attributes can still be changed or not.
+        */
+       return memcg->use_hierarchy &&
+               !list_empty(&memcg->css.cgroup->children);
 }
 
 /*
@@ -4852,7 +5049,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
         */
        if ((!parent_memcg || !parent_memcg->use_hierarchy) &&
                                (val == 1 || val == 0)) {
-               if (!__memcg_has_children(memcg))
+               if (list_empty(&memcg->css.cgroup->children))
                        memcg->use_hierarchy = val;
                else
                        retval = -EBUSY;
@@ -5911,6 +6108,8 @@ static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
        for (zone = 0; zone < MAX_NR_ZONES; zone++) {
                mz = &pn->zoneinfo[zone];
                lruvec_init(&mz->lruvec);
+               mz->usage_in_excess = 0;
+               mz->on_tree = false;
                mz->memcg = memcg;
        }
        memcg->nodeinfo[node] = pn;
@@ -5966,6 +6165,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
        int node;
        size_t size = memcg_size();
 
+       mem_cgroup_remove_from_trees(memcg);
        free_css_id(&mem_cgroup_subsys, &memcg->css);
 
        for_each_node(node)
@@ -6002,6 +6202,29 @@ struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
 }
 EXPORT_SYMBOL(parent_mem_cgroup);
 
+static void __init mem_cgroup_soft_limit_tree_init(void)
+{
+       struct mem_cgroup_tree_per_node *rtpn;
+       struct mem_cgroup_tree_per_zone *rtpz;
+       int tmp, node, zone;
+
+       for_each_node(node) {
+               tmp = node;
+               if (!node_state(node, N_NORMAL_MEMORY))
+                       tmp = -1;
+               rtpn = kzalloc_node(sizeof(*rtpn), GFP_KERNEL, tmp);
+               BUG_ON(!rtpn);
+
+               soft_limit_tree.rb_tree_per_node[node] = rtpn;
+
+               for (zone = 0; zone < MAX_NR_ZONES; zone++) {
+                       rtpz = &rtpn->rb_tree_per_zone[zone];
+                       rtpz->rb_root = RB_ROOT;
+                       spin_lock_init(&rtpz->lock);
+               }
+       }
+}
+
 static struct cgroup_subsys_state * __ref
 mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
 {
@@ -6031,7 +6254,6 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
        mutex_init(&memcg->thresholds_lock);
        spin_lock_init(&memcg->move_lock);
        vmpressure_init(&memcg->vmpressure);
-       spin_lock_init(&memcg->soft_lock);
 
        return &memcg->css;
 
@@ -6109,13 +6331,6 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
 
        mem_cgroup_invalidate_reclaim_iterators(memcg);
        mem_cgroup_reparent_charges(memcg);
-       if (memcg->soft_contributed) {
-               while ((memcg = parent_mem_cgroup(memcg)))
-                       atomic_dec(&memcg->children_in_excess);
-
-               if (memcg != root_mem_cgroup && !root_mem_cgroup->use_hierarchy)
-                       atomic_dec(&root_mem_cgroup->children_in_excess);
-       }
        mem_cgroup_destroy_all_caches(memcg);
        vmpressure_cleanup(&memcg->vmpressure);
 }
@@ -6790,6 +7005,7 @@ static int __init mem_cgroup_init(void)
 {
        hotcpu_notifier(memcg_cpu_hotplug_callback, 0);
        enable_swap_cgroup();
+       mem_cgroup_soft_limit_tree_init();
        memcg_stock_init();
        return 0;
 }
index 947ed54..bf3351b 100644 (file)
@@ -1114,8 +1114,10 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
                         * shake_page could have turned it free.
                         */
                        if (is_free_buddy_page(p)) {
-                               action_result(pfn, "free buddy, 2nd try",
-                                               DELAYED);
+                               if (flags & MF_COUNT_INCREASED)
+                                       action_result(pfn, "free buddy", DELAYED);
+                               else
+                                       action_result(pfn, "free buddy, 2nd try", DELAYED);
                                return 0;
                        }
                        action_result(pfn, "non LRU", IGNORED);
@@ -1349,7 +1351,7 @@ int unpoison_memory(unsigned long pfn)
         * worked by memory_failure() and the page lock is not held yet.
         * In such case, we yield to memory_failure() and make unpoison fail.
         */
-       if (PageTransHuge(page)) {
+       if (!PageHuge(page) && PageTransHuge(page)) {
                pr_info("MCE: Memory failure is now running on %#lx\n", pfn);
                        return 0;
        }
index ca00039..d176154 100644 (file)
@@ -837,6 +837,8 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                                         */
                                        make_migration_entry_read(&entry);
                                        pte = swp_entry_to_pte(entry);
+                                       if (pte_swp_soft_dirty(*src_pte))
+                                               pte = pte_swp_mksoft_dirty(pte);
                                        set_pte_at(src_mm, addr, src_pte, pte);
                                }
                        }
@@ -3519,12 +3521,12 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 }
 
 int numa_migrate_prep(struct page *page, struct vm_area_struct *vma,
-                               unsigned long addr, int current_nid)
+                               unsigned long addr, int page_nid)
 {
        get_page(page);
 
        count_vm_numa_event(NUMA_HINT_FAULTS);
-       if (current_nid == numa_node_id())
+       if (page_nid == numa_node_id())
                count_vm_numa_event(NUMA_HINT_FAULTS_LOCAL);
 
        return mpol_misplaced(page, vma, addr);
@@ -3535,7 +3537,7 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
 {
        struct page *page = NULL;
        spinlock_t *ptl;
-       int current_nid = -1;
+       int page_nid = -1;
        int target_nid;
        bool migrated = false;
 
@@ -3565,15 +3567,10 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                return 0;
        }
 
-       current_nid = page_to_nid(page);
-       target_nid = numa_migrate_prep(page, vma, addr, current_nid);
+       page_nid = page_to_nid(page);
+       target_nid = numa_migrate_prep(page, vma, addr, page_nid);
        pte_unmap_unlock(ptep, ptl);
        if (target_nid == -1) {
-               /*
-                * Account for the fault against the current node if it not
-                * being replaced regardless of where the page is located.
-                */
-               current_nid = numa_node_id();
                put_page(page);
                goto out;
        }
@@ -3581,11 +3578,11 @@ int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        /* Migrate to the requested node */
        migrated = migrate_misplaced_page(page, target_nid);
        if (migrated)
-               current_nid = target_nid;
+               page_nid = target_nid;
 
 out:
-       if (current_nid != -1)
-               task_numa_fault(current_nid, 1, migrated);
+       if (page_nid != -1)
+               task_numa_fault(page_nid, 1, migrated);
        return 0;
 }
 
@@ -3600,7 +3597,6 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        unsigned long offset;
        spinlock_t *ptl;
        bool numa = false;
-       int local_nid = numa_node_id();
 
        spin_lock(&mm->page_table_lock);
        pmd = *pmdp;
@@ -3623,9 +3619,10 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
        for (addr = _addr + offset; addr < _addr + PMD_SIZE; pte++, addr += PAGE_SIZE) {
                pte_t pteval = *pte;
                struct page *page;
-               int curr_nid = local_nid;
+               int page_nid = -1;
                int target_nid;
-               bool migrated;
+               bool migrated = false;
+
                if (!pte_present(pteval))
                        continue;
                if (!pte_numa(pteval))
@@ -3647,25 +3644,19 @@ static int do_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
                if (unlikely(page_mapcount(page) != 1))
                        continue;
 
-               /*
-                * Note that the NUMA fault is later accounted to either
-                * the node that is currently running or where the page is
-                * migrated to.
-                */
-               curr_nid = local_nid;
-               target_nid = numa_migrate_prep(page, vma, addr,
-                                              page_to_nid(page));
-               if (target_nid == -1) {
+               page_nid = page_to_nid(page);
+               target_nid = numa_migrate_prep(page, vma, addr, page_nid);
+               pte_unmap_unlock(pte, ptl);
+               if (target_nid != -1) {
+                       migrated = migrate_misplaced_page(page, target_nid);
+                       if (migrated)
+                               page_nid = target_nid;
+               } else {
                        put_page(page);
-                       continue;
                }
 
-               /* Migrate to the requested node */
-               pte_unmap_unlock(pte, ptl);
-               migrated = migrate_misplaced_page(page, target_nid);
-               if (migrated)
-                       curr_nid = target_nid;
-               task_numa_fault(curr_nid, 1, migrated);
+               if (page_nid != -1)
+                       task_numa_fault(page_nid, 1, migrated);
 
                pte = pte_offset_map_lock(mm, pmdp, addr, &ptl);
        }
@@ -3863,15 +3854,21 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
         * space.  Kernel faults are handled more gracefully.
         */
        if (flags & FAULT_FLAG_USER)
-               mem_cgroup_enable_oom();
+               mem_cgroup_oom_enable();
 
        ret = __handle_mm_fault(mm, vma, address, flags);
 
-       if (flags & FAULT_FLAG_USER)
-               mem_cgroup_disable_oom();
-
-       if (WARN_ON(task_in_memcg_oom(current) && !(ret & VM_FAULT_OOM)))
-               mem_cgroup_oom_synchronize();
+       if (flags & FAULT_FLAG_USER) {
+               mem_cgroup_oom_disable();
+                /*
+                 * The task may have entered a memcg OOM situation but
+                 * if the allocation error was handled gracefully (no
+                 * VM_FAULT_OOM), there is no need to kill anything.
+                 * Just clean up the OOM state peacefully.
+                 */
+                if (task_in_memcg_oom(current) && !(ret & VM_FAULT_OOM))
+                        mem_cgroup_oom_synchronize(false);
+       }
 
        return ret;
 }
index 9c8d5f5..c046927 100644 (file)
@@ -107,7 +107,7 @@ void putback_movable_pages(struct list_head *l)
                list_del(&page->lru);
                dec_zone_page_state(page, NR_ISOLATED_ANON +
                                page_is_file_cache(page));
-               if (unlikely(balloon_page_movable(page)))
+               if (unlikely(isolated_balloon_page(page)))
                        balloon_page_putback(page);
                else
                        putback_lru_page(page);
@@ -161,6 +161,8 @@ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma,
 
        get_page(new);
        pte = pte_mkold(mk_pte(new, vma->vm_page_prot));
+       if (pte_swp_soft_dirty(*ptep))
+               pte = pte_mksoft_dirty(pte);
        if (is_write_migration_entry(entry))
                pte = pte_mkwrite(pte);
 #ifdef CONFIG_HUGETLB_PAGE
@@ -1713,12 +1715,12 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
                unlock_page(new_page);
                put_page(new_page);             /* Free it */
 
-               unlock_page(page);
+               /* Retake the callers reference and putback on LRU */
+               get_page(page);
                putback_lru_page(page);
-
-               count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
-               isolated = 0;
-               goto out;
+               mod_zone_page_state(page_zone(page),
+                        NR_ISOLATED_ANON + page_lru, -HPAGE_PMD_NR);
+               goto out_fail;
        }
 
        /*
@@ -1735,9 +1737,9 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
        entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
        entry = pmd_mkhuge(entry);
 
-       page_add_new_anon_rmap(new_page, vma, haddr);
-
+       pmdp_clear_flush(vma, haddr, pmd);
        set_pmd_at(mm, haddr, pmd, entry);
+       page_add_new_anon_rmap(new_page, vma, haddr);
        update_mmu_cache_pmd(vma, address, &entry);
        page_remove_rmap(page);
        /*
@@ -1756,7 +1758,6 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
        count_vm_events(PGMIGRATE_SUCCESS, HPAGE_PMD_NR);
        count_vm_numa_events(NUMA_PAGE_MIGRATE, HPAGE_PMD_NR);
 
-out:
        mod_zone_page_state(page_zone(page),
                        NR_ISOLATED_ANON + page_lru,
                        -HPAGE_PMD_NR);
@@ -1765,6 +1766,10 @@ out:
 out_fail:
        count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
 out_dropref:
+       entry = pmd_mknonnuma(entry);
+       set_pmd_at(mm, haddr, pmd, entry);
+       update_mmu_cache_pmd(vma, address, &entry);
+
        unlock_page(page);
        put_page(page);
        return 0;
index d638026..d480cd6 100644 (file)
@@ -379,10 +379,14 @@ static unsigned long __munlock_pagevec_fill(struct pagevec *pvec,
 
        /*
         * Initialize pte walk starting at the already pinned page where we
-        * are sure that there is a pte.
+        * are sure that there is a pte, as it was pinned under the same
+        * mmap_sem write op.
         */
        pte = get_locked_pte(vma->vm_mm, start, &ptl);
-       end = min(end, pmd_addr_end(start, end));
+       /* Make sure we do not cross the page table boundary */
+       end = pgd_addr_end(start, end);
+       end = pud_addr_end(start, end);
+       end = pmd_addr_end(start, end);
 
        /* The page next to the pinned page is the first we will try to get */
        start += PAGE_SIZE;
@@ -736,6 +740,7 @@ static int do_mlockall(int flags)
 
                /* Ignore errors */
                mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags);
+               cond_resched();
        }
 out:
        return 0;
index 94722a4..412ba2b 100644 (file)
@@ -94,13 +94,16 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
                        swp_entry_t entry = pte_to_swp_entry(oldpte);
 
                        if (is_write_migration_entry(entry)) {
+                               pte_t newpte;
                                /*
                                 * A protection check is difficult so
                                 * just be safe and disable write
                                 */
                                make_migration_entry_read(&entry);
-                               set_pte_at(mm, addr, pte,
-                                       swp_entry_to_pte(entry));
+                               newpte = swp_entry_to_pte(entry);
+                               if (pte_swp_soft_dirty(oldpte))
+                                       newpte = pte_swp_mksoft_dirty(newpte);
+                               set_pte_at(mm, addr, pte, newpte);
                        }
                        pages++;
                }
@@ -145,7 +148,7 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
                                split_huge_page_pmd(vma, addr, pmd);
                        else if (change_huge_pmd(vma, pmd, addr, newprot,
                                                 prot_numa)) {
-                               pages += HPAGE_PMD_NR;
+                               pages++;
                                continue;
                        }
                        /* fall through */
index 91b13d6..0843feb 100644 (file)
@@ -25,7 +25,6 @@
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
-#include <asm/pgalloc.h>
 
 #include "internal.h"
 
@@ -63,10 +62,8 @@ static pmd_t *alloc_new_pmd(struct mm_struct *mm, struct vm_area_struct *vma,
                return NULL;
 
        pmd = pmd_alloc(mm, pud, addr);
-       if (!pmd) {
-               pud_free(mm, pud);
+       if (!pmd)
                return NULL;
-       }
 
        VM_BUG_ON(pmd_trans_huge(*pmd));
 
index 314e9d2..6738c47 100644 (file)
@@ -680,7 +680,7 @@ void pagefault_out_of_memory(void)
 {
        struct zonelist *zonelist;
 
-       if (mem_cgroup_oom_synchronize())
+       if (mem_cgroup_oom_synchronize(true))
                return;
 
        zonelist = node_zonelist(first_online_node, GFP_KERNEL);
index f5236f8..6380758 100644 (file)
@@ -1210,11 +1210,11 @@ static unsigned long dirty_poll_interval(unsigned long dirty,
        return 1;
 }
 
-static long bdi_max_pause(struct backing_dev_info *bdi,
-                         unsigned long bdi_dirty)
+static unsigned long bdi_max_pause(struct backing_dev_info *bdi,
+                                  unsigned long bdi_dirty)
 {
-       long bw = bdi->avg_write_bandwidth;
-       long t;
+       unsigned long bw = bdi->avg_write_bandwidth;
+       unsigned long t;
 
        /*
         * Limit pause time for small memory systems. If sleeping for too long
@@ -1226,7 +1226,7 @@ static long bdi_max_pause(struct backing_dev_info *bdi,
        t = bdi_dirty / (1 + bw / roundup_pow_of_two(1 + HZ / 8));
        t++;
 
-       return min_t(long, t, MAX_PAUSE);
+       return min_t(unsigned long, t, MAX_PAUSE);
 }
 
 static long bdi_min_pause(struct backing_dev_info *bdi,
index 0ee638f..dd886fa 100644 (file)
@@ -6366,10 +6366,6 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn)
                list_del(&page->lru);
                rmv_page_order(page);
                zone->free_area[order].nr_free--;
-#ifdef CONFIG_HIGHMEM
-               if (PageHighMem(page))
-                       totalhigh_pages -= 1 << order;
-#endif
                for (i = 0; i < (1 << order); i++)
                        SetPageReserved((page+i));
                pfn += (1 << order);
index 5da2cbc..2beeabf 100644 (file)
@@ -242,7 +242,7 @@ int walk_page_range(unsigned long addr, unsigned long end,
                if (err)
                        break;
                pgd++;
-       } while (addr = next, addr != end);
+       } while (addr = next, addr < end);
 
        return err;
 }
index a344327..e2e98af 100644 (file)
@@ -56,6 +56,7 @@ static int kmem_cache_sanity_check(struct mem_cgroup *memcg, const char *name,
                        continue;
                }
 
+#if !defined(CONFIG_SLUB) || !defined(CONFIG_SLUB_DEBUG_ON)
                /*
                 * For simplicity, we won't check this in the list of memcg
                 * caches. We have control over memcg naming, and if there
@@ -69,6 +70,7 @@ static int kmem_cache_sanity_check(struct mem_cgroup *memcg, const char *name,
                        s = NULL;
                        return -EINVAL;
                }
+#endif
        }
 
        WARN_ON(strchr(name, ' '));     /* It confuses parsers */
index 3963fc2..de7c904 100644 (file)
@@ -1824,6 +1824,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
        struct filename *pathname;
        int i, type, prev;
        int err;
+       unsigned int old_block_size;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -1914,6 +1915,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
        }
 
        swap_file = p->swap_file;
+       old_block_size = p->old_block_size;
        p->swap_file = NULL;
        p->max = 0;
        swap_map = p->swap_map;
@@ -1938,7 +1940,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
        inode = mapping->host;
        if (S_ISBLK(inode->i_mode)) {
                struct block_device *bdev = I_BDEV(inode);
-               set_blocksize(bdev, p->old_block_size);
+               set_blocksize(bdev, old_block_size);
                blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
        } else {
                mutex_lock(&inode->i_mutex);
index 8ed1b77..eea668d 100644 (file)
@@ -48,6 +48,7 @@
 #include <asm/div64.h>
 
 #include <linux/swapops.h>
+#include <linux/balloon_compaction.h>
 
 #include "internal.h"
 
@@ -139,23 +140,11 @@ static bool global_reclaim(struct scan_control *sc)
 {
        return !sc->target_mem_cgroup;
 }
-
-static bool mem_cgroup_should_soft_reclaim(struct scan_control *sc)
-{
-       struct mem_cgroup *root = sc->target_mem_cgroup;
-       return !mem_cgroup_disabled() &&
-               mem_cgroup_soft_reclaim_eligible(root, root) != SKIP_TREE;
-}
 #else
 static bool global_reclaim(struct scan_control *sc)
 {
        return true;
 }
-
-static bool mem_cgroup_should_soft_reclaim(struct scan_control *sc)
-{
-       return false;
-}
 #endif
 
 unsigned long zone_reclaimable_pages(struct zone *zone)
@@ -222,6 +211,7 @@ void unregister_shrinker(struct shrinker *shrinker)
        down_write(&shrinker_rwsem);
        list_del(&shrinker->list);
        up_write(&shrinker_rwsem);
+       kfree(shrinker->nr_deferred);
 }
 EXPORT_SYMBOL(unregister_shrinker);
 
@@ -1125,7 +1115,8 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone,
        LIST_HEAD(clean_pages);
 
        list_for_each_entry_safe(page, next, page_list, lru) {
-               if (page_is_file_cache(page) && !PageDirty(page)) {
+               if (page_is_file_cache(page) && !PageDirty(page) &&
+                   !isolated_balloon_page(page)) {
                        ClearPageActive(page);
                        list_move(&page->lru, &clean_pages);
                }
@@ -2176,11 +2167,9 @@ static inline bool should_continue_reclaim(struct zone *zone,
        }
 }
 
-static int
-__shrink_zone(struct zone *zone, struct scan_control *sc, bool soft_reclaim)
+static void shrink_zone(struct zone *zone, struct scan_control *sc)
 {
        unsigned long nr_reclaimed, nr_scanned;
-       int groups_scanned = 0;
 
        do {
                struct mem_cgroup *root = sc->target_mem_cgroup;
@@ -2188,17 +2177,15 @@ __shrink_zone(struct zone *zone, struct scan_control *sc, bool soft_reclaim)
                        .zone = zone,
                        .priority = sc->priority,
                };
-               struct mem_cgroup *memcg = NULL;
-               mem_cgroup_iter_filter filter = (soft_reclaim) ?
-                       mem_cgroup_soft_reclaim_eligible : NULL;
+               struct mem_cgroup *memcg;
 
                nr_reclaimed = sc->nr_reclaimed;
                nr_scanned = sc->nr_scanned;
 
-               while ((memcg = mem_cgroup_iter_cond(root, memcg, &reclaim, filter))) {
+               memcg = mem_cgroup_iter(root, NULL, &reclaim);
+               do {
                        struct lruvec *lruvec;
 
-                       groups_scanned++;
                        lruvec = mem_cgroup_zone_lruvec(zone, memcg);
 
                        shrink_lruvec(lruvec, sc);
@@ -2218,7 +2205,8 @@ __shrink_zone(struct zone *zone, struct scan_control *sc, bool soft_reclaim)
                                mem_cgroup_iter_break(root, memcg);
                                break;
                        }
-               }
+                       memcg = mem_cgroup_iter(root, memcg, &reclaim);
+               } while (memcg);
 
                vmpressure(sc->gfp_mask, sc->target_mem_cgroup,
                           sc->nr_scanned - nr_scanned,
@@ -2226,37 +2214,6 @@ __shrink_zone(struct zone *zone, struct scan_control *sc, bool soft_reclaim)
 
        } while (should_continue_reclaim(zone, sc->nr_reclaimed - nr_reclaimed,
                                         sc->nr_scanned - nr_scanned, sc));
-
-       return groups_scanned;
-}
-
-
-static void shrink_zone(struct zone *zone, struct scan_control *sc)
-{
-       bool do_soft_reclaim = mem_cgroup_should_soft_reclaim(sc);
-       unsigned long nr_scanned = sc->nr_scanned;
-       int scanned_groups;
-
-       scanned_groups = __shrink_zone(zone, sc, do_soft_reclaim);
-       /*
-        * memcg iterator might race with other reclaimer or start from
-        * a incomplete tree walk so the tree walk in __shrink_zone
-        * might have missed groups that are above the soft limit. Try
-        * another loop to catch up with others. Do it just once to
-        * prevent from reclaim latencies when other reclaimers always
-        * preempt this one.
-        */
-       if (do_soft_reclaim && !scanned_groups)
-               __shrink_zone(zone, sc, do_soft_reclaim);
-
-       /*
-        * No group is over the soft limit or those that are do not have
-        * pages in the zone we are reclaiming so we have to reclaim everybody
-        */
-       if (do_soft_reclaim && (sc->nr_scanned == nr_scanned)) {
-               __shrink_zone(zone, sc, false);
-               return;
-       }
 }
 
 /* Returns true if compaction should go ahead for a high-order request */
@@ -2320,6 +2277,8 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
 {
        struct zoneref *z;
        struct zone *zone;
+       unsigned long nr_soft_reclaimed;
+       unsigned long nr_soft_scanned;
        bool aborted_reclaim = false;
 
        /*
@@ -2359,6 +2318,18 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
                                        continue;
                                }
                        }
+                       /*
+                        * This steals pages from memory cgroups over softlimit
+                        * and returns the number of reclaimed pages and
+                        * scanned pages. This works for global memory pressure
+                        * and balancing, not for a memcg's limit.
+                        */
+                       nr_soft_scanned = 0;
+                       nr_soft_reclaimed = mem_cgroup_soft_limit_reclaim(zone,
+                                               sc->order, sc->gfp_mask,
+                                               &nr_soft_scanned);
+                       sc->nr_reclaimed += nr_soft_reclaimed;
+                       sc->nr_scanned += nr_soft_scanned;
                        /* need some check for avoid more shrink_zone() */
                }
 
@@ -2952,6 +2923,8 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order,
 {
        int i;
        int end_zone = 0;       /* Inclusive.  0 = ZONE_DMA */
+       unsigned long nr_soft_reclaimed;
+       unsigned long nr_soft_scanned;
        struct scan_control sc = {
                .gfp_mask = GFP_KERNEL,
                .priority = DEF_PRIORITY,
@@ -3066,6 +3039,15 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order,
 
                        sc.nr_scanned = 0;
 
+                       nr_soft_scanned = 0;
+                       /*
+                        * Call soft limit reclaim before calling shrink_zone.
+                        */
+                       nr_soft_reclaimed = mem_cgroup_soft_limit_reclaim(zone,
+                                                       order, sc.gfp_mask,
+                                                       &nr_soft_scanned);
+                       sc.nr_reclaimed += nr_soft_reclaimed;
+
                        /*
                         * There should be no need to raise the scanning
                         * priority if enough pages are already being scanned
index 841e35f..d93510c 100644 (file)
@@ -804,6 +804,10 @@ static void zswap_frontswap_invalidate_area(unsigned type)
        }
        tree->rbroot = RB_ROOT;
        spin_unlock(&tree->lock);
+
+       zbud_destroy_pool(tree->pool);
+       kfree(tree);
+       zswap_trees[type] = NULL;
 }
 
 static struct zbud_ops zswap_zbud_ops = {
index 1eb05d8..3ed6162 100644 (file)
 static unsigned int mrp_join_time __read_mostly = 200;
 module_param(mrp_join_time, uint, 0644);
 MODULE_PARM_DESC(mrp_join_time, "Join time in ms (default 200ms)");
+
+static unsigned int mrp_periodic_time __read_mostly = 1000;
+module_param(mrp_periodic_time, uint, 0644);
+MODULE_PARM_DESC(mrp_periodic_time, "Periodic time in ms (default 1s)");
+
 MODULE_LICENSE("GPL");
 
 static const u8
@@ -595,6 +600,24 @@ static void mrp_join_timer(unsigned long data)
        mrp_join_timer_arm(app);
 }
 
+static void mrp_periodic_timer_arm(struct mrp_applicant *app)
+{
+       mod_timer(&app->periodic_timer,
+                 jiffies + msecs_to_jiffies(mrp_periodic_time));
+}
+
+static void mrp_periodic_timer(unsigned long data)
+{
+       struct mrp_applicant *app = (struct mrp_applicant *)data;
+
+       spin_lock(&app->lock);
+       mrp_mad_event(app, MRP_EVENT_PERIODIC);
+       mrp_pdu_queue(app);
+       spin_unlock(&app->lock);
+
+       mrp_periodic_timer_arm(app);
+}
+
 static int mrp_pdu_parse_end_mark(struct sk_buff *skb, int *offset)
 {
        __be16 endmark;
@@ -845,6 +868,9 @@ int mrp_init_applicant(struct net_device *dev, struct mrp_application *appl)
        rcu_assign_pointer(dev->mrp_port->applicants[appl->type], app);
        setup_timer(&app->join_timer, mrp_join_timer, (unsigned long)app);
        mrp_join_timer_arm(app);
+       setup_timer(&app->periodic_timer, mrp_periodic_timer,
+                   (unsigned long)app);
+       mrp_periodic_timer_arm(app);
        return 0;
 
 err3:
@@ -870,6 +896,7 @@ void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *appl)
         * all pending messages before the applicant is gone.
         */
        del_timer_sync(&app->join_timer);
+       del_timer_sync(&app->periodic_timer);
 
        spin_lock_bh(&app->lock);
        mrp_mad_event(app, MRP_EVENT_TX);
index 61fc573..b3d17d1 100644 (file)
@@ -98,14 +98,14 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
                vlan_gvrp_request_leave(dev);
 
        vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL);
+
+       netdev_upper_dev_unlink(real_dev, dev);
        /* Because unregister_netdevice_queue() makes sure at least one rcu
         * grace period is respected before device freeing,
         * we dont need to call synchronize_net() here.
         */
        unregister_netdevice_queue(dev, head);
 
-       netdev_upper_dev_unlink(real_dev, dev);
-
        if (grp->nr_vlan_devs == 0) {
                vlan_mvrp_uninit_applicant(real_dev);
                vlan_gvrp_uninit_applicant(real_dev);
@@ -169,13 +169,13 @@ int register_vlan_dev(struct net_device *dev)
        if (err < 0)
                goto out_uninit_mvrp;
 
-       err = netdev_upper_dev_link(real_dev, dev);
-       if (err)
-               goto out_uninit_mvrp;
-
        err = register_netdevice(dev);
        if (err < 0)
-               goto out_upper_dev_unlink;
+               goto out_uninit_mvrp;
+
+       err = netdev_upper_dev_link(real_dev, dev);
+       if (err)
+               goto out_unregister_netdev;
 
        /* Account for reference in struct vlan_dev_priv */
        dev_hold(real_dev);
@@ -191,8 +191,8 @@ int register_vlan_dev(struct net_device *dev)
 
        return 0;
 
-out_upper_dev_unlink:
-       netdev_upper_dev_unlink(real_dev, dev);
+out_unregister_netdev:
+       unregister_netdevice(dev);
 out_uninit_mvrp:
        if (grp->nr_vlan_devs == 0)
                vlan_mvrp_uninit_applicant(real_dev);
index ba5983f..5704ed9 100644 (file)
@@ -5,83 +5,6 @@
 #include <linux/u64_stats_sync.h>
 #include <linux/list.h>
 
-
-/**
- *     struct vlan_priority_tci_mapping - vlan egress priority mappings
- *     @priority: skb priority
- *     @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000
- *     @next: pointer to next struct
- */
-struct vlan_priority_tci_mapping {
-       u32                                     priority;
-       u16                                     vlan_qos;
-       struct vlan_priority_tci_mapping        *next;
-};
-
-
-/**
- *     struct vlan_pcpu_stats - VLAN percpu rx/tx stats
- *     @rx_packets: number of received packets
- *     @rx_bytes: number of received bytes
- *     @rx_multicast: number of received multicast packets
- *     @tx_packets: number of transmitted packets
- *     @tx_bytes: number of transmitted bytes
- *     @syncp: synchronization point for 64bit counters
- *     @rx_errors: number of rx errors
- *     @tx_dropped: number of tx drops
- */
-struct vlan_pcpu_stats {
-       u64                     rx_packets;
-       u64                     rx_bytes;
-       u64                     rx_multicast;
-       u64                     tx_packets;
-       u64                     tx_bytes;
-       struct u64_stats_sync   syncp;
-       u32                     rx_errors;
-       u32                     tx_dropped;
-};
-
-struct netpoll;
-
-/**
- *     struct vlan_dev_priv - VLAN private device data
- *     @nr_ingress_mappings: number of ingress priority mappings
- *     @ingress_priority_map: ingress priority mappings
- *     @nr_egress_mappings: number of egress priority mappings
- *     @egress_priority_map: hash of egress priority mappings
- *     @vlan_proto: VLAN encapsulation protocol
- *     @vlan_id: VLAN identifier
- *     @flags: device flags
- *     @real_dev: underlying netdevice
- *     @real_dev_addr: address of underlying netdevice
- *     @dent: proc dir entry
- *     @vlan_pcpu_stats: ptr to percpu rx stats
- */
-struct vlan_dev_priv {
-       unsigned int                            nr_ingress_mappings;
-       u32                                     ingress_priority_map[8];
-       unsigned int                            nr_egress_mappings;
-       struct vlan_priority_tci_mapping        *egress_priority_map[16];
-
-       __be16                                  vlan_proto;
-       u16                                     vlan_id;
-       u16                                     flags;
-
-       struct net_device                       *real_dev;
-       unsigned char                           real_dev_addr[ETH_ALEN];
-
-       struct proc_dir_entry                   *dent;
-       struct vlan_pcpu_stats __percpu         *vlan_pcpu_stats;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       struct netpoll                          *netpoll;
-#endif
-};
-
-static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
-{
-       return netdev_priv(dev);
-}
-
 /* if this changes, algorithm will have to be reworked because this
  * depends on completely exhausting the VLAN identifier space.  Thus
  * it gives constant time look-up, but in many cases it wastes memory.
@@ -196,12 +119,12 @@ static inline u32 vlan_get_ingress_priority(struct net_device *dev,
 }
 
 #ifdef CONFIG_VLAN_8021Q_GVRP
-extern int vlan_gvrp_request_join(const struct net_device *dev);
-extern void vlan_gvrp_request_leave(const struct net_device *dev);
-extern int vlan_gvrp_init_applicant(struct net_device *dev);
-extern void vlan_gvrp_uninit_applicant(struct net_device *dev);
-extern int vlan_gvrp_init(void);
-extern void vlan_gvrp_uninit(void);
+int vlan_gvrp_request_join(const struct net_device *dev);
+void vlan_gvrp_request_leave(const struct net_device *dev);
+int vlan_gvrp_init_applicant(struct net_device *dev);
+void vlan_gvrp_uninit_applicant(struct net_device *dev);
+int vlan_gvrp_init(void);
+void vlan_gvrp_uninit(void);
 #else
 static inline int vlan_gvrp_request_join(const struct net_device *dev) { return 0; }
 static inline void vlan_gvrp_request_leave(const struct net_device *dev) {}
@@ -212,12 +135,12 @@ static inline void vlan_gvrp_uninit(void) {}
 #endif
 
 #ifdef CONFIG_VLAN_8021Q_MVRP
-extern int vlan_mvrp_request_join(const struct net_device *dev);
-extern void vlan_mvrp_request_leave(const struct net_device *dev);
-extern int vlan_mvrp_init_applicant(struct net_device *dev);
-extern void vlan_mvrp_uninit_applicant(struct net_device *dev);
-extern int vlan_mvrp_init(void);
-extern void vlan_mvrp_uninit(void);
+int vlan_mvrp_request_join(const struct net_device *dev);
+void vlan_mvrp_request_leave(const struct net_device *dev);
+int vlan_mvrp_init_applicant(struct net_device *dev);
+void vlan_mvrp_uninit_applicant(struct net_device *dev);
+int vlan_mvrp_init(void);
+void vlan_mvrp_uninit(void);
 #else
 static inline int vlan_mvrp_request_join(const struct net_device *dev) { return 0; }
 static inline void vlan_mvrp_request_leave(const struct net_device *dev) {}
@@ -229,8 +152,8 @@ static inline void vlan_mvrp_uninit(void) {}
 
 extern const char vlan_fullname[];
 extern const char vlan_version[];
-extern int vlan_netlink_init(void);
-extern void vlan_netlink_fini(void);
+int vlan_netlink_init(void);
+void vlan_netlink_fini(void);
 
 extern struct rtnl_link_ops vlan_link_ops;
 
index 09bf1c3..8db1b98 100644 (file)
@@ -68,25 +68,6 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb)
        return 0;
 }
 
-static inline u16
-vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb)
-{
-       struct vlan_priority_tci_mapping *mp;
-
-       smp_rmb(); /* coupled with smp_wmb() in vlan_dev_set_egress_priority() */
-
-       mp = vlan_dev_priv(dev)->egress_priority_map[(skb->priority & 0xF)];
-       while (mp) {
-               if (mp->priority == skb->priority) {
-                       return mp->vlan_qos; /* This should already be shifted
-                                             * to mask correctly with the
-                                             * VLAN's TCI */
-               }
-               mp = mp->next;
-       }
-       return 0;
-}
-
 /*
  *     Create the VLAN header for an arbitrary protocol layer
  *
@@ -111,7 +92,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
                vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN);
 
                vlan_tci = vlan->vlan_id;
-               vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
+               vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
                vhdr->h_vlan_TCI = htons(vlan_tci);
 
                /*
@@ -168,7 +149,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
            vlan->flags & VLAN_FLAG_REORDER_HDR) {
                u16 vlan_tci;
                vlan_tci = vlan->vlan_id;
-               vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
+               vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
                skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
        }
 
index 3091297..c7e634a 100644 (file)
@@ -171,7 +171,7 @@ static size_t vlan_get_size(const struct net_device *dev)
 
        return nla_total_size(2) +      /* IFLA_VLAN_PROTOCOL */
               nla_total_size(2) +      /* IFLA_VLAN_ID */
-              sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
+              nla_total_size(sizeof(struct ifla_vlan_flags)) + /* IFLA_VLAN_FLAGS */
               vlan_qos_map_size(vlan->nr_ingress_mappings) +
               vlan_qos_map_size(vlan->nr_egress_mappings);
 }
index b50dacc..0715db6 100644 (file)
@@ -220,6 +220,7 @@ source "net/openvswitch/Kconfig"
 source "net/vmw_vsock/Kconfig"
 source "net/netlink/Kconfig"
 source "net/mpls/Kconfig"
+source "net/hsr/Kconfig"
 
 config RPS
        boolean
index 9492e8c..8fa2f91 100644 (file)
@@ -71,3 +71,4 @@ obj-$(CONFIG_NFC)             += nfc/
 obj-$(CONFIG_OPENVSWITCH)      += openvswitch/
 obj-$(CONFIG_VSOCKETS) += vmw_vsock/
 obj-$(CONFIG_NET_MPLS_GSO)     += mpls/
+obj-$(CONFIG_HSR)              += hsr/
index 4b4d2b7..a00123e 100644 (file)
@@ -1735,7 +1735,7 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        res = -EFAULT;
                        break;
                }
-               if (amount > AX25_NOUID_BLOCK) {
+               if (amount < 0 || amount > AX25_NOUID_BLOCK) {
                        res = -EINVAL;
                        break;
                }
index 489bb36..4f4aabb 100644 (file)
@@ -24,6 +24,7 @@ batman-adv-y += bitarray.o
 batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
 batman-adv-y += debugfs.o
 batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
+batman-adv-y += fragmentation.o
 batman-adv-y += gateway_client.o
 batman-adv-y += gateway_common.o
 batman-adv-y += hard-interface.o
@@ -37,5 +38,3 @@ batman-adv-y += send.o
 batman-adv-y += soft-interface.o
 batman-adv-y += sysfs.o
 batman-adv-y += translation-table.o
-batman-adv-y += unicast.o
-batman-adv-y += vis.o
index 0a8a80c..a2b480a 100644 (file)
@@ -87,22 +87,198 @@ static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[])
        return (uint8_t)(sum / count);
 }
 
+/**
+ * batadv_iv_ogm_orig_free - free the private resources allocated for this
+ *  orig_node
+ * @orig_node: the orig_node for which the resources have to be free'd
+ */
+static void batadv_iv_ogm_orig_free(struct batadv_orig_node *orig_node)
+{
+       kfree(orig_node->bat_iv.bcast_own);
+       kfree(orig_node->bat_iv.bcast_own_sum);
+}
+
+/**
+ * batadv_iv_ogm_orig_add_if - change the private structures of the orig_node to
+ *  include the new hard-interface
+ * @orig_node: the orig_node that has to be changed
+ * @max_if_num: the current amount of interfaces
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node,
+                                    int max_if_num)
+{
+       void *data_ptr;
+       size_t data_size, old_size;
+       int ret = -ENOMEM;
+
+       spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+       data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS;
+       old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS;
+       data_ptr = kmalloc(data_size, GFP_ATOMIC);
+       if (!data_ptr)
+               goto unlock;
+
+       memcpy(data_ptr, orig_node->bat_iv.bcast_own, old_size);
+       kfree(orig_node->bat_iv.bcast_own);
+       orig_node->bat_iv.bcast_own = data_ptr;
+
+       data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
+       if (!data_ptr) {
+               kfree(orig_node->bat_iv.bcast_own);
+               goto unlock;
+       }
+
+       memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum,
+              (max_if_num - 1) * sizeof(uint8_t));
+       kfree(orig_node->bat_iv.bcast_own_sum);
+       orig_node->bat_iv.bcast_own_sum = data_ptr;
+
+       ret = 0;
+
+unlock:
+       spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+       return ret;
+}
+
+/**
+ * batadv_iv_ogm_orig_del_if - change the private structures of the orig_node to
+ *  exclude the removed interface
+ * @orig_node: the orig_node that has to be changed
+ * @max_if_num: the current amount of interfaces
+ * @del_if_num: the index of the interface being removed
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
+                                    int max_if_num, int del_if_num)
+{
+       int chunk_size,  ret = -ENOMEM, if_offset;
+       void *data_ptr = NULL;
+
+       spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+       /* last interface was removed */
+       if (max_if_num == 0)
+               goto free_bcast_own;
+
+       chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS;
+       data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
+       if (!data_ptr)
+               goto unlock;
+
+       /* copy first part */
+       memcpy(data_ptr, orig_node->bat_iv.bcast_own, del_if_num * chunk_size);
+
+       /* copy second part */
+       memcpy((char *)data_ptr + del_if_num * chunk_size,
+              orig_node->bat_iv.bcast_own + ((del_if_num + 1) * chunk_size),
+              (max_if_num - del_if_num) * chunk_size);
+
+free_bcast_own:
+       kfree(orig_node->bat_iv.bcast_own);
+       orig_node->bat_iv.bcast_own = data_ptr;
+
+       if (max_if_num == 0)
+               goto free_own_sum;
+
+       data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
+       if (!data_ptr) {
+               kfree(orig_node->bat_iv.bcast_own);
+               goto unlock;
+       }
+
+       memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum,
+              del_if_num * sizeof(uint8_t));
+
+       if_offset = (del_if_num + 1) * sizeof(uint8_t);
+       memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t),
+              orig_node->bat_iv.bcast_own_sum + if_offset,
+              (max_if_num - del_if_num) * sizeof(uint8_t));
+
+free_own_sum:
+       kfree(orig_node->bat_iv.bcast_own_sum);
+       orig_node->bat_iv.bcast_own_sum = data_ptr;
+
+       ret = 0;
+unlock:
+       spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+       return ret;
+}
+
+/**
+ * batadv_iv_ogm_orig_get - retrieve or create (if does not exist) an originator
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: mac address of the originator
+ *
+ * Returns the originator object corresponding to the passed mac address or NULL
+ * on failure.
+ * If the object does not exists it is created an initialised.
+ */
+static struct batadv_orig_node *
+batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr)
+{
+       struct batadv_orig_node *orig_node;
+       int size, hash_added;
+
+       orig_node = batadv_orig_hash_find(bat_priv, addr);
+       if (orig_node)
+               return orig_node;
+
+       orig_node = batadv_orig_node_new(bat_priv, addr);
+       if (!orig_node)
+               return NULL;
+
+       spin_lock_init(&orig_node->bat_iv.ogm_cnt_lock);
+
+       size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS;
+       orig_node->bat_iv.bcast_own = kzalloc(size, GFP_ATOMIC);
+       if (!orig_node->bat_iv.bcast_own)
+               goto free_orig_node;
+
+       size = bat_priv->num_ifaces * sizeof(uint8_t);
+       orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC);
+       if (!orig_node->bat_iv.bcast_own_sum)
+               goto free_bcast_own;
+
+       hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
+                                    batadv_choose_orig, orig_node,
+                                    &orig_node->hash_entry);
+       if (hash_added != 0)
+               goto free_bcast_own;
+
+       return orig_node;
+
+free_bcast_own:
+       kfree(orig_node->bat_iv.bcast_own);
+free_orig_node:
+       batadv_orig_node_free_ref(orig_node);
+
+       return NULL;
+}
+
 static struct batadv_neigh_node *
 batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
                        const uint8_t *neigh_addr,
                        struct batadv_orig_node *orig_node,
                        struct batadv_orig_node *orig_neigh)
 {
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct batadv_neigh_node *neigh_node;
 
-       neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr);
+       neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node);
        if (!neigh_node)
                goto out;
 
-       INIT_LIST_HEAD(&neigh_node->bonding_list);
+       spin_lock_init(&neigh_node->bat_iv.lq_update_lock);
 
-       neigh_node->orig_node = orig_neigh;
-       neigh_node->if_incoming = hard_iface;
+       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                  "Creating new neighbor %pM for orig_node %pM on interface %s\n",
+                  neigh_addr, orig_node->orig, hard_iface->net_dev->name);
 
        spin_lock_bh(&orig_node->neigh_list_lock);
        hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
@@ -135,9 +311,8 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
        batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION;
        batadv_ogm_packet->header.ttl = 2;
        batadv_ogm_packet->flags = BATADV_NO_FLAGS;
+       batadv_ogm_packet->reserved = 0;
        batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
-       batadv_ogm_packet->tt_num_changes = 0;
-       batadv_ogm_packet->ttvn = 0;
 
        res = 0;
 
@@ -207,12 +382,12 @@ static uint8_t batadv_hop_penalty(uint8_t tq,
 
 /* is there another aggregated packet here? */
 static int batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
-                                    int tt_num_changes)
+                                    __be16 tvlv_len)
 {
        int next_buff_pos = 0;
 
        next_buff_pos += buff_pos + BATADV_OGM_HLEN;
-       next_buff_pos += batadv_tt_len(tt_num_changes);
+       next_buff_pos += ntohs(tvlv_len);
 
        return (next_buff_pos <= packet_len) &&
               (next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES);
@@ -240,7 +415,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
 
        /* adjust all flags and log packets */
        while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
-                                        batadv_ogm_packet->tt_num_changes)) {
+                                        batadv_ogm_packet->tvlv_len)) {
                /* we might have aggregated direct link packets with an
                 * ordinary base packet
                 */
@@ -256,18 +431,18 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
                        fwd_str = "Sending own";
 
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n",
+                          "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s) on interface %s [%pM]\n",
                           fwd_str, (packet_num > 0 ? "aggregated " : ""),
                           batadv_ogm_packet->orig,
                           ntohl(batadv_ogm_packet->seqno),
                           batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl,
                           (batadv_ogm_packet->flags & BATADV_DIRECTLINK ?
                            "on" : "off"),
-                          batadv_ogm_packet->ttvn, hard_iface->net_dev->name,
+                          hard_iface->net_dev->name,
                           hard_iface->net_dev->dev_addr);
 
                buff_pos += BATADV_OGM_HLEN;
-               buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
+               buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
                packet_num++;
                packet_pos = forw_packet->skb->data + buff_pos;
                batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
@@ -601,7 +776,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
                                  struct batadv_hard_iface *if_incoming)
 {
        struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
-       uint8_t tt_num_changes;
+       uint16_t tvlv_len;
 
        if (batadv_ogm_packet->header.ttl <= 1) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
@@ -621,7 +796,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
                        return;
        }
 
-       tt_num_changes = batadv_ogm_packet->tt_num_changes;
+       tvlv_len = ntohs(batadv_ogm_packet->tvlv_len);
 
        batadv_ogm_packet->header.ttl--;
        memcpy(batadv_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
@@ -642,7 +817,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
                batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
 
        batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet,
-                               BATADV_OGM_HLEN + batadv_tt_len(tt_num_changes),
+                               BATADV_OGM_HLEN + tvlv_len,
                                if_incoming, 0, batadv_iv_ogm_fwd_send_time());
 }
 
@@ -662,20 +837,22 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
        uint32_t i;
        size_t word_index;
        uint8_t *w;
+       int if_num;
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       spin_lock_bh(&orig_node->ogm_cnt_lock);
+                       spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
                        word_index = hard_iface->if_num * BATADV_NUM_WORDS;
-                       word = &(orig_node->bcast_own[word_index]);
+                       word = &(orig_node->bat_iv.bcast_own[word_index]);
 
                        batadv_bit_get_packet(bat_priv, word, 1, 0);
-                       w = &orig_node->bcast_own_sum[hard_iface->if_num];
+                       if_num = hard_iface->if_num;
+                       w = &orig_node->bat_iv.bcast_own_sum[if_num];
                        *w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE);
-                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
+                       spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
                }
                rcu_read_unlock();
        }
@@ -688,43 +865,29 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
        struct batadv_ogm_packet *batadv_ogm_packet;
        struct batadv_hard_iface *primary_if;
        int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
-       int vis_server, tt_num_changes = 0;
        uint32_t seqno;
-       uint8_t bandwidth;
+       uint16_t tvlv_len = 0;
 
-       vis_server = atomic_read(&bat_priv->vis_mode);
        primary_if = batadv_primary_if_get_selected(bat_priv);
 
-       if (hard_iface == primary_if)
-               tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff,
-                                                      ogm_buff_len,
-                                                      BATADV_OGM_HLEN);
+       if (hard_iface == primary_if) {
+               /* tt changes have to be committed before the tvlv data is
+                * appended as it may alter the tt tvlv container
+                */
+               batadv_tt_local_commit_changes(bat_priv);
+               tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+                                                           ogm_buff_len,
+                                                           BATADV_OGM_HLEN);
+       }
 
        batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+       batadv_ogm_packet->tvlv_len = htons(tvlv_len);
 
        /* change sequence number to network order */
        seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno);
        batadv_ogm_packet->seqno = htonl(seqno);
        atomic_inc(&hard_iface->bat_iv.ogm_seqno);
 
-       batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn);
-       batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc);
-       if (tt_num_changes >= 0)
-               batadv_ogm_packet->tt_num_changes = tt_num_changes;
-
-       if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC)
-               batadv_ogm_packet->flags |= BATADV_VIS_SERVER;
-       else
-               batadv_ogm_packet->flags &= ~BATADV_VIS_SERVER;
-
-       if (hard_iface == primary_if &&
-           atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_SERVER) {
-               bandwidth = (uint8_t)atomic_read(&bat_priv->gw_bandwidth);
-               batadv_ogm_packet->gw_flags = bandwidth;
-       } else {
-               batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS;
-       }
-
        batadv_iv_ogm_slide_own_bcast_window(hard_iface);
        batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
                                hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
@@ -770,18 +933,18 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
                if (dup_status != BATADV_NO_DUP)
                        continue;
 
-               spin_lock_bh(&tmp_neigh_node->lq_update_lock);
-               batadv_ring_buffer_set(tmp_neigh_node->tq_recv,
-                                      &tmp_neigh_node->tq_index, 0);
-               tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->tq_recv);
-               tmp_neigh_node->tq_avg = tq_avg;
-               spin_unlock_bh(&tmp_neigh_node->lq_update_lock);
+               spin_lock_bh(&tmp_neigh_node->bat_iv.lq_update_lock);
+               batadv_ring_buffer_set(tmp_neigh_node->bat_iv.tq_recv,
+                                      &tmp_neigh_node->bat_iv.tq_index, 0);
+               tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->bat_iv.tq_recv);
+               tmp_neigh_node->bat_iv.tq_avg = tq_avg;
+               spin_unlock_bh(&tmp_neigh_node->bat_iv.lq_update_lock);
        }
 
        if (!neigh_node) {
                struct batadv_orig_node *orig_tmp;
 
-               orig_tmp = batadv_get_orig_node(bat_priv, ethhdr->h_source);
+               orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source);
                if (!orig_tmp)
                        goto unlock;
 
@@ -798,80 +961,55 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
 
        rcu_read_unlock();
 
-       orig_node->flags = batadv_ogm_packet->flags;
        neigh_node->last_seen = jiffies;
 
-       spin_lock_bh(&neigh_node->lq_update_lock);
-       batadv_ring_buffer_set(neigh_node->tq_recv,
-                              &neigh_node->tq_index,
+       spin_lock_bh(&neigh_node->bat_iv.lq_update_lock);
+       batadv_ring_buffer_set(neigh_node->bat_iv.tq_recv,
+                              &neigh_node->bat_iv.tq_index,
                               batadv_ogm_packet->tq);
-       neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv);
-       spin_unlock_bh(&neigh_node->lq_update_lock);
+       tq_avg = batadv_ring_buffer_avg(neigh_node->bat_iv.tq_recv);
+       neigh_node->bat_iv.tq_avg = tq_avg;
+       spin_unlock_bh(&neigh_node->bat_iv.lq_update_lock);
 
        if (dup_status == BATADV_NO_DUP) {
                orig_node->last_ttl = batadv_ogm_packet->header.ttl;
                neigh_node->last_ttl = batadv_ogm_packet->header.ttl;
        }
 
-       batadv_bonding_candidate_add(orig_node, neigh_node);
+       batadv_bonding_candidate_add(bat_priv, orig_node, neigh_node);
 
        /* if this neighbor already is our next hop there is nothing
         * to change
         */
        router = batadv_orig_node_get_router(orig_node);
        if (router == neigh_node)
-               goto update_tt;
+               goto out;
 
        /* if this neighbor does not offer a better TQ we won't consider it */
-       if (router && (router->tq_avg > neigh_node->tq_avg))
-               goto update_tt;
+       if (router && (router->bat_iv.tq_avg > neigh_node->bat_iv.tq_avg))
+               goto out;
 
        /* if the TQ is the same and the link not more symmetric we
         * won't consider it either
         */
-       if (router && (neigh_node->tq_avg == router->tq_avg)) {
+       if (router && (neigh_node->bat_iv.tq_avg == router->bat_iv.tq_avg)) {
                orig_node_tmp = router->orig_node;
-               spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
+               spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
                if_num = router->if_incoming->if_num;
-               sum_orig = orig_node_tmp->bcast_own_sum[if_num];
-               spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
+               sum_orig = orig_node_tmp->bat_iv.bcast_own_sum[if_num];
+               spin_unlock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
 
                orig_node_tmp = neigh_node->orig_node;
-               spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
+               spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
                if_num = neigh_node->if_incoming->if_num;
-               sum_neigh = orig_node_tmp->bcast_own_sum[if_num];
-               spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
+               sum_neigh = orig_node_tmp->bat_iv.bcast_own_sum[if_num];
+               spin_unlock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
 
                if (sum_orig >= sum_neigh)
-                       goto update_tt;
+                       goto out;
        }
 
        batadv_update_route(bat_priv, orig_node, neigh_node);
-
-update_tt:
-       /* I have to check for transtable changes only if the OGM has been
-        * sent through a primary interface
-        */
-       if (((batadv_ogm_packet->orig != ethhdr->h_source) &&
-            (batadv_ogm_packet->header.ttl > 2)) ||
-           (batadv_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP))
-               batadv_tt_update_orig(bat_priv, orig_node, tt_buff,
-                                     batadv_ogm_packet->tt_num_changes,
-                                     batadv_ogm_packet->ttvn,
-                                     ntohs(batadv_ogm_packet->tt_crc));
-
-       if (orig_node->gw_flags != batadv_ogm_packet->gw_flags)
-               batadv_gw_node_update(bat_priv, orig_node,
-                                     batadv_ogm_packet->gw_flags);
-
-       orig_node->gw_flags = batadv_ogm_packet->gw_flags;
-
-       /* restart gateway selection if fast or late switching was enabled */
-       if ((orig_node->gw_flags) &&
-           (atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) &&
-           (atomic_read(&bat_priv->gw_sel_class) > 2))
-               batadv_gw_check_election(bat_priv, orig_node);
-
        goto out;
 
 unlock:
@@ -893,7 +1031,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
        uint8_t total_count;
        uint8_t orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own;
        unsigned int neigh_rq_inv_cube, neigh_rq_max_cube;
-       int tq_asym_penalty, inv_asym_penalty, ret = 0;
+       int tq_asym_penalty, inv_asym_penalty, if_num, ret = 0;
        unsigned int combined_tq;
 
        /* find corresponding one hop neighbor */
@@ -931,10 +1069,11 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
        orig_node->last_seen = jiffies;
 
        /* find packet count of corresponding one hop neighbor */
-       spin_lock_bh(&orig_node->ogm_cnt_lock);
-       orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num];
-       neigh_rq_count = neigh_node->real_packet_count;
-       spin_unlock_bh(&orig_node->ogm_cnt_lock);
+       spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+       if_num = if_incoming->if_num;
+       orig_eq_count = orig_neigh_node->bat_iv.bcast_own_sum[if_num];
+       neigh_rq_count = neigh_node->bat_iv.real_packet_count;
+       spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
 
        /* pay attention to not get a value bigger than 100 % */
        if (orig_eq_count > neigh_rq_count)
@@ -1016,12 +1155,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
        uint32_t seqno = ntohl(batadv_ogm_packet->seqno);
        uint8_t *neigh_addr;
        uint8_t packet_count;
+       unsigned long *bitmap;
 
-       orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
+       orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig);
        if (!orig_node)
                return BATADV_NO_DUP;
 
-       spin_lock_bh(&orig_node->ogm_cnt_lock);
+       spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
        seq_diff = seqno - orig_node->last_real_seqno;
 
        /* signalize caller that the packet is to be dropped. */
@@ -1036,7 +1176,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
        hlist_for_each_entry_rcu(tmp_neigh_node,
                                 &orig_node->neigh_list, list) {
                neigh_addr = tmp_neigh_node->addr;
-               is_dup = batadv_test_bit(tmp_neigh_node->real_bits,
+               is_dup = batadv_test_bit(tmp_neigh_node->bat_iv.real_bits,
                                         orig_node->last_real_seqno,
                                         seqno);
 
@@ -1052,13 +1192,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
                }
 
                /* if the window moved, set the update flag. */
-               need_update |= batadv_bit_get_packet(bat_priv,
-                                                    tmp_neigh_node->real_bits,
+               bitmap = tmp_neigh_node->bat_iv.real_bits;
+               need_update |= batadv_bit_get_packet(bat_priv, bitmap,
                                                     seq_diff, set_mark);
 
-               packet_count = bitmap_weight(tmp_neigh_node->real_bits,
+               packet_count = bitmap_weight(tmp_neigh_node->bat_iv.real_bits,
                                             BATADV_TQ_LOCAL_WINDOW_SIZE);
-               tmp_neigh_node->real_packet_count = packet_count;
+               tmp_neigh_node->bat_iv.real_packet_count = packet_count;
        }
        rcu_read_unlock();
 
@@ -1070,7 +1210,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
        }
 
 out:
-       spin_unlock_bh(&orig_node->ogm_cnt_lock);
+       spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
        batadv_orig_node_free_ref(orig_node);
        return ret;
 }
@@ -1082,7 +1222,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
 {
        struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
        struct batadv_hard_iface *hard_iface;
-       struct batadv_orig_node *orig_neigh_node, *orig_node;
+       struct batadv_orig_node *orig_neigh_node, *orig_node, *orig_node_tmp;
        struct batadv_neigh_node *router = NULL, *router_router = NULL;
        struct batadv_neigh_node *orig_neigh_router = NULL;
        int has_directlink_flag;
@@ -1122,13 +1262,11 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                is_single_hop_neigh = true;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                  "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, tq %d, TTL %d, V %d, IDF %d)\n",
+                  "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, tq %d, TTL %d, V %d, IDF %d)\n",
                   ethhdr->h_source, if_incoming->net_dev->name,
                   if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig,
                   batadv_ogm_packet->prev_sender,
-                  ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->ttvn,
-                  ntohs(batadv_ogm_packet->tt_crc),
-                  batadv_ogm_packet->tt_num_changes, batadv_ogm_packet->tq,
+                  ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->tq,
                   batadv_ogm_packet->header.ttl,
                   batadv_ogm_packet->header.version, has_directlink_flag);
 
@@ -1168,8 +1306,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                int16_t if_num;
                uint8_t *weight;
 
-               orig_neigh_node = batadv_get_orig_node(bat_priv,
-                                                      ethhdr->h_source);
+               orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
+                                                        ethhdr->h_source);
                if (!orig_neigh_node)
                        return;
 
@@ -1183,15 +1321,15 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                        if_num = if_incoming->if_num;
                        offset = if_num * BATADV_NUM_WORDS;
 
-                       spin_lock_bh(&orig_neigh_node->ogm_cnt_lock);
-                       word = &(orig_neigh_node->bcast_own[offset]);
+                       spin_lock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock);
+                       word = &(orig_neigh_node->bat_iv.bcast_own[offset]);
                        bit_pos = if_incoming_seqno - 2;
                        bit_pos -= ntohl(batadv_ogm_packet->seqno);
                        batadv_set_bit(word, bit_pos);
-                       weight = &orig_neigh_node->bcast_own_sum[if_num];
+                       weight = &orig_neigh_node->bat_iv.bcast_own_sum[if_num];
                        *weight = bitmap_weight(word,
                                                BATADV_TQ_LOCAL_WINDOW_SIZE);
-                       spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock);
+                       spin_unlock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock);
                }
 
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
@@ -1214,7 +1352,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                return;
        }
 
-       orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
+       orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig);
        if (!orig_node)
                return;
 
@@ -1235,10 +1373,12 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
        }
 
        router = batadv_orig_node_get_router(orig_node);
-       if (router)
-               router_router = batadv_orig_node_get_router(router->orig_node);
+       if (router) {
+               orig_node_tmp = router->orig_node;
+               router_router = batadv_orig_node_get_router(orig_node_tmp);
+       }
 
-       if ((router && router->tq_avg != 0) &&
+       if ((router && router->bat_iv.tq_avg != 0) &&
            (batadv_compare_eth(router->addr, ethhdr->h_source)))
                is_from_best_next_hop = true;
 
@@ -1254,14 +1394,16 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                goto out;
        }
 
+       batadv_tvlv_ogm_receive(bat_priv, batadv_ogm_packet, orig_node);
+
        /* if sender is a direct neighbor the sender mac equals
         * originator mac
         */
        if (is_single_hop_neigh)
                orig_neigh_node = orig_node;
        else
-               orig_neigh_node = batadv_get_orig_node(bat_priv,
-                                                      ethhdr->h_source);
+               orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
+                                                        ethhdr->h_source);
 
        if (!orig_neigh_node)
                goto out;
@@ -1350,9 +1492,9 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
        struct batadv_ogm_packet *batadv_ogm_packet;
        struct ethhdr *ethhdr;
        int buff_pos = 0, packet_len;
-       unsigned char *tt_buff, *packet_buff;
-       bool ret;
+       unsigned char *tvlv_buff, *packet_buff;
        uint8_t *packet_pos;
+       bool ret;
 
        ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN);
        if (!ret)
@@ -1375,14 +1517,14 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
 
        /* unpack the aggregated packets and process them one by one */
        while (batadv_iv_ogm_aggr_packet(buff_pos, packet_len,
-                                        batadv_ogm_packet->tt_num_changes)) {
-               tt_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
+                                        batadv_ogm_packet->tvlv_len)) {
+               tvlv_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
 
-               batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, tt_buff,
-                                     if_incoming);
+               batadv_iv_ogm_process(ethhdr, batadv_ogm_packet,
+                                     tvlv_buff, if_incoming);
 
                buff_pos += BATADV_OGM_HLEN;
-               buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
+               buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
 
                packet_pos = packet_buff + buff_pos;
                batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
@@ -1392,6 +1534,106 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
        return NET_RX_SUCCESS;
 }
 
+/**
+ * batadv_iv_ogm_orig_print - print the originator table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @seq: debugfs table seq_file struct
+ */
+static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv,
+                                    struct seq_file *seq)
+{
+       struct batadv_neigh_node *neigh_node, *neigh_node_tmp;
+       struct batadv_hashtable *hash = bat_priv->orig_hash;
+       int last_seen_msecs, last_seen_secs;
+       struct batadv_orig_node *orig_node;
+       unsigned long last_seen_jiffies;
+       struct hlist_head *head;
+       int batman_count = 0;
+       uint32_t i;
+
+       seq_printf(seq, "  %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
+                  "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE,
+                  "Nexthop", "outgoingIF", "Potential nexthops");
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+                       neigh_node = batadv_orig_node_get_router(orig_node);
+                       if (!neigh_node)
+                               continue;
+
+                       if (neigh_node->bat_iv.tq_avg == 0)
+                               goto next;
+
+                       last_seen_jiffies = jiffies - orig_node->last_seen;
+                       last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
+                       last_seen_secs = last_seen_msecs / 1000;
+                       last_seen_msecs = last_seen_msecs % 1000;
+
+                       seq_printf(seq, "%pM %4i.%03is   (%3i) %pM [%10s]:",
+                                  orig_node->orig, last_seen_secs,
+                                  last_seen_msecs, neigh_node->bat_iv.tq_avg,
+                                  neigh_node->addr,
+                                  neigh_node->if_incoming->net_dev->name);
+
+                       hlist_for_each_entry_rcu(neigh_node_tmp,
+                                                &orig_node->neigh_list, list) {
+                               seq_printf(seq, " %pM (%3i)",
+                                          neigh_node_tmp->addr,
+                                          neigh_node_tmp->bat_iv.tq_avg);
+                       }
+
+                       seq_puts(seq, "\n");
+                       batman_count++;
+
+next:
+                       batadv_neigh_node_free_ref(neigh_node);
+               }
+               rcu_read_unlock();
+       }
+
+       if (batman_count == 0)
+               seq_puts(seq, "No batman nodes in range ...\n");
+}
+
+/**
+ * batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors
+ * @neigh1: the first neighbor object of the comparison
+ * @neigh2: the second neighbor object of the comparison
+ *
+ * Returns a value less, equal to or greater than 0 if the metric via neigh1 is
+ * lower, the same as or higher than the metric via neigh2
+ */
+static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
+                                  struct batadv_neigh_node *neigh2)
+{
+       uint8_t tq1, tq2;
+
+       tq1 = neigh1->bat_iv.tq_avg;
+       tq2 = neigh2->bat_iv.tq_avg;
+
+       return tq1 - tq2;
+}
+
+/**
+ * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than
+ *  neigh2 from the metric prospective
+ * @neigh1: the first neighbor object of the comparison
+ * @neigh2: the second neighbor object of the comparison
+ *
+ * Returns true if the metric via neigh1 is equally good or better than the
+ * metric via neigh2, false otherwise.
+ */
+static bool batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1,
+                                      struct batadv_neigh_node *neigh2)
+{
+       int diff = batadv_iv_ogm_neigh_cmp(neigh1, neigh2);
+
+       return diff > -BATADV_TQ_SIMILARITY_THRESHOLD;
+}
+
 static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
        .name = "BATMAN_IV",
        .bat_iface_enable = batadv_iv_ogm_iface_enable,
@@ -1400,6 +1642,12 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
        .bat_primary_iface_set = batadv_iv_ogm_primary_iface_set,
        .bat_ogm_schedule = batadv_iv_ogm_schedule,
        .bat_ogm_emit = batadv_iv_ogm_emit,
+       .bat_neigh_cmp = batadv_iv_ogm_neigh_cmp,
+       .bat_neigh_is_equiv_or_better = batadv_iv_ogm_neigh_is_eob,
+       .bat_orig_print = batadv_iv_ogm_orig_print,
+       .bat_orig_free = batadv_iv_ogm_orig_free,
+       .bat_orig_add_if = batadv_iv_ogm_orig_add_if,
+       .bat_orig_del_if = batadv_iv_ogm_orig_del_if,
 };
 
 int __init batadv_iv_init(void)
index 264de88..28eb5e6 100644 (file)
@@ -411,10 +411,10 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
                return NULL;
        }
 
-       /* this is a gateway now, remove any tt entries */
+       /* this is a gateway now, remove any TT entry on this VLAN */
        orig_node = batadv_orig_hash_find(bat_priv, orig);
        if (orig_node) {
-               batadv_tt_global_del_orig(bat_priv, orig_node,
+               batadv_tt_global_del_orig(bat_priv, orig_node, vid,
                                          "became a backbone gateway");
                batadv_orig_node_free_ref(orig_node);
        }
@@ -858,30 +858,28 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
                                    struct batadv_hard_iface *primary_if,
                                    struct sk_buff *skb)
 {
-       struct ethhdr *ethhdr;
+       struct batadv_bla_claim_dst *bla_dst;
+       uint8_t *hw_src, *hw_dst;
        struct vlan_ethhdr *vhdr;
+       struct ethhdr *ethhdr;
        struct arphdr *arphdr;
-       uint8_t *hw_src, *hw_dst;
-       struct batadv_bla_claim_dst *bla_dst;
-       uint16_t proto;
+       unsigned short vid;
+       __be16 proto;
        int headlen;
-       unsigned short vid = BATADV_NO_FLAGS;
        int ret;
 
+       vid = batadv_get_vid(skb, 0);
        ethhdr = eth_hdr(skb);
 
-       if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+       proto = ethhdr->h_proto;
+       headlen = ETH_HLEN;
+       if (vid & BATADV_VLAN_HAS_TAG) {
                vhdr = (struct vlan_ethhdr *)ethhdr;
-               vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
-               vid |= BATADV_VLAN_HAS_TAG;
-               proto = ntohs(vhdr->h_vlan_encapsulated_proto);
-               headlen = sizeof(*vhdr);
-       } else {
-               proto = ntohs(ethhdr->h_proto);
-               headlen = ETH_HLEN;
+               proto = vhdr->h_vlan_encapsulated_proto;
+               headlen += VLAN_HLEN;
        }
 
-       if (proto != ETH_P_ARP)
+       if (proto != htons(ETH_P_ARP))
                return 0; /* not a claim frame */
 
        /* this must be a ARP frame. check if it is a claim. */
@@ -1317,12 +1315,14 @@ out:
 
 /* @bat_priv: the bat priv with all the soft interface information
  * @orig: originator mac address
+ * @vid: VLAN identifier
  *
- * check if the originator is a gateway for any VLAN ID.
+ * Check if the originator is a gateway for the VLAN identified by vid.
  *
- * returns 1 if it is found, 0 otherwise
+ * Returns true if orig is a backbone for this vid, false otherwise.
  */
-int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig)
+bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig,
+                                   unsigned short vid)
 {
        struct batadv_hashtable *hash = bat_priv->bla.backbone_hash;
        struct hlist_head *head;
@@ -1330,25 +1330,26 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig)
        int i;
 
        if (!atomic_read(&bat_priv->bridge_loop_avoidance))
-               return 0;
+               return false;
 
        if (!hash)
-               return 0;
+               return false;
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) {
-                       if (batadv_compare_eth(backbone_gw->orig, orig)) {
+                       if (batadv_compare_eth(backbone_gw->orig, orig) &&
+                           backbone_gw->vid == vid) {
                                rcu_read_unlock();
-                               return 1;
+                               return true;
                        }
                }
                rcu_read_unlock();
        }
 
-       return 0;
+       return false;
 }
 
 
@@ -1365,10 +1366,8 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig)
 int batadv_bla_is_backbone_gw(struct sk_buff *skb,
                              struct batadv_orig_node *orig_node, int hdr_size)
 {
-       struct ethhdr *ethhdr;
-       struct vlan_ethhdr *vhdr;
        struct batadv_bla_backbone_gw *backbone_gw;
-       unsigned short vid = BATADV_NO_FLAGS;
+       unsigned short vid;
 
        if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance))
                return 0;
@@ -1377,16 +1376,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
        if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
                return 0;
 
-       ethhdr = (struct ethhdr *)(((uint8_t *)skb->data) + hdr_size);
-
-       if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
-               if (!pskb_may_pull(skb, hdr_size + sizeof(struct vlan_ethhdr)))
-                       return 0;
-
-               vhdr = (struct vlan_ethhdr *)(skb->data + hdr_size);
-               vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
-               vid |= BATADV_VLAN_HAS_TAG;
-       }
+       vid = batadv_get_vid(skb, hdr_size);
 
        /* see if this originator is a backbone gw for this VLAN */
        backbone_gw = batadv_backbone_hash_find(orig_node->bat_priv,
index 4b102e7..da173e7 100644 (file)
@@ -30,7 +30,8 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
 int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset);
 int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
                                             void *offset);
-int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig);
+bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig,
+                                   unsigned short vid);
 int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
                                   struct sk_buff *skb);
 void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
@@ -74,10 +75,11 @@ static inline int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
        return 0;
 }
 
-static inline int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
-                                                uint8_t *orig)
+static inline bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
+                                                 uint8_t *orig,
+                                                 unsigned short vid)
 {
-       return 0;
+       return false;
 }
 
 static inline int
index f186a55..049a7a2 100644 (file)
@@ -28,7 +28,6 @@
 #include "gateway_common.h"
 #include "gateway_client.h"
 #include "soft-interface.h"
-#include "vis.h"
 #include "icmp_socket.h"
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
@@ -300,12 +299,6 @@ static int batadv_transtable_local_open(struct inode *inode, struct file *file)
        return single_open(file, batadv_tt_local_seq_print_text, net_dev);
 }
 
-static int batadv_vis_data_open(struct inode *inode, struct file *file)
-{
-       struct net_device *net_dev = (struct net_device *)inode->i_private;
-       return single_open(file, batadv_vis_seq_print_text, net_dev);
-}
-
 struct batadv_debuginfo {
        struct attribute attr;
        const struct file_operations fops;
@@ -356,7 +349,6 @@ static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open);
 #endif
 static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
                        batadv_transtable_local_open);
-static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open);
 #ifdef CONFIG_BATMAN_ADV_NC
 static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
 #endif
@@ -373,7 +365,6 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
        &batadv_debuginfo_dat_cache,
 #endif
        &batadv_debuginfo_transtable_local,
-       &batadv_debuginfo_vis_data,
 #ifdef CONFIG_BATMAN_ADV_NC
        &batadv_debuginfo_nc_nodes,
 #endif
index 06345d4..6c8c393 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/if_ether.h>
 #include <linux/if_arp.h>
+#include <linux/if_vlan.h>
 #include <net/arp.h>
 
 #include "main.h"
@@ -29,7 +30,6 @@
 #include "send.h"
 #include "types.h"
 #include "translation-table.h"
-#include "unicast.h"
 
 static void batadv_dat_purge(struct work_struct *work);
 
@@ -206,15 +206,11 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
  */
 static uint32_t batadv_hash_dat(const void *data, uint32_t size)
 {
-       const unsigned char *key = data;
        uint32_t hash = 0;
-       size_t i;
+       const struct batadv_dat_entry *dat = data;
 
-       for (i = 0; i < 4; i++) {
-               hash += key[i];
-               hash += (hash << 10);
-               hash ^= (hash >> 6);
-       }
+       hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip));
+       hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid));
 
        hash += (hash << 3);
        hash ^= (hash >> 11);
@@ -228,21 +224,26 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
  * table
  * @bat_priv: the bat priv with all the soft interface information
  * @ip: search key
+ * @vid: VLAN identifier
  *
  * Returns the dat_entry if found, NULL otherwise.
  */
 static struct batadv_dat_entry *
-batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
+batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+                          unsigned short vid)
 {
        struct hlist_head *head;
-       struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL;
+       struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL;
        struct batadv_hashtable *hash = bat_priv->dat.hash;
        uint32_t index;
 
        if (!hash)
                return NULL;
 
-       index = batadv_hash_dat(&ip, hash->size);
+       to_find.ip = ip;
+       to_find.vid = vid;
+
+       index = batadv_hash_dat(&to_find, hash->size);
        head = &hash->table[index];
 
        rcu_read_lock();
@@ -266,22 +267,24 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
  * @bat_priv: the bat priv with all the soft interface information
  * @ip: ipv4 to add/edit
  * @mac_addr: mac address to assign to the given ipv4
+ * @vid: VLAN identifier
  */
 static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
-                                uint8_t *mac_addr)
+                                uint8_t *mac_addr, unsigned short vid)
 {
        struct batadv_dat_entry *dat_entry;
        int hash_added;
 
-       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip);
+       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
        /* if this entry is already known, just update it */
        if (dat_entry) {
                if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
                        memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
                dat_entry->last_update = jiffies;
                batadv_dbg(BATADV_DBG_DAT, bat_priv,
-                          "Entry updated: %pI4 %pM\n", &dat_entry->ip,
-                          dat_entry->mac_addr);
+                          "Entry updated: %pI4 %pM (vid: %d)\n",
+                          &dat_entry->ip, dat_entry->mac_addr,
+                          BATADV_PRINT_VID(vid));
                goto out;
        }
 
@@ -290,12 +293,13 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
                goto out;
 
        dat_entry->ip = ip;
+       dat_entry->vid = vid;
        memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
        dat_entry->last_update = jiffies;
        atomic_set(&dat_entry->refcount, 2);
 
        hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
-                                    batadv_hash_dat, &dat_entry->ip,
+                                    batadv_hash_dat, dat_entry,
                                     &dat_entry->hash_entry);
 
        if (unlikely(hash_added != 0)) {
@@ -304,8 +308,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
                goto out;
        }
 
-       batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n",
-                  &dat_entry->ip, dat_entry->mac_addr);
+       batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
+                  &dat_entry->ip, dat_entry->mac_addr, BATADV_PRINT_VID(vid));
 
 out:
        if (dat_entry)
@@ -419,6 +423,10 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
        bool ret = false;
        int j;
 
+       /* check if orig node candidate is running DAT */
+       if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT))
+               goto out;
+
        /* Check if this node has already been selected... */
        for (j = 0; j < select; j++)
                if (res[j].orig_node == candidate)
@@ -588,9 +596,9 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
                        goto free_orig;
 
                tmp_skb = pskb_copy(skb, GFP_ATOMIC);
-               if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb,
-                                                     cand[i].orig_node,
-                                                     packet_subtype)) {
+               if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb,
+                                                          cand[i].orig_node,
+                                                          packet_subtype)) {
                        kfree_skb(tmp_skb);
                        goto free_neigh;
                }
@@ -625,6 +633,59 @@ out:
        return ret;
 }
 
+/**
+ * batadv_dat_tvlv_container_update - update the dat tvlv container after dat
+ *  setting change
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv)
+{
+       char dat_mode;
+
+       dat_mode = atomic_read(&bat_priv->distributed_arp_table);
+
+       switch (dat_mode) {
+       case 0:
+               batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
+               break;
+       case 1:
+               batadv_tvlv_container_register(bat_priv, BATADV_TVLV_DAT, 1,
+                                              NULL, 0);
+               break;
+       }
+}
+
+/**
+ * batadv_dat_status_update - update the dat tvlv container after dat
+ *  setting change
+ * @net_dev: the soft interface net device
+ */
+void batadv_dat_status_update(struct net_device *net_dev)
+{
+       struct batadv_priv *bat_priv = netdev_priv(net_dev);
+       batadv_dat_tvlv_container_update(bat_priv);
+}
+
+/**
+ * batadv_gw_tvlv_ogm_handler_v1 - process incoming dat tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+                                          struct batadv_orig_node *orig,
+                                          uint8_t flags,
+                                          void *tvlv_value,
+                                          uint16_t tvlv_value_len)
+{
+       if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
+               orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT;
+       else
+               orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT;
+}
+
 /**
  * batadv_dat_hash_free - free the local DAT hash table
  * @bat_priv: the bat priv with all the soft interface information
@@ -657,6 +718,10 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
 
        batadv_dat_start_timer(bat_priv);
 
+       batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1,
+                                    NULL, BATADV_TVLV_DAT, 1,
+                                    BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+       batadv_dat_tvlv_container_update(bat_priv);
        return 0;
 }
 
@@ -666,6 +731,9 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
  */
 void batadv_dat_free(struct batadv_priv *bat_priv)
 {
+       batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
+       batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1);
+
        cancel_delayed_work_sync(&bat_priv->dat.work);
 
        batadv_dat_hash_free(bat_priv);
@@ -693,8 +761,8 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
                goto out;
 
        seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name);
-       seq_printf(seq, "          %-7s          %-13s %5s\n", "IPv4", "MAC",
-                  "last-seen");
+       seq_printf(seq, "          %-7s          %-9s %4s %11s\n", "IPv4",
+                  "MAC", "VID", "last-seen");
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
@@ -707,8 +775,9 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
                        last_seen_msecs = last_seen_msecs % 60000;
                        last_seen_secs = last_seen_msecs / 1000;
 
-                       seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n",
+                       seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n",
                                   &dat_entry->ip, dat_entry->mac_addr,
+                                  BATADV_PRINT_VID(dat_entry->vid),
                                   last_seen_mins, last_seen_secs);
                }
                rcu_read_unlock();
@@ -794,6 +863,31 @@ out:
        return type;
 }
 
+/**
+ * batadv_dat_get_vid - extract the VLAN identifier from skb if any
+ * @skb: the buffer containing the packet to extract the VID from
+ * @hdr_size: the size of the batman-adv header encapsulating the packet
+ *
+ * If the packet embedded in the skb is vlan tagged this function returns the
+ * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
+ */
+static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size)
+{
+       unsigned short vid;
+
+       vid = batadv_get_vid(skb, *hdr_size);
+
+       /* ARP parsing functions jump forward of hdr_size + ETH_HLEN.
+        * If the header contained in the packet is a VLAN one (which is longer)
+        * hdr_size is updated so that the functions will still skip the
+        * correct amount of bytes.
+        */
+       if (vid & BATADV_VLAN_HAS_TAG)
+               *hdr_size += VLAN_HLEN;
+
+       return vid;
+}
+
 /**
  * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to
  * answer using DAT
@@ -813,26 +907,31 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
        bool ret = false;
        struct batadv_dat_entry *dat_entry = NULL;
        struct sk_buff *skb_new;
+       int hdr_size = 0;
+       unsigned short vid;
 
        if (!atomic_read(&bat_priv->distributed_arp_table))
                goto out;
 
-       type = batadv_arp_get_type(bat_priv, skb, 0);
+       vid = batadv_dat_get_vid(skb, &hdr_size);
+
+       type = batadv_arp_get_type(bat_priv, skb, hdr_size);
        /* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast
         * message to the selected DHT candidates
         */
        if (type != ARPOP_REQUEST)
                goto out;
 
-       batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST");
+       batadv_dbg_arp(bat_priv, skb, type, hdr_size,
+                      "Parsing outgoing ARP REQUEST");
 
-       ip_src = batadv_arp_ip_src(skb, 0);
-       hw_src = batadv_arp_hw_src(skb, 0);
-       ip_dst = batadv_arp_ip_dst(skb, 0);
+       ip_src = batadv_arp_ip_src(skb, hdr_size);
+       hw_src = batadv_arp_hw_src(skb, hdr_size);
+       ip_dst = batadv_arp_ip_dst(skb, hdr_size);
 
-       batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+       batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
 
-       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
        if (dat_entry) {
                /* If the ARP request is destined for a local client the local
                 * client will answer itself. DAT would only generate a
@@ -842,7 +941,8 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                 * additional DAT answer may trigger kernel warnings about
                 * a packet coming from the wrong port.
                 */
-               if (batadv_is_my_client(bat_priv, dat_entry->mac_addr)) {
+               if (batadv_is_my_client(bat_priv, dat_entry->mac_addr,
+                                       BATADV_NO_FLAGS)) {
                        ret = true;
                        goto out;
                }
@@ -853,11 +953,15 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                if (!skb_new)
                        goto out;
 
+               if (vid & BATADV_VLAN_HAS_TAG)
+                       skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
+                                                 vid & VLAN_VID_MASK);
+
                skb_reset_mac_header(skb_new);
                skb_new->protocol = eth_type_trans(skb_new,
                                                   bat_priv->soft_iface);
                bat_priv->stats.rx_packets++;
-               bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
+               bat_priv->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size;
                bat_priv->soft_iface->last_rx = jiffies;
 
                netif_rx(skb_new);
@@ -892,11 +996,14 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
        struct sk_buff *skb_new;
        struct batadv_dat_entry *dat_entry = NULL;
        bool ret = false;
+       unsigned short vid;
        int err;
 
        if (!atomic_read(&bat_priv->distributed_arp_table))
                goto out;
 
+       vid = batadv_dat_get_vid(skb, &hdr_size);
+
        type = batadv_arp_get_type(bat_priv, skb, hdr_size);
        if (type != ARPOP_REQUEST)
                goto out;
@@ -908,9 +1015,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
        batadv_dbg_arp(bat_priv, skb, type, hdr_size,
                       "Parsing incoming ARP REQUEST");
 
-       batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+       batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
 
-       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
        if (!dat_entry)
                goto out;
 
@@ -921,17 +1028,22 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
        if (!skb_new)
                goto out;
 
+       if (vid & BATADV_VLAN_HAS_TAG)
+               skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
+                                         vid & VLAN_VID_MASK);
+
        /* To preserve backwards compatibility, the node has choose the outgoing
         * format based on the incoming request packet type. The assumption is
         * that a node not using the 4addr packet format doesn't support it.
         */
        if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
-               err = batadv_unicast_4addr_send_skb(bat_priv, skb_new,
-                                                   BATADV_P_DAT_CACHE_REPLY);
+               err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new,
+                                                  BATADV_P_DAT_CACHE_REPLY,
+                                                  vid);
        else
-               err = batadv_unicast_send_skb(bat_priv, skb_new);
+               err = batadv_send_skb_via_tt(bat_priv, skb_new, vid);
 
-       if (!err) {
+       if (err != NET_XMIT_DROP) {
                batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);
                ret = true;
        }
@@ -954,23 +1066,28 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
        uint16_t type;
        __be32 ip_src, ip_dst;
        uint8_t *hw_src, *hw_dst;
+       int hdr_size = 0;
+       unsigned short vid;
 
        if (!atomic_read(&bat_priv->distributed_arp_table))
                return;
 
-       type = batadv_arp_get_type(bat_priv, skb, 0);
+       vid = batadv_dat_get_vid(skb, &hdr_size);
+
+       type = batadv_arp_get_type(bat_priv, skb, hdr_size);
        if (type != ARPOP_REPLY)
                return;
 
-       batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY");
+       batadv_dbg_arp(bat_priv, skb, type, hdr_size,
+                      "Parsing outgoing ARP REPLY");
 
-       hw_src = batadv_arp_hw_src(skb, 0);
-       ip_src = batadv_arp_ip_src(skb, 0);
-       hw_dst = batadv_arp_hw_dst(skb, 0);
-       ip_dst = batadv_arp_ip_dst(skb, 0);
+       hw_src = batadv_arp_hw_src(skb, hdr_size);
+       ip_src = batadv_arp_ip_src(skb, hdr_size);
+       hw_dst = batadv_arp_hw_dst(skb, hdr_size);
+       ip_dst = batadv_arp_ip_dst(skb, hdr_size);
 
-       batadv_dat_entry_add(bat_priv, ip_src, hw_src);
-       batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
+       batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+       batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
 
        /* Send the ARP reply to the candidates for both the IP addresses that
         * the node obtained from the ARP reply
@@ -992,10 +1109,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
        __be32 ip_src, ip_dst;
        uint8_t *hw_src, *hw_dst;
        bool ret = false;
+       unsigned short vid;
 
        if (!atomic_read(&bat_priv->distributed_arp_table))
                goto out;
 
+       vid = batadv_dat_get_vid(skb, &hdr_size);
+
        type = batadv_arp_get_type(bat_priv, skb, hdr_size);
        if (type != ARPOP_REPLY)
                goto out;
@@ -1011,13 +1131,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
        /* Update our internal cache with both the IP addresses the node got
         * within the ARP reply
         */
-       batadv_dat_entry_add(bat_priv, ip_src, hw_src);
-       batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
+       batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+       batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
 
        /* if this REPLY is directed to a client of mine, let's deliver the
         * packet to the interface
         */
-       ret = !batadv_is_my_client(bat_priv, hw_dst);
+       ret = !batadv_is_my_client(bat_priv, hw_dst, vid);
 out:
        if (ret)
                kfree_skb(skb);
@@ -1040,7 +1160,8 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
        __be32 ip_dst;
        struct batadv_dat_entry *dat_entry = NULL;
        bool ret = false;
-       const size_t bcast_len = sizeof(struct batadv_bcast_packet);
+       int hdr_size = sizeof(struct batadv_bcast_packet);
+       unsigned short vid;
 
        if (!atomic_read(&bat_priv->distributed_arp_table))
                goto out;
@@ -1051,12 +1172,14 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
        if (forw_packet->num_packets)
                goto out;
 
-       type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len);
+       vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size);
+
+       type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size);
        if (type != ARPOP_REQUEST)
                goto out;
 
-       ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len);
-       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+       ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size);
+       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
        /* check if the node already got this entry */
        if (!dat_entry) {
                batadv_dbg(BATADV_DBG_DAT, bat_priv,
index 125c8c6..60d853b 100644 (file)
@@ -29,6 +29,7 @@
 
 #define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
 
+void batadv_dat_status_update(struct net_device *net_dev);
 bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                                           struct sk_buff *skb);
 bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
@@ -98,6 +99,10 @@ static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
 
 #else
 
+static inline void batadv_dat_status_update(struct net_device *net_dev)
+{
+}
+
 static inline bool
 batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                                      struct sk_buff *skb)
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
new file mode 100644 (file)
index 0000000..271d321
--- /dev/null
@@ -0,0 +1,491 @@
+/* Copyright (C) 2013 B.A.T.M.A.N. contributors:
+ *
+ * Martin Hundebøll <martin@hundeboll.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "main.h"
+#include "fragmentation.h"
+#include "send.h"
+#include "originator.h"
+#include "routing.h"
+#include "hard-interface.h"
+#include "soft-interface.h"
+
+
+/**
+ * batadv_frag_clear_chain - delete entries in the fragment buffer chain
+ * @head: head of chain with entries.
+ *
+ * Free fragments in the passed hlist. Should be called with appropriate lock.
+ */
+static void batadv_frag_clear_chain(struct hlist_head *head)
+{
+       struct batadv_frag_list_entry *entry;
+       struct hlist_node *node;
+
+       hlist_for_each_entry_safe(entry, node, head, list) {
+               hlist_del(&entry->list);
+               kfree_skb(entry->skb);
+               kfree(entry);
+       }
+}
+
+/**
+ * batadv_frag_purge_orig - free fragments associated to an orig
+ * @orig_node: originator to free fragments from
+ * @check_cb: optional function to tell if an entry should be purged
+ */
+void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
+                           bool (*check_cb)(struct batadv_frag_table_entry *))
+{
+       struct batadv_frag_table_entry *chain;
+       uint8_t i;
+
+       for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
+               chain = &orig_node->fragments[i];
+               spin_lock_bh(&orig_node->fragments[i].lock);
+
+               if (!check_cb || check_cb(chain)) {
+                       batadv_frag_clear_chain(&orig_node->fragments[i].head);
+                       orig_node->fragments[i].size = 0;
+               }
+
+               spin_unlock_bh(&orig_node->fragments[i].lock);
+       }
+}
+
+/**
+ * batadv_frag_size_limit - maximum possible size of packet to be fragmented
+ *
+ * Returns the maximum size of payload that can be fragmented.
+ */
+static int batadv_frag_size_limit(void)
+{
+       int limit = BATADV_FRAG_MAX_FRAG_SIZE;
+
+       limit -= sizeof(struct batadv_frag_packet);
+       limit *= BATADV_FRAG_MAX_FRAGMENTS;
+
+       return limit;
+}
+
+/**
+ * batadv_frag_init_chain - check and prepare fragment chain for new fragment
+ * @chain: chain in fragments table to init
+ * @seqno: sequence number of the received fragment
+ *
+ * Make chain ready for a fragment with sequence number "seqno". Delete existing
+ * entries if they have an "old" sequence number.
+ *
+ * Caller must hold chain->lock.
+ *
+ * Returns true if chain is empty and caller can just insert the new fragment
+ * without searching for the right position.
+ */
+static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
+                                  uint16_t seqno)
+{
+       if (chain->seqno == seqno)
+               return false;
+
+       if (!hlist_empty(&chain->head))
+               batadv_frag_clear_chain(&chain->head);
+
+       chain->size = 0;
+       chain->seqno = seqno;
+
+       return true;
+}
+
+/**
+ * batadv_frag_insert_packet - insert a fragment into a fragment chain
+ * @orig_node: originator that the fragment was received from
+ * @skb: skb to insert
+ * @chain_out: list head to attach complete chains of fragments to
+ *
+ * Insert a new fragment into the reverse ordered chain in the right table
+ * entry. The hash table entry is cleared if "old" fragments exist in it.
+ *
+ * Returns true if skb is buffered, false on error. If the chain has all the
+ * fragments needed to merge the packet, the chain is moved to the passed head
+ * to avoid locking the chain in the table.
+ */
+static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
+                                     struct sk_buff *skb,
+                                     struct hlist_head *chain_out)
+{
+       struct batadv_frag_table_entry *chain;
+       struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
+       struct batadv_frag_packet *frag_packet;
+       uint8_t bucket;
+       uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet);
+       bool ret = false;
+
+       /* Linearize packet to avoid linearizing 16 packets in a row when doing
+        * the later merge. Non-linear merge should be added to remove this
+        * linearization.
+        */
+       if (skb_linearize(skb) < 0)
+               goto err;
+
+       frag_packet = (struct batadv_frag_packet *)skb->data;
+       seqno = ntohs(frag_packet->seqno);
+       bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
+
+       frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC);
+       if (!frag_entry_new)
+               goto err;
+
+       frag_entry_new->skb = skb;
+       frag_entry_new->no = frag_packet->no;
+
+       /* Select entry in the "chain table" and delete any prior fragments
+        * with another sequence number. batadv_frag_init_chain() returns true,
+        * if the list is empty at return.
+        */
+       chain = &orig_node->fragments[bucket];
+       spin_lock_bh(&chain->lock);
+       if (batadv_frag_init_chain(chain, seqno)) {
+               hlist_add_head(&frag_entry_new->list, &chain->head);
+               chain->size = skb->len - hdr_size;
+               chain->timestamp = jiffies;
+               ret = true;
+               goto out;
+       }
+
+       /* Find the position for the new fragment. */
+       hlist_for_each_entry(frag_entry_curr, &chain->head, list) {
+               /* Drop packet if fragment already exists. */
+               if (frag_entry_curr->no == frag_entry_new->no)
+                       goto err_unlock;
+
+               /* Order fragments from highest to lowest. */
+               if (frag_entry_curr->no < frag_entry_new->no) {
+                       hlist_add_before(&frag_entry_new->list,
+                                        &frag_entry_curr->list);
+                       chain->size += skb->len - hdr_size;
+                       chain->timestamp = jiffies;
+                       ret = true;
+                       goto out;
+               }
+       }
+
+       /* Reached the end of the list, so insert after 'frag_entry_curr'. */
+       if (likely(frag_entry_curr)) {
+               hlist_add_after(&frag_entry_curr->list, &frag_entry_new->list);
+               chain->size += skb->len - hdr_size;
+               chain->timestamp = jiffies;
+               ret = true;
+       }
+
+out:
+       if (chain->size > batadv_frag_size_limit() ||
+           ntohs(frag_packet->total_size) > batadv_frag_size_limit()) {
+               /* Clear chain if total size of either the list or the packet
+                * exceeds the maximum size of one merged packet.
+                */
+               batadv_frag_clear_chain(&chain->head);
+               chain->size = 0;
+       } else if (ntohs(frag_packet->total_size) == chain->size) {
+               /* All fragments received. Hand over chain to caller. */
+               hlist_move_list(&chain->head, chain_out);
+               chain->size = 0;
+       }
+
+err_unlock:
+       spin_unlock_bh(&chain->lock);
+
+err:
+       if (!ret)
+               kfree(frag_entry_new);
+
+       return ret;
+}
+
+/**
+ * batadv_frag_merge_packets - merge a chain of fragments
+ * @chain: head of chain with fragments
+ * @skb: packet with total size of skb after merging
+ *
+ * Expand the first skb in the chain and copy the content of the remaining
+ * skb's into the expanded one. After doing so, clear the chain.
+ *
+ * Returns the merged skb or NULL on error.
+ */
+static struct sk_buff *
+batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
+{
+       struct batadv_frag_packet *packet;
+       struct batadv_frag_list_entry *entry;
+       struct sk_buff *skb_out = NULL;
+       int size, hdr_size = sizeof(struct batadv_frag_packet);
+
+       /* Make sure incoming skb has non-bogus data. */
+       packet = (struct batadv_frag_packet *)skb->data;
+       size = ntohs(packet->total_size);
+       if (size > batadv_frag_size_limit())
+               goto free;
+
+       /* Remove first entry, as this is the destination for the rest of the
+        * fragments.
+        */
+       entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list);
+       hlist_del(&entry->list);
+       skb_out = entry->skb;
+       kfree(entry);
+
+       /* Make room for the rest of the fragments. */
+       if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) {
+               kfree_skb(skb_out);
+               skb_out = NULL;
+               goto free;
+       }
+
+       /* Move the existing MAC header to just before the payload. (Override
+        * the fragment header.)
+        */
+       skb_pull_rcsum(skb_out, hdr_size);
+       memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
+       skb_set_mac_header(skb_out, -ETH_HLEN);
+       skb_reset_network_header(skb_out);
+       skb_reset_transport_header(skb_out);
+
+       /* Copy the payload of the each fragment into the last skb */
+       hlist_for_each_entry(entry, chain, list) {
+               size = entry->skb->len - hdr_size;
+               memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size,
+                      size);
+       }
+
+free:
+       /* Locking is not needed, because 'chain' is not part of any orig. */
+       batadv_frag_clear_chain(chain);
+       return skb_out;
+}
+
+/**
+ * batadv_frag_skb_buffer - buffer fragment for later merge
+ * @skb: skb to buffer
+ * @orig_node_src: originator that the skb is received from
+ *
+ * Add fragment to buffer and merge fragments if possible.
+ *
+ * There are three possible outcomes: 1) Packet is merged: Return true and
+ * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
+ * to NULL; 3) Error: Return false and leave skb as is.
+ */
+bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node_src)
+{
+       struct sk_buff *skb_out = NULL;
+       struct hlist_head head = HLIST_HEAD_INIT;
+       bool ret = false;
+
+       /* Add packet to buffer and table entry if merge is possible. */
+       if (!batadv_frag_insert_packet(orig_node_src, *skb, &head))
+               goto out_err;
+
+       /* Leave if more fragments are needed to merge. */
+       if (hlist_empty(&head))
+               goto out;
+
+       skb_out = batadv_frag_merge_packets(&head, *skb);
+       if (!skb_out)
+               goto out_err;
+
+out:
+       *skb = skb_out;
+       ret = true;
+out_err:
+       return ret;
+}
+
+/**
+ * batadv_frag_skb_fwd - forward fragments that would exceed MTU when merged
+ * @skb: skb to forward
+ * @recv_if: interface that the skb is received on
+ * @orig_node_src: originator that the skb is received from
+ *
+ * Look up the next-hop of the fragments payload and check if the merged packet
+ * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+ * without merging it.
+ *
+ * Returns true if the fragment is consumed/forwarded, false otherwise.
+ */
+bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+                        struct batadv_orig_node *orig_node_src)
+{
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_orig_node *orig_node_dst = NULL;
+       struct batadv_neigh_node *neigh_node = NULL;
+       struct batadv_frag_packet *packet;
+       uint16_t total_size;
+       bool ret = false;
+
+       packet = (struct batadv_frag_packet *)skb->data;
+       orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest);
+       if (!orig_node_dst)
+               goto out;
+
+       neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if);
+       if (!neigh_node)
+               goto out;
+
+       /* Forward the fragment, if the merged packet would be too big to
+        * be assembled.
+        */
+       total_size = ntohs(packet->total_size);
+       if (total_size > neigh_node->if_incoming->net_dev->mtu) {
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+                                  skb->len + ETH_HLEN);
+
+               packet->header.ttl--;
+               batadv_send_skb_packet(skb, neigh_node->if_incoming,
+                                      neigh_node->addr);
+               ret = true;
+       }
+
+out:
+       if (orig_node_dst)
+               batadv_orig_node_free_ref(orig_node_dst);
+       if (neigh_node)
+               batadv_neigh_node_free_ref(neigh_node);
+       return ret;
+}
+
+/**
+ * batadv_frag_create - create a fragment from skb
+ * @skb: skb to create fragment from
+ * @frag_head: header to use in new fragment
+ * @mtu: size of new fragment
+ *
+ * Split the passed skb into two fragments: A new one with size matching the
+ * passed mtu and the old one with the rest. The new skb contains data from the
+ * tail of the old skb.
+ *
+ * Returns the new fragment, NULL on error.
+ */
+static struct sk_buff *batadv_frag_create(struct sk_buff *skb,
+                                         struct batadv_frag_packet *frag_head,
+                                         unsigned int mtu)
+{
+       struct sk_buff *skb_fragment;
+       unsigned header_size = sizeof(*frag_head);
+       unsigned fragment_size = mtu - header_size;
+
+       skb_fragment = netdev_alloc_skb(NULL, mtu + ETH_HLEN);
+       if (!skb_fragment)
+               goto err;
+
+       skb->priority = TC_PRIO_CONTROL;
+
+       /* Eat the last mtu-bytes of the skb */
+       skb_reserve(skb_fragment, header_size + ETH_HLEN);
+       skb_split(skb, skb_fragment, skb->len - fragment_size);
+
+       /* Add the header */
+       skb_push(skb_fragment, header_size);
+       memcpy(skb_fragment->data, frag_head, header_size);
+
+err:
+       return skb_fragment;
+}
+
+/**
+ * batadv_frag_send_packet - create up to 16 fragments from the passed skb
+ * @skb: skb to create fragments from
+ * @orig_node: final destination of the created fragments
+ * @neigh_node: next-hop of the created fragments
+ *
+ * Returns true on success, false otherwise.
+ */
+bool batadv_frag_send_packet(struct sk_buff *skb,
+                            struct batadv_orig_node *orig_node,
+                            struct batadv_neigh_node *neigh_node)
+{
+       struct batadv_priv *bat_priv;
+       struct batadv_hard_iface *primary_if;
+       struct batadv_frag_packet frag_header;
+       struct sk_buff *skb_fragment;
+       unsigned mtu = neigh_node->if_incoming->net_dev->mtu;
+       unsigned header_size = sizeof(frag_header);
+       unsigned max_fragment_size, max_packet_size;
+
+       /* To avoid merge and refragmentation at next-hops we never send
+        * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE
+        */
+       mtu = min_t(unsigned, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
+       max_fragment_size = (mtu - header_size - ETH_HLEN);
+       max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS;
+
+       /* Don't even try to fragment, if we need more than 16 fragments */
+       if (skb->len > max_packet_size)
+               goto out_err;
+
+       bat_priv = orig_node->bat_priv;
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out_err;
+
+       /* Create one header to be copied to all fragments */
+       frag_header.header.packet_type = BATADV_UNICAST_FRAG;
+       frag_header.header.version = BATADV_COMPAT_VERSION;
+       frag_header.header.ttl = BATADV_TTL;
+       frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno));
+       frag_header.reserved = 0;
+       frag_header.no = 0;
+       frag_header.total_size = htons(skb->len);
+       memcpy(frag_header.orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+       memcpy(frag_header.dest, orig_node->orig, ETH_ALEN);
+
+       /* Eat and send fragments from the tail of skb */
+       while (skb->len > max_fragment_size) {
+               skb_fragment = batadv_frag_create(skb, &frag_header, mtu);
+               if (!skb_fragment)
+                       goto out_err;
+
+               batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
+               batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
+                                  skb_fragment->len + ETH_HLEN);
+               batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming,
+                                      neigh_node->addr);
+               frag_header.no++;
+
+               /* The initial check in this function should cover this case */
+               if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1)
+                       goto out_err;
+       }
+
+       /* Make room for the fragment header. */
+       if (batadv_skb_head_push(skb, header_size) < 0 ||
+           pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0)
+               goto out_err;
+
+       memcpy(skb->data, &frag_header, header_size);
+
+       /* Send the last fragment */
+       batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
+       batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
+                          skb->len + ETH_HLEN);
+       batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+
+       return true;
+out_err:
+       return false;
+}
diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
new file mode 100644 (file)
index 0000000..ca029e2
--- /dev/null
@@ -0,0 +1,50 @@
+/* Copyright (C) 2013 B.A.T.M.A.N. contributors:
+ *
+ * Martin Hundebøll <martin@hundeboll.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_
+#define _NET_BATMAN_ADV_FRAGMENTATION_H_
+
+void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+                           bool (*check_cb)(struct batadv_frag_table_entry *));
+bool batadv_frag_skb_fwd(struct sk_buff *skb,
+                        struct batadv_hard_iface *recv_if,
+                        struct batadv_orig_node *orig_node_src);
+bool batadv_frag_skb_buffer(struct sk_buff **skb,
+                           struct batadv_orig_node *orig_node);
+bool batadv_frag_send_packet(struct sk_buff *skb,
+                            struct batadv_orig_node *orig_node,
+                            struct batadv_neigh_node *neigh_node);
+
+/**
+ * batadv_frag_check_entry - check if a list of fragments has timed out
+ * @frags_entry: table entry to check
+ *
+ * Returns true if the frags entry has timed out, false otherwise.
+ */
+static inline bool
+batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry)
+{
+       if (!hlist_empty(&frags_entry->head) &&
+           batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT))
+               return true;
+       else
+               return false;
+}
+
+#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */
index 1ce4b87..2449afa 100644 (file)
@@ -118,7 +118,6 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
        uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
        uint32_t gw_divisor;
        uint8_t max_tq = 0;
-       int down, up;
        uint8_t tq_avg;
        struct batadv_orig_node *orig_node;
 
@@ -138,14 +137,13 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
                if (!atomic_inc_not_zero(&gw_node->refcount))
                        goto next;
 
-               tq_avg = router->tq_avg;
+               tq_avg = router->bat_iv.tq_avg;
 
                switch (atomic_read(&bat_priv->gw_sel_class)) {
                case 1: /* fast connection */
-                       batadv_gw_bandwidth_to_kbit(orig_node->gw_flags,
-                                                   &down, &up);
-
-                       tmp_gw_factor = tq_avg * tq_avg * down * 100 * 100;
+                       tmp_gw_factor = tq_avg * tq_avg;
+                       tmp_gw_factor *= gw_node->bandwidth_down;
+                       tmp_gw_factor *= 100 * 100;
                        tmp_gw_factor /= gw_divisor;
 
                        if ((tmp_gw_factor > max_gw_factor) ||
@@ -223,11 +221,6 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
        struct batadv_neigh_node *router = NULL;
        char gw_addr[18] = { '\0' };
 
-       /* The batman daemon checks here if we already passed a full originator
-        * cycle in order to make sure we don't choose the first gateway we
-        * hear about. This check is based on the daemon's uptime which we
-        * don't have.
-        */
        if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
                goto out;
 
@@ -258,16 +251,22 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
                                    NULL);
        } else if ((!curr_gw) && (next_gw)) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n",
+                          "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
                           next_gw->orig_node->orig,
-                          next_gw->orig_node->gw_flags, router->tq_avg);
+                          next_gw->bandwidth_down / 10,
+                          next_gw->bandwidth_down % 10,
+                          next_gw->bandwidth_up / 10,
+                          next_gw->bandwidth_up % 10, router->bat_iv.tq_avg);
                batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
                                    gw_addr);
        } else {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Changing route to gateway %pM (gw_flags: %i, tq: %i)\n",
+                          "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
                           next_gw->orig_node->orig,
-                          next_gw->orig_node->gw_flags, router->tq_avg);
+                          next_gw->bandwidth_down / 10,
+                          next_gw->bandwidth_down % 10,
+                          next_gw->bandwidth_up / 10,
+                          next_gw->bandwidth_up % 10, router->bat_iv.tq_avg);
                batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
                                    gw_addr);
        }
@@ -306,8 +305,8 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv,
        if (!router_orig)
                goto out;
 
-       gw_tq_avg = router_gw->tq_avg;
-       orig_tq_avg = router_orig->tq_avg;
+       gw_tq_avg = router_gw->bat_iv.tq_avg;
+       orig_tq_avg = router_orig->bat_iv.tq_avg;
 
        /* the TQ value has to be better */
        if (orig_tq_avg < gw_tq_avg)
@@ -337,12 +336,20 @@ out:
        return;
 }
 
+/**
+ * batadv_gw_node_add - add gateway node to list of available gateways
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator announcing gateway capabilities
+ * @gateway: announced bandwidth information
+ */
 static void batadv_gw_node_add(struct batadv_priv *bat_priv,
                               struct batadv_orig_node *orig_node,
-                              uint8_t new_gwflags)
+                              struct batadv_tvlv_gateway_data *gateway)
 {
        struct batadv_gw_node *gw_node;
-       int down, up;
+
+       if (gateway->bandwidth_down == 0)
+               return;
 
        gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
        if (!gw_node)
@@ -356,73 +363,116 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
        hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list);
        spin_unlock_bh(&bat_priv->gw.list_lock);
 
-       batadv_gw_bandwidth_to_kbit(new_gwflags, &down, &up);
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                  "Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n",
-                  orig_node->orig, new_gwflags,
-                  (down > 2048 ? down / 1024 : down),
-                  (down > 2048 ? "MBit" : "KBit"),
-                  (up > 2048 ? up / 1024 : up),
-                  (up > 2048 ? "MBit" : "KBit"));
+                  "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
+                  orig_node->orig,
+                  ntohl(gateway->bandwidth_down) / 10,
+                  ntohl(gateway->bandwidth_down) % 10,
+                  ntohl(gateway->bandwidth_up) / 10,
+                  ntohl(gateway->bandwidth_up) % 10);
 }
 
-void batadv_gw_node_update(struct batadv_priv *bat_priv,
-                          struct batadv_orig_node *orig_node,
-                          uint8_t new_gwflags)
+/**
+ * batadv_gw_node_get - retrieve gateway node from list of available gateways
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator announcing gateway capabilities
+ *
+ * Returns gateway node if found or NULL otherwise.
+ */
+static struct batadv_gw_node *
+batadv_gw_node_get(struct batadv_priv *bat_priv,
+                  struct batadv_orig_node *orig_node)
 {
-       struct batadv_gw_node *gw_node, *curr_gw;
-
-       /* Note: We don't need a NULL check here, since curr_gw never gets
-        * dereferenced. If curr_gw is NULL we also should not exit as we may
-        * have this gateway in our list (duplication check!) even though we
-        * have no currently selected gateway.
-        */
-       curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
+       struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
 
        rcu_read_lock();
-       hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
-               if (gw_node->orig_node != orig_node)
+       hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) {
+               if (gw_node_tmp->orig_node != orig_node)
                        continue;
 
-               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Gateway class of originator %pM changed from %i to %i\n",
-                          orig_node->orig, gw_node->orig_node->gw_flags,
-                          new_gwflags);
+               if (gw_node_tmp->deleted)
+                       continue;
 
-               gw_node->deleted = 0;
+               if (!atomic_inc_not_zero(&gw_node_tmp->refcount))
+                       continue;
 
-               if (new_gwflags == BATADV_NO_FLAGS) {
-                       gw_node->deleted = jiffies;
-                       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                                  "Gateway %pM removed from gateway list\n",
-                                  orig_node->orig);
+               gw_node = gw_node_tmp;
+               break;
+       }
+       rcu_read_unlock();
 
-                       if (gw_node == curr_gw)
-                               goto deselect;
-               }
+       return gw_node;
+}
 
-               goto unlock;
+/**
+ * batadv_gw_node_update - update list of available gateways with changed
+ *  bandwidth information
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator announcing gateway capabilities
+ * @gateway: announced bandwidth information
+ */
+void batadv_gw_node_update(struct batadv_priv *bat_priv,
+                          struct batadv_orig_node *orig_node,
+                          struct batadv_tvlv_gateway_data *gateway)
+{
+       struct batadv_gw_node *gw_node, *curr_gw = NULL;
+
+       gw_node = batadv_gw_node_get(bat_priv, orig_node);
+       if (!gw_node) {
+               batadv_gw_node_add(bat_priv, orig_node, gateway);
+               goto out;
        }
 
-       if (new_gwflags == BATADV_NO_FLAGS)
-               goto unlock;
+       if ((gw_node->bandwidth_down == ntohl(gateway->bandwidth_down)) &&
+           (gw_node->bandwidth_up == ntohl(gateway->bandwidth_up)))
+               goto out;
 
-       batadv_gw_node_add(bat_priv, orig_node, new_gwflags);
-       goto unlock;
+       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                  "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
+                  orig_node->orig,
+                  gw_node->bandwidth_down / 10,
+                  gw_node->bandwidth_down % 10,
+                  gw_node->bandwidth_up / 10,
+                  gw_node->bandwidth_up % 10,
+                  ntohl(gateway->bandwidth_down) / 10,
+                  ntohl(gateway->bandwidth_down) % 10,
+                  ntohl(gateway->bandwidth_up) / 10,
+                  ntohl(gateway->bandwidth_up) % 10);
+
+       gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
+       gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
+
+       gw_node->deleted = 0;
+       if (ntohl(gateway->bandwidth_down) == 0) {
+               gw_node->deleted = jiffies;
+               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                          "Gateway %pM removed from gateway list\n",
+                          orig_node->orig);
 
-deselect:
-       batadv_gw_deselect(bat_priv);
-unlock:
-       rcu_read_unlock();
+               /* Note: We don't need a NULL check here, since curr_gw never
+                * gets dereferenced.
+                */
+               curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
+               if (gw_node == curr_gw)
+                       batadv_gw_deselect(bat_priv);
+       }
 
+out:
        if (curr_gw)
                batadv_gw_node_free_ref(curr_gw);
+       if (gw_node)
+               batadv_gw_node_free_ref(gw_node);
 }
 
 void batadv_gw_node_delete(struct batadv_priv *bat_priv,
                           struct batadv_orig_node *orig_node)
 {
-       batadv_gw_node_update(bat_priv, orig_node, 0);
+       struct batadv_tvlv_gateway_data gateway;
+
+       gateway.bandwidth_down = 0;
+       gateway.bandwidth_up = 0;
+
+       batadv_gw_node_update(bat_priv, orig_node, &gateway);
 }
 
 void batadv_gw_node_purge(struct batadv_priv *bat_priv)
@@ -467,9 +517,7 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
 {
        struct batadv_gw_node *curr_gw;
        struct batadv_neigh_node *router;
-       int down, up, ret = -1;
-
-       batadv_gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
+       int ret = -1;
 
        router = batadv_orig_node_get_router(gw_node->orig_node);
        if (!router)
@@ -477,16 +525,15 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
 
        curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
 
-       ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
+       ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n",
                         (curr_gw == gw_node ? "=>" : "  "),
                         gw_node->orig_node->orig,
-                        router->tq_avg, router->addr,
+                        router->bat_iv.tq_avg, router->addr,
                         router->if_incoming->net_dev->name,
-                        gw_node->orig_node->gw_flags,
-                        (down > 2048 ? down / 1024 : down),
-                        (down > 2048 ? "MBit" : "KBit"),
-                        (up > 2048 ? up / 1024 : up),
-                        (up > 2048 ? "MBit" : "KBit"));
+                        gw_node->bandwidth_down / 10,
+                        gw_node->bandwidth_down % 10,
+                        gw_node->bandwidth_up / 10,
+                        gw_node->bandwidth_up % 10);
 
        batadv_neigh_node_free_ref(router);
        if (curr_gw)
@@ -508,7 +555,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
                goto out;
 
        seq_printf(seq,
-                  "      %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
+                  "      %-12s (%s/%i) %17s [%10s]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
                   "Gateway", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF",
                   BATADV_SOURCE_VERSION, primary_if->net_dev->name,
                   primary_if->net_dev->dev_addr, net_dev->name);
@@ -603,24 +650,29 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
        struct iphdr *iphdr;
        struct ipv6hdr *ipv6hdr;
        struct udphdr *udphdr;
+       struct vlan_ethhdr *vhdr;
+       __be16 proto;
 
        /* check for ethernet header */
        if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
                return false;
        ethhdr = (struct ethhdr *)skb->data;
+       proto = ethhdr->h_proto;
        *header_len += ETH_HLEN;
 
        /* check for initial vlan header */
-       if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+       if (proto == htons(ETH_P_8021Q)) {
                if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
                        return false;
-               ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
+
+               vhdr = (struct vlan_ethhdr *)skb->data;
+               proto = vhdr->h_vlan_encapsulated_proto;
                *header_len += VLAN_HLEN;
        }
 
        /* check for ip header */
-       switch (ntohs(ethhdr->h_proto)) {
-       case ETH_P_IP:
+       switch (proto) {
+       case htons(ETH_P_IP):
                if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
                        return false;
                iphdr = (struct iphdr *)(skb->data + *header_len);
@@ -631,7 +683,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
                        return false;
 
                break;
-       case ETH_P_IPV6:
+       case htons(ETH_P_IPV6):
                if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
                        return false;
                ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
@@ -658,28 +710,44 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
        *header_len += sizeof(*udphdr);
 
        /* check for bootp port */
-       if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
-           (ntohs(udphdr->dest) != 67))
+       if ((proto == htons(ETH_P_IP)) &&
+           (udphdr->dest != htons(67)))
                return false;
 
-       if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
-           (ntohs(udphdr->dest) != 547))
+       if ((proto == htons(ETH_P_IPV6)) &&
+           (udphdr->dest != htons(547)))
                return false;
 
        return true;
 }
 
-/* this call might reallocate skb data */
+/**
+ * batadv_gw_out_of_range - check if the dhcp request destination is the best gw
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the outgoing packet
+ *
+ * Check if the skb is a DHCP request and if it is sent to the current best GW
+ * server. Due to topology changes it may be the case that the GW server
+ * previously selected is not the best one anymore.
+ *
+ * Returns true if the packet destination is unicast and it is not the best gw,
+ * false otherwise.
+ *
+ * This call might reallocate skb data.
+ */
 bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
                            struct sk_buff *skb)
 {
        struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL;
        struct batadv_orig_node *orig_dst_node = NULL;
-       struct batadv_gw_node *curr_gw = NULL;
+       struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL;
        struct ethhdr *ethhdr;
        bool ret, out_of_range = false;
        unsigned int header_len = 0;
        uint8_t curr_tq_avg;
+       unsigned short vid;
+
+       vid = batadv_get_vid(skb, 0);
 
        ret = batadv_gw_is_dhcp_target(skb, &header_len);
        if (!ret)
@@ -687,11 +755,12 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
 
        ethhdr = (struct ethhdr *)skb->data;
        orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
-                                                ethhdr->h_dest);
+                                                ethhdr->h_dest, vid);
        if (!orig_dst_node)
                goto out;
 
-       if (!orig_dst_node->gw_flags)
+       gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
+       if (!gw_node->bandwidth_down == 0)
                goto out;
 
        ret = batadv_is_type_dhcprequest(skb, header_len);
@@ -723,7 +792,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
                if (!neigh_curr)
                        goto out;
 
-               curr_tq_avg = neigh_curr->tq_avg;
+               curr_tq_avg = neigh_curr->bat_iv.tq_avg;
                break;
        case BATADV_GW_MODE_OFF:
        default:
@@ -734,7 +803,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
        if (!neigh_old)
                goto out;
 
-       if (curr_tq_avg - neigh_old->tq_avg > BATADV_GW_THRESHOLD)
+       if (curr_tq_avg - neigh_old->bat_iv.tq_avg > BATADV_GW_THRESHOLD)
                out_of_range = true;
 
 out:
@@ -742,6 +811,8 @@ out:
                batadv_orig_node_free_ref(orig_dst_node);
        if (curr_gw)
                batadv_gw_node_free_ref(curr_gw);
+       if (gw_node)
+               batadv_gw_node_free_ref(gw_node);
        if (neigh_old)
                batadv_neigh_node_free_ref(neigh_old);
        if (neigh_curr)
index ceef4eb..d95c2d2 100644 (file)
@@ -29,7 +29,7 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv,
                              struct batadv_orig_node *orig_node);
 void batadv_gw_node_update(struct batadv_priv *bat_priv,
                           struct batadv_orig_node *orig_node,
-                          uint8_t new_gwflags);
+                          struct batadv_tvlv_gateway_data *gateway);
 void batadv_gw_node_delete(struct batadv_priv *bat_priv,
                           struct batadv_orig_node *orig_node);
 void batadv_gw_node_purge(struct batadv_priv *bat_priv);
index 84bb2b1..b211b0f 100644 (file)
 #include "gateway_common.h"
 #include "gateway_client.h"
 
-/* calculates the gateway class from kbit */
-static void batadv_kbit_to_gw_bandwidth(int down, int up, long *gw_srv_class)
-{
-       int mdown = 0, tdown, tup, difference;
-       uint8_t sbit, part;
-
-       *gw_srv_class = 0;
-       difference = 0x0FFFFFFF;
-
-       /* test all downspeeds */
-       for (sbit = 0; sbit < 2; sbit++) {
-               for (part = 0; part < 16; part++) {
-                       tdown = 32 * (sbit + 2) * (1 << part);
-
-                       if (abs(tdown - down) < difference) {
-                               *gw_srv_class = (sbit << 7) + (part << 3);
-                               difference = abs(tdown - down);
-                               mdown = tdown;
-                       }
-               }
-       }
-
-       /* test all upspeeds */
-       difference = 0x0FFFFFFF;
-
-       for (part = 0; part < 8; part++) {
-               tup = ((part + 1) * (mdown)) / 8;
-
-               if (abs(tup - up) < difference) {
-                       *gw_srv_class = (*gw_srv_class & 0xF8) | part;
-                       difference = abs(tup - up);
-               }
-       }
-}
-
-/* returns the up and downspeeds in kbit, calculated from the class */
-void batadv_gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up)
-{
-       int sbit = (gw_srv_class & 0x80) >> 7;
-       int dpart = (gw_srv_class & 0x78) >> 3;
-       int upart = (gw_srv_class & 0x07);
-
-       if (!gw_srv_class) {
-               *down = 0;
-               *up = 0;
-               return;
-       }
-
-       *down = 32 * (sbit + 2) * (1 << dpart);
-       *up = ((upart + 1) * (*down)) / 8;
-}
-
+/**
+ * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
+ *  and upload bandwidth information
+ * @net_dev: the soft interface net device
+ * @buff: string buffer to parse
+ * @down: pointer holding the returned download bandwidth information
+ * @up: pointer holding the returned upload bandwidth information
+ *
+ * Returns false on parse error and true otherwise.
+ */
 static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
-                                     int *up, int *down)
+                                     uint32_t *down, uint32_t *up)
 {
-       int ret, multi = 1;
+       enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
        char *slash_ptr, *tmp_ptr;
        long ldown, lup;
+       int ret;
 
        slash_ptr = strchr(buff, '/');
        if (slash_ptr)
@@ -88,10 +47,10 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
                tmp_ptr = buff + strlen(buff) - 4;
 
                if (strnicmp(tmp_ptr, "mbit", 4) == 0)
-                       multi = 1024;
+                       bw_unit_type = BATADV_BW_UNIT_MBIT;
 
                if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
-                   (multi > 1))
+                   (bw_unit_type == BATADV_BW_UNIT_MBIT))
                        *tmp_ptr = '\0';
        }
 
@@ -103,20 +62,28 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
                return false;
        }
 
-       *down = ldown * multi;
+       switch (bw_unit_type) {
+       case BATADV_BW_UNIT_MBIT:
+               *down = ldown * 10;
+               break;
+       case BATADV_BW_UNIT_KBIT:
+       default:
+               *down = ldown / 100;
+               break;
+       }
 
        /* we also got some upload info */
        if (slash_ptr) {
-               multi = 1;
+               bw_unit_type = BATADV_BW_UNIT_KBIT;
 
                if (strlen(slash_ptr + 1) > 4) {
                        tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
 
                        if (strnicmp(tmp_ptr, "mbit", 4) == 0)
-                               multi = 1024;
+                               bw_unit_type = BATADV_BW_UNIT_MBIT;
 
                        if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
-                           (multi > 1))
+                           (bw_unit_type == BATADV_BW_UNIT_MBIT))
                                *tmp_ptr = '\0';
                }
 
@@ -128,52 +95,149 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
                        return false;
                }
 
-               *up = lup * multi;
+               switch (bw_unit_type) {
+               case BATADV_BW_UNIT_MBIT:
+                       *up = lup * 10;
+                       break;
+               case BATADV_BW_UNIT_KBIT:
+               default:
+                       *up = lup / 100;
+                       break;
+               }
        }
 
        return true;
 }
 
+/**
+ * batadv_gw_tvlv_container_update - update the gw tvlv container after gateway
+ *  setting change
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv)
+{
+       struct batadv_tvlv_gateway_data gw;
+       uint32_t down, up;
+       char gw_mode;
+
+       gw_mode = atomic_read(&bat_priv->gw_mode);
+
+       switch (gw_mode) {
+       case BATADV_GW_MODE_OFF:
+       case BATADV_GW_MODE_CLIENT:
+               batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
+               break;
+       case BATADV_GW_MODE_SERVER:
+               down = atomic_read(&bat_priv->gw.bandwidth_down);
+               up = atomic_read(&bat_priv->gw.bandwidth_up);
+               gw.bandwidth_down = htonl(down);
+               gw.bandwidth_up = htonl(up);
+               batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1,
+                                              &gw, sizeof(gw));
+               break;
+       }
+}
+
 ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
                                size_t count)
 {
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
-       long gw_bandwidth_tmp = 0;
-       int up = 0, down = 0;
+       uint32_t down_curr, up_curr, down_new = 0, up_new = 0;
        bool ret;
 
-       ret = batadv_parse_gw_bandwidth(net_dev, buff, &up, &down);
+       down_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_down);
+       up_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_up);
+
+       ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new);
        if (!ret)
                goto end;
 
-       if ((!down) || (down < 256))
-               down = 2000;
-
-       if (!up)
-               up = down / 5;
+       if (!down_new)
+               down_new = 1;
 
-       batadv_kbit_to_gw_bandwidth(down, up, &gw_bandwidth_tmp);
+       if (!up_new)
+               up_new = down_new / 5;
 
-       /* the gw bandwidth we guessed above might not match the given
-        * speeds, hence we need to calculate it back to show the number
-        * that is going to be propagated
-        */
-       batadv_gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, &down, &up);
+       if (!up_new)
+               up_new = 1;
 
-       if (atomic_read(&bat_priv->gw_bandwidth) == gw_bandwidth_tmp)
+       if ((down_curr == down_new) && (up_curr == up_new))
                return count;
 
        batadv_gw_deselect(bat_priv);
        batadv_info(net_dev,
-                   "Changing gateway bandwidth from: '%i' to: '%ld' (propagating: %d%s/%d%s)\n",
-                   atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp,
-                   (down > 2048 ? down / 1024 : down),
-                   (down > 2048 ? "MBit" : "KBit"),
-                   (up > 2048 ? up / 1024 : up),
-                   (up > 2048 ? "MBit" : "KBit"));
+                   "Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n",
+                   down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10,
+                   down_new / 10, down_new % 10, up_new / 10, up_new % 10);
 
-       atomic_set(&bat_priv->gw_bandwidth, gw_bandwidth_tmp);
+       atomic_set(&bat_priv->gw.bandwidth_down, down_new);
+       atomic_set(&bat_priv->gw.bandwidth_up, up_new);
+       batadv_gw_tvlv_container_update(bat_priv);
 
 end:
        return count;
 }
+
+/**
+ * batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+                                         struct batadv_orig_node *orig,
+                                         uint8_t flags,
+                                         void *tvlv_value,
+                                         uint16_t tvlv_value_len)
+{
+       struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
+
+       /* only fetch the tvlv value if the handler wasn't called via the
+        * CIFNOTFND flag and if there is data to fetch
+        */
+       if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) ||
+           (tvlv_value_len < sizeof(gateway))) {
+               gateway.bandwidth_down = 0;
+               gateway.bandwidth_up = 0;
+       } else {
+               gateway_ptr = tvlv_value;
+               gateway.bandwidth_down = gateway_ptr->bandwidth_down;
+               gateway.bandwidth_up = gateway_ptr->bandwidth_up;
+               if ((gateway.bandwidth_down == 0) ||
+                   (gateway.bandwidth_up == 0)) {
+                       gateway.bandwidth_down = 0;
+                       gateway.bandwidth_up = 0;
+               }
+       }
+
+       batadv_gw_node_update(bat_priv, orig, &gateway);
+
+       /* restart gateway selection if fast or late switching was enabled */
+       if ((gateway.bandwidth_down != 0) &&
+           (atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) &&
+           (atomic_read(&bat_priv->gw_sel_class) > 2))
+               batadv_gw_check_election(bat_priv, orig);
+}
+
+/**
+ * batadv_gw_init - initialise the gateway handling internals
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_gw_init(struct batadv_priv *bat_priv)
+{
+       batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1,
+                                    NULL, BATADV_TVLV_GW, 1,
+                                    BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+}
+
+/**
+ * batadv_gw_free - free the gateway handling internals
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_gw_free(struct batadv_priv *bat_priv)
+{
+       batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
+       batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1);
+}
index 509b2bf..56384a4 100644 (file)
@@ -26,12 +26,24 @@ enum batadv_gw_modes {
        BATADV_GW_MODE_SERVER,
 };
 
+/**
+ * enum batadv_bandwidth_units - bandwidth unit types
+ * @BATADV_BW_UNIT_KBIT: unit type kbit
+ * @BATADV_BW_UNIT_MBIT: unit type mbit
+ */
+enum batadv_bandwidth_units {
+       BATADV_BW_UNIT_KBIT,
+       BATADV_BW_UNIT_MBIT,
+};
+
 #define BATADV_GW_MODE_OFF_NAME        "off"
 #define BATADV_GW_MODE_CLIENT_NAME     "client"
 #define BATADV_GW_MODE_SERVER_NAME     "server"
 
-void batadv_gw_bandwidth_to_kbit(uint8_t gw_class, int *down, int *up);
 ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
                                size_t count);
+void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv);
+void batadv_gw_init(struct batadv_priv *bat_priv);
+void batadv_gw_free(struct batadv_priv *bat_priv);
 
 #endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */
index c478e6b..57c2a19 100644 (file)
@@ -28,6 +28,7 @@
 #include "originator.h"
 #include "hash.h"
 #include "bridge_loop_avoidance.h"
+#include "gateway_client.h"
 
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
@@ -124,8 +125,11 @@ static int batadv_is_valid_iface(const struct net_device *net_dev)
  *
  * Returns true if the net device is a 802.11 wireless device, false otherwise.
  */
-static bool batadv_is_wifi_netdev(struct net_device *net_device)
+bool batadv_is_wifi_netdev(struct net_device *net_device)
 {
+       if (!net_device)
+               return false;
+
 #ifdef CONFIG_WIRELESS_EXT
        /* pre-cfg80211 drivers have to implement WEXT, so it is possible to
         * check for wireless_handlers != NULL
@@ -141,34 +145,6 @@ static bool batadv_is_wifi_netdev(struct net_device *net_device)
        return false;
 }
 
-/**
- * batadv_is_wifi_iface - check if the given interface represented by ifindex
- *  is a wifi interface
- * @ifindex: interface index to check
- *
- * Returns true if the interface represented by ifindex is a 802.11 wireless
- * device, false otherwise.
- */
-bool batadv_is_wifi_iface(int ifindex)
-{
-       struct net_device *net_device = NULL;
-       bool ret = false;
-
-       if (ifindex == BATADV_NULL_IFINDEX)
-               goto out;
-
-       net_device = dev_get_by_index(&init_net, ifindex);
-       if (!net_device)
-               goto out;
-
-       ret = batadv_is_wifi_netdev(net_device);
-
-out:
-       if (net_device)
-               dev_put(net_device);
-       return ret;
-}
-
 static struct batadv_hard_iface *
 batadv_hardif_get_active(const struct net_device *soft_iface)
 {
@@ -194,22 +170,13 @@ out:
 static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
                                          struct batadv_hard_iface *oldif)
 {
-       struct batadv_vis_packet *vis_packet;
        struct batadv_hard_iface *primary_if;
-       struct sk_buff *skb;
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
        if (!primary_if)
                goto out;
 
        batadv_dat_init_own_addr(bat_priv, primary_if);
-
-       skb = bat_priv->vis.my_info->skb_packet;
-       vis_packet = (struct batadv_vis_packet *)skb->data;
-       memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
-       memcpy(vis_packet->sender_orig,
-              primary_if->net_dev->dev_addr, ETH_ALEN);
-
        batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
 out:
        if (primary_if)
@@ -275,16 +242,10 @@ static void batadv_check_known_mac_addr(const struct net_device *net_dev)
 
 int batadv_hardif_min_mtu(struct net_device *soft_iface)
 {
-       const struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+       struct batadv_priv *bat_priv = netdev_priv(soft_iface);
        const struct batadv_hard_iface *hard_iface;
-       /* allow big frames if all devices are capable to do so
-        * (have MTU > 1500 + BAT_HEADER_LEN)
-        */
        int min_mtu = ETH_DATA_LEN;
 
-       if (atomic_read(&bat_priv->fragmentation))
-               goto out;
-
        rcu_read_lock();
        list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
                if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
@@ -294,23 +255,40 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface)
                if (hard_iface->soft_iface != soft_iface)
                        continue;
 
-               min_mtu = min_t(int,
-                               hard_iface->net_dev->mtu - BATADV_HEADER_LEN,
-                               min_mtu);
+               min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
        }
        rcu_read_unlock();
+
+       atomic_set(&bat_priv->packet_size_max, min_mtu);
+
+       if (atomic_read(&bat_priv->fragmentation) == 0)
+               goto out;
+
+       /* with fragmentation enabled the maximum size of internally generated
+        * packets such as translation table exchanges or tvlv containers, etc
+        * has to be calculated
+        */
+       min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
+       min_mtu -= sizeof(struct batadv_frag_packet);
+       min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
+       atomic_set(&bat_priv->packet_size_max, min_mtu);
+
+       /* with fragmentation enabled we can fragment external packets easily */
+       min_mtu = min_t(int, min_mtu, ETH_DATA_LEN);
+
 out:
-       return min_mtu;
+       return min_mtu - batadv_max_header_len();
 }
 
 /* adjusts the MTU if a new interface with a smaller MTU appeared. */
 void batadv_update_min_mtu(struct net_device *soft_iface)
 {
-       int min_mtu;
+       soft_iface->mtu = batadv_hardif_min_mtu(soft_iface);
 
-       min_mtu = batadv_hardif_min_mtu(soft_iface);
-       if (soft_iface->mtu != min_mtu)
-               soft_iface->mtu = min_mtu;
+       /* Check if the local translate table should be cleaned up to match a
+        * new (and smaller) MTU.
+        */
+       batadv_tt_local_resize_to_mtu(soft_iface);
 }
 
 static void
@@ -388,7 +366,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 {
        struct batadv_priv *bat_priv;
        struct net_device *soft_iface, *master;
-       __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+       __be16 ethertype = htons(ETH_P_BATMAN);
+       int max_header_len = batadv_max_header_len();
        int ret;
 
        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
@@ -453,23 +432,22 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
        hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
        dev_add_pack(&hard_iface->batman_adv_ptype);
 
-       atomic_set(&hard_iface->frag_seqno, 1);
        batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
                    hard_iface->net_dev->name);
 
        if (atomic_read(&bat_priv->fragmentation) &&
-           hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN)
+           hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
                batadv_info(hard_iface->soft_iface,
-                           "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %zi would solve the problem.\n",
+                           "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n",
                            hard_iface->net_dev->name, hard_iface->net_dev->mtu,
-                           ETH_DATA_LEN + BATADV_HEADER_LEN);
+                           ETH_DATA_LEN + max_header_len);
 
        if (!atomic_read(&bat_priv->fragmentation) &&
-           hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN)
+           hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
                batadv_info(hard_iface->soft_iface,
-                           "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %zi.\n",
+                           "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n",
                            hard_iface->net_dev->name, hard_iface->net_dev->mtu,
-                           ETH_DATA_LEN + BATADV_HEADER_LEN);
+                           ETH_DATA_LEN + max_header_len);
 
        if (batadv_hardif_is_iface_up(hard_iface))
                batadv_hardif_activate_interface(hard_iface);
@@ -533,8 +511,12 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
        dev_put(hard_iface->soft_iface);
 
        /* nobody uses this interface anymore */
-       if (!bat_priv->num_ifaces && autodel == BATADV_IF_CLEANUP_AUTO)
-               batadv_softif_destroy_sysfs(hard_iface->soft_iface);
+       if (!bat_priv->num_ifaces) {
+               batadv_gw_check_client_stop(bat_priv);
+
+               if (autodel == BATADV_IF_CLEANUP_AUTO)
+                       batadv_softif_destroy_sysfs(hard_iface->soft_iface);
+       }
 
        netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
        hard_iface->soft_iface = NULL;
@@ -652,6 +634,8 @@ static int batadv_hard_if_event(struct notifier_block *this,
 
        if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) {
                batadv_sysfs_add_meshif(net_dev);
+               bat_priv = netdev_priv(net_dev);
+               batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
                return NOTIFY_DONE;
        }
 
index 4989288..df4c8bd 100644 (file)
@@ -41,6 +41,7 @@ enum batadv_hard_if_cleanup {
 
 extern struct notifier_block batadv_hard_if_notifier;
 
+bool batadv_is_wifi_netdev(struct net_device *net_device);
 struct batadv_hard_iface*
 batadv_hardif_get_by_netdev(const struct net_device *net_dev);
 int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
@@ -51,7 +52,6 @@ void batadv_hardif_remove_interfaces(void);
 int batadv_hardif_min_mtu(struct net_device *soft_iface);
 void batadv_update_min_mtu(struct net_device *soft_iface);
 void batadv_hardif_free_rcu(struct rcu_head *rcu);
-bool batadv_is_wifi_iface(int ifindex);
 
 static inline void
 batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface)
index 5a99bb4..29ae4ef 100644 (file)
@@ -29,7 +29,7 @@
 static struct batadv_socket_client *batadv_socket_client_hash[256];
 
 static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
-                                    struct batadv_icmp_packet_rr *icmp_packet,
+                                    struct batadv_icmp_header *icmph,
                                     size_t icmp_len);
 
 void batadv_socket_init(void)
@@ -155,13 +155,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
        struct batadv_priv *bat_priv = socket_client->bat_priv;
        struct batadv_hard_iface *primary_if = NULL;
        struct sk_buff *skb;
-       struct batadv_icmp_packet_rr *icmp_packet;
-
+       struct batadv_icmp_packet_rr *icmp_packet_rr;
+       struct batadv_icmp_header *icmp_header;
        struct batadv_orig_node *orig_node = NULL;
        struct batadv_neigh_node *neigh_node = NULL;
        size_t packet_len = sizeof(struct batadv_icmp_packet);
 
-       if (len < sizeof(struct batadv_icmp_packet)) {
+       if (len < sizeof(struct batadv_icmp_header)) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Error - can't send packet from char device: invalid packet size\n");
                return -EINVAL;
@@ -174,8 +174,10 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
                goto out;
        }
 
-       if (len >= sizeof(struct batadv_icmp_packet_rr))
-               packet_len = sizeof(struct batadv_icmp_packet_rr);
+       if (len >= BATADV_ICMP_MAX_PACKET_SIZE)
+               packet_len = BATADV_ICMP_MAX_PACKET_SIZE;
+       else
+               packet_len = len;
 
        skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN);
        if (!skb) {
@@ -185,67 +187,78 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
 
        skb->priority = TC_PRIO_CONTROL;
        skb_reserve(skb, ETH_HLEN);
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len);
+       icmp_header = (struct batadv_icmp_header *)skb_put(skb, packet_len);
 
-       if (copy_from_user(icmp_packet, buff, packet_len)) {
+       if (copy_from_user(icmp_header, buff, packet_len)) {
                len = -EFAULT;
                goto free_skb;
        }
 
-       if (icmp_packet->header.packet_type != BATADV_ICMP) {
+       if (icmp_header->header.packet_type != BATADV_ICMP) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n");
                len = -EINVAL;
                goto free_skb;
        }
 
-       if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) {
+       switch (icmp_header->msg_type) {
+       case BATADV_ECHO_REQUEST:
+               if (len < sizeof(struct batadv_icmp_packet)) {
+                       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                                  "Error - can't send packet from char device: invalid packet size\n");
+                       len = -EINVAL;
+                       goto free_skb;
+               }
+
+               if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
+                       goto dst_unreach;
+
+               orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst);
+               if (!orig_node)
+                       goto dst_unreach;
+
+               neigh_node = batadv_orig_node_get_router(orig_node);
+               if (!neigh_node)
+                       goto dst_unreach;
+
+               if (!neigh_node->if_incoming)
+                       goto dst_unreach;
+
+               if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
+                       goto dst_unreach;
+
+               icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header;
+               if (packet_len == sizeof(*icmp_packet_rr))
+                       memcpy(icmp_packet_rr->rr,
+                              neigh_node->if_incoming->net_dev->dev_addr,
+                              ETH_ALEN);
+
+               break;
+       default:
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n");
+                          "Error - can't send packet from char device: got unknown message type\n");
                len = -EINVAL;
                goto free_skb;
        }
 
-       icmp_packet->uid = socket_client->index;
+       icmp_header->uid = socket_client->index;
 
-       if (icmp_packet->header.version != BATADV_COMPAT_VERSION) {
-               icmp_packet->msg_type = BATADV_PARAMETER_PROBLEM;
-               icmp_packet->header.version = BATADV_COMPAT_VERSION;
-               batadv_socket_add_packet(socket_client, icmp_packet,
+       if (icmp_header->header.version != BATADV_COMPAT_VERSION) {
+               icmp_header->msg_type = BATADV_PARAMETER_PROBLEM;
+               icmp_header->header.version = BATADV_COMPAT_VERSION;
+               batadv_socket_add_packet(socket_client, icmp_header,
                                         packet_len);
                goto free_skb;
        }
 
-       if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
-               goto dst_unreach;
-
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst);
-       if (!orig_node)
-               goto dst_unreach;
-
-       neigh_node = batadv_orig_node_get_router(orig_node);
-       if (!neigh_node)
-               goto dst_unreach;
-
-       if (!neigh_node->if_incoming)
-               goto dst_unreach;
-
-       if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
-               goto dst_unreach;
-
-       memcpy(icmp_packet->orig,
-              primary_if->net_dev->dev_addr, ETH_ALEN);
-
-       if (packet_len == sizeof(struct batadv_icmp_packet_rr))
-               memcpy(icmp_packet->rr,
-                      neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN);
+       memcpy(icmp_header->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 
        batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
        goto out;
 
 dst_unreach:
-       icmp_packet->msg_type = BATADV_DESTINATION_UNREACHABLE;
-       batadv_socket_add_packet(socket_client, icmp_packet, packet_len);
+       icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE;
+       batadv_socket_add_packet(socket_client, icmp_header, packet_len);
 free_skb:
        kfree_skb(skb);
 out:
@@ -298,27 +311,40 @@ err:
        return -ENOMEM;
 }
 
+/**
+ * batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace
+ *  on an icmp socket.
+ * @socket_client: the socket this packet belongs to
+ * @icmph: pointer to the header of the icmp packet
+ * @icmp_len: total length of the icmp packet
+ */
 static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
-                                    struct batadv_icmp_packet_rr *icmp_packet,
+                                    struct batadv_icmp_header *icmph,
                                     size_t icmp_len)
 {
        struct batadv_socket_packet *socket_packet;
+       size_t len;
 
        socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC);
 
        if (!socket_packet)
                return;
 
+       len = icmp_len;
+       /* check the maximum length before filling the buffer */
+       if (len > sizeof(socket_packet->icmp_packet))
+               len = sizeof(socket_packet->icmp_packet);
+
        INIT_LIST_HEAD(&socket_packet->list);
-       memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len);
-       socket_packet->icmp_len = icmp_len;
+       memcpy(&socket_packet->icmp_packet, icmph, len);
+       socket_packet->icmp_len = len;
 
        spin_lock_bh(&socket_client->lock);
 
        /* while waiting for the lock the socket_client could have been
         * deleted
         */
-       if (!batadv_socket_client_hash[icmp_packet->uid]) {
+       if (!batadv_socket_client_hash[icmph->uid]) {
                spin_unlock_bh(&socket_client->lock);
                kfree(socket_packet);
                return;
@@ -342,12 +368,18 @@ static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
        wake_up(&socket_client->queue_wait);
 }
 
-void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet,
+/**
+ * batadv_socket_receive_packet - schedule an icmp packet to be received
+ *  locally and sent to userspace.
+ * @icmph: pointer to the header of the icmp packet
+ * @icmp_len: total length of the icmp packet
+ */
+void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
                                  size_t icmp_len)
 {
        struct batadv_socket_client *hash;
 
-       hash = batadv_socket_client_hash[icmp_packet->uid];
+       hash = batadv_socket_client_hash[icmph->uid];
        if (hash)
-               batadv_socket_add_packet(hash, icmp_packet, icmp_len);
+               batadv_socket_add_packet(hash, icmph, icmp_len);
 }
index 1fcca37..6665080 100644 (file)
@@ -24,7 +24,7 @@
 
 void batadv_socket_init(void);
 int batadv_socket_setup(struct batadv_priv *bat_priv);
-void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet,
+void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
                                  size_t icmp_len);
 
 #endif /* _NET_BATMAN_ADV_ICMP_SOCKET_H_ */
index c72d1bc..c51a5e5 100644 (file)
 #include "gateway_client.h"
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
-#include "vis.h"
+#include "gateway_common.h"
 #include "hash.h"
 #include "bat_algo.h"
 #include "network-coding.h"
+#include "fragmentation.h"
 
 
 /* List manipulations on hardif_list have to be rtnl_lock()'ed,
@@ -65,6 +66,7 @@ static int __init batadv_init(void)
        batadv_recv_handler_init();
 
        batadv_iv_init();
+       batadv_nc_init();
 
        batadv_event_workqueue = create_singlethread_workqueue("bat_events");
 
@@ -108,9 +110,11 @@ int batadv_mesh_init(struct net_device *soft_iface)
        spin_lock_init(&bat_priv->tt.req_list_lock);
        spin_lock_init(&bat_priv->tt.roam_list_lock);
        spin_lock_init(&bat_priv->tt.last_changeset_lock);
+       spin_lock_init(&bat_priv->tt.commit_lock);
        spin_lock_init(&bat_priv->gw.list_lock);
-       spin_lock_init(&bat_priv->vis.hash_lock);
-       spin_lock_init(&bat_priv->vis.list_lock);
+       spin_lock_init(&bat_priv->tvlv.container_list_lock);
+       spin_lock_init(&bat_priv->tvlv.handler_list_lock);
+       spin_lock_init(&bat_priv->softif_vlan_list_lock);
 
        INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
        INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
@@ -118,6 +122,9 @@ int batadv_mesh_init(struct net_device *soft_iface)
        INIT_LIST_HEAD(&bat_priv->tt.changes_list);
        INIT_LIST_HEAD(&bat_priv->tt.req_list);
        INIT_LIST_HEAD(&bat_priv->tt.roam_list);
+       INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
+       INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
+       INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
 
        ret = batadv_originator_init(bat_priv);
        if (ret < 0)
@@ -127,13 +134,6 @@ int batadv_mesh_init(struct net_device *soft_iface)
        if (ret < 0)
                goto err;
 
-       batadv_tt_local_add(soft_iface, soft_iface->dev_addr,
-                           BATADV_NULL_IFINDEX);
-
-       ret = batadv_vis_init(bat_priv);
-       if (ret < 0)
-               goto err;
-
        ret = batadv_bla_init(bat_priv);
        if (ret < 0)
                goto err;
@@ -142,10 +142,12 @@ int batadv_mesh_init(struct net_device *soft_iface)
        if (ret < 0)
                goto err;
 
-       ret = batadv_nc_init(bat_priv);
+       ret = batadv_nc_mesh_init(bat_priv);
        if (ret < 0)
                goto err;
 
+       batadv_gw_init(bat_priv);
+
        atomic_set(&bat_priv->gw.reselect, 0);
        atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
 
@@ -164,10 +166,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
 
        batadv_purge_outstanding_packets(bat_priv, NULL);
 
-       batadv_vis_quit(bat_priv);
-
        batadv_gw_node_purge(bat_priv);
-       batadv_nc_free(bat_priv);
+       batadv_nc_mesh_free(bat_priv);
        batadv_dat_free(bat_priv);
        batadv_bla_free(bat_priv);
 
@@ -184,6 +184,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
         */
        batadv_originator_free(bat_priv);
 
+       batadv_gw_free(bat_priv);
+
        free_percpu(bat_priv->bat_counters);
        bat_priv->bat_counters = NULL;
 
@@ -253,6 +255,31 @@ out:
        return primary_if;
 }
 
+/**
+ * batadv_max_header_len - calculate maximum encapsulation overhead for a
+ *  payload packet
+ *
+ * Return the maximum encapsulation overhead in bytes.
+ */
+int batadv_max_header_len(void)
+{
+       int header_len = 0;
+
+       header_len = max_t(int, header_len,
+                          sizeof(struct batadv_unicast_packet));
+       header_len = max_t(int, header_len,
+                          sizeof(struct batadv_unicast_4addr_packet));
+       header_len = max_t(int, header_len,
+                          sizeof(struct batadv_bcast_packet));
+
+#ifdef CONFIG_BATMAN_ADV_NC
+       header_len = max_t(int, header_len,
+                          sizeof(struct batadv_coded_packet));
+#endif
+
+       return header_len;
+}
+
 /**
  * batadv_skb_set_priority - sets skb priority according to packet content
  * @skb: the packet to be sent
@@ -391,22 +418,31 @@ static void batadv_recv_handler_init(void)
        for (i = 0; i < ARRAY_SIZE(batadv_rx_handler); i++)
                batadv_rx_handler[i] = batadv_recv_unhandled_packet;
 
-       /* batman icmp packet */
-       batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
+       for (i = BATADV_UNICAST_MIN; i <= BATADV_UNICAST_MAX; i++)
+               batadv_rx_handler[i] = batadv_recv_unhandled_unicast_packet;
+
+       /* compile time checks for struct member offsets */
+       BUILD_BUG_ON(offsetof(struct batadv_unicast_4addr_packet, src) != 10);
+       BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, dest) != 4);
+       BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, dst) != 4);
+       BUILD_BUG_ON(offsetof(struct batadv_frag_packet, dest) != 4);
+       BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, icmph.dst) != 4);
+       BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, icmph.dst) != 4);
+
+       /* broadcast packet */
+       batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
+
+       /* unicast packets ... */
        /* unicast with 4 addresses packet */
        batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet;
        /* unicast packet */
        batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet;
-       /* fragmented unicast packet */
-       batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_ucast_frag_packet;
-       /* broadcast packet */
-       batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
-       /* vis packet */
-       batadv_rx_handler[BATADV_VIS] = batadv_recv_vis_packet;
-       /* Translation table query (request or response) */
-       batadv_rx_handler[BATADV_TT_QUERY] = batadv_recv_tt_query;
-       /* Roaming advertisement */
-       batadv_rx_handler[BATADV_ROAM_ADV] = batadv_recv_roam_adv;
+       /* unicast tvlv packet */
+       batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv;
+       /* batman icmp packet */
+       batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
+       /* Fragmented packets */
+       batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_frag_packet;
 }
 
 int
@@ -414,7 +450,12 @@ batadv_recv_handler_register(uint8_t packet_type,
                             int (*recv_handler)(struct sk_buff *,
                                                 struct batadv_hard_iface *))
 {
-       if (batadv_rx_handler[packet_type] != &batadv_recv_unhandled_packet)
+       int (*curr)(struct sk_buff *,
+                   struct batadv_hard_iface *);
+       curr = batadv_rx_handler[packet_type];
+
+       if ((curr != batadv_recv_unhandled_packet) &&
+           (curr != batadv_recv_unhandled_unicast_packet))
                return -EBUSY;
 
        batadv_rx_handler[packet_type] = recv_handler;
@@ -460,7 +501,9 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
            !bat_algo_ops->bat_iface_update_mac ||
            !bat_algo_ops->bat_primary_iface_set ||
            !bat_algo_ops->bat_ogm_schedule ||
-           !bat_algo_ops->bat_ogm_emit) {
+           !bat_algo_ops->bat_ogm_emit ||
+           !bat_algo_ops->bat_neigh_cmp ||
+           !bat_algo_ops->bat_neigh_is_equiv_or_better) {
                pr_info("Routing algo '%s' does not implement required ops\n",
                        bat_algo_ops->name);
                ret = -EINVAL;
@@ -535,6 +578,601 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
        return htonl(crc);
 }
 
+/**
+ * batadv_tvlv_handler_free_ref - decrement the tvlv handler refcounter and
+ *  possibly free it
+ * @tvlv_handler: the tvlv handler to free
+ */
+static void
+batadv_tvlv_handler_free_ref(struct batadv_tvlv_handler *tvlv_handler)
+{
+       if (atomic_dec_and_test(&tvlv_handler->refcount))
+               kfree_rcu(tvlv_handler, rcu);
+}
+
+/**
+ * batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list
+ *  based on the provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv handler type to look for
+ * @version: tvlv handler version to look for
+ *
+ * Returns tvlv handler if found or NULL otherwise.
+ */
+static struct batadv_tvlv_handler
+*batadv_tvlv_handler_get(struct batadv_priv *bat_priv,
+                        uint8_t type, uint8_t version)
+{
+       struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tvlv_handler_tmp,
+                                &bat_priv->tvlv.handler_list, list) {
+               if (tvlv_handler_tmp->type != type)
+                       continue;
+
+               if (tvlv_handler_tmp->version != version)
+                       continue;
+
+               if (!atomic_inc_not_zero(&tvlv_handler_tmp->refcount))
+                       continue;
+
+               tvlv_handler = tvlv_handler_tmp;
+               break;
+       }
+       rcu_read_unlock();
+
+       return tvlv_handler;
+}
+
+/**
+ * batadv_tvlv_container_free_ref - decrement the tvlv container refcounter and
+ *  possibly free it
+ * @tvlv_handler: the tvlv container to free
+ */
+static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv)
+{
+       if (atomic_dec_and_test(&tvlv->refcount))
+               kfree(tvlv);
+}
+
+/**
+ * batadv_tvlv_container_get - retrieve tvlv container from the tvlv container
+ *  list based on the provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv container type to look for
+ * @version: tvlv container version to look for
+ *
+ * Has to be called with the appropriate locks being acquired
+ * (tvlv.container_list_lock).
+ *
+ * Returns tvlv container if found or NULL otherwise.
+ */
+static struct batadv_tvlv_container
+*batadv_tvlv_container_get(struct batadv_priv *bat_priv,
+                          uint8_t type, uint8_t version)
+{
+       struct batadv_tvlv_container *tvlv_tmp, *tvlv = NULL;
+
+       hlist_for_each_entry(tvlv_tmp, &bat_priv->tvlv.container_list, list) {
+               if (tvlv_tmp->tvlv_hdr.type != type)
+                       continue;
+
+               if (tvlv_tmp->tvlv_hdr.version != version)
+                       continue;
+
+               if (!atomic_inc_not_zero(&tvlv_tmp->refcount))
+                       continue;
+
+               tvlv = tvlv_tmp;
+               break;
+       }
+
+       return tvlv;
+}
+
+/**
+ * batadv_tvlv_container_list_size - calculate the size of the tvlv container
+ *  list entries
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Has to be called with the appropriate locks being acquired
+ * (tvlv.container_list_lock).
+ *
+ * Returns size of all currently registered tvlv containers in bytes.
+ */
+static uint16_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+{
+       struct batadv_tvlv_container *tvlv;
+       uint16_t tvlv_len = 0;
+
+       hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
+               tvlv_len += sizeof(struct batadv_tvlv_hdr);
+               tvlv_len += ntohs(tvlv->tvlv_hdr.len);
+       }
+
+       return tvlv_len;
+}
+
+/**
+ * batadv_tvlv_container_remove - remove tvlv container from the tvlv container
+ *  list
+ * @tvlv: the to be removed tvlv container
+ *
+ * Has to be called with the appropriate locks being acquired
+ * (tvlv.container_list_lock).
+ */
+static void batadv_tvlv_container_remove(struct batadv_tvlv_container *tvlv)
+{
+       if (!tvlv)
+               return;
+
+       hlist_del(&tvlv->list);
+
+       /* first call to decrement the counter, second call to free */
+       batadv_tvlv_container_free_ref(tvlv);
+       batadv_tvlv_container_free_ref(tvlv);
+}
+
+/**
+ * batadv_tvlv_container_unregister - unregister tvlv container based on the
+ *  provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv container type to unregister
+ * @version: tvlv container type to unregister
+ */
+void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
+                                     uint8_t type, uint8_t version)
+{
+       struct batadv_tvlv_container *tvlv;
+
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv = batadv_tvlv_container_get(bat_priv, type, version);
+       batadv_tvlv_container_remove(tvlv);
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+}
+
+/**
+ * batadv_tvlv_container_register - register tvlv type, version and content
+ *  to be propagated with each (primary interface) OGM
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv container type
+ * @version: tvlv container version
+ * @tvlv_value: tvlv container content
+ * @tvlv_value_len: tvlv container content length
+ *
+ * If a container of the same type and version was already registered the new
+ * content is going to replace the old one.
+ */
+void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   uint8_t type, uint8_t version,
+                                   void *tvlv_value, uint16_t tvlv_value_len)
+{
+       struct batadv_tvlv_container *tvlv_old, *tvlv_new;
+
+       if (!tvlv_value)
+               tvlv_value_len = 0;
+
+       tvlv_new = kzalloc(sizeof(*tvlv_new) + tvlv_value_len, GFP_ATOMIC);
+       if (!tvlv_new)
+               return;
+
+       tvlv_new->tvlv_hdr.version = version;
+       tvlv_new->tvlv_hdr.type = type;
+       tvlv_new->tvlv_hdr.len = htons(tvlv_value_len);
+
+       memcpy(tvlv_new + 1, tvlv_value, ntohs(tvlv_new->tvlv_hdr.len));
+       INIT_HLIST_NODE(&tvlv_new->list);
+       atomic_set(&tvlv_new->refcount, 1);
+
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_old = batadv_tvlv_container_get(bat_priv, type, version);
+       batadv_tvlv_container_remove(tvlv_old);
+       hlist_add_head(&tvlv_new->list, &bat_priv->tvlv.container_list);
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+}
+
+/**
+ * batadv_tvlv_realloc_packet_buff - reallocate packet buffer to accomodate
+ *  requested packet size
+ * @packet_buff: packet buffer
+ * @packet_buff_len: packet buffer size
+ * @packet_min_len: requested packet minimum size
+ * @additional_packet_len: requested additional packet size on top of minimum
+ *  size
+ *
+ * Returns true of the packet buffer could be changed to the requested size,
+ * false otherwise.
+ */
+static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+                                           int *packet_buff_len,
+                                           int min_packet_len,
+                                           int additional_packet_len)
+{
+       unsigned char *new_buff;
+
+       new_buff = kmalloc(min_packet_len + additional_packet_len, GFP_ATOMIC);
+
+       /* keep old buffer if kmalloc should fail */
+       if (new_buff) {
+               memcpy(new_buff, *packet_buff, min_packet_len);
+               kfree(*packet_buff);
+               *packet_buff = new_buff;
+               *packet_buff_len = min_packet_len + additional_packet_len;
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * batadv_tvlv_container_ogm_append - append tvlv container content to given
+ *  OGM packet buffer
+ * @bat_priv: the bat priv with all the soft interface information
+ * @packet_buff: ogm packet buffer
+ * @packet_buff_len: ogm packet buffer size including ogm header and tvlv
+ *  content
+ * @packet_min_len: ogm header size to be preserved for the OGM itself
+ *
+ * The ogm packet might be enlarged or shrunk depending on the current size
+ * and the size of the to-be-appended tvlv containers.
+ *
+ * Returns size of all appended tvlv containers in bytes.
+ */
+uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                         unsigned char **packet_buff,
+                                         int *packet_buff_len,
+                                         int packet_min_len)
+{
+       struct batadv_tvlv_container *tvlv;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       uint16_t tvlv_value_len;
+       void *tvlv_value;
+       bool ret;
+
+       spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+       tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
+
+       ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+                                             packet_min_len, tvlv_value_len);
+
+       if (!ret)
+               goto end;
+
+       if (!tvlv_value_len)
+               goto end;
+
+       tvlv_value = (*packet_buff) + packet_min_len;
+
+       hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
+               tvlv_hdr = tvlv_value;
+               tvlv_hdr->type = tvlv->tvlv_hdr.type;
+               tvlv_hdr->version = tvlv->tvlv_hdr.version;
+               tvlv_hdr->len = tvlv->tvlv_hdr.len;
+               tvlv_value = tvlv_hdr + 1;
+               memcpy(tvlv_value, tvlv + 1, ntohs(tvlv->tvlv_hdr.len));
+               tvlv_value = (uint8_t *)tvlv_value + ntohs(tvlv->tvlv_hdr.len);
+       }
+
+end:
+       spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+       return tvlv_value_len;
+}
+
+/**
+ * batadv_tvlv_call_handler - parse the given tvlv buffer to call the
+ *  appropriate handlers
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tvlv_handler: tvlv callback function handling the tvlv content
+ * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @orig_node: orig node emitting the ogm packet
+ * @src: source mac address of the unicast packet
+ * @dst: destination mac address of the unicast packet
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ *
+ * Returns success if handler was not found or the return value of the handler
+ * callback.
+ */
+static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+                                   struct batadv_tvlv_handler *tvlv_handler,
+                                   bool ogm_source,
+                                   struct batadv_orig_node *orig_node,
+                                   uint8_t *src, uint8_t *dst,
+                                   void *tvlv_value, uint16_t tvlv_value_len)
+{
+       if (!tvlv_handler)
+               return NET_RX_SUCCESS;
+
+       if (ogm_source) {
+               if (!tvlv_handler->ogm_handler)
+                       return NET_RX_SUCCESS;
+
+               if (!orig_node)
+                       return NET_RX_SUCCESS;
+
+               tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                         BATADV_NO_FLAGS,
+                                         tvlv_value, tvlv_value_len);
+               tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+       } else {
+               if (!src)
+                       return NET_RX_SUCCESS;
+
+               if (!dst)
+                       return NET_RX_SUCCESS;
+
+               if (!tvlv_handler->unicast_handler)
+                       return NET_RX_SUCCESS;
+
+               return tvlv_handler->unicast_handler(bat_priv, src,
+                                                    dst, tvlv_value,
+                                                    tvlv_value_len);
+       }
+
+       return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_tvlv_containers_process - parse the given tvlv buffer to call the
+ *  appropriate handlers
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @orig_node: orig node emitting the ogm packet
+ * @src: source mac address of the unicast packet
+ * @dst: destination mac address of the unicast packet
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ *
+ * Returns success when processing an OGM or the return value of all called
+ * handler callbacks.
+ */
+int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  bool ogm_source,
+                                  struct batadv_orig_node *orig_node,
+                                  uint8_t *src, uint8_t *dst,
+                                  void *tvlv_value, uint16_t tvlv_value_len)
+{
+       struct batadv_tvlv_handler *tvlv_handler;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       uint16_t tvlv_value_cont_len;
+       uint8_t cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+       int ret = NET_RX_SUCCESS;
+
+       while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
+               tvlv_hdr = tvlv_value;
+               tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+               tvlv_value = tvlv_hdr + 1;
+               tvlv_value_len -= sizeof(*tvlv_hdr);
+
+               if (tvlv_value_cont_len > tvlv_value_len)
+                       break;
+
+               tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+                                                      tvlv_hdr->type,
+                                                      tvlv_hdr->version);
+
+               ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
+                                               ogm_source, orig_node,
+                                               src, dst, tvlv_value,
+                                               tvlv_value_cont_len);
+               if (tvlv_handler)
+                       batadv_tvlv_handler_free_ref(tvlv_handler);
+               tvlv_value = (uint8_t *)tvlv_value + tvlv_value_cont_len;
+               tvlv_value_len -= tvlv_value_cont_len;
+       }
+
+       if (!ogm_source)
+               return ret;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tvlv_handler,
+                                &bat_priv->tvlv.handler_list, list) {
+               if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+                   !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+                       tvlv_handler->ogm_handler(bat_priv, orig_node,
+                                                 cifnotfound, NULL, 0);
+
+               tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
+       }
+       rcu_read_unlock();
+
+       return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate
+ *  handlers
+ * @bat_priv: the bat priv with all the soft interface information
+ * @batadv_ogm_packet: ogm packet containing the tvlv containers
+ * @orig_node: orig node emitting the ogm packet
+ */
+void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+                            struct batadv_ogm_packet *batadv_ogm_packet,
+                            struct batadv_orig_node *orig_node)
+{
+       void *tvlv_value;
+       uint16_t tvlv_value_len;
+
+       if (!batadv_ogm_packet)
+               return;
+
+       tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len);
+       if (!tvlv_value_len)
+               return;
+
+       tvlv_value = batadv_ogm_packet + 1;
+
+       batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL,
+                                      tvlv_value, tvlv_value_len);
+}
+
+/**
+ * batadv_tvlv_handler_register - register tvlv handler based on the provided
+ *  type and version (both need to match) for ogm tvlv payload and/or unicast
+ *  payload
+ * @bat_priv: the bat priv with all the soft interface information
+ * @optr: ogm tvlv handler callback function. This function receives the orig
+ *  node, flags and the tvlv content as argument to process.
+ * @uptr: unicast tvlv handler callback function. This function receives the
+ *  source & destination of the unicast packet as well as the tvlv content
+ *  to process.
+ * @type: tvlv handler type to be registered
+ * @version: tvlv handler version to be registered
+ * @flags: flags to enable or disable TVLV API behavior
+ */
+void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
+                                 void (*optr)(struct batadv_priv *bat_priv,
+                                              struct batadv_orig_node *orig,
+                                              uint8_t flags,
+                                              void *tvlv_value,
+                                              uint16_t tvlv_value_len),
+                                 int (*uptr)(struct batadv_priv *bat_priv,
+                                             uint8_t *src, uint8_t *dst,
+                                             void *tvlv_value,
+                                             uint16_t tvlv_value_len),
+                                 uint8_t type, uint8_t version, uint8_t flags)
+{
+       struct batadv_tvlv_handler *tvlv_handler;
+
+       tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
+       if (tvlv_handler) {
+               batadv_tvlv_handler_free_ref(tvlv_handler);
+               return;
+       }
+
+       tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC);
+       if (!tvlv_handler)
+               return;
+
+       tvlv_handler->ogm_handler = optr;
+       tvlv_handler->unicast_handler = uptr;
+       tvlv_handler->type = type;
+       tvlv_handler->version = version;
+       tvlv_handler->flags = flags;
+       atomic_set(&tvlv_handler->refcount, 1);
+       INIT_HLIST_NODE(&tvlv_handler->list);
+
+       spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
+       hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list);
+       spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
+}
+
+/**
+ * batadv_tvlv_handler_unregister - unregister tvlv handler based on the
+ *  provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv handler type to be unregistered
+ * @version: tvlv handler version to be unregistered
+ */
+void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
+                                   uint8_t type, uint8_t version)
+{
+       struct batadv_tvlv_handler *tvlv_handler;
+
+       tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
+       if (!tvlv_handler)
+               return;
+
+       batadv_tvlv_handler_free_ref(tvlv_handler);
+       spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
+       hlist_del_rcu(&tvlv_handler->list);
+       spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
+       batadv_tvlv_handler_free_ref(tvlv_handler);
+}
+
+/**
+ * batadv_tvlv_unicast_send - send a unicast packet with tvlv payload to the
+ *  specified host
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: source mac address of the unicast packet
+ * @dst: destination mac address of the unicast packet
+ * @type: tvlv type
+ * @version: tvlv version
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ */
+void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src,
+                             uint8_t *dst, uint8_t type, uint8_t version,
+                             void *tvlv_value, uint16_t tvlv_value_len)
+{
+       struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
+       struct batadv_tvlv_hdr *tvlv_hdr;
+       struct batadv_orig_node *orig_node;
+       struct sk_buff *skb = NULL;
+       unsigned char *tvlv_buff;
+       unsigned int tvlv_len;
+       ssize_t hdr_len = sizeof(*unicast_tvlv_packet);
+       bool ret = false;
+
+       orig_node = batadv_orig_hash_find(bat_priv, dst);
+       if (!orig_node)
+               goto out;
+
+       tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len;
+
+       skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + hdr_len + tvlv_len);
+       if (!skb)
+               goto out;
+
+       skb->priority = TC_PRIO_CONTROL;
+       skb_reserve(skb, ETH_HLEN);
+       tvlv_buff = skb_put(skb, sizeof(*unicast_tvlv_packet) + tvlv_len);
+       unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)tvlv_buff;
+       unicast_tvlv_packet->header.packet_type = BATADV_UNICAST_TVLV;
+       unicast_tvlv_packet->header.version = BATADV_COMPAT_VERSION;
+       unicast_tvlv_packet->header.ttl = BATADV_TTL;
+       unicast_tvlv_packet->reserved = 0;
+       unicast_tvlv_packet->tvlv_len = htons(tvlv_len);
+       unicast_tvlv_packet->align = 0;
+       memcpy(unicast_tvlv_packet->src, src, ETH_ALEN);
+       memcpy(unicast_tvlv_packet->dst, dst, ETH_ALEN);
+
+       tvlv_buff = (unsigned char *)(unicast_tvlv_packet + 1);
+       tvlv_hdr = (struct batadv_tvlv_hdr *)tvlv_buff;
+       tvlv_hdr->version = version;
+       tvlv_hdr->type = type;
+       tvlv_hdr->len = htons(tvlv_value_len);
+       tvlv_buff += sizeof(*tvlv_hdr);
+       memcpy(tvlv_buff, tvlv_value, tvlv_value_len);
+
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
+               ret = true;
+
+out:
+       if (skb && !ret)
+               kfree_skb(skb);
+       if (orig_node)
+               batadv_orig_node_free_ref(orig_node);
+}
+
+/**
+ * batadv_get_vid - extract the VLAN identifier from skb if any
+ * @skb: the buffer containing the packet
+ * @header_len: length of the batman header preceding the ethernet header
+ *
+ * If the packet embedded in the skb is vlan tagged this function returns the
+ * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
+ */
+unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)(skb->data + header_len);
+       struct vlan_ethhdr *vhdr;
+       unsigned short vid;
+
+       if (ethhdr->h_proto != htons(ETH_P_8021Q))
+               return BATADV_NO_FLAGS;
+
+       if (!pskb_may_pull(skb, header_len + VLAN_ETH_HLEN))
+               return BATADV_NO_FLAGS;
+
+       vhdr = (struct vlan_ethhdr *)(skb->data + header_len);
+       vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+       vid |= BATADV_VLAN_HAS_TAG;
+
+       return vid;
+}
+
 static int batadv_param_set_ra(const char *val, const struct kernel_param *kp)
 {
        struct batadv_algo_ops *bat_algo_ops;
index 2467552..f94f287 100644 (file)
 #ifndef _NET_BATMAN_ADV_MAIN_H_
 #define _NET_BATMAN_ADV_MAIN_H_
 
-#define BATADV_DRIVER_AUTHOR "Marek Lindner <lindner_marek@yahoo.de>, " \
-                            "Simon Wunderlich <siwu@hrz.tu-chemnitz.de>"
+#define BATADV_DRIVER_AUTHOR "Marek Lindner <mareklindner@neomailbox.ch>, " \
+                            "Simon Wunderlich <sw@simonwunderlich.de>"
 #define BATADV_DRIVER_DESC   "B.A.T.M.A.N. advanced"
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2013.4.0"
+#define BATADV_SOURCE_VERSION "2013.5.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
 /* numbers of originator to contact for any PUT/GET DHT operation */
 #define BATADV_DAT_CANDIDATES_NUM 3
 
-#define BATADV_VIS_INTERVAL 5000       /* 5 seconds */
+/**
+ * BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ
+ *  at most from the primary one in order to be still considered acceptable
+ */
+#define BATADV_TQ_SIMILARITY_THRESHOLD 50
 
 /* how much worse secondary interfaces may be to be considered as bonding
  * candidates
@@ -133,6 +137,15 @@ enum batadv_uev_type {
 
 #define BATADV_GW_THRESHOLD    50
 
+/* Number of fragment chains for each orig_node */
+#define BATADV_FRAG_BUFFER_COUNT 8
+/* Maximum number of fragments for one packet */
+#define BATADV_FRAG_MAX_FRAGMENTS 16
+/* Maxumim size of each fragment */
+#define BATADV_FRAG_MAX_FRAG_SIZE 1400
+/* Time to keep fragments while waiting for rest of the fragments */
+#define BATADV_FRAG_TIMEOUT 10000
+
 #define BATADV_DAT_CANDIDATE_NOT_FOUND 0
 #define BATADV_DAT_CANDIDATE_ORIG      1
 
@@ -160,15 +173,9 @@ enum batadv_uev_type {
 #include <net/rtnetlink.h>
 #include <linux/jiffies.h>
 #include <linux/seq_file.h>
-#include "types.h"
+#include <linux/if_vlan.h>
 
-/**
- * batadv_vlan_flags - flags for the four MSB of any vlan ID field
- * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
- */
-enum batadv_vlan_flags {
-       BATADV_VLAN_HAS_TAG     = BIT(15),
-};
+#include "types.h"
 
 #define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \
                               (int)(vid & VLAN_VID_MASK) : -1)
@@ -184,6 +191,7 @@ void batadv_mesh_free(struct net_device *soft_iface);
 int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr);
 struct batadv_hard_iface *
 batadv_seq_print_text_primary_if_get(struct seq_file *seq);
+int batadv_max_header_len(void);
 void batadv_skb_set_priority(struct sk_buff *skb, int offset);
 int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
                           struct packet_type *ptype,
@@ -326,4 +334,40 @@ static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv,
  */
 #define BATADV_SKB_CB(__skb)       ((struct batadv_skb_cb *)&((__skb)->cb[0]))
 
+void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+                                   uint8_t type, uint8_t version,
+                                   void *tvlv_value, uint16_t tvlv_value_len);
+uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+                                         unsigned char **packet_buff,
+                                         int *packet_buff_len,
+                                         int packet_min_len);
+void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+                            struct batadv_ogm_packet *batadv_ogm_packet,
+                            struct batadv_orig_node *orig_node);
+void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
+                                     uint8_t type, uint8_t version);
+
+void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
+                                 void (*optr)(struct batadv_priv *bat_priv,
+                                              struct batadv_orig_node *orig,
+                                              uint8_t flags,
+                                              void *tvlv_value,
+                                              uint16_t tvlv_value_len),
+                                 int (*uptr)(struct batadv_priv *bat_priv,
+                                             uint8_t *src, uint8_t *dst,
+                                             void *tvlv_value,
+                                             uint16_t tvlv_value_len),
+                                 uint8_t type, uint8_t version, uint8_t flags);
+void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
+                                   uint8_t type, uint8_t version);
+int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+                                  bool ogm_source,
+                                  struct batadv_orig_node *orig_node,
+                                  uint8_t *src, uint8_t *dst,
+                                  void *tvlv_buff, uint16_t tvlv_buff_len);
+void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src,
+                             uint8_t *dst, uint8_t type, uint8_t version,
+                             void *tvlv_value, uint16_t tvlv_value_len);
+unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len);
+
 #endif /* _NET_BATMAN_ADV_MAIN_H_ */
index a487d46..351e199 100644 (file)
@@ -34,6 +34,20 @@ static void batadv_nc_worker(struct work_struct *work);
 static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
                                       struct batadv_hard_iface *recv_if);
 
+/**
+ * batadv_nc_init - one-time initialization for network coding
+ */
+int __init batadv_nc_init(void)
+{
+       int ret;
+
+       /* Register our packet type */
+       ret = batadv_recv_handler_register(BATADV_CODED,
+                                          batadv_nc_recv_coded_packet);
+
+       return ret;
+}
+
 /**
  * batadv_nc_start_timer - initialise the nc periodic worker
  * @bat_priv: the bat priv with all the soft interface information
@@ -45,10 +59,63 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
 }
 
 /**
- * batadv_nc_init - initialise coding hash table and start house keeping
+ * batadv_nc_tvlv_container_update - update the network coding tvlv container
+ *  after network coding setting change
  * @bat_priv: the bat priv with all the soft interface information
  */
-int batadv_nc_init(struct batadv_priv *bat_priv)
+static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv)
+{
+       char nc_mode;
+
+       nc_mode = atomic_read(&bat_priv->network_coding);
+
+       switch (nc_mode) {
+       case 0:
+               batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1);
+               break;
+       case 1:
+               batadv_tvlv_container_register(bat_priv, BATADV_TVLV_NC, 1,
+                                              NULL, 0);
+               break;
+       }
+}
+
+/**
+ * batadv_nc_status_update - update the network coding tvlv container after
+ *  network coding setting change
+ * @net_dev: the soft interface net device
+ */
+void batadv_nc_status_update(struct net_device *net_dev)
+{
+       struct batadv_priv *bat_priv = netdev_priv(net_dev);
+       batadv_nc_tvlv_container_update(bat_priv);
+}
+
+/**
+ * batadv_nc_tvlv_ogm_handler_v1 - process incoming nc tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+                                         struct batadv_orig_node *orig,
+                                         uint8_t flags,
+                                         void *tvlv_value,
+                                         uint16_t tvlv_value_len)
+{
+       if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
+               orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_NC;
+       else
+               orig->capabilities |= BATADV_ORIG_CAPA_HAS_NC;
+}
+
+/**
+ * batadv_nc_mesh_init - initialise coding hash table and start house keeping
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
 {
        bat_priv->nc.timestamp_fwd_flush = jiffies;
        bat_priv->nc.timestamp_sniffed_purge = jiffies;
@@ -70,14 +137,13 @@ int batadv_nc_init(struct batadv_priv *bat_priv)
        batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
                                   &batadv_nc_decoding_hash_lock_class_key);
 
-       /* Register our packet type */
-       if (batadv_recv_handler_register(BATADV_CODED,
-                                        batadv_nc_recv_coded_packet) < 0)
-               goto err;
-
        INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
        batadv_nc_start_timer(bat_priv);
 
+       batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1,
+                                    NULL, BATADV_TVLV_NC, 1,
+                                    BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+       batadv_nc_tvlv_container_update(bat_priv);
        return 0;
 
 err:
@@ -793,6 +859,10 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
        if (!atomic_read(&bat_priv->network_coding))
                goto out;
 
+       /* check if orig node is network coding enabled */
+       if (!(orig_node->capabilities & BATADV_ORIG_CAPA_HAS_NC))
+               goto out;
+
        /* accept ogms from 'good' neighbors and single hop neighbors */
        if (!batadv_can_nc_with_orig(bat_priv, orig_node, ogm_packet) &&
            !is_single_hop_neigh)
@@ -933,7 +1003,7 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
                                   struct batadv_nc_packet *nc_packet,
                                   struct batadv_neigh_node *neigh_node)
 {
-       uint8_t tq_weighted_neigh, tq_weighted_coding;
+       uint8_t tq_weighted_neigh, tq_weighted_coding, tq_tmp;
        struct sk_buff *skb_dest, *skb_src;
        struct batadv_unicast_packet *packet1;
        struct batadv_unicast_packet *packet2;
@@ -958,8 +1028,10 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
        if (!router_coding)
                goto out;
 
-       tq_weighted_neigh = batadv_nc_random_weight_tq(router_neigh->tq_avg);
-       tq_weighted_coding = batadv_nc_random_weight_tq(router_coding->tq_avg);
+       tq_tmp = batadv_nc_random_weight_tq(router_neigh->bat_iv.tq_avg);
+       tq_weighted_neigh = tq_tmp;
+       tq_tmp = batadv_nc_random_weight_tq(router_coding->bat_iv.tq_avg);
+       tq_weighted_coding = tq_tmp;
 
        /* Select one destination for the MAC-header dst-field based on
         * weighted TQ-values.
@@ -1721,12 +1793,13 @@ free_nc_packet:
 }
 
 /**
- * batadv_nc_free - clean up network coding memory
+ * batadv_nc_mesh_free - clean up network coding memory
  * @bat_priv: the bat priv with all the soft interface information
  */
-void batadv_nc_free(struct batadv_priv *bat_priv)
+void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
 {
-       batadv_recv_handler_unregister(BATADV_CODED);
+       batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1);
+       batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_NC, 1);
        cancel_delayed_work_sync(&bat_priv->nc.work);
 
        batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
index 85a4ec8..d4fd315 100644 (file)
 
 #ifdef CONFIG_BATMAN_ADV_NC
 
-int batadv_nc_init(struct batadv_priv *bat_priv);
-void batadv_nc_free(struct batadv_priv *bat_priv);
+void batadv_nc_status_update(struct net_device *net_dev);
+int batadv_nc_init(void);
+int batadv_nc_mesh_init(struct batadv_priv *bat_priv);
+void batadv_nc_mesh_free(struct batadv_priv *bat_priv);
 void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
                              struct batadv_orig_node *orig_node,
                              struct batadv_orig_node *orig_neigh_node,
@@ -46,12 +48,21 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
 
 #else /* ifdef CONFIG_BATMAN_ADV_NC */
 
-static inline int batadv_nc_init(struct batadv_priv *bat_priv)
+static inline void batadv_nc_status_update(struct net_device *net_dev)
+{
+}
+
+static inline int batadv_nc_init(void)
+{
+       return 0;
+}
+
+static inline int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
 {
        return 0;
 }
 
-static inline void batadv_nc_free(struct batadv_priv *bat_priv)
+static inline void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
 {
        return;
 }
index f50553a..8ab1434 100644 (file)
 #include "routing.h"
 #include "gateway_client.h"
 #include "hard-interface.h"
-#include "unicast.h"
 #include "soft-interface.h"
 #include "bridge_loop_avoidance.h"
 #include "network-coding.h"
+#include "fragmentation.h"
 
 /* hash class keys */
 static struct lock_class_key batadv_orig_hash_lock_class_key;
@@ -36,7 +36,7 @@ static struct lock_class_key batadv_orig_hash_lock_class_key;
 static void batadv_purge_orig(struct work_struct *work);
 
 /* returns 1 if they are the same originator */
-static int batadv_compare_orig(const struct hlist_node *node, const void *data2)
+int batadv_compare_orig(const struct hlist_node *node, const void *data2)
 {
        const void *data1 = container_of(node, struct batadv_orig_node,
                                         hash_entry);
@@ -44,6 +44,88 @@ static int batadv_compare_orig(const struct hlist_node *node, const void *data2)
        return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
 }
 
+/**
+ * batadv_orig_node_vlan_get - get an orig_node_vlan object
+ * @orig_node: the originator serving the VLAN
+ * @vid: the VLAN identifier
+ *
+ * Returns the vlan object identified by vid and belonging to orig_node or NULL
+ * if it does not exist.
+ */
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
+                         unsigned short vid)
+{
+       struct batadv_orig_node_vlan *vlan = NULL, *tmp;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(tmp, &orig_node->vlan_list, list) {
+               if (tmp->vid != vid)
+                       continue;
+
+               if (!atomic_inc_not_zero(&tmp->refcount))
+                       continue;
+
+               vlan = tmp;
+
+               break;
+       }
+       rcu_read_unlock();
+
+       return vlan;
+}
+
+/**
+ * batadv_orig_node_vlan_new - search and possibly create an orig_node_vlan
+ *  object
+ * @orig_node: the originator serving the VLAN
+ * @vid: the VLAN identifier
+ *
+ * Returns NULL in case of failure or the vlan object identified by vid and
+ * belonging to orig_node otherwise. The object is created and added to the list
+ * if it does not exist.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
+                         unsigned short vid)
+{
+       struct batadv_orig_node_vlan *vlan;
+
+       spin_lock_bh(&orig_node->vlan_list_lock);
+
+       /* first look if an object for this vid already exists */
+       vlan = batadv_orig_node_vlan_get(orig_node, vid);
+       if (vlan)
+               goto out;
+
+       vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
+       if (!vlan)
+               goto out;
+
+       atomic_set(&vlan->refcount, 2);
+       vlan->vid = vid;
+
+       list_add_rcu(&vlan->list, &orig_node->vlan_list);
+
+out:
+       spin_unlock_bh(&orig_node->vlan_list_lock);
+
+       return vlan;
+}
+
+/**
+ * batadv_orig_node_vlan_free_ref - decrement the refcounter and possibly free
+ *  the originator-vlan object
+ * @orig_vlan: the originator-vlan object to release
+ */
+void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan)
+{
+       if (atomic_dec_and_test(&orig_vlan->refcount))
+               kfree_rcu(orig_vlan, rcu);
+}
+
 int batadv_originator_init(struct batadv_priv *bat_priv)
 {
        if (bat_priv->orig_hash)
@@ -90,11 +172,20 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node)
        return router;
 }
 
+/**
+ * batadv_neigh_node_new - create and init a new neigh_node object
+ * @hard_iface: the interface where the neighbour is connected to
+ * @neigh_addr: the mac address of the neighbour interface
+ * @orig_node: originator object representing the neighbour
+ *
+ * Allocates a new neigh_node object and initialises all the generic fields.
+ * Returns the new object or NULL on failure.
+ */
 struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
-                     const uint8_t *neigh_addr)
+                     const uint8_t *neigh_addr,
+                     struct batadv_orig_node *orig_node)
 {
-       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct batadv_neigh_node *neigh_node;
 
        neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
@@ -104,15 +195,14 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
        INIT_HLIST_NODE(&neigh_node->list);
 
        memcpy(neigh_node->addr, neigh_addr, ETH_ALEN);
-       spin_lock_init(&neigh_node->lq_update_lock);
+       neigh_node->if_incoming = hard_iface;
+       neigh_node->orig_node = orig_node;
+
+       INIT_LIST_HEAD(&neigh_node->bonding_list);
 
        /* extra reference for return */
        atomic_set(&neigh_node->refcount, 2);
 
-       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                  "Creating new neighbor %pM on interface %s\n", neigh_addr,
-                  hard_iface->net_dev->name);
-
 out:
        return neigh_node;
 }
@@ -146,13 +236,15 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
        /* Free nc_nodes */
        batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
 
-       batadv_frag_list_free(&orig_node->frag_list);
-       batadv_tt_global_del_orig(orig_node->bat_priv, orig_node,
+       batadv_frag_purge_orig(orig_node, NULL);
+
+       batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, -1,
                                  "originator timed out");
 
+       if (orig_node->bat_priv->bat_algo_ops->bat_orig_free)
+               orig_node->bat_priv->bat_algo_ops->bat_orig_free(orig_node);
+
        kfree(orig_node->tt_buff);
-       kfree(orig_node->bcast_own);
-       kfree(orig_node->bcast_own_sum);
        kfree(orig_node);
 }
 
@@ -210,20 +302,22 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
        batadv_hash_destroy(hash);
 }
 
-/* this function finds or creates an originator entry for the given
- * address if it does not exits
+/**
+ * batadv_orig_node_new - creates a new orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the originator
+ *
+ * Creates a new originator object and initialise all the generic fields.
+ * The new object is not added to the originator list.
+ * Returns the newly created object or NULL on failure.
  */
-struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
+struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
                                              const uint8_t *addr)
 {
        struct batadv_orig_node *orig_node;
-       int size;
-       int hash_added;
+       struct batadv_orig_node_vlan *vlan;
        unsigned long reset_time;
-
-       orig_node = batadv_orig_hash_find(bat_priv, addr);
-       if (orig_node)
-               return orig_node;
+       int i;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                   "Creating new originator: %pM\n", addr);
@@ -234,10 +328,12 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
 
        INIT_HLIST_HEAD(&orig_node->neigh_list);
        INIT_LIST_HEAD(&orig_node->bond_list);
-       spin_lock_init(&orig_node->ogm_cnt_lock);
+       INIT_LIST_HEAD(&orig_node->vlan_list);
        spin_lock_init(&orig_node->bcast_seqno_lock);
        spin_lock_init(&orig_node->neigh_list_lock);
        spin_lock_init(&orig_node->tt_buff_lock);
+       spin_lock_init(&orig_node->tt_lock);
+       spin_lock_init(&orig_node->vlan_list_lock);
 
        batadv_nc_init_orig(orig_node);
 
@@ -249,43 +345,32 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
        memcpy(orig_node->orig, addr, ETH_ALEN);
        batadv_dat_init_orig_node_addr(orig_node);
        orig_node->router = NULL;
-       orig_node->tt_crc = 0;
        atomic_set(&orig_node->last_ttvn, 0);
        orig_node->tt_buff = NULL;
        orig_node->tt_buff_len = 0;
-       atomic_set(&orig_node->tt_size, 0);
        reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
        orig_node->bcast_seqno_reset = reset_time;
        orig_node->batman_seqno_reset = reset_time;
 
        atomic_set(&orig_node->bond_candidates, 0);
 
-       size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS;
-
-       orig_node->bcast_own = kzalloc(size, GFP_ATOMIC);
-       if (!orig_node->bcast_own)
+       /* create a vlan object for the "untagged" LAN */
+       vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS);
+       if (!vlan)
                goto free_orig_node;
+       /* batadv_orig_node_vlan_new() increases the refcounter.
+        * Immediately release vlan since it is not needed anymore in this
+        * context
+        */
+       batadv_orig_node_vlan_free_ref(vlan);
 
-       size = bat_priv->num_ifaces * sizeof(uint8_t);
-       orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
-
-       INIT_LIST_HEAD(&orig_node->frag_list);
-       orig_node->last_frag_packet = 0;
-
-       if (!orig_node->bcast_own_sum)
-               goto free_bcast_own;
-
-       hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
-                                    batadv_choose_orig, orig_node,
-                                    &orig_node->hash_entry);
-       if (hash_added != 0)
-               goto free_bcast_own_sum;
+       for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
+               INIT_HLIST_HEAD(&orig_node->fragments[i].head);
+               spin_lock_init(&orig_node->fragments[i].lock);
+               orig_node->fragments[i].size = 0;
+       }
 
        return orig_node;
-free_bcast_own_sum:
-       kfree(orig_node->bcast_own_sum);
-free_bcast_own:
-       kfree(orig_node->bcast_own);
 free_orig_node:
        kfree(orig_node);
        return NULL;
@@ -294,15 +379,16 @@ free_orig_node:
 static bool
 batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
                            struct batadv_orig_node *orig_node,
-                           struct batadv_neigh_node **best_neigh_node)
+                           struct batadv_neigh_node **best_neigh)
 {
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct hlist_node *node_tmp;
        struct batadv_neigh_node *neigh_node;
        bool neigh_purged = false;
        unsigned long last_seen;
        struct batadv_hard_iface *if_incoming;
 
-       *best_neigh_node = NULL;
+       *best_neigh = NULL;
 
        spin_lock_bh(&orig_node->neigh_list_lock);
 
@@ -335,9 +421,12 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
                        batadv_bonding_candidate_del(orig_node, neigh_node);
                        batadv_neigh_node_free_ref(neigh_node);
                } else {
-                       if ((!*best_neigh_node) ||
-                           (neigh_node->tq_avg > (*best_neigh_node)->tq_avg))
-                               *best_neigh_node = neigh_node;
+                       /* store the best_neighbour if this is the first
+                        * iteration or if a better neighbor has been found
+                        */
+                       if (!*best_neigh ||
+                           bao->bat_neigh_cmp(neigh_node, *best_neigh) > 0)
+                               *best_neigh = neigh_node;
                }
        }
 
@@ -388,17 +477,14 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv)
                hlist_for_each_entry_safe(orig_node, node_tmp,
                                          head, hash_entry) {
                        if (batadv_purge_orig_node(bat_priv, orig_node)) {
-                               if (orig_node->gw_flags)
-                                       batadv_gw_node_delete(bat_priv,
-                                                             orig_node);
+                               batadv_gw_node_delete(bat_priv, orig_node);
                                hlist_del_rcu(&orig_node->hash_entry);
                                batadv_orig_node_free_ref(orig_node);
                                continue;
                        }
 
-                       if (batadv_has_timed_out(orig_node->last_frag_packet,
-                                                BATADV_FRAG_TIMEOUT))
-                               batadv_frag_list_free(&orig_node->frag_list);
+                       batadv_frag_purge_orig(orig_node,
+                                              batadv_frag_check_entry);
                }
                spin_unlock_bh(list_lock);
        }
@@ -429,100 +515,26 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
 {
        struct net_device *net_dev = (struct net_device *)seq->private;
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
-       struct batadv_hashtable *hash = bat_priv->orig_hash;
-       struct hlist_head *head;
        struct batadv_hard_iface *primary_if;
-       struct batadv_orig_node *orig_node;
-       struct batadv_neigh_node *neigh_node, *neigh_node_tmp;
-       int batman_count = 0;
-       int last_seen_secs;
-       int last_seen_msecs;
-       unsigned long last_seen_jiffies;
-       uint32_t i;
 
        primary_if = batadv_seq_print_text_primary_if_get(seq);
        if (!primary_if)
-               goto out;
+               return 0;
 
-       seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
+       seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
                   BATADV_SOURCE_VERSION, primary_if->net_dev->name,
-                  primary_if->net_dev->dev_addr, net_dev->name);
-       seq_printf(seq, "  %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
-                  "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE,
-                  "Nexthop", "outgoingIF", "Potential nexthops");
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       neigh_node = batadv_orig_node_get_router(orig_node);
-                       if (!neigh_node)
-                               continue;
-
-                       if (neigh_node->tq_avg == 0)
-                               goto next;
-
-                       last_seen_jiffies = jiffies - orig_node->last_seen;
-                       last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
-                       last_seen_secs = last_seen_msecs / 1000;
-                       last_seen_msecs = last_seen_msecs % 1000;
-
-                       seq_printf(seq, "%pM %4i.%03is   (%3i) %pM [%10s]:",
-                                  orig_node->orig, last_seen_secs,
-                                  last_seen_msecs, neigh_node->tq_avg,
-                                  neigh_node->addr,
-                                  neigh_node->if_incoming->net_dev->name);
-
-                       hlist_for_each_entry_rcu(neigh_node_tmp,
-                                                &orig_node->neigh_list, list) {
-                               seq_printf(seq, " %pM (%3i)",
-                                          neigh_node_tmp->addr,
-                                          neigh_node_tmp->tq_avg);
-                       }
+                  primary_if->net_dev->dev_addr, net_dev->name,
+                  bat_priv->bat_algo_ops->name);
 
-                       seq_puts(seq, "\n");
-                       batman_count++;
+       batadv_hardif_free_ref(primary_if);
 
-next:
-                       batadv_neigh_node_free_ref(neigh_node);
-               }
-               rcu_read_unlock();
+       if (!bat_priv->bat_algo_ops->bat_orig_print) {
+               seq_puts(seq,
+                        "No printing function for this routing protocol\n");
+               return 0;
        }
 
-       if (batman_count == 0)
-               seq_puts(seq, "No batman nodes in range ...\n");
-
-out:
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-       return 0;
-}
-
-static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node,
-                                  int max_if_num)
-{
-       void *data_ptr;
-       size_t data_size, old_size;
-
-       data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS;
-       old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS;
-       data_ptr = kmalloc(data_size, GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       memcpy(data_ptr, orig_node->bcast_own, old_size);
-       kfree(orig_node->bcast_own);
-       orig_node->bcast_own = data_ptr;
-
-       data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       memcpy(data_ptr, orig_node->bcast_own_sum,
-              (max_if_num - 1) * sizeof(uint8_t));
-       kfree(orig_node->bcast_own_sum);
-       orig_node->bcast_own_sum = data_ptr;
+       bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq);
 
        return 0;
 }
@@ -531,6 +543,7 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
                            int max_if_num)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct batadv_hashtable *hash = bat_priv->orig_hash;
        struct hlist_head *head;
        struct batadv_orig_node *orig_node;
@@ -545,10 +558,10 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       spin_lock_bh(&orig_node->ogm_cnt_lock);
-                       ret = batadv_orig_node_add_if(orig_node, max_if_num);
-                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
-
+                       ret = 0;
+                       if (bao->bat_orig_add_if)
+                               ret = bao->bat_orig_add_if(orig_node,
+                                                          max_if_num);
                        if (ret == -ENOMEM)
                                goto err;
                }
@@ -562,54 +575,6 @@ err:
        return -ENOMEM;
 }
 
-static int batadv_orig_node_del_if(struct batadv_orig_node *orig_node,
-                                  int max_if_num, int del_if_num)
-{
-       void *data_ptr = NULL;
-       int chunk_size;
-
-       /* last interface was removed */
-       if (max_if_num == 0)
-               goto free_bcast_own;
-
-       chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS;
-       data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       /* copy first part */
-       memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size);
-
-       /* copy second part */
-       memcpy((char *)data_ptr + del_if_num * chunk_size,
-              orig_node->bcast_own + ((del_if_num + 1) * chunk_size),
-              (max_if_num - del_if_num) * chunk_size);
-
-free_bcast_own:
-       kfree(orig_node->bcast_own);
-       orig_node->bcast_own = data_ptr;
-
-       if (max_if_num == 0)
-               goto free_own_sum;
-
-       data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       memcpy(data_ptr, orig_node->bcast_own_sum,
-              del_if_num * sizeof(uint8_t));
-
-       memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t),
-              orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)),
-              (max_if_num - del_if_num) * sizeof(uint8_t));
-
-free_own_sum:
-       kfree(orig_node->bcast_own_sum);
-       orig_node->bcast_own_sum = data_ptr;
-
-       return 0;
-}
-
 int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
                            int max_if_num)
 {
@@ -618,6 +583,7 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
        struct hlist_head *head;
        struct batadv_hard_iface *hard_iface_tmp;
        struct batadv_orig_node *orig_node;
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        uint32_t i;
        int ret;
 
@@ -629,11 +595,11 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       spin_lock_bh(&orig_node->ogm_cnt_lock);
-                       ret = batadv_orig_node_del_if(orig_node, max_if_num,
-                                                     hard_iface->if_num);
-                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
-
+                       ret = 0;
+                       if (bao->bat_orig_del_if)
+                               ret = bao->bat_orig_del_if(orig_node,
+                                                          max_if_num,
+                                                          hard_iface->if_num);
                        if (ret == -ENOMEM)
                                goto err;
                }
index 7887b84..6f77d80 100644 (file)
 
 #include "hash.h"
 
+int batadv_compare_orig(const struct hlist_node *node, const void *data2);
 int batadv_originator_init(struct batadv_priv *bat_priv);
 void batadv_originator_free(struct batadv_priv *bat_priv);
 void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
 void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
 void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
-struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
+struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
                                              const uint8_t *addr);
 struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
-                     const uint8_t *neigh_addr);
+                     const uint8_t *neigh_addr,
+                     struct batadv_orig_node *orig_node);
 void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node);
 struct batadv_neigh_node *
 batadv_orig_node_get_router(struct batadv_orig_node *orig_node);
@@ -40,6 +42,13 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
                            int max_if_num);
 int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
                            int max_if_num);
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
+                         unsigned short vid);
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
+                         unsigned short vid);
+void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan);
 
 
 /* hashfunction to choose an entry in a hash table of given size
index a51ccfc..207459b 100644 (file)
 #ifndef _NET_BATMAN_ADV_PACKET_H_
 #define _NET_BATMAN_ADV_PACKET_H_
 
+/**
+ * enum batadv_packettype - types for batman-adv encapsulated packets
+ * @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV
+ * @BATADV_BCAST: broadcast packets carrying broadcast payload
+ * @BATADV_CODED: network coded packets
+ *
+ * @BATADV_UNICAST: unicast packets carrying unicast payload traffic
+ * @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original
+ *     payload packet
+ * @BATADV_UNICAST_4ADDR: unicast packet including the originator address of
+ *     the sender
+ * @BATADV_ICMP: unicast packet like IP ICMP used for ping or traceroute
+ * @BATADV_UNICAST_TVLV: unicast packet carrying TVLV containers
+ */
 enum batadv_packettype {
-       BATADV_IV_OGM           = 0x01,
-       BATADV_ICMP             = 0x02,
-       BATADV_UNICAST          = 0x03,
-       BATADV_BCAST            = 0x04,
-       BATADV_VIS              = 0x05,
-       BATADV_UNICAST_FRAG     = 0x06,
-       BATADV_TT_QUERY         = 0x07,
-       BATADV_ROAM_ADV         = 0x08,
-       BATADV_UNICAST_4ADDR    = 0x09,
-       BATADV_CODED            = 0x0a,
+       /* 0x00 - 0x3f: local packets or special rules for handling */
+       BATADV_IV_OGM           = 0x00,
+       BATADV_BCAST            = 0x01,
+       BATADV_CODED            = 0x02,
+       /* 0x40 - 0x7f: unicast */
+#define BATADV_UNICAST_MIN     0x40
+       BATADV_UNICAST          = 0x40,
+       BATADV_UNICAST_FRAG     = 0x41,
+       BATADV_UNICAST_4ADDR    = 0x42,
+       BATADV_ICMP             = 0x43,
+       BATADV_UNICAST_TVLV     = 0x44,
+#define BATADV_UNICAST_MAX     0x7f
+       /* 0x80 - 0xff: reserved */
 };
 
 /**
@@ -48,13 +65,21 @@ enum batadv_subtype {
 };
 
 /* this file is included by batctl which needs these defines */
-#define BATADV_COMPAT_VERSION 14
+#define BATADV_COMPAT_VERSION 15
 
+/**
+ * enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets
+ * @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was
+ *     previously received from someone else than the best neighbor.
+ * @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address
+ *     is used, and the packet travels its first hop.
+ * @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a
+ *     one hop neighbor on the interface where it was originally received.
+ */
 enum batadv_iv_flags {
-       BATADV_NOT_BEST_NEXT_HOP   = BIT(3),
-       BATADV_PRIMARIES_FIRST_HOP = BIT(4),
-       BATADV_VIS_SERVER          = BIT(5),
-       BATADV_DIRECTLINK          = BIT(6),
+       BATADV_NOT_BEST_NEXT_HOP   = BIT(0),
+       BATADV_PRIMARIES_FIRST_HOP = BIT(1),
+       BATADV_DIRECTLINK          = BIT(2),
 };
 
 /* ICMP message types */
@@ -66,43 +91,44 @@ enum batadv_icmp_packettype {
        BATADV_PARAMETER_PROBLEM       = 12,
 };
 
-/* vis defines */
-enum batadv_vis_packettype {
-       BATADV_VIS_TYPE_SERVER_SYNC   = 0,
-       BATADV_VIS_TYPE_CLIENT_UPDATE = 1,
-};
-
-/* fragmentation defines */
-enum batadv_unicast_frag_flags {
-       BATADV_UNI_FRAG_HEAD      = BIT(0),
-       BATADV_UNI_FRAG_LARGETAIL = BIT(1),
-};
+/* tt data subtypes */
+#define BATADV_TT_DATA_TYPE_MASK 0x0F
 
-/* TT_QUERY subtypes */
-#define BATADV_TT_QUERY_TYPE_MASK 0x3
-
-enum batadv_tt_query_packettype {
-       BATADV_TT_REQUEST  = 0,
-       BATADV_TT_RESPONSE = 1,
-};
-
-/* TT_QUERY flags */
-enum batadv_tt_query_flags {
-       BATADV_TT_FULL_TABLE = BIT(2),
+/**
+ * enum batadv_tt_data_flags - flags for tt data tvlv
+ * @BATADV_TT_OGM_DIFF: TT diff propagated through OGM
+ * @BATADV_TT_REQUEST: TT request message
+ * @BATADV_TT_RESPONSE: TT response message
+ * @BATADV_TT_FULL_TABLE: contains full table to replace existing table
+ */
+enum batadv_tt_data_flags {
+       BATADV_TT_OGM_DIFF   = BIT(0),
+       BATADV_TT_REQUEST    = BIT(1),
+       BATADV_TT_RESPONSE   = BIT(2),
+       BATADV_TT_FULL_TABLE = BIT(4),
 };
 
 /* BATADV_TT_CLIENT flags.
  * Flags from BIT(0) to BIT(7) are sent on the wire, while flags from BIT(8) to
- * BIT(15) are used for local computation only
+ * BIT(15) are used for local computation only.
+ * Flags from BIT(4) to BIT(7) are kept in sync with the rest of the network.
  */
 enum batadv_tt_client_flags {
        BATADV_TT_CLIENT_DEL     = BIT(0),
        BATADV_TT_CLIENT_ROAM    = BIT(1),
-       BATADV_TT_CLIENT_WIFI    = BIT(2),
-       BATADV_TT_CLIENT_TEMP    = BIT(3),
+       BATADV_TT_CLIENT_WIFI    = BIT(4),
        BATADV_TT_CLIENT_NOPURGE = BIT(8),
        BATADV_TT_CLIENT_NEW     = BIT(9),
        BATADV_TT_CLIENT_PENDING = BIT(10),
+       BATADV_TT_CLIENT_TEMP    = BIT(11),
+};
+
+/**
+ * batadv_vlan_flags - flags for the four MSB of any vlan ID field
+ * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
+ */
+enum batadv_vlan_flags {
+       BATADV_VLAN_HAS_TAG     = BIT(15),
 };
 
 /* claim frame types for the bridge loop avoidance */
@@ -113,6 +139,22 @@ enum batadv_bla_claimframe {
        BATADV_CLAIM_TYPE_REQUEST       = 0x03,
 };
 
+/**
+ * enum batadv_tvlv_type - tvlv type definitions
+ * @BATADV_TVLV_GW: gateway tvlv
+ * @BATADV_TVLV_DAT: distributed arp table tvlv
+ * @BATADV_TVLV_NC: network coding tvlv
+ * @BATADV_TVLV_TT: translation table tvlv
+ * @BATADV_TVLV_ROAM: roaming advertisement tvlv
+ */
+enum batadv_tvlv_type {
+       BATADV_TVLV_GW          = 0x01,
+       BATADV_TVLV_DAT         = 0x02,
+       BATADV_TVLV_NC          = 0x03,
+       BATADV_TVLV_TT          = 0x04,
+       BATADV_TVLV_ROAM        = 0x05,
+};
+
 /* the destination hardware field in the ARP frame is used to
  * transport the claim type and the group id
  */
@@ -131,47 +173,74 @@ struct batadv_header {
         */
 };
 
+/**
+ * struct batadv_ogm_packet - ogm (routing protocol) packet
+ * @header: common batman packet header
+ * @flags: contains routing relevant flags - see enum batadv_iv_flags
+ * @tvlv_len: length of tvlv data following the ogm header
+ */
 struct batadv_ogm_packet {
        struct batadv_header header;
-       uint8_t  flags;    /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */
+       uint8_t  flags;
        __be32   seqno;
        uint8_t  orig[ETH_ALEN];
        uint8_t  prev_sender[ETH_ALEN];
-       uint8_t  gw_flags;  /* flags related to gateway class */
+       uint8_t  reserved;
        uint8_t  tq;
-       uint8_t  tt_num_changes;
-       uint8_t  ttvn; /* translation table version number */
-       __be16   tt_crc;
-} __packed;
+       __be16   tvlv_len;
+       /* __packed is not needed as the struct size is divisible by 4,
+        * and the largest data type in this struct has a size of 4.
+        */
+};
 
 #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet)
 
-struct batadv_icmp_packet {
+/**
+ * batadv_icmp_header - common ICMP header
+ * @header: common batman header
+ * @msg_type: ICMP packet type
+ * @dst: address of the destination node
+ * @orig: address of the source node
+ * @uid: local ICMP socket identifier
+ */
+struct batadv_icmp_header {
        struct batadv_header header;
        uint8_t  msg_type; /* see ICMP message types above */
        uint8_t  dst[ETH_ALEN];
        uint8_t  orig[ETH_ALEN];
-       __be16   seqno;
        uint8_t  uid;
+};
+
+/**
+ * batadv_icmp_packet - ICMP packet
+ * @icmph: common ICMP header
+ * @reserved: not used - useful for alignment
+ * @seqno: ICMP sequence number
+ */
+struct batadv_icmp_packet {
+       struct batadv_icmp_header icmph;
        uint8_t  reserved;
+       __be16   seqno;
 };
 
 #define BATADV_RR_LEN 16
 
-/* icmp_packet_rr must start with all fields from imcp_packet
- * as this is assumed by code that handles ICMP packets
+/**
+ * batadv_icmp_packet_rr - ICMP RouteRecord packet
+ * @icmph: common ICMP header
+ * @rr_cur: number of entries the rr array
+ * @seqno: ICMP sequence number
+ * @rr: route record array
  */
 struct batadv_icmp_packet_rr {
-       struct batadv_header header;
-       uint8_t  msg_type; /* see ICMP message types above */
-       uint8_t  dst[ETH_ALEN];
-       uint8_t  orig[ETH_ALEN];
-       __be16   seqno;
-       uint8_t  uid;
+       struct batadv_icmp_header icmph;
        uint8_t  rr_cur;
+       __be16   seqno;
        uint8_t  rr[BATADV_RR_LEN][ETH_ALEN];
 };
 
+#define BATADV_ICMP_MAX_PACKET_SIZE    sizeof(struct batadv_icmp_packet_rr)
+
 /* All packet headers in front of an ethernet header have to be completely
  * divisible by 2 but not by 4 to make the payload after the ethernet
  * header again 4 bytes boundary aligned.
@@ -209,15 +278,32 @@ struct batadv_unicast_4addr_packet {
         */
 };
 
-struct batadv_unicast_frag_packet {
-       struct batadv_header header;
-       uint8_t  ttvn; /* destination translation table version number */
-       uint8_t  dest[ETH_ALEN];
-       uint8_t  flags;
-       uint8_t  align;
-       uint8_t  orig[ETH_ALEN];
-       __be16   seqno;
-} __packed;
+/**
+ * struct batadv_frag_packet - fragmented packet
+ * @header: common batman packet header with type, compatversion, and ttl
+ * @dest: final destination used when routing fragments
+ * @orig: originator of the fragment used when merging the packet
+ * @no: fragment number within this sequence
+ * @reserved: reserved byte for alignment
+ * @seqno: sequence identification
+ * @total_size: size of the merged packet
+ */
+struct batadv_frag_packet {
+       struct  batadv_header header;
+#if defined(__BIG_ENDIAN_BITFIELD)
+       uint8_t no:4;
+       uint8_t reserved:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       uint8_t reserved:4;
+       uint8_t no:4;
+#else
+#error "unknown bitfield endianess"
+#endif
+       uint8_t dest[ETH_ALEN];
+       uint8_t orig[ETH_ALEN];
+       __be16  seqno;
+       __be16  total_size;
+};
 
 struct batadv_bcast_packet {
        struct batadv_header header;
@@ -231,54 +317,6 @@ struct batadv_bcast_packet {
 
 #pragma pack()
 
-struct batadv_vis_packet {
-       struct batadv_header header;
-       uint8_t  vis_type;       /* which type of vis-participant sent this? */
-       __be32   seqno;          /* sequence number */
-       uint8_t  entries;        /* number of entries behind this struct */
-       uint8_t  reserved;
-       uint8_t  vis_orig[ETH_ALEN];    /* originator reporting its neighbors */
-       uint8_t  target_orig[ETH_ALEN]; /* who should receive this packet */
-       uint8_t  sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */
-};
-
-struct batadv_tt_query_packet {
-       struct batadv_header header;
-       /* the flag field is a combination of:
-        * - TT_REQUEST or TT_RESPONSE
-        * - TT_FULL_TABLE
-        */
-       uint8_t  flags;
-       uint8_t  dst[ETH_ALEN];
-       uint8_t  src[ETH_ALEN];
-       /* the ttvn field is:
-        * if TT_REQUEST: ttvn that triggered the
-        *                request
-        * if TT_RESPONSE: new ttvn for the src
-        *                 orig_node
-        */
-       uint8_t  ttvn;
-       /* tt_data field is:
-        * if TT_REQUEST: crc associated with the
-        *                ttvn
-        * if TT_RESPONSE: table_size
-        */
-       __be16 tt_data;
-} __packed;
-
-struct batadv_roam_adv_packet {
-       struct batadv_header header;
-       uint8_t  reserved;
-       uint8_t  dst[ETH_ALEN];
-       uint8_t  src[ETH_ALEN];
-       uint8_t  client[ETH_ALEN];
-} __packed;
-
-struct batadv_tt_change {
-       uint8_t flags;
-       uint8_t addr[ETH_ALEN];
-} __packed;
-
 /**
  * struct batadv_coded_packet - network coded packet
  * @header: common batman packet header and ttl of first included packet
@@ -311,4 +349,96 @@ struct batadv_coded_packet {
        __be16   coded_len;
 };
 
+/**
+ * struct batadv_unicast_tvlv - generic unicast packet with tvlv payload
+ * @header: common batman packet header
+ * @reserved: reserved field (for packet alignment)
+ * @src: address of the source
+ * @dst: address of the destination
+ * @tvlv_len: length of tvlv data following the unicast tvlv header
+ * @align: 2 bytes to align the header to a 4 byte boundry
+ */
+struct batadv_unicast_tvlv_packet {
+       struct batadv_header header;
+       uint8_t  reserved;
+       uint8_t  dst[ETH_ALEN];
+       uint8_t  src[ETH_ALEN];
+       __be16   tvlv_len;
+       uint16_t align;
+};
+
+/**
+ * struct batadv_tvlv_hdr - base tvlv header struct
+ * @type: tvlv container type (see batadv_tvlv_type)
+ * @version: tvlv container version
+ * @len: tvlv container length
+ */
+struct batadv_tvlv_hdr {
+       uint8_t type;
+       uint8_t version;
+       __be16  len;
+};
+
+/**
+ * struct batadv_tvlv_gateway_data - gateway data propagated through gw tvlv
+ *  container
+ * @bandwidth_down: advertised uplink download bandwidth
+ * @bandwidth_up: advertised uplink upload bandwidth
+ */
+struct batadv_tvlv_gateway_data {
+       __be32 bandwidth_down;
+       __be32 bandwidth_up;
+};
+
+/**
+ * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container
+ * @flags: translation table flags (see batadv_tt_data_flags)
+ * @ttvn: translation table version number
+ * @vlan_num: number of announced VLANs. In the TVLV this struct is followed by
+ *  one batadv_tvlv_tt_vlan_data object per announced vlan
+ */
+struct batadv_tvlv_tt_data {
+       uint8_t flags;
+       uint8_t ttvn;
+       __be16  num_vlan;
+};
+
+/**
+ * struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through
+ *  the tt tvlv container
+ * @crc: crc32 checksum of the entries belonging to this vlan
+ * @vid: vlan identifier
+ * @reserved: unused, useful for alignment purposes
+ */
+struct batadv_tvlv_tt_vlan_data {
+       __be32  crc;
+       __be16  vid;
+       uint16_t reserved;
+};
+
+/**
+ * struct batadv_tvlv_tt_change - translation table diff data
+ * @flags: status indicators concerning the non-mesh client (see
+ *  batadv_tt_client_flags)
+ * @reserved: reserved field
+ * @addr: mac address of non-mesh client that triggered this tt change
+ * @vid: VLAN identifier
+ */
+struct batadv_tvlv_tt_change {
+       uint8_t flags;
+       uint8_t reserved;
+       uint8_t addr[ETH_ALEN];
+       __be16 vid;
+};
+
+/**
+ * struct batadv_tvlv_roam_adv - roaming advertisement
+ * @client: mac address of roaming client
+ * @vid: VLAN identifier
+ */
+struct batadv_tvlv_roam_adv {
+       uint8_t  client[ETH_ALEN];
+       __be16 vid;
+};
+
 #endif /* _NET_BATMAN_ADV_PACKET_H_ */
index 0439395..d4114d7 100644 (file)
 #include "icmp_socket.h"
 #include "translation-table.h"
 #include "originator.h"
-#include "vis.h"
-#include "unicast.h"
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
 #include "network-coding.h"
+#include "fragmentation.h"
+
+#include <linux/if_vlan.h>
 
 static int batadv_route_unicast_packet(struct sk_buff *skb,
                                       struct batadv_hard_iface *recv_if);
@@ -46,7 +47,7 @@ static void _batadv_update_route(struct batadv_priv *bat_priv,
        if ((curr_router) && (!neigh_node)) {
                batadv_dbg(BATADV_DBG_ROUTES, bat_priv,
                           "Deleting route towards: %pM\n", orig_node->orig);
-               batadv_tt_global_del_orig(bat_priv, orig_node,
+               batadv_tt_global_del_orig(bat_priv, orig_node, -1,
                                          "Deleted route towards originator");
 
        /* route added */
@@ -114,9 +115,19 @@ out:
        return;
 }
 
-void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node,
+/**
+ * batadv_bonding_candidate_add - consider a new link for bonding mode towards
+ *  the given originator
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the target node
+ * @neigh_node: the neighbor representing the new link to consider for bonding
+ *  mode
+ */
+void batadv_bonding_candidate_add(struct batadv_priv *bat_priv,
+                                 struct batadv_orig_node *orig_node,
                                  struct batadv_neigh_node *neigh_node)
 {
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct batadv_neigh_node *tmp_neigh_node, *router = NULL;
        uint8_t interference_candidate = 0;
 
@@ -131,8 +142,9 @@ void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node,
        if (!router)
                goto candidate_del;
 
+
        /* ... and is good enough to be considered */
-       if (neigh_node->tq_avg < router->tq_avg - BATADV_BONDING_TQ_THRESHOLD)
+       if (bao->bat_neigh_is_equiv_or_better(neigh_node, router))
                goto candidate_del;
 
        /* check if we have another candidate with the same mac address or
@@ -248,46 +260,65 @@ bool batadv_check_management_packet(struct sk_buff *skb,
        return true;
 }
 
+/**
+ * batadv_recv_my_icmp_packet - receive an icmp packet locally
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: icmp packet to process
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
 static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
-                                     struct sk_buff *skb, size_t icmp_len)
+                                     struct sk_buff *skb)
 {
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_orig_node *orig_node = NULL;
-       struct batadv_icmp_packet_rr *icmp_packet;
-       int ret = NET_RX_DROP;
+       struct batadv_icmp_header *icmph;
+       int res, ret = NET_RX_DROP;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+       icmph = (struct batadv_icmp_header *)skb->data;
 
-       /* add data to device queue */
-       if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) {
-               batadv_socket_receive_packet(icmp_packet, icmp_len);
-               goto out;
-       }
+       switch (icmph->msg_type) {
+       case BATADV_ECHO_REPLY:
+       case BATADV_DESTINATION_UNREACHABLE:
+       case BATADV_TTL_EXCEEDED:
+               /* receive the packet */
+               if (skb_linearize(skb) < 0)
+                       break;
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
+               batadv_socket_receive_packet(icmph, skb->len);
+               break;
+       case BATADV_ECHO_REQUEST:
+               /* answer echo request (ping) */
+               primary_if = batadv_primary_if_get_selected(bat_priv);
+               if (!primary_if)
+                       goto out;
 
-       /* answer echo request (ping) */
-       /* get routing information */
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig);
-       if (!orig_node)
-               goto out;
+               /* get routing information */
+               orig_node = batadv_orig_hash_find(bat_priv, icmph->orig);
+               if (!orig_node)
+                       goto out;
 
-       /* create a copy of the skb, if needed, to modify it. */
-       if (skb_cow(skb, ETH_HLEN) < 0)
-               goto out;
+               /* create a copy of the skb, if needed, to modify it. */
+               if (skb_cow(skb, ETH_HLEN) < 0)
+                       goto out;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+               icmph = (struct batadv_icmp_header *)skb->data;
 
-       memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
-       memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
-       icmp_packet->msg_type = BATADV_ECHO_REPLY;
-       icmp_packet->header.ttl = BATADV_TTL;
+               memcpy(icmph->dst, icmph->orig, ETH_ALEN);
+               memcpy(icmph->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+               icmph->msg_type = BATADV_ECHO_REPLY;
+               icmph->header.ttl = BATADV_TTL;
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
-               ret = NET_RX_SUCCESS;
+               res = batadv_send_skb_to_orig(skb, orig_node, NULL);
+               if (res != NET_XMIT_DROP)
+                       ret = NET_RX_SUCCESS;
 
+               break;
+       default:
+               /* drop unknown type */
+               goto out;
+       }
 out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
@@ -307,9 +338,9 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
        icmp_packet = (struct batadv_icmp_packet *)skb->data;
 
        /* send TTL exceeded if packet is an echo request (traceroute) */
-       if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) {
+       if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) {
                pr_debug("Warning - can't forward icmp packet from %pM to %pM: ttl exceeded\n",
-                        icmp_packet->orig, icmp_packet->dst);
+                        icmp_packet->icmph.orig, icmp_packet->icmph.dst);
                goto out;
        }
 
@@ -318,7 +349,7 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
                goto out;
 
        /* get routing information */
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig);
+       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.orig);
        if (!orig_node)
                goto out;
 
@@ -328,10 +359,11 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
 
        icmp_packet = (struct batadv_icmp_packet *)skb->data;
 
-       memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
-       memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
-       icmp_packet->msg_type = BATADV_TTL_EXCEEDED;
-       icmp_packet->header.ttl = BATADV_TTL;
+       memcpy(icmp_packet->icmph.dst, icmp_packet->icmph.orig, ETH_ALEN);
+       memcpy(icmp_packet->icmph.orig, primary_if->net_dev->dev_addr,
+              ETH_ALEN);
+       icmp_packet->icmph.msg_type = BATADV_TTL_EXCEEDED;
+       icmp_packet->icmph.header.ttl = BATADV_TTL;
 
        if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
                ret = NET_RX_SUCCESS;
@@ -349,16 +381,13 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
                            struct batadv_hard_iface *recv_if)
 {
        struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
-       struct batadv_icmp_packet_rr *icmp_packet;
+       struct batadv_icmp_header *icmph;
+       struct batadv_icmp_packet_rr *icmp_packet_rr;
        struct ethhdr *ethhdr;
        struct batadv_orig_node *orig_node = NULL;
-       int hdr_size = sizeof(struct batadv_icmp_packet);
+       int hdr_size = sizeof(struct batadv_icmp_header);
        int ret = NET_RX_DROP;
 
-       /* we truncate all incoming icmp packets if they don't match our size */
-       if (skb->len >= sizeof(struct batadv_icmp_packet_rr))
-               hdr_size = sizeof(struct batadv_icmp_packet_rr);
-
        /* drop packet if it has not necessary minimum size */
        if (unlikely(!pskb_may_pull(skb, hdr_size)))
                goto out;
@@ -377,26 +406,39 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
        if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
                goto out;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+       icmph = (struct batadv_icmp_header *)skb->data;
 
        /* add record route information if not full */
-       if ((hdr_size == sizeof(struct batadv_icmp_packet_rr)) &&
-           (icmp_packet->rr_cur < BATADV_RR_LEN)) {
-               memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]),
+       if ((icmph->msg_type == BATADV_ECHO_REPLY ||
+            icmph->msg_type == BATADV_ECHO_REQUEST) &&
+           (skb->len >= sizeof(struct batadv_icmp_packet_rr))) {
+               if (skb_linearize(skb) < 0)
+                       goto out;
+
+               /* create a copy of the skb, if needed, to modify it. */
+               if (skb_cow(skb, ETH_HLEN) < 0)
+                       goto out;
+
+               icmph = (struct batadv_icmp_header *)skb->data;
+               icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph;
+               if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN)
+                       goto out;
+
+               memcpy(&(icmp_packet_rr->rr[icmp_packet_rr->rr_cur]),
                       ethhdr->h_dest, ETH_ALEN);
-               icmp_packet->rr_cur++;
+               icmp_packet_rr->rr_cur++;
        }
 
        /* packet for me */
-       if (batadv_is_my_mac(bat_priv, icmp_packet->dst))
-               return batadv_recv_my_icmp_packet(bat_priv, skb, hdr_size);
+       if (batadv_is_my_mac(bat_priv, icmph->dst))
+               return batadv_recv_my_icmp_packet(bat_priv, skb);
 
        /* TTL exceeded */
-       if (icmp_packet->header.ttl < 2)
+       if (icmph->header.ttl < 2)
                return batadv_recv_icmp_ttl_exceeded(bat_priv, skb);
 
        /* get routing information */
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst);
+       orig_node = batadv_orig_hash_find(bat_priv, icmph->dst);
        if (!orig_node)
                goto out;
 
@@ -404,10 +446,10 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
        if (skb_cow(skb, ETH_HLEN) < 0)
                goto out;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+       icmph = (struct batadv_icmp_header *)skb->data;
 
        /* decrement ttl */
-       icmp_packet->header.ttl--;
+       icmph->header.ttl--;
 
        /* route it */
        if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP)
@@ -474,18 +516,25 @@ out:
        return router;
 }
 
-/* Interface Alternating: Use the best of the
- * remaining candidates which are not using
- * this interface.
+/**
+ * batadv_find_ifalter_router - find the best of the remaining candidates which
+ *  are not using this interface
+ * @bat_priv: the bat priv with all the soft interface information
+ * @primary_orig: the destination
+ * @recv_if: the interface that the router returned by this function has to not
+ *  use
  *
- * Increases the returned router's refcount
+ * Returns the best candidate towards primary_orig that is not using recv_if.
+ * Increases the returned neighbor's refcount
  */
 static struct batadv_neigh_node *
-batadv_find_ifalter_router(struct batadv_orig_node *primary_orig,
+batadv_find_ifalter_router(struct batadv_priv *bat_priv,
+                          struct batadv_orig_node *primary_orig,
                           const struct batadv_hard_iface *recv_if)
 {
-       struct batadv_neigh_node *tmp_neigh_node;
        struct batadv_neigh_node *router = NULL, *first_candidate = NULL;
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
+       struct batadv_neigh_node *tmp_neigh_node;
 
        rcu_read_lock();
        list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list,
@@ -497,7 +546,7 @@ batadv_find_ifalter_router(struct batadv_orig_node *primary_orig,
                if (tmp_neigh_node->if_incoming == recv_if)
                        continue;
 
-               if (router && tmp_neigh_node->tq_avg <= router->tq_avg)
+               if (router && bao->bat_neigh_cmp(tmp_neigh_node, router))
                        continue;
 
                if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
@@ -557,126 +606,6 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
        return 0;
 }
 
-int batadv_recv_tt_query(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
-{
-       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
-       struct batadv_tt_query_packet *tt_query;
-       uint16_t tt_size;
-       int hdr_size = sizeof(*tt_query);
-       char tt_flag;
-       size_t packet_size;
-
-       if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
-               return NET_RX_DROP;
-
-       /* I could need to modify it */
-       if (skb_cow(skb, sizeof(struct batadv_tt_query_packet)) < 0)
-               goto out;
-
-       tt_query = (struct batadv_tt_query_packet *)skb->data;
-
-       switch (tt_query->flags & BATADV_TT_QUERY_TYPE_MASK) {
-       case BATADV_TT_REQUEST:
-               batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
-
-               /* If we cannot provide an answer the tt_request is
-                * forwarded
-                */
-               if (!batadv_send_tt_response(bat_priv, tt_query)) {
-                       if (tt_query->flags & BATADV_TT_FULL_TABLE)
-                               tt_flag = 'F';
-                       else
-                               tt_flag = '.';
-
-                       batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Routing TT_REQUEST to %pM [%c]\n",
-                                  tt_query->dst,
-                                  tt_flag);
-                       return batadv_route_unicast_packet(skb, recv_if);
-               }
-               break;
-       case BATADV_TT_RESPONSE:
-               batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
-
-               if (batadv_is_my_mac(bat_priv, tt_query->dst)) {
-                       /* packet needs to be linearized to access the TT
-                        * changes
-                        */
-                       if (skb_linearize(skb) < 0)
-                               goto out;
-                       /* skb_linearize() possibly changed skb->data */
-                       tt_query = (struct batadv_tt_query_packet *)skb->data;
-
-                       tt_size = batadv_tt_len(ntohs(tt_query->tt_data));
-
-                       /* Ensure we have all the claimed data */
-                       packet_size = sizeof(struct batadv_tt_query_packet);
-                       packet_size += tt_size;
-                       if (unlikely(skb_headlen(skb) < packet_size))
-                               goto out;
-
-                       batadv_handle_tt_response(bat_priv, tt_query);
-               } else {
-                       if (tt_query->flags & BATADV_TT_FULL_TABLE)
-                               tt_flag =  'F';
-                       else
-                               tt_flag = '.';
-                       batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Routing TT_RESPONSE to %pM [%c]\n",
-                                  tt_query->dst,
-                                  tt_flag);
-                       return batadv_route_unicast_packet(skb, recv_if);
-               }
-               break;
-       }
-
-out:
-       /* returning NET_RX_DROP will make the caller function kfree the skb */
-       return NET_RX_DROP;
-}
-
-int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
-{
-       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
-       struct batadv_roam_adv_packet *roam_adv_packet;
-       struct batadv_orig_node *orig_node;
-
-       if (batadv_check_unicast_packet(bat_priv, skb,
-                                       sizeof(*roam_adv_packet)) < 0)
-               goto out;
-
-       batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
-
-       roam_adv_packet = (struct batadv_roam_adv_packet *)skb->data;
-
-       if (!batadv_is_my_mac(bat_priv, roam_adv_packet->dst))
-               return batadv_route_unicast_packet(skb, recv_if);
-
-       /* check if it is a backbone gateway. we don't accept
-        * roaming advertisement from it, as it has the same
-        * entries as we have.
-        */
-       if (batadv_bla_is_backbone_gw_orig(bat_priv, roam_adv_packet->src))
-               goto out;
-
-       orig_node = batadv_orig_hash_find(bat_priv, roam_adv_packet->src);
-       if (!orig_node)
-               goto out;
-
-       batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Received ROAMING_ADV from %pM (client %pM)\n",
-                  roam_adv_packet->src, roam_adv_packet->client);
-
-       batadv_tt_global_add(bat_priv, orig_node, roam_adv_packet->client,
-                            BATADV_TT_CLIENT_ROAM,
-                            atomic_read(&orig_node->last_ttvn) + 1);
-
-       batadv_orig_node_free_ref(orig_node);
-out:
-       /* returning NET_RX_DROP will make the caller function kfree the skb */
-       return NET_RX_DROP;
-}
-
 /* find a suitable router for this originator, and use
  * bonding if possible. increases the found neighbors
  * refcount.
@@ -751,7 +680,8 @@ batadv_find_router(struct batadv_priv *bat_priv,
        if (bonding_enabled)
                router = batadv_find_bond_router(primary_orig_node, recv_if);
        else
-               router = batadv_find_ifalter_router(primary_orig_node, recv_if);
+               router = batadv_find_ifalter_router(bat_priv, primary_orig_node,
+                                                   recv_if);
 
 return_router:
        if (router && router->if_incoming->if_status != BATADV_IF_ACTIVE)
@@ -772,11 +702,9 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
 {
        struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
        struct batadv_orig_node *orig_node = NULL;
-       struct batadv_neigh_node *neigh_node = NULL;
        struct batadv_unicast_packet *unicast_packet;
        struct ethhdr *ethhdr = eth_hdr(skb);
        int res, hdr_len, ret = NET_RX_DROP;
-       struct sk_buff *new_skb;
 
        unicast_packet = (struct batadv_unicast_packet *)skb->data;
 
@@ -793,46 +721,12 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
        if (!orig_node)
                goto out;
 
-       /* find_router() increases neigh_nodes refcount if found. */
-       neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
-
-       if (!neigh_node)
-               goto out;
-
        /* create a copy of the skb, if needed, to modify it. */
        if (skb_cow(skb, ETH_HLEN) < 0)
                goto out;
 
-       unicast_packet = (struct batadv_unicast_packet *)skb->data;
-
-       if (unicast_packet->header.packet_type == BATADV_UNICAST &&
-           atomic_read(&bat_priv->fragmentation) &&
-           skb->len > neigh_node->if_incoming->net_dev->mtu) {
-               ret = batadv_frag_send_skb(skb, bat_priv,
-                                          neigh_node->if_incoming,
-                                          neigh_node->addr);
-               goto out;
-       }
-
-       if (unicast_packet->header.packet_type == BATADV_UNICAST_FRAG &&
-           batadv_frag_can_reassemble(skb,
-                                      neigh_node->if_incoming->net_dev->mtu)) {
-               ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb);
-
-               if (ret == NET_RX_DROP)
-                       goto out;
-
-               /* packet was buffered for late merge */
-               if (!new_skb) {
-                       ret = NET_RX_SUCCESS;
-                       goto out;
-               }
-
-               skb = new_skb;
-               unicast_packet = (struct batadv_unicast_packet *)skb->data;
-       }
-
        /* decrement ttl */
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
        unicast_packet->header.ttl--;
 
        switch (unicast_packet->header.packet_type) {
@@ -867,8 +761,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
        }
 
 out:
-       if (neigh_node)
-               batadv_neigh_node_free_ref(neigh_node);
        if (orig_node)
                batadv_orig_node_free_ref(orig_node);
        return ret;
@@ -879,6 +771,7 @@ out:
  * @bat_priv: the bat priv with all the soft interface information
  * @unicast_packet: the unicast header to be updated
  * @dst_addr: the payload destination
+ * @vid: VLAN identifier
  *
  * Search the translation table for dst_addr and update the unicast header with
  * the new corresponding information (originator address where the destination
@@ -889,21 +782,22 @@ out:
 static bool
 batadv_reroute_unicast_packet(struct batadv_priv *bat_priv,
                              struct batadv_unicast_packet *unicast_packet,
-                             uint8_t *dst_addr)
+                             uint8_t *dst_addr, unsigned short vid)
 {
        struct batadv_orig_node *orig_node = NULL;
        struct batadv_hard_iface *primary_if = NULL;
        bool ret = false;
        uint8_t *orig_addr, orig_ttvn;
 
-       if (batadv_is_my_client(bat_priv, dst_addr)) {
+       if (batadv_is_my_client(bat_priv, dst_addr, vid)) {
                primary_if = batadv_primary_if_get_selected(bat_priv);
                if (!primary_if)
                        goto out;
                orig_addr = primary_if->net_dev->dev_addr;
                orig_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
        } else {
-               orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr);
+               orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr,
+                                                    vid);
                if (!orig_node)
                        goto out;
 
@@ -930,11 +824,12 @@ out:
 
 static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
                                     struct sk_buff *skb, int hdr_len) {
-       uint8_t curr_ttvn, old_ttvn;
+       struct batadv_unicast_packet *unicast_packet;
+       struct batadv_hard_iface *primary_if;
        struct batadv_orig_node *orig_node;
+       uint8_t curr_ttvn, old_ttvn;
        struct ethhdr *ethhdr;
-       struct batadv_hard_iface *primary_if;
-       struct batadv_unicast_packet *unicast_packet;
+       unsigned short vid;
        int is_old_ttvn;
 
        /* check if there is enough data before accessing it */
@@ -946,6 +841,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
                return 0;
 
        unicast_packet = (struct batadv_unicast_packet *)skb->data;
+       vid = batadv_get_vid(skb, hdr_len);
        ethhdr = (struct ethhdr *)(skb->data + hdr_len);
 
        /* check if the destination client was served by this node and it is now
@@ -953,9 +849,9 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
         * message and that it knows the new destination in the mesh to re-route
         * the packet to
         */
-       if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest)) {
+       if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) {
                if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
-                                                 ethhdr->h_dest))
+                                                 ethhdr->h_dest, vid))
                        net_ratelimited_function(batadv_dbg, BATADV_DBG_TT,
                                                 bat_priv,
                                                 "Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n",
@@ -1001,7 +897,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
         * target host
         */
        if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
-                                         ethhdr->h_dest)) {
+                                         ethhdr->h_dest, vid)) {
                net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv,
                                         "Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n",
                                         unicast_packet->dest, ethhdr->h_dest,
@@ -1013,7 +909,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
         * currently served by this node or there is no destination at all and
         * it is possible to drop the packet
         */
-       if (!batadv_is_my_client(bat_priv, ethhdr->h_dest))
+       if (!batadv_is_my_client(bat_priv, ethhdr->h_dest, vid))
                return 0;
 
        /* update the header in order to let the packet be delivered to this
@@ -1032,6 +928,34 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
        return 1;
 }
 
+/**
+ * batadv_recv_unhandled_unicast_packet - receive and process packets which
+ *     are in the unicast number space but not yet known to the implementation
+ * @skb: unicast tvlv packet to process
+ * @recv_if: pointer to interface this packet was received on
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
+int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
+                                        struct batadv_hard_iface *recv_if)
+{
+       struct batadv_unicast_packet *unicast_packet;
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       int check, hdr_size = sizeof(*unicast_packet);
+
+       check = batadv_check_unicast_packet(bat_priv, skb, hdr_size);
+       if (check < 0)
+               return NET_RX_DROP;
+
+       /* we don't know about this type, drop it. */
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
+       if (batadv_is_my_mac(bat_priv, unicast_packet->dest))
+               return NET_RX_DROP;
+
+       return batadv_route_unicast_packet(skb, recv_if);
+}
+
 int batadv_recv_unicast_packet(struct sk_buff *skb,
                               struct batadv_hard_iface *recv_if)
 {
@@ -1094,51 +1018,112 @@ rx_success:
        return batadv_route_unicast_packet(skb, recv_if);
 }
 
-int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
-                                 struct batadv_hard_iface *recv_if)
+/**
+ * batadv_recv_unicast_tvlv - receive and process unicast tvlv packets
+ * @skb: unicast tvlv packet to process
+ * @recv_if: pointer to interface this packet was received on
+ * @dst_addr: the payload destination
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
+int batadv_recv_unicast_tvlv(struct sk_buff *skb,
+                            struct batadv_hard_iface *recv_if)
 {
        struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
-       struct batadv_unicast_frag_packet *unicast_packet;
-       int hdr_size = sizeof(*unicast_packet);
-       struct sk_buff *new_skb = NULL;
-       int ret;
+       struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
+       unsigned char *tvlv_buff;
+       uint16_t tvlv_buff_len;
+       int hdr_size = sizeof(*unicast_tvlv_packet);
+       int ret = NET_RX_DROP;
 
        if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
                return NET_RX_DROP;
 
-       if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size))
+       /* the header is likely to be modified while forwarding */
+       if (skb_cow(skb, hdr_size) < 0)
                return NET_RX_DROP;
 
-       unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
+       /* packet needs to be linearized to access the tvlv content */
+       if (skb_linearize(skb) < 0)
+               return NET_RX_DROP;
 
-       /* packet for me */
-       if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) {
-               ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb);
+       unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data;
 
-               if (ret == NET_RX_DROP)
-                       return NET_RX_DROP;
+       tvlv_buff = (unsigned char *)(skb->data + hdr_size);
+       tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len);
 
-               /* packet was buffered for late merge */
-               if (!new_skb)
-                       return NET_RX_SUCCESS;
+       if (tvlv_buff_len > skb->len - hdr_size)
+               return NET_RX_DROP;
 
-               if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb,
-                                                         hdr_size))
-                       goto rx_success;
-               if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb,
-                                                       hdr_size))
-                       goto rx_success;
+       ret = batadv_tvlv_containers_process(bat_priv, false, NULL,
+                                            unicast_tvlv_packet->src,
+                                            unicast_tvlv_packet->dst,
+                                            tvlv_buff, tvlv_buff_len);
 
-               batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if,
-                                   sizeof(struct batadv_unicast_packet), NULL);
+       if (ret != NET_RX_SUCCESS)
+               ret = batadv_route_unicast_packet(skb, recv_if);
 
-rx_success:
-               return NET_RX_SUCCESS;
+       return ret;
+}
+
+/**
+ * batadv_recv_frag_packet - process received fragment
+ * @skb: the received fragment
+ * @recv_if: interface that the skb is received on
+ *
+ * This function does one of the three following things: 1) Forward fragment, if
+ * the assembled packet will exceed our MTU; 2) Buffer fragment, if we till
+ * lack further fragments; 3) Merge fragments, if we have all needed parts.
+ *
+ * Return NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
+ */
+int batadv_recv_frag_packet(struct sk_buff *skb,
+                           struct batadv_hard_iface *recv_if)
+{
+       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+       struct batadv_orig_node *orig_node_src = NULL;
+       struct batadv_frag_packet *frag_packet;
+       int ret = NET_RX_DROP;
+
+       if (batadv_check_unicast_packet(bat_priv, skb,
+                                       sizeof(*frag_packet)) < 0)
+               goto out;
+
+       frag_packet = (struct batadv_frag_packet *)skb->data;
+       orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig);
+       if (!orig_node_src)
+               goto out;
+
+       /* Route the fragment if it is not for us and too big to be merged. */
+       if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+           batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
+               ret = NET_RX_SUCCESS;
+               goto out;
        }
 
-       return batadv_route_unicast_packet(skb, recv_if);
-}
+       batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX);
+       batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, skb->len);
+
+       /* Add fragment to buffer and merge if possible. */
+       if (!batadv_frag_skb_buffer(&skb, orig_node_src))
+               goto out;
 
+       /* Deliver merged packet to the appropriate handler, if it was
+        * merged
+        */
+       if (skb)
+               batadv_batman_skb_recv(skb, recv_if->net_dev,
+                                      &recv_if->batman_adv_ptype, NULL);
+
+       ret = NET_RX_SUCCESS;
+
+out:
+       if (orig_node_src)
+               batadv_orig_node_free_ref(orig_node_src);
+
+       return ret;
+}
 
 int batadv_recv_bcast_packet(struct sk_buff *skb,
                             struct batadv_hard_iface *recv_if)
@@ -1240,53 +1225,3 @@ out:
                batadv_orig_node_free_ref(orig_node);
        return ret;
 }
-
-int batadv_recv_vis_packet(struct sk_buff *skb,
-                          struct batadv_hard_iface *recv_if)
-{
-       struct batadv_vis_packet *vis_packet;
-       struct ethhdr *ethhdr;
-       struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
-       int hdr_size = sizeof(*vis_packet);
-
-       /* keep skb linear */
-       if (skb_linearize(skb) < 0)
-               return NET_RX_DROP;
-
-       if (unlikely(!pskb_may_pull(skb, hdr_size)))
-               return NET_RX_DROP;
-
-       vis_packet = (struct batadv_vis_packet *)skb->data;
-       ethhdr = eth_hdr(skb);
-
-       /* not for me */
-       if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
-               return NET_RX_DROP;
-
-       /* ignore own packets */
-       if (batadv_is_my_mac(bat_priv, vis_packet->vis_orig))
-               return NET_RX_DROP;
-
-       if (batadv_is_my_mac(bat_priv, vis_packet->sender_orig))
-               return NET_RX_DROP;
-
-       switch (vis_packet->vis_type) {
-       case BATADV_VIS_TYPE_SERVER_SYNC:
-               batadv_receive_server_sync_packet(bat_priv, vis_packet,
-                                                 skb_headlen(skb));
-               break;
-
-       case BATADV_VIS_TYPE_CLIENT_UPDATE:
-               batadv_receive_client_update_packet(bat_priv, vis_packet,
-                                                   skb_headlen(skb));
-               break;
-
-       default:        /* ignore unknown packet */
-               break;
-       }
-
-       /* We take a copy of the data in the packet, so we should
-        * always free the skbuf.
-        */
-       return NET_RX_DROP;
-}
index 72a29bd..19544dd 100644 (file)
@@ -30,23 +30,26 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
                            struct batadv_hard_iface *recv_if);
 int batadv_recv_unicast_packet(struct sk_buff *skb,
                               struct batadv_hard_iface *recv_if);
-int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
-                                 struct batadv_hard_iface *recv_if);
+int batadv_recv_frag_packet(struct sk_buff *skb,
+                           struct batadv_hard_iface *iface);
 int batadv_recv_bcast_packet(struct sk_buff *skb,
                             struct batadv_hard_iface *recv_if);
-int batadv_recv_vis_packet(struct sk_buff *skb,
-                          struct batadv_hard_iface *recv_if);
 int batadv_recv_tt_query(struct sk_buff *skb,
                         struct batadv_hard_iface *recv_if);
 int batadv_recv_roam_adv(struct sk_buff *skb,
                         struct batadv_hard_iface *recv_if);
+int batadv_recv_unicast_tvlv(struct sk_buff *skb,
+                            struct batadv_hard_iface *recv_if);
+int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
+                                        struct batadv_hard_iface *recv_if);
 struct batadv_neigh_node *
 batadv_find_router(struct batadv_priv *bat_priv,
                   struct batadv_orig_node *orig_node,
                   const struct batadv_hard_iface *recv_if);
 void batadv_bonding_candidate_del(struct batadv_orig_node *orig_node,
                                  struct batadv_neigh_node *neigh_node);
-void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node,
+void batadv_bonding_candidate_add(struct batadv_priv *bat_priv,
+                                 struct batadv_orig_node *orig_node,
                                  struct batadv_neigh_node *neigh_node);
 void batadv_bonding_save_primary(const struct batadv_orig_node *orig_node,
                                 struct batadv_orig_node *orig_neigh_node,
index 0266edd..c83be5e 100644 (file)
 #include "translation-table.h"
 #include "soft-interface.h"
 #include "hard-interface.h"
-#include "vis.h"
 #include "gateway_common.h"
+#include "gateway_client.h"
 #include "originator.h"
 #include "network-coding.h"
-
-#include <linux/if_ether.h>
+#include "fragmentation.h"
 
 static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
 
@@ -64,10 +63,10 @@ int batadv_send_skb_packet(struct sk_buff *skb,
        ethhdr = eth_hdr(skb);
        memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
        memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
-       ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
+       ethhdr->h_proto = htons(ETH_P_BATMAN);
 
        skb_set_network_header(skb, ETH_HLEN);
-       skb->protocol = __constant_htons(ETH_P_BATMAN);
+       skb->protocol = htons(ETH_P_BATMAN);
 
        skb->dev = hard_iface->net_dev;
 
@@ -109,7 +108,19 @@ int batadv_send_skb_to_orig(struct sk_buff *skb,
        /* batadv_find_router() increases neigh_nodes refcount if found. */
        neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
        if (!neigh_node)
-               return ret;
+               goto out;
+
+       /* Check if the skb is too large to send in one piece and fragment
+        * it if needed.
+        */
+       if (atomic_read(&bat_priv->fragmentation) &&
+           skb->len > neigh_node->if_incoming->net_dev->mtu) {
+               /* Fragment and send packet. */
+               if (batadv_frag_send_packet(skb, orig_node, neigh_node))
+                       ret = NET_XMIT_SUCCESS;
+
+               goto out;
+       }
 
        /* try to network code the packet, if it is received on an interface
         * (i.e. being forwarded). If the packet originates from this node or if
@@ -123,11 +134,225 @@ int batadv_send_skb_to_orig(struct sk_buff *skb,
                ret = NET_XMIT_SUCCESS;
        }
 
-       batadv_neigh_node_free_ref(neigh_node);
+out:
+       if (neigh_node)
+               batadv_neigh_node_free_ref(neigh_node);
+
+       return ret;
+}
+
+/**
+ * batadv_send_skb_push_fill_unicast - extend the buffer and initialize the
+ *  common fields for unicast packets
+ * @skb: the skb carrying the unicast header to initialize
+ * @hdr_size: amount of bytes to push at the beginning of the skb
+ * @orig_node: the destination node
+ *
+ * Returns false if the buffer extension was not possible or true otherwise.
+ */
+static bool
+batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size,
+                                 struct batadv_orig_node *orig_node)
+{
+       struct batadv_unicast_packet *unicast_packet;
+       uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
+
+       if (batadv_skb_head_push(skb, hdr_size) < 0)
+               return false;
+
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
+       unicast_packet->header.version = BATADV_COMPAT_VERSION;
+       /* batman packet type: unicast */
+       unicast_packet->header.packet_type = BATADV_UNICAST;
+       /* set unicast ttl */
+       unicast_packet->header.ttl = BATADV_TTL;
+       /* copy the destination for faster routing */
+       memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
+       /* set the destination tt version number */
+       unicast_packet->ttvn = ttvn;
+
+       return true;
+}
+
+/**
+ * batadv_send_skb_prepare_unicast - encapsulate an skb with a unicast header
+ * @skb: the skb containing the payload to encapsulate
+ * @orig_node: the destination node
+ *
+ * Returns false if the payload could not be encapsulated or true otherwise.
+ */
+static bool batadv_send_skb_prepare_unicast(struct sk_buff *skb,
+                                           struct batadv_orig_node *orig_node)
+{
+       size_t uni_size = sizeof(struct batadv_unicast_packet);
+
+       return batadv_send_skb_push_fill_unicast(skb, uni_size, orig_node);
+}
+
+/**
+ * batadv_send_skb_prepare_unicast_4addr - encapsulate an skb with a
+ *  unicast 4addr header
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the skb containing the payload to encapsulate
+ * @orig_node: the destination node
+ * @packet_subtype: the unicast 4addr packet subtype to use
+ *
+ * Returns false if the payload could not be encapsulated or true otherwise.
+ */
+bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
+                                          struct sk_buff *skb,
+                                          struct batadv_orig_node *orig,
+                                          int packet_subtype)
+{
+       struct batadv_hard_iface *primary_if;
+       struct batadv_unicast_4addr_packet *uc_4addr_packet;
+       bool ret = false;
+
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
+
+       /* Pull the header space and fill the unicast_packet substructure.
+        * We can do that because the first member of the uc_4addr_packet
+        * is of type struct unicast_packet
+        */
+       if (!batadv_send_skb_push_fill_unicast(skb, sizeof(*uc_4addr_packet),
+                                              orig))
+               goto out;
+
+       uc_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
+       uc_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR;
+       memcpy(uc_4addr_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
+       uc_4addr_packet->subtype = packet_subtype;
+       uc_4addr_packet->reserved = 0;
+
+       ret = true;
+out:
+       if (primary_if)
+               batadv_hardif_free_ref(primary_if);
+       return ret;
+}
+
+/**
+ * batadv_send_skb_unicast - encapsulate and send an skb via unicast
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @packet_type: the batman unicast packet type to use
+ * @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
+ *  4addr packets)
+ * @orig_node: the originator to send the packet to
+ * @vid: the vid to be used to search the translation table
+ *
+ * Wrap the given skb into a batman-adv unicast or unicast-4addr header
+ * depending on whether BATADV_UNICAST or BATADV_UNICAST_4ADDR was supplied
+ * as packet_type. Then send this frame to the given orig_node and release a
+ * reference to this orig_node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, int packet_type,
+                                  int packet_subtype,
+                                  struct batadv_orig_node *orig_node,
+                                  unsigned short vid)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+       struct batadv_unicast_packet *unicast_packet;
+       int ret = NET_XMIT_DROP;
+
+       if (!orig_node)
+               goto out;
+
+       switch (packet_type) {
+       case BATADV_UNICAST:
+               if (!batadv_send_skb_prepare_unicast(skb, orig_node))
+                       goto out;
+               break;
+       case BATADV_UNICAST_4ADDR:
+               if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb,
+                                                          orig_node,
+                                                          packet_subtype))
+                       goto out;
+               break;
+       default:
+               /* this function supports UNICAST and UNICAST_4ADDR only. It
+                * should never be invoked with any other packet type
+                */
+               goto out;
+       }
+
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
+
+       /* inform the destination node that we are still missing a correct route
+        * for this client. The destination will receive this packet and will
+        * try to reroute it because the ttvn contained in the header is less
+        * than the current one
+        */
+       if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest, vid))
+               unicast_packet->ttvn = unicast_packet->ttvn - 1;
 
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
+               ret = NET_XMIT_SUCCESS;
+
+out:
+       if (orig_node)
+               batadv_orig_node_free_ref(orig_node);
+       if (ret == NET_XMIT_DROP)
+               kfree_skb(skb);
        return ret;
 }
 
+/**
+ * batadv_send_skb_via_tt_generic - send an skb via TT lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @packet_type: the batman unicast packet type to use
+ * @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
+ *  4addr packets)
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the recipient node for the destination address in the ethernet
+ * header via the translation table. Wrap the given skb into a batman-adv
+ * unicast or unicast-4addr header depending on whether BATADV_UNICAST or
+ * BATADV_UNICAST_4ADDR was supplied as packet_type. Then send this frame
+ * to the according destination node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, int packet_type,
+                                  int packet_subtype, unsigned short vid)
+{
+       struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+       struct batadv_orig_node *orig_node;
+
+       orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
+                                            ethhdr->h_dest, vid);
+       return batadv_send_skb_unicast(bat_priv, skb, packet_type,
+                                      packet_subtype, orig_node, vid);
+}
+
+/**
+ * batadv_send_skb_via_gw - send an skb via gateway lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the currently selected gateway. Wrap the given skb into a batman-adv
+ * unicast header and send this frame to this gateway node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                          unsigned short vid)
+{
+       struct batadv_orig_node *orig_node;
+
+       orig_node = batadv_gw_get_selected_orig(bat_priv);
+       return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
+                                      orig_node, vid);
+}
+
 void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
index e7b1788..aa2e253 100644 (file)
@@ -34,5 +34,58 @@ void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work);
 void
 batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
                                 const struct batadv_hard_iface *hard_iface);
+bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
+                                          struct sk_buff *skb,
+                                          struct batadv_orig_node *orig_node,
+                                          int packet_subtype);
+int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, int packet_type,
+                                  int packet_subtype, unsigned short vid);
+int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                          unsigned short vid);
+
+/**
+ * batadv_send_skb_via_tt - send an skb via TT lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the payload to send
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the recipient node for the destination address in the ethernet
+ * header via the translation table. Wrap the given skb into a batman-adv
+ * unicast header. Then send this frame to the according destination node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
+                                        struct sk_buff *skb,
+                                        unsigned short vid)
+{
+       return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST, 0,
+                                             vid);
+}
+
+/**
+ * batadv_send_skb_via_tt_4addr - send an skb via TT lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the payload to send
+ * @packet_subtype: the unicast 4addr packet subtype to use
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the recipient node for the destination address in the ethernet
+ * header via the translation table. Wrap the given skb into a batman-adv
+ * unicast-4addr header. Then send this frame to the according destination
+ * node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv,
+                                              struct sk_buff *skb,
+                                              int packet_subtype,
+                                              unsigned short vid)
+{
+       return batadv_send_skb_via_tt_generic(bat_priv, skb,
+                                             BATADV_UNICAST_4ADDR,
+                                             packet_subtype, vid);
+}
 
 #endif /* _NET_BATMAN_ADV_SEND_H_ */
index 813db4e..36f0508 100644 (file)
@@ -34,8 +34,6 @@
 #include <linux/ethtool.h>
 #include <linux/etherdevice.h>
 #include <linux/if_vlan.h>
-#include <linux/if_ether.h>
-#include "unicast.h"
 #include "bridge_loop_avoidance.h"
 #include "network-coding.h"
 
@@ -120,9 +118,10 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
 
        /* only modify transtable if it has been initialized before */
        if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
-               batadv_tt_local_remove(bat_priv, old_addr,
+               batadv_tt_local_remove(bat_priv, old_addr, BATADV_NO_FLAGS,
                                       "mac address changed", false);
-               batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX);
+               batadv_tt_local_add(dev, addr->sa_data, BATADV_NO_FLAGS,
+                                   BATADV_NULL_IFINDEX);
        }
 
        return 0;
@@ -139,36 +138,48 @@ static int batadv_interface_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
+/**
+ * batadv_interface_set_rx_mode - set the rx mode of a device
+ * @dev: registered network device to modify
+ *
+ * We do not actually need to set any rx filters for the virtual batman
+ * soft interface. However a dummy handler enables a user to set static
+ * multicast listeners for instance.
+ */
+static void batadv_interface_set_rx_mode(struct net_device *dev)
+{
+}
+
 static int batadv_interface_tx(struct sk_buff *skb,
                               struct net_device *soft_iface)
 {
-       struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+       struct ethhdr *ethhdr;
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_bcast_packet *bcast_packet;
-       struct vlan_ethhdr *vhdr;
-       __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+       __be16 ethertype = htons(ETH_P_BATMAN);
        static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00,
                                                   0x00, 0x00};
        static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00,
                                                    0x00, 0x00};
+       struct vlan_ethhdr *vhdr;
        unsigned int header_len = 0;
        int data_len = skb->len, ret;
-       unsigned short vid __maybe_unused = BATADV_NO_FLAGS;
-       bool do_bcast = false;
-       uint32_t seqno;
        unsigned long brd_delay = 1;
+       bool do_bcast = false, client_added;
+       unsigned short vid;
+       uint32_t seqno;
 
        if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
                goto dropped;
 
        soft_iface->trans_start = jiffies;
+       vid = batadv_get_vid(skb, 0);
+       ethhdr = (struct ethhdr *)skb->data;
 
        switch (ntohs(ethhdr->h_proto)) {
        case ETH_P_8021Q:
                vhdr = (struct vlan_ethhdr *)skb->data;
-               vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
-               vid |= BATADV_VLAN_HAS_TAG;
 
                if (vhdr->h_vlan_encapsulated_proto != ethertype)
                        break;
@@ -185,8 +196,12 @@ static int batadv_interface_tx(struct sk_buff *skb,
        ethhdr = (struct ethhdr *)skb->data;
 
        /* Register the client MAC in the transtable */
-       if (!is_multicast_ether_addr(ethhdr->h_source))
-               batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif);
+       if (!is_multicast_ether_addr(ethhdr->h_source)) {
+               client_added = batadv_tt_local_add(soft_iface, ethhdr->h_source,
+                                                  vid, skb->skb_iif);
+               if (!client_added)
+                       goto dropped;
+       }
 
        /* don't accept stp packets. STP does not help in meshes.
         * better use the bridge loop avoidance ...
@@ -286,8 +301,12 @@ static int batadv_interface_tx(struct sk_buff *skb,
 
                batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb);
 
-               ret = batadv_unicast_send_skb(bat_priv, skb);
-               if (ret != 0)
+               if (is_multicast_ether_addr(ethhdr->h_dest))
+                       ret = batadv_send_skb_via_gw(bat_priv, skb, vid);
+               else
+                       ret = batadv_send_skb_via_tt(bat_priv, skb, vid);
+
+               if (ret == NET_XMIT_DROP)
                        goto dropped_freed;
        }
 
@@ -309,12 +328,12 @@ void batadv_interface_rx(struct net_device *soft_iface,
                         struct sk_buff *skb, struct batadv_hard_iface *recv_if,
                         int hdr_size, struct batadv_orig_node *orig_node)
 {
+       struct batadv_header *batadv_header = (struct batadv_header *)skb->data;
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
-       struct ethhdr *ethhdr;
+       __be16 ethertype = htons(ETH_P_BATMAN);
        struct vlan_ethhdr *vhdr;
-       struct batadv_header *batadv_header = (struct batadv_header *)skb->data;
-       unsigned short vid __maybe_unused = BATADV_NO_FLAGS;
-       __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+       struct ethhdr *ethhdr;
+       unsigned short vid;
        bool is_bcast;
 
        is_bcast = (batadv_header->packet_type == BATADV_BCAST);
@@ -326,13 +345,12 @@ void batadv_interface_rx(struct net_device *soft_iface,
        skb_pull_rcsum(skb, hdr_size);
        skb_reset_mac_header(skb);
 
+       vid = batadv_get_vid(skb, hdr_size);
        ethhdr = eth_hdr(skb);
 
        switch (ntohs(ethhdr->h_proto)) {
        case ETH_P_8021Q:
                vhdr = (struct vlan_ethhdr *)skb->data;
-               vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
-               vid |= BATADV_VLAN_HAS_TAG;
 
                if (vhdr->h_vlan_encapsulated_proto != ethertype)
                        break;
@@ -368,9 +386,10 @@ void batadv_interface_rx(struct net_device *soft_iface,
 
        if (orig_node)
                batadv_tt_add_temporary_global_entry(bat_priv, orig_node,
-                                                    ethhdr->h_source);
+                                                    ethhdr->h_source, vid);
 
-       if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest))
+       if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest,
+                                 vid))
                goto dropped;
 
        netif_rx(skb);
@@ -382,6 +401,177 @@ out:
        return;
 }
 
+/**
+ * batadv_softif_vlan_free_ref - decrease the vlan object refcounter and
+ *  possibly free it
+ * @softif_vlan: the vlan object to release
+ */
+void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan)
+{
+       if (atomic_dec_and_test(&softif_vlan->refcount))
+               kfree_rcu(softif_vlan, rcu);
+}
+
+/**
+ * batadv_softif_vlan_get - get the vlan object for a specific vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the identifier of the vlan object to retrieve
+ *
+ * Returns the private data of the vlan matching the vid passed as argument or
+ * NULL otherwise. The refcounter of the returned object is incremented by 1.
+ */
+struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
+                                                 unsigned short vid)
+{
+       struct batadv_softif_vlan *vlan_tmp, *vlan = NULL;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) {
+               if (vlan_tmp->vid != vid)
+                       continue;
+
+               if (!atomic_inc_not_zero(&vlan_tmp->refcount))
+                       continue;
+
+               vlan = vlan_tmp;
+               break;
+       }
+       rcu_read_unlock();
+
+       return vlan;
+}
+
+/**
+ * batadv_create_vlan - allocate the needed resources for a new vlan
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ *
+ * Returns 0 on success, a negative error otherwise.
+ */
+int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
+{
+       struct batadv_softif_vlan *vlan;
+       int err;
+
+       vlan = batadv_softif_vlan_get(bat_priv, vid);
+       if (vlan) {
+               batadv_softif_vlan_free_ref(vlan);
+               return -EEXIST;
+       }
+
+       vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
+       if (!vlan)
+               return -ENOMEM;
+
+       vlan->vid = vid;
+       atomic_set(&vlan->refcount, 1);
+
+       atomic_set(&vlan->ap_isolation, 0);
+
+       err = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan);
+       if (err) {
+               kfree(vlan);
+               return err;
+       }
+
+       /* add a new TT local entry. This one will be marked with the NOPURGE
+        * flag
+        */
+       batadv_tt_local_add(bat_priv->soft_iface,
+                           bat_priv->soft_iface->dev_addr, vid,
+                           BATADV_NULL_IFINDEX);
+
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
+       spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
+
+       return 0;
+}
+
+/**
+ * batadv_softif_destroy_vlan - remove and destroy a softif_vlan object
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: the object to remove
+ */
+static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
+                                      struct batadv_softif_vlan *vlan)
+{
+       spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+       hlist_del_rcu(&vlan->list);
+       spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
+
+       batadv_sysfs_del_vlan(bat_priv, vlan);
+
+       /* explicitly remove the associated TT local entry because it is marked
+        * with the NOPURGE flag
+        */
+       batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr,
+                              vlan->vid, "vlan interface destroyed", false);
+
+       batadv_softif_vlan_free_ref(vlan);
+}
+
+/**
+ * batadv_interface_add_vid - ndo_add_vid API implementation
+ * @dev: the netdev of the mesh interface
+ * @vid: identifier of the new vlan
+ *
+ * Set up all the internal structures for handling the new vlan on top of the
+ * mesh interface
+ *
+ * Returns 0 on success or a negative error code in case of failure.
+ */
+static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
+                                   unsigned short vid)
+{
+       struct batadv_priv *bat_priv = netdev_priv(dev);
+
+       /* only 802.1Q vlans are supported.
+        * batman-adv does not know how to handle other types
+        */
+       if (proto != htons(ETH_P_8021Q))
+               return -EINVAL;
+
+       vid |= BATADV_VLAN_HAS_TAG;
+
+       return batadv_softif_create_vlan(bat_priv, vid);
+}
+
+/**
+ * batadv_interface_kill_vid - ndo_kill_vid API implementation
+ * @dev: the netdev of the mesh interface
+ * @vid: identifier of the deleted vlan
+ *
+ * Destroy all the internal structures used to handle the vlan identified by vid
+ * on top of the mesh interface
+ *
+ * Returns 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q
+ * or -ENOENT if the specified vlan id wasn't registered.
+ */
+static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
+                                    unsigned short vid)
+{
+       struct batadv_priv *bat_priv = netdev_priv(dev);
+       struct batadv_softif_vlan *vlan;
+
+       /* only 802.1Q vlans are supported. batman-adv does not know how to
+        * handle other types
+        */
+       if (proto != htons(ETH_P_8021Q))
+               return -EINVAL;
+
+       vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
+       if (!vlan)
+               return -ENOENT;
+
+       batadv_softif_destroy_vlan(bat_priv, vlan);
+
+       /* finally free the vlan object */
+       batadv_softif_vlan_free_ref(vlan);
+
+       return 0;
+}
+
 /* batman-adv network devices have devices nesting below it and are a special
  * "super class" of normal network devices; split their locks off into a
  * separate class since they always nest.
@@ -421,6 +611,7 @@ static void batadv_set_lockdep_class(struct net_device *dev)
  */
 static void batadv_softif_destroy_finish(struct work_struct *work)
 {
+       struct batadv_softif_vlan *vlan;
        struct batadv_priv *bat_priv;
        struct net_device *soft_iface;
 
@@ -428,6 +619,13 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
                                cleanup_work);
        soft_iface = bat_priv->soft_iface;
 
+       /* destroy the "untagged" VLAN */
+       vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+       if (vlan) {
+               batadv_softif_destroy_vlan(bat_priv, vlan);
+               batadv_softif_vlan_free_ref(vlan);
+       }
+
        batadv_sysfs_del_meshif(soft_iface);
 
        rtnl_lock();
@@ -444,6 +642,7 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
 static int batadv_softif_init_late(struct net_device *dev)
 {
        struct batadv_priv *bat_priv;
+       uint32_t random_seqno;
        int ret;
        size_t cnt_len = sizeof(uint64_t) * BATADV_CNT_NUM;
 
@@ -468,17 +667,17 @@ static int batadv_softif_init_late(struct net_device *dev)
 #ifdef CONFIG_BATMAN_ADV_DAT
        atomic_set(&bat_priv->distributed_arp_table, 1);
 #endif
-       atomic_set(&bat_priv->ap_isolation, 0);
-       atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE);
        atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
        atomic_set(&bat_priv->gw_sel_class, 20);
-       atomic_set(&bat_priv->gw_bandwidth, 41);
+       atomic_set(&bat_priv->gw.bandwidth_down, 100);
+       atomic_set(&bat_priv->gw.bandwidth_up, 20);
        atomic_set(&bat_priv->orig_interval, 1000);
        atomic_set(&bat_priv->hop_penalty, 30);
 #ifdef CONFIG_BATMAN_ADV_DEBUG
        atomic_set(&bat_priv->log_level, 0);
 #endif
        atomic_set(&bat_priv->fragmentation, 1);
+       atomic_set(&bat_priv->packet_size_max, ETH_DATA_LEN);
        atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN);
        atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN);
 
@@ -493,6 +692,10 @@ static int batadv_softif_init_late(struct net_device *dev)
        bat_priv->tt.last_changeset = NULL;
        bat_priv->tt.last_changeset_len = 0;
 
+       /* randomize initial seqno to avoid collision */
+       get_random_bytes(&random_seqno, sizeof(random_seqno));
+       atomic_set(&bat_priv->frag_seqno, random_seqno);
+
        bat_priv->primary_if = NULL;
        bat_priv->num_ifaces = 0;
 
@@ -578,8 +781,11 @@ static const struct net_device_ops batadv_netdev_ops = {
        .ndo_open = batadv_interface_open,
        .ndo_stop = batadv_interface_release,
        .ndo_get_stats = batadv_interface_stats,
+       .ndo_vlan_rx_add_vid = batadv_interface_add_vid,
+       .ndo_vlan_rx_kill_vid = batadv_interface_kill_vid,
        .ndo_set_mac_address = batadv_interface_set_mac_addr,
        .ndo_change_mtu = batadv_interface_change_mtu,
+       .ndo_set_rx_mode = batadv_interface_set_rx_mode,
        .ndo_start_xmit = batadv_interface_tx,
        .ndo_validate_addr = eth_validate_addr,
        .ndo_add_slave = batadv_softif_slave_add,
@@ -616,6 +822,7 @@ static void batadv_softif_init_early(struct net_device *dev)
 
        dev->netdev_ops = &batadv_netdev_ops;
        dev->destructor = batadv_softif_free;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
        dev->tx_queue_len = 0;
 
        /* can't call min_mtu, because the needed variables
@@ -623,7 +830,7 @@ static void batadv_softif_init_early(struct net_device *dev)
         */
        dev->mtu = ETH_DATA_LEN;
        /* reserve more space in the skbuff for our header */
-       dev->hard_header_len = BATADV_HEADER_LEN;
+       dev->hard_header_len = batadv_max_header_len();
 
        /* generate random address */
        eth_hw_addr_random(dev);
@@ -760,6 +967,12 @@ static const struct {
        { "mgmt_tx_bytes" },
        { "mgmt_rx" },
        { "mgmt_rx_bytes" },
+       { "frag_tx" },
+       { "frag_tx_bytes" },
+       { "frag_rx" },
+       { "frag_rx_bytes" },
+       { "frag_fwd" },
+       { "frag_fwd_bytes" },
        { "tt_request_tx" },
        { "tt_request_rx" },
        { "tt_response_tx" },
index 2f2472c..06fc91f 100644 (file)
@@ -28,5 +28,9 @@ struct net_device *batadv_softif_create(const char *name);
 void batadv_softif_destroy_sysfs(struct net_device *soft_iface);
 int batadv_softif_is_valid(const struct net_device *net_dev);
 extern struct rtnl_link_ops batadv_link_ops;
+int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
+void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan);
+struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
+                                                 unsigned short vid);
 
 #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */
index 4114b96..6335433 100644 (file)
 #include "sysfs.h"
 #include "translation-table.h"
 #include "distributed-arp-table.h"
+#include "network-coding.h"
 #include "originator.h"
 #include "hard-interface.h"
+#include "soft-interface.h"
 #include "gateway_common.h"
 #include "gateway_client.h"
-#include "vis.h"
 
 static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
 {
@@ -39,6 +40,53 @@ static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
        return netdev_priv(net_dev);
 }
 
+/**
+ * batadv_vlan_kobj_to_batpriv - convert a vlan kobj in the associated batpriv
+ * @obj: kobject to covert
+ *
+ * Returns the associated batadv_priv struct.
+ */
+static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj)
+{
+       /* VLAN specific attributes are located in the root sysfs folder if they
+        * refer to the untagged VLAN..
+        */
+       if (!strcmp(BATADV_SYSFS_IF_MESH_SUBDIR, obj->name))
+               return batadv_kobj_to_batpriv(obj);
+
+       /* ..while the attributes for the tagged vlans are located in
+        * the in the corresponding "vlan%VID" subfolder
+        */
+       return batadv_kobj_to_batpriv(obj->parent);
+}
+
+/**
+ * batadv_kobj_to_vlan - convert a kobj in the associated softif_vlan struct
+ * @obj: kobject to covert
+ *
+ * Returns the associated softif_vlan struct if found, NULL otherwise.
+ */
+static struct batadv_softif_vlan *
+batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj)
+{
+       struct batadv_softif_vlan *vlan_tmp, *vlan = NULL;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) {
+               if (vlan_tmp->kobj != obj)
+                       continue;
+
+               if (!atomic_inc_not_zero(&vlan_tmp->refcount))
+                       continue;
+
+               vlan = vlan_tmp;
+               break;
+       }
+       rcu_read_unlock();
+
+       return vlan;
+}
+
 #define BATADV_UEV_TYPE_VAR    "BATTYPE="
 #define BATADV_UEV_ACTION_VAR  "BATACTION="
 #define BATADV_UEV_DATA_VAR    "BATDATA="
@@ -53,6 +101,15 @@ static char *batadv_uev_type_str[] = {
        "gw"
 };
 
+/* Use this, if you have customized show and store functions for vlan attrs */
+#define BATADV_ATTR_VLAN(_name, _mode, _show, _store)  \
+struct batadv_attribute batadv_attr_vlan_##_name = {   \
+       .attr = {.name = __stringify(_name),            \
+                .mode = _mode },                       \
+       .show   = _show,                                \
+       .store  = _store,                               \
+};
+
 /* Use this, if you have customized show and store functions */
 #define BATADV_ATTR(_name, _mode, _show, _store)       \
 struct batadv_attribute batadv_attr_##_name = {                \
@@ -122,6 +179,41 @@ ssize_t batadv_show_##_name(struct kobject *kobj,                  \
        static BATADV_ATTR(_name, _mode, batadv_show_##_name,           \
                           batadv_store_##_name)
 
+#define BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func)                 \
+ssize_t batadv_store_vlan_##_name(struct kobject *kobj,                        \
+                                 struct attribute *attr, char *buff,   \
+                                 size_t count)                         \
+{                                                                      \
+       struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\
+       struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \
+                                                             kobj);    \
+       size_t res = __batadv_store_bool_attr(buff, count, _post_func,  \
+                                             attr, &vlan->_name,       \
+                                             bat_priv->soft_iface);    \
+       batadv_softif_vlan_free_ref(vlan);                              \
+       return res;                                                     \
+}
+
+#define BATADV_ATTR_VLAN_SHOW_BOOL(_name)                              \
+ssize_t batadv_show_vlan_##_name(struct kobject *kobj,                 \
+                                struct attribute *attr, char *buff)    \
+{                                                                      \
+       struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\
+       struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \
+                                                             kobj);    \
+       size_t res = sprintf(buff, "%s\n",                              \
+                            atomic_read(&vlan->_name) == 0 ?           \
+                            "disabled" : "enabled");                   \
+       batadv_softif_vlan_free_ref(vlan);                              \
+       return res;                                                     \
+}
+
+/* Use this, if you are going to turn a [name] in the vlan struct on or off */
+#define BATADV_ATTR_VLAN_BOOL(_name, _mode, _post_func)                        \
+       static BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func)           \
+       static BATADV_ATTR_VLAN_SHOW_BOOL(_name)                        \
+       static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \
+                               batadv_store_vlan_##_name)
 
 static int batadv_store_bool_attr(char *buff, size_t count,
                                  struct net_device *net_dev,
@@ -230,74 +322,6 @@ __batadv_store_uint_attr(const char *buff, size_t count,
        return ret;
 }
 
-static ssize_t batadv_show_vis_mode(struct kobject *kobj,
-                                   struct attribute *attr, char *buff)
-{
-       struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
-       int vis_mode = atomic_read(&bat_priv->vis_mode);
-       const char *mode;
-
-       if (vis_mode == BATADV_VIS_TYPE_CLIENT_UPDATE)
-               mode = "client";
-       else
-               mode = "server";
-
-       return sprintf(buff, "%s\n", mode);
-}
-
-static ssize_t batadv_store_vis_mode(struct kobject *kobj,
-                                    struct attribute *attr, char *buff,
-                                    size_t count)
-{
-       struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
-       struct batadv_priv *bat_priv = netdev_priv(net_dev);
-       unsigned long val;
-       int ret, vis_mode_tmp = -1;
-       const char *old_mode, *new_mode;
-
-       ret = kstrtoul(buff, 10, &val);
-
-       if (((count == 2) && (!ret) &&
-            (val == BATADV_VIS_TYPE_CLIENT_UPDATE)) ||
-           (strncmp(buff, "client", 6) == 0) ||
-           (strncmp(buff, "off", 3) == 0))
-               vis_mode_tmp = BATADV_VIS_TYPE_CLIENT_UPDATE;
-
-       if (((count == 2) && (!ret) &&
-            (val == BATADV_VIS_TYPE_SERVER_SYNC)) ||
-           (strncmp(buff, "server", 6) == 0))
-               vis_mode_tmp = BATADV_VIS_TYPE_SERVER_SYNC;
-
-       if (vis_mode_tmp < 0) {
-               if (buff[count - 1] == '\n')
-                       buff[count - 1] = '\0';
-
-               batadv_info(net_dev,
-                           "Invalid parameter for 'vis mode' setting received: %s\n",
-                           buff);
-               return -EINVAL;
-       }
-
-       if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp)
-               return count;
-
-       if (atomic_read(&bat_priv->vis_mode) == BATADV_VIS_TYPE_CLIENT_UPDATE)
-               old_mode =  "client";
-       else
-               old_mode = "server";
-
-       if (vis_mode_tmp == BATADV_VIS_TYPE_CLIENT_UPDATE)
-               new_mode =  "client";
-       else
-               new_mode = "server";
-
-       batadv_info(net_dev, "Changing vis mode from: %s to: %s\n", old_mode,
-                   new_mode);
-
-       atomic_set(&bat_priv->vis_mode, (unsigned int)vis_mode_tmp);
-       return count;
-}
-
 static ssize_t batadv_show_bat_algo(struct kobject *kobj,
                                    struct attribute *attr, char *buff)
 {
@@ -390,6 +414,7 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj,
         */
        batadv_gw_check_client_stop(bat_priv);
        atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp);
+       batadv_gw_tvlv_container_update(bat_priv);
        return count;
 }
 
@@ -397,15 +422,13 @@ static ssize_t batadv_show_gw_bwidth(struct kobject *kobj,
                                     struct attribute *attr, char *buff)
 {
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
-       int down, up;
-       int gw_bandwidth = atomic_read(&bat_priv->gw_bandwidth);
-
-       batadv_gw_bandwidth_to_kbit(gw_bandwidth, &down, &up);
-       return sprintf(buff, "%i%s/%i%s\n",
-                      (down > 2048 ? down / 1024 : down),
-                      (down > 2048 ? "MBit" : "KBit"),
-                      (up > 2048 ? up / 1024 : up),
-                      (up > 2048 ? "MBit" : "KBit"));
+       uint32_t down, up;
+
+       down = atomic_read(&bat_priv->gw.bandwidth_down);
+       up = atomic_read(&bat_priv->gw.bandwidth_up);
+
+       return sprintf(buff, "%u.%u/%u.%u MBit\n", down / 10,
+                      down % 10, up / 10, up % 10);
 }
 
 static ssize_t batadv_store_gw_bwidth(struct kobject *kobj,
@@ -426,12 +449,10 @@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
 BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
 #endif
 #ifdef CONFIG_BATMAN_ADV_DAT
-BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, NULL);
+BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR,
+                    batadv_dat_status_update);
 #endif
 BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu);
-BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
-static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode,
-                  batadv_store_vis_mode);
 static BATADV_ATTR(routing_algo, S_IRUGO, batadv_show_bat_algo, NULL);
 static BATADV_ATTR(gw_mode, S_IRUGO | S_IWUSR, batadv_show_gw_mode,
                   batadv_store_gw_mode);
@@ -447,7 +468,8 @@ static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
 BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
 #endif
 #ifdef CONFIG_BATMAN_ADV_NC
-BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, NULL);
+BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR,
+                    batadv_nc_status_update);
 #endif
 
 static struct batadv_attribute *batadv_mesh_attrs[] = {
@@ -460,8 +482,6 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
        &batadv_attr_distributed_arp_table,
 #endif
        &batadv_attr_fragmentation,
-       &batadv_attr_ap_isolation,
-       &batadv_attr_vis_mode,
        &batadv_attr_routing_algo,
        &batadv_attr_gw_mode,
        &batadv_attr_orig_interval,
@@ -477,6 +497,16 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
        NULL,
 };
 
+BATADV_ATTR_VLAN_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
+
+/**
+ * batadv_vlan_attrs - array of vlan specific sysfs attributes
+ */
+static struct batadv_attribute *batadv_vlan_attrs[] = {
+       &batadv_attr_vlan_ap_isolation,
+       NULL,
+};
+
 int batadv_sysfs_add_meshif(struct net_device *dev)
 {
        struct kobject *batif_kobject = &dev->dev.kobj;
@@ -527,6 +557,80 @@ void batadv_sysfs_del_meshif(struct net_device *dev)
        bat_priv->mesh_obj = NULL;
 }
 
+/**
+ * batadv_sysfs_add_vlan - add all the needed sysfs objects for the new vlan
+ * @dev: netdev of the mesh interface
+ * @vlan: private data of the newly added VLAN interface
+ *
+ * Returns 0 on success and -ENOMEM if any of the structure allocations fails.
+ */
+int batadv_sysfs_add_vlan(struct net_device *dev,
+                         struct batadv_softif_vlan *vlan)
+{
+       char vlan_subdir[sizeof(BATADV_SYSFS_VLAN_SUBDIR_PREFIX) + 5];
+       struct batadv_priv *bat_priv = netdev_priv(dev);
+       struct batadv_attribute **bat_attr;
+       int err;
+
+       if (vlan->vid & BATADV_VLAN_HAS_TAG) {
+               sprintf(vlan_subdir, BATADV_SYSFS_VLAN_SUBDIR_PREFIX "%hu",
+                       vlan->vid & VLAN_VID_MASK);
+
+               vlan->kobj = kobject_create_and_add(vlan_subdir,
+                                                   bat_priv->mesh_obj);
+               if (!vlan->kobj) {
+                       batadv_err(dev, "Can't add sysfs directory: %s/%s\n",
+                                  dev->name, vlan_subdir);
+                       goto out;
+               }
+       } else {
+               /* the untagged LAN uses the root folder to store its "VLAN
+                * specific attributes"
+                */
+               vlan->kobj = bat_priv->mesh_obj;
+               kobject_get(bat_priv->mesh_obj);
+       }
+
+       for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) {
+               err = sysfs_create_file(vlan->kobj,
+                                       &((*bat_attr)->attr));
+               if (err) {
+                       batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n",
+                                  dev->name, vlan_subdir,
+                                  ((*bat_attr)->attr).name);
+                       goto rem_attr;
+               }
+       }
+
+       return 0;
+
+rem_attr:
+       for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
+               sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
+
+       kobject_put(vlan->kobj);
+       vlan->kobj = NULL;
+out:
+       return -ENOMEM;
+}
+
+/**
+ * batadv_sysfs_del_vlan - remove all the sysfs objects for a given VLAN
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: the private data of the VLAN to destroy
+ */
+void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
+                          struct batadv_softif_vlan *vlan)
+{
+       struct batadv_attribute **bat_attr;
+
+       for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
+               sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
+
+       kobject_put(vlan->kobj);
+       vlan->kobj = NULL;
+}
+
 static ssize_t batadv_show_mesh_iface(struct kobject *kobj,
                                      struct attribute *attr, char *buff)
 {
index 479acf4..c7d725d 100644 (file)
 
 #define BATADV_SYSFS_IF_MESH_SUBDIR "mesh"
 #define BATADV_SYSFS_IF_BAT_SUBDIR "batman_adv"
+/**
+ * BATADV_SYSFS_VLAN_SUBDIR_PREFIX - prefix of the subfolder that will be
+ *  created in the sysfs hierarchy for each VLAN interface. The subfolder will
+ *  be named "BATADV_SYSFS_VLAN_SUBDIR_PREFIX%vid".
+ */
+#define BATADV_SYSFS_VLAN_SUBDIR_PREFIX "vlan"
 
 struct batadv_attribute {
        struct attribute attr;
@@ -36,6 +42,10 @@ void batadv_sysfs_del_meshif(struct net_device *dev);
 int batadv_sysfs_add_hardif(struct kobject **hardif_obj,
                            struct net_device *dev);
 void batadv_sysfs_del_hardif(struct kobject **hardif_obj);
+int batadv_sysfs_add_vlan(struct net_device *dev,
+                         struct batadv_softif_vlan *vlan);
+void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
+                          struct batadv_softif_vlan *vlan);
 int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
                        enum batadv_uev_action action, const char *data);
 
index 34510f3..4add57d 100644 (file)
 #include "routing.h"
 #include "bridge_loop_avoidance.h"
 
-#include <linux/crc16.h>
+#include <linux/crc32c.h>
 
 /* hash class keys */
 static struct lock_class_key batadv_tt_local_hash_lock_class_key;
 static struct lock_class_key batadv_tt_global_hash_lock_class_key;
 
 static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
+                                unsigned short vid,
                                 struct batadv_orig_node *orig_node);
 static void batadv_tt_purge(struct work_struct *work);
 static void
@@ -41,7 +42,8 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry);
 static void batadv_tt_global_del(struct batadv_priv *bat_priv,
                                 struct batadv_orig_node *orig_node,
                                 const unsigned char *addr,
-                                const char *message, bool roaming);
+                                unsigned short vid, const char *message,
+                                bool roaming);
 
 /* returns 1 if they are the same mac addr */
 static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
@@ -52,43 +54,93 @@ static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
        return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
 }
 
+/**
+ * batadv_choose_tt - return the index of the tt entry in the hash table
+ * @data: pointer to the tt_common_entry object to map
+ * @size: the size of the hash table
+ *
+ * Returns the hash index where the object represented by 'data' should be
+ * stored at.
+ */
+static inline uint32_t batadv_choose_tt(const void *data, uint32_t size)
+{
+       struct batadv_tt_common_entry *tt;
+       uint32_t hash = 0;
+
+       tt = (struct batadv_tt_common_entry *)data;
+       hash = batadv_hash_bytes(hash, &tt->addr, ETH_ALEN);
+       hash = batadv_hash_bytes(hash, &tt->vid, sizeof(tt->vid));
+
+       hash += (hash << 3);
+       hash ^= (hash >> 11);
+       hash += (hash << 15);
+
+       return hash % size;
+}
+
+/**
+ * batadv_tt_hash_find - look for a client in the given hash table
+ * @hash: the hash table to search
+ * @addr: the mac address of the client to look for
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the tt_common struct belonging to the searched client if
+ * found, NULL otherwise.
+ */
 static struct batadv_tt_common_entry *
-batadv_tt_hash_find(struct batadv_hashtable *hash, const void *data)
+batadv_tt_hash_find(struct batadv_hashtable *hash, const uint8_t *addr,
+                   unsigned short vid)
 {
        struct hlist_head *head;
-       struct batadv_tt_common_entry *tt_common_entry;
-       struct batadv_tt_common_entry *tt_common_entry_tmp = NULL;
+       struct batadv_tt_common_entry to_search, *tt, *tt_tmp = NULL;
        uint32_t index;
 
        if (!hash)
                return NULL;
 
-       index = batadv_choose_orig(data, hash->size);
+       memcpy(to_search.addr, addr, ETH_ALEN);
+       to_search.vid = vid;
+
+       index = batadv_choose_tt(&to_search, hash->size);
        head = &hash->table[index];
 
        rcu_read_lock();
-       hlist_for_each_entry_rcu(tt_common_entry, head, hash_entry) {
-               if (!batadv_compare_eth(tt_common_entry, data))
+       hlist_for_each_entry_rcu(tt, head, hash_entry) {
+               if (!batadv_compare_eth(tt, addr))
+                       continue;
+
+               if (tt->vid != vid)
                        continue;
 
-               if (!atomic_inc_not_zero(&tt_common_entry->refcount))
+               if (!atomic_inc_not_zero(&tt->refcount))
                        continue;
 
-               tt_common_entry_tmp = tt_common_entry;
+               tt_tmp = tt;
                break;
        }
        rcu_read_unlock();
 
-       return tt_common_entry_tmp;
+       return tt_tmp;
 }
 
+/**
+ * batadv_tt_local_hash_find - search the local table for a given client
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the client to look for
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the corresponding tt_local_entry struct if the client is
+ * found, NULL otherwise.
+ */
 static struct batadv_tt_local_entry *
-batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const void *data)
+batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const uint8_t *addr,
+                         unsigned short vid)
 {
        struct batadv_tt_common_entry *tt_common_entry;
        struct batadv_tt_local_entry *tt_local_entry = NULL;
 
-       tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, data);
+       tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, addr,
+                                             vid);
        if (tt_common_entry)
                tt_local_entry = container_of(tt_common_entry,
                                              struct batadv_tt_local_entry,
@@ -96,13 +148,24 @@ batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const void *data)
        return tt_local_entry;
 }
 
+/**
+ * batadv_tt_global_hash_find - search the global table for a given client
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the client to look for
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the corresponding tt_global_entry struct if the client
+ * is found, NULL otherwise.
+ */
 static struct batadv_tt_global_entry *
-batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const void *data)
+batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const uint8_t *addr,
+                          unsigned short vid)
 {
        struct batadv_tt_common_entry *tt_common_entry;
        struct batadv_tt_global_entry *tt_global_entry = NULL;
 
-       tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, data);
+       tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, addr,
+                                             vid);
        if (tt_common_entry)
                tt_global_entry = container_of(tt_common_entry,
                                               struct batadv_tt_global_entry,
@@ -117,25 +180,17 @@ batadv_tt_local_entry_free_ref(struct batadv_tt_local_entry *tt_local_entry)
                kfree_rcu(tt_local_entry, common.rcu);
 }
 
-static void batadv_tt_global_entry_free_rcu(struct rcu_head *rcu)
-{
-       struct batadv_tt_common_entry *tt_common_entry;
-       struct batadv_tt_global_entry *tt_global_entry;
-
-       tt_common_entry = container_of(rcu, struct batadv_tt_common_entry, rcu);
-       tt_global_entry = container_of(tt_common_entry,
-                                      struct batadv_tt_global_entry, common);
-
-       kfree(tt_global_entry);
-}
-
+/**
+ * batadv_tt_global_entry_free_ref - decrement the refcounter for a
+ *  tt_global_entry and possibly free it
+ * @tt_global_entry: the object to free
+ */
 static void
 batadv_tt_global_entry_free_ref(struct batadv_tt_global_entry *tt_global_entry)
 {
        if (atomic_dec_and_test(&tt_global_entry->common.refcount)) {
                batadv_tt_global_del_orig_list(tt_global_entry);
-               call_rcu(&tt_global_entry->common.rcu,
-                        batadv_tt_global_entry_free_rcu);
+               kfree_rcu(tt_global_entry, common.rcu);
        }
 }
 
@@ -153,13 +208,107 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
        kfree(orig_entry);
 }
 
+/**
+ * batadv_tt_local_size_mod - change the size by v of the local table identified
+ *  by vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier of the sub-table to change
+ * @v: the amount to sum to the local table size
+ */
+static void batadv_tt_local_size_mod(struct batadv_priv *bat_priv,
+                                    unsigned short vid, int v)
+{
+       struct batadv_softif_vlan *vlan;
+
+       vlan = batadv_softif_vlan_get(bat_priv, vid);
+       if (!vlan)
+               return;
+
+       atomic_add(v, &vlan->tt.num_entries);
+
+       batadv_softif_vlan_free_ref(vlan);
+}
+
+/**
+ * batadv_tt_local_size_inc - increase by one the local table size for the given
+ *  vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ */
+static void batadv_tt_local_size_inc(struct batadv_priv *bat_priv,
+                                    unsigned short vid)
+{
+       batadv_tt_local_size_mod(bat_priv, vid, 1);
+}
+
+/**
+ * batadv_tt_local_size_dec - decrease by one the local table size for the given
+ *  vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ */
+static void batadv_tt_local_size_dec(struct batadv_priv *bat_priv,
+                                    unsigned short vid)
+{
+       batadv_tt_local_size_mod(bat_priv, vid, -1);
+}
+
+/**
+ * batadv_tt_global_size_mod - change the size by v of the local table
+ *  identified by vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ * @v: the amount to sum to the global table size
+ */
+static void batadv_tt_global_size_mod(struct batadv_orig_node *orig_node,
+                                     unsigned short vid, int v)
+{
+       struct batadv_orig_node_vlan *vlan;
+
+       vlan = batadv_orig_node_vlan_new(orig_node, vid);
+       if (!vlan)
+               return;
+
+       if (atomic_add_return(v, &vlan->tt.num_entries) == 0) {
+               spin_lock_bh(&orig_node->vlan_list_lock);
+               list_del_rcu(&vlan->list);
+               spin_unlock_bh(&orig_node->vlan_list_lock);
+               batadv_orig_node_vlan_free_ref(vlan);
+       }
+
+       batadv_orig_node_vlan_free_ref(vlan);
+}
+
+/**
+ * batadv_tt_global_size_inc - increase by one the global table size for the
+ *  given vid
+ * @orig_node: the originator which global table size has to be decreased
+ * @vid: the vlan identifier
+ */
+static void batadv_tt_global_size_inc(struct batadv_orig_node *orig_node,
+                                     unsigned short vid)
+{
+       batadv_tt_global_size_mod(orig_node, vid, 1);
+}
+
+/**
+ * batadv_tt_global_size_dec - decrease by one the global table size for the
+ *  given vid
+ * @orig_node: the originator which global table size has to be decreased
+ * @vid: the vlan identifier
+ */
+static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
+                                     unsigned short vid)
+{
+       batadv_tt_global_size_mod(orig_node, vid, -1);
+}
+
 static void
 batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry)
 {
        if (!atomic_dec_and_test(&orig_entry->refcount))
                return;
-       /* to avoid race conditions, immediately decrease the tt counter */
-       atomic_dec(&orig_entry->orig_node->tt_size);
+
        call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu);
 }
 
@@ -180,12 +329,13 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
        bool del_op_requested, del_op_entry;
 
        tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC);
-
        if (!tt_change_node)
                return;
 
        tt_change_node->change.flags = flags;
+       tt_change_node->change.reserved = 0;
        memcpy(tt_change_node->change.addr, common->addr, ETH_ALEN);
+       tt_change_node->change.vid = htons(common->vid);
 
        del_op_requested = flags & BATADV_TT_CLIENT_DEL;
 
@@ -208,6 +358,13 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
                        goto del;
                if (del_op_requested && !del_op_entry)
                        goto del;
+
+               /* this is a second add in the same originator interval. It
+                * means that flags have been changed: update them!
+                */
+               if (!del_op_requested && !del_op_entry)
+                       entry->change.flags = flags;
+
                continue;
 del:
                list_del(&entry->list);
@@ -229,9 +386,55 @@ unlock:
                atomic_inc(&bat_priv->tt.local_changes);
 }
 
-int batadv_tt_len(int changes_num)
+/**
+ * batadv_tt_len - compute length in bytes of given number of tt changes
+ * @changes_num: number of tt changes
+ *
+ * Returns computed length in bytes.
+ */
+static int batadv_tt_len(int changes_num)
+{
+       return changes_num * sizeof(struct batadv_tvlv_tt_change);
+}
+
+/**
+ * batadv_tt_entries - compute the number of entries fitting in tt_len bytes
+ * @tt_len: available space
+ *
+ * Returns the number of entries.
+ */
+static uint16_t batadv_tt_entries(uint16_t tt_len)
+{
+       return tt_len / batadv_tt_len(1);
+}
+
+/**
+ * batadv_tt_local_table_transmit_size - calculates the local translation table
+ *  size when transmitted over the air
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Returns local translation table size in bytes.
+ */
+static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
 {
-       return changes_num * sizeof(struct batadv_tt_change);
+       uint16_t num_vlan = 0, tt_local_entries = 0;
+       struct batadv_softif_vlan *vlan;
+       int hdr_size;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+               num_vlan++;
+               tt_local_entries += atomic_read(&vlan->tt.num_entries);
+       }
+       rcu_read_unlock();
+
+       /* header size of tvlv encapsulated tt response payload */
+       hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
+       hdr_size += sizeof(struct batadv_tvlv_hdr);
+       hdr_size += sizeof(struct batadv_tvlv_tt_data);
+       hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
+
+       return hdr_size + batadv_tt_len(tt_local_entries);
 }
 
 static int batadv_tt_local_init(struct batadv_priv *bat_priv)
@@ -255,33 +458,51 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv,
                                  const char *message)
 {
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Deleting global tt entry %pM: %s\n",
-                  tt_global->common.addr, message);
+                  "Deleting global tt entry %pM (vid: %d): %s\n",
+                  tt_global->common.addr,
+                  BATADV_PRINT_VID(tt_global->common.vid), message);
 
        batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
-                          batadv_choose_orig, tt_global->common.addr);
+                          batadv_choose_tt, &tt_global->common);
        batadv_tt_global_entry_free_ref(tt_global);
 }
 
-void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
-                        int ifindex)
+/**
+ * batadv_tt_local_add - add a new client to the local table or update an
+ *  existing client
+ * @soft_iface: netdev struct of the mesh interface
+ * @addr: the mac address of the client to add
+ * @vid: VLAN identifier
+ * @ifindex: index of the interface where the client is connected to (useful to
+ *  identify wireless clients)
+ *
+ * Returns true if the client was successfully added, false otherwise.
+ */
+bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
+                        unsigned short vid, int ifindex)
 {
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
        struct batadv_tt_local_entry *tt_local;
        struct batadv_tt_global_entry *tt_global;
+       struct net_device *in_dev = NULL;
        struct hlist_head *head;
        struct batadv_tt_orig_list_entry *orig_entry;
-       int hash_added;
-       bool roamed_back = false;
+       int hash_added, table_size, packet_size_max;
+       bool ret = false, roamed_back = false;
+       uint8_t remote_flags;
 
-       tt_local = batadv_tt_local_hash_find(bat_priv, addr);
-       tt_global = batadv_tt_global_hash_find(bat_priv, addr);
+       if (ifindex != BATADV_NULL_IFINDEX)
+               in_dev = dev_get_by_index(&init_net, ifindex);
+
+       tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
+       tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
 
        if (tt_local) {
                tt_local->last_seen = jiffies;
                if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
                        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Re-adding pending client %pM\n", addr);
+                                  "Re-adding pending client %pM (vid: %d)\n",
+                                  addr, BATADV_PRINT_VID(vid));
                        /* whatever the reason why the PENDING flag was set,
                         * this is a client which was enqueued to be removed in
                         * this orig_interval. Since it popped up again, the
@@ -293,8 +514,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
 
                if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
                        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Roaming client %pM came back to its original location\n",
-                                  addr);
+                                  "Roaming client %pM (vid: %d) came back to its original location\n",
+                                  addr, BATADV_PRINT_VID(vid));
                        /* the ROAM flag is set because this client roamed away
                         * and the node got a roaming_advertisement message. Now
                         * that the client popped up again at its original
@@ -306,12 +527,24 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
                goto check_roaming;
        }
 
+       /* Ignore the client if we cannot send it in a full table response. */
+       table_size = batadv_tt_local_table_transmit_size(bat_priv);
+       table_size += batadv_tt_len(1);
+       packet_size_max = atomic_read(&bat_priv->packet_size_max);
+       if (table_size > packet_size_max) {
+               net_ratelimited_function(batadv_info, soft_iface,
+                                        "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
+                                        table_size, packet_size_max, addr);
+               goto out;
+       }
+
        tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC);
        if (!tt_local)
                goto out;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
+                  "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n",
+                  addr, BATADV_PRINT_VID(vid),
                   (uint8_t)atomic_read(&bat_priv->tt.vn));
 
        memcpy(tt_local->common.addr, addr, ETH_ALEN);
@@ -320,7 +553,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
         * (consistency check)
         */
        tt_local->common.flags = BATADV_TT_CLIENT_NEW;
-       if (batadv_is_wifi_iface(ifindex))
+       tt_local->common.vid = vid;
+       if (batadv_is_wifi_netdev(in_dev))
                tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
        atomic_set(&tt_local->common.refcount, 2);
        tt_local->last_seen = jiffies;
@@ -331,7 +565,7 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
                tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
 
        hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
-                                    batadv_choose_orig, &tt_local->common,
+                                    batadv_choose_tt, &tt_local->common,
                                     &tt_local->common.hash_entry);
 
        if (unlikely(hash_added != 0)) {
@@ -353,6 +587,7 @@ check_roaming:
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_entry, head, list) {
                        batadv_send_roam_adv(bat_priv, tt_global->common.addr,
+                                            tt_global->common.vid,
                                             orig_entry->orig_node);
                }
                rcu_read_unlock();
@@ -369,78 +604,219 @@ check_roaming:
                }
        }
 
+       /* store the current remote flags before altering them. This helps
+        * understanding is flags are changing or not
+        */
+       remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK;
+
+       if (batadv_is_wifi_netdev(in_dev))
+               tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
+       else
+               tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI;
+
+       /* if any "dynamic" flag has been modified, resend an ADD event for this
+        * entry so that all the nodes can get the new flags
+        */
+       if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK))
+               batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
+
+       ret = true;
 out:
+       if (in_dev)
+               dev_put(in_dev);
        if (tt_local)
                batadv_tt_local_entry_free_ref(tt_local);
        if (tt_global)
                batadv_tt_global_entry_free_ref(tt_global);
+       return ret;
 }
 
-static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff,
-                                         int *packet_buff_len,
-                                         int min_packet_len,
-                                         int new_packet_len)
+/**
+ * batadv_tt_prepare_tvlv_global_data - prepare the TVLV TT header to send
+ *  within a TT Response directed to another node
+ * @orig_node: originator for which the TT data has to be prepared
+ * @tt_data: uninitialised pointer to the address of the TVLV buffer
+ * @tt_change: uninitialised pointer to the address of the area where the TT
+ *  changed can be stored
+ * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
+ *  function reserves the amount of space needed to send the entire global TT
+ *  table. In case of success the value is updated with the real amount of
+ *  reserved bytes
+
+ * Allocate the needed amount of memory for the entire TT TVLV and write its
+ * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
+ * objects, one per active VLAN served by the originator node.
+ *
+ * Return the size of the allocated buffer or 0 in case of failure.
+ */
+static uint16_t
+batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+                                  struct batadv_tvlv_tt_data **tt_data,
+                                  struct batadv_tvlv_tt_change **tt_change,
+                                  int32_t *tt_len)
 {
-       unsigned char *new_buff;
+       uint16_t num_vlan = 0, num_entries = 0, change_offset, tvlv_len;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_orig_node_vlan *vlan;
+       uint8_t *tt_change_ptr;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
+               num_vlan++;
+               num_entries += atomic_read(&vlan->tt.num_entries);
+       }
+
+       change_offset = sizeof(**tt_data);
+       change_offset += num_vlan * sizeof(*tt_vlan);
 
-       new_buff = kmalloc(new_packet_len, GFP_ATOMIC);
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+               *tt_len = batadv_tt_len(num_entries);
 
-       /* keep old buffer if kmalloc should fail */
-       if (new_buff) {
-               memcpy(new_buff, *packet_buff, min_packet_len);
-               kfree(*packet_buff);
-               *packet_buff = new_buff;
-               *packet_buff_len = new_packet_len;
+       tvlv_len = *tt_len;
+       tvlv_len += change_offset;
+
+       *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+       if (!*tt_data) {
+               *tt_len = 0;
+               goto out;
+       }
+
+       (*tt_data)->flags = BATADV_NO_FLAGS;
+       (*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn);
+       (*tt_data)->num_vlan = htons(num_vlan);
+
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
+       list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
+
+               tt_vlan++;
        }
+
+       tt_change_ptr = (uint8_t *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+
+out:
+       rcu_read_unlock();
+       return tvlv_len;
 }
 
-static void batadv_tt_prepare_packet_buff(struct batadv_priv *bat_priv,
-                                         unsigned char **packet_buff,
-                                         int *packet_buff_len,
-                                         int min_packet_len)
-{
-       int req_len;
+/**
+ * batadv_tt_prepare_tvlv_local_data - allocate and prepare the TT TVLV for this
+ *  node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: uninitialised pointer to the address of the TVLV buffer
+ * @tt_change: uninitialised pointer to the address of the area where the TT
+ *  changes can be stored
+ * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
+ *  function reserves the amount of space needed to send the entire local TT
+ *  table. In case of success the value is updated with the real amount of
+ *  reserved bytes
+ *
+ * Allocate the needed amount of memory for the entire TT TVLV and write its
+ * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
+ * objects, one per active VLAN.
+ *
+ * Return the size of the allocated buffer or 0 in case of failure.
+ */
+static uint16_t
+batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+                                 struct batadv_tvlv_tt_data **tt_data,
+                                 struct batadv_tvlv_tt_change **tt_change,
+                                 int32_t *tt_len)
+{
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_softif_vlan *vlan;
+       uint16_t num_vlan = 0, num_entries = 0, tvlv_len;
+       uint8_t *tt_change_ptr;
+       int change_offset;
 
-       req_len = min_packet_len;
-       req_len += batadv_tt_len(atomic_read(&bat_priv->tt.local_changes));
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+               num_vlan++;
+               num_entries += atomic_read(&vlan->tt.num_entries);
+       }
 
-       /* if we have too many changes for one packet don't send any
-        * and wait for the tt table request which will be fragmented
-        */
-       if (req_len > bat_priv->soft_iface->mtu)
-               req_len = min_packet_len;
+       change_offset = sizeof(**tt_data);
+       change_offset += num_vlan * sizeof(*tt_vlan);
+
+       /* if tt_len is negative, allocate the space needed by the full table */
+       if (*tt_len < 0)
+               *tt_len = batadv_tt_len(num_entries);
+
+       tvlv_len = *tt_len;
+       tvlv_len += change_offset;
+
+       *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+       if (!*tt_data) {
+               tvlv_len = 0;
+               goto out;
+       }
+
+       (*tt_data)->flags = BATADV_NO_FLAGS;
+       (*tt_data)->ttvn = atomic_read(&bat_priv->tt.vn);
+       (*tt_data)->num_vlan = htons(num_vlan);
+
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
+       hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+               tt_vlan->vid = htons(vlan->vid);
+               tt_vlan->crc = htonl(vlan->tt.crc);
 
-       batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len,
-                                     min_packet_len, req_len);
+               tt_vlan++;
+       }
+
+       tt_change_ptr = (uint8_t *)*tt_data + change_offset;
+       *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+
+out:
+       rcu_read_unlock();
+       return tvlv_len;
 }
 
-static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv,
-                                      unsigned char **packet_buff,
-                                      int *packet_buff_len,
-                                      int min_packet_len)
+/**
+ * batadv_tt_tvlv_container_update - update the translation table tvlv container
+ *  after local tt changes have been committed
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
 {
        struct batadv_tt_change_node *entry, *safe;
-       int count = 0, tot_changes = 0, new_len;
-       unsigned char *tt_buff;
+       struct batadv_tvlv_tt_data *tt_data;
+       struct batadv_tvlv_tt_change *tt_change;
+       int tt_diff_len, tt_change_len = 0;
+       int tt_diff_entries_num = 0, tt_diff_entries_count = 0;
+       uint16_t tvlv_len;
+
+       tt_diff_entries_num = atomic_read(&bat_priv->tt.local_changes);
+       tt_diff_len = batadv_tt_len(tt_diff_entries_num);
+
+       /* if we have too many changes for one packet don't send any
+        * and wait for the tt table request which will be fragmented
+        */
+       if (tt_diff_len > bat_priv->soft_iface->mtu)
+               tt_diff_len = 0;
 
-       batadv_tt_prepare_packet_buff(bat_priv, packet_buff,
-                                     packet_buff_len, min_packet_len);
+       tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data,
+                                                    &tt_change, &tt_diff_len);
+       if (!tvlv_len)
+               return;
 
-       new_len = *packet_buff_len - min_packet_len;
-       tt_buff = *packet_buff + min_packet_len;
+       tt_data->flags = BATADV_TT_OGM_DIFF;
 
-       if (new_len > 0)
-               tot_changes = new_len / batadv_tt_len(1);
+       if (tt_diff_len == 0)
+               goto container_register;
 
        spin_lock_bh(&bat_priv->tt.changes_list_lock);
        atomic_set(&bat_priv->tt.local_changes, 0);
 
        list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
                                 list) {
-               if (count < tot_changes) {
-                       memcpy(tt_buff + batadv_tt_len(count),
-                              &entry->change, sizeof(struct batadv_tt_change));
-                       count++;
+               if (tt_diff_entries_count < tt_diff_entries_num) {
+                       memcpy(tt_change + tt_diff_entries_count,
+                              &entry->change,
+                              sizeof(struct batadv_tvlv_tt_change));
+                       tt_diff_entries_count++;
                }
                list_del(&entry->list);
                kfree(entry);
@@ -452,20 +828,25 @@ static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv,
        kfree(bat_priv->tt.last_changeset);
        bat_priv->tt.last_changeset_len = 0;
        bat_priv->tt.last_changeset = NULL;
+       tt_change_len = batadv_tt_len(tt_diff_entries_count);
        /* check whether this new OGM has no changes due to size problems */
-       if (new_len > 0) {
+       if (tt_diff_entries_count > 0) {
                /* if kmalloc() fails we will reply with the full table
                 * instead of providing the diff
                 */
-               bat_priv->tt.last_changeset = kmalloc(new_len, GFP_ATOMIC);
+               bat_priv->tt.last_changeset = kzalloc(tt_diff_len, GFP_ATOMIC);
                if (bat_priv->tt.last_changeset) {
-                       memcpy(bat_priv->tt.last_changeset, tt_buff, new_len);
-                       bat_priv->tt.last_changeset_len = new_len;
+                       memcpy(bat_priv->tt.last_changeset,
+                              tt_change, tt_change_len);
+                       bat_priv->tt.last_changeset_len = tt_diff_len;
                }
        }
        spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
 
-       return count;
+container_register:
+       batadv_tvlv_container_register(bat_priv, BATADV_TVLV_TT, 1, tt_data,
+                                      tvlv_len);
+       kfree(tt_data);
 }
 
 int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
@@ -476,7 +857,9 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
        struct batadv_tt_common_entry *tt_common_entry;
        struct batadv_tt_local_entry *tt_local;
        struct batadv_hard_iface *primary_if;
+       struct batadv_softif_vlan *vlan;
        struct hlist_head *head;
+       unsigned short vid;
        uint32_t i;
        int last_seen_secs;
        int last_seen_msecs;
@@ -489,11 +872,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
                goto out;
 
        seq_printf(seq,
-                  "Locally retrieved addresses (from %s) announced via TT (TTVN: %u CRC: %#.4x):\n",
-                  net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn),
-                  bat_priv->tt.local_crc);
-       seq_printf(seq, "       %-13s %-7s %-10s\n", "Client", "Flags",
-                  "Last seen");
+                  "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n",
+                  net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn));
+       seq_printf(seq, "       %-13s  %s %-7s %-9s (%-10s)\n", "Client", "VID",
+                  "Flags", "Last seen", "CRC");
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
@@ -504,6 +886,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
                        tt_local = container_of(tt_common_entry,
                                                struct batadv_tt_local_entry,
                                                common);
+                       vid = tt_common_entry->vid;
                        last_seen_jiffies = jiffies - tt_local->last_seen;
                        last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
                        last_seen_secs = last_seen_msecs / 1000;
@@ -511,8 +894,17 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
 
                        no_purge = tt_common_entry->flags & np_flag;
 
-                       seq_printf(seq, " * %pM [%c%c%c%c%c] %3u.%03u\n",
+                       vlan = batadv_softif_vlan_get(bat_priv, vid);
+                       if (!vlan) {
+                               seq_printf(seq, "Cannot retrieve VLAN %d\n",
+                                          BATADV_PRINT_VID(vid));
+                               continue;
+                       }
+
+                       seq_printf(seq,
+                                  " * %pM %4i [%c%c%c%c%c] %3u.%03u   (%#.8x)\n",
                                   tt_common_entry->addr,
+                                  BATADV_PRINT_VID(tt_common_entry->vid),
                                   (tt_common_entry->flags &
                                    BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
                                   no_purge ? 'P' : '.',
@@ -523,7 +915,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
                                   (tt_common_entry->flags &
                                    BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
                                   no_purge ? 0 : last_seen_secs,
-                                  no_purge ? 0 : last_seen_msecs);
+                                  no_purge ? 0 : last_seen_msecs,
+                                  vlan->tt.crc);
+
+                       batadv_softif_vlan_free_ref(vlan);
                }
                rcu_read_unlock();
        }
@@ -547,27 +942,29 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
        tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Local tt entry (%pM) pending to be removed: %s\n",
-                  tt_local_entry->common.addr, message);
+                  "Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
+                  tt_local_entry->common.addr,
+                  BATADV_PRINT_VID(tt_local_entry->common.vid), message);
 }
 
 /**
  * batadv_tt_local_remove - logically remove an entry from the local table
  * @bat_priv: the bat priv with all the soft interface information
  * @addr: the MAC address of the client to remove
+ * @vid: VLAN identifier
  * @message: message to append to the log on deletion
  * @roaming: true if the deletion is due to a roaming event
  *
  * Returns the flags assigned to the local entry before being deleted
  */
 uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
-                               const uint8_t *addr, const char *message,
-                               bool roaming)
+                               const uint8_t *addr, unsigned short vid,
+                               const char *message, bool roaming)
 {
        struct batadv_tt_local_entry *tt_local_entry;
        uint16_t flags, curr_flags = BATADV_NO_FLAGS;
 
-       tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+       tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
        if (!tt_local_entry)
                goto out;
 
@@ -603,8 +1000,16 @@ out:
        return curr_flags;
 }
 
+/**
+ * batadv_tt_local_purge_list - purge inactive tt local entries
+ * @bat_priv: the bat priv with all the soft interface information
+ * @head: pointer to the list containing the local tt entries
+ * @timeout: parameter deciding whether a given tt local entry is considered
+ *  inactive or not
+ */
 static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
-                                      struct hlist_head *head)
+                                      struct hlist_head *head,
+                                      int timeout)
 {
        struct batadv_tt_local_entry *tt_local_entry;
        struct batadv_tt_common_entry *tt_common_entry;
@@ -622,8 +1027,7 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
                if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
                        continue;
 
-               if (!batadv_has_timed_out(tt_local_entry->last_seen,
-                                         BATADV_TT_LOCAL_TIMEOUT))
+               if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
                        continue;
 
                batadv_tt_local_set_pending(bat_priv, tt_local_entry,
@@ -631,7 +1035,14 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
        }
 }
 
-static void batadv_tt_local_purge(struct batadv_priv *bat_priv)
+/**
+ * batadv_tt_local_purge - purge inactive tt local entries
+ * @bat_priv: the bat priv with all the soft interface information
+ * @timeout: parameter deciding whether a given tt local entry is considered
+ *  inactive or not
+ */
+static void batadv_tt_local_purge(struct batadv_priv *bat_priv,
+                                 int timeout)
 {
        struct batadv_hashtable *hash = bat_priv->tt.local_hash;
        struct hlist_head *head;
@@ -643,7 +1054,7 @@ static void batadv_tt_local_purge(struct batadv_priv *bat_priv)
                list_lock = &hash->list_locks[i];
 
                spin_lock_bh(list_lock);
-               batadv_tt_local_purge_list(bat_priv, head);
+               batadv_tt_local_purge_list(bat_priv, head, timeout);
                spin_unlock_bh(list_lock);
        }
 }
@@ -784,7 +1195,7 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
 
        INIT_HLIST_NODE(&orig_entry->list);
        atomic_inc(&orig_node->refcount);
-       atomic_inc(&orig_node->tt_size);
+       batadv_tt_global_size_inc(orig_node, tt_global->common.vid);
        orig_entry->orig_node = orig_node;
        orig_entry->ttvn = ttvn;
        atomic_set(&orig_entry->refcount, 2);
@@ -803,6 +1214,7 @@ out:
  * @bat_priv: the bat priv with all the soft interface information
  * @orig_node: the originator announcing the client
  * @tt_addr: the mac address of the non-mesh client
+ * @vid: VLAN identifier
  * @flags: TT flags that have to be set for this non-mesh client
  * @ttvn: the tt version number ever announcing this non-mesh client
  *
@@ -813,21 +1225,28 @@ out:
  * If a TT local entry exists for this non-mesh client remove it.
  *
  * The caller must hold orig_node refcount.
+ *
+ * Return true if the new entry has been added, false otherwise
  */
-int batadv_tt_global_add(struct batadv_priv *bat_priv,
-                        struct batadv_orig_node *orig_node,
-                        const unsigned char *tt_addr, uint16_t flags,
-                        uint8_t ttvn)
+static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
+                                struct batadv_orig_node *orig_node,
+                                const unsigned char *tt_addr,
+                                unsigned short vid, uint16_t flags,
+                                uint8_t ttvn)
 {
        struct batadv_tt_global_entry *tt_global_entry;
        struct batadv_tt_local_entry *tt_local_entry;
-       int ret = 0;
+       bool ret = false;
        int hash_added;
        struct batadv_tt_common_entry *common;
        uint16_t local_flags;
 
-       tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr);
-       tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr);
+       /* ignore global entries from backbone nodes */
+       if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid))
+               return true;
+
+       tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr, vid);
+       tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr, vid);
 
        /* if the node already has a local client for this entry, it has to wait
         * for a roaming advertisement instead of manually messing up the global
@@ -844,6 +1263,7 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
 
                common = &tt_global_entry->common;
                memcpy(common->addr, tt_addr, ETH_ALEN);
+               common->vid = vid;
 
                common->flags = flags;
                tt_global_entry->roam_at = 0;
@@ -861,7 +1281,7 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
 
                hash_added = batadv_hash_add(bat_priv->tt.global_hash,
                                             batadv_compare_tt,
-                                            batadv_choose_orig, common,
+                                            batadv_choose_tt, common,
                                             &common->hash_entry);
 
                if (unlikely(hash_added != 0)) {
@@ -920,14 +1340,15 @@ add_orig_entry:
        batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn);
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Creating new global tt entry: %pM (via %pM)\n",
-                  common->addr, orig_node->orig);
-       ret = 1;
+                  "Creating new global tt entry: %pM (vid: %d, via %pM)\n",
+                  common->addr, BATADV_PRINT_VID(common->vid),
+                  orig_node->orig);
+       ret = true;
 
 out_remove:
 
        /* remove address from local hash if present */
-       local_flags = batadv_tt_local_remove(bat_priv, tt_addr,
+       local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid,
                                             "global tt received",
                                             flags & BATADV_TT_CLIENT_ROAM);
        tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
@@ -947,18 +1368,20 @@ out:
 }
 
 /* batadv_transtable_best_orig - Get best originator list entry from tt entry
+ * @bat_priv: the bat priv with all the soft interface information
  * @tt_global_entry: global translation table entry to be analyzed
  *
  * This functon assumes the caller holds rcu_read_lock().
  * Returns best originator list entry or NULL on errors.
  */
 static struct batadv_tt_orig_list_entry *
-batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry)
+batadv_transtable_best_orig(struct batadv_priv *bat_priv,
+                           struct batadv_tt_global_entry *tt_global_entry)
 {
-       struct batadv_neigh_node *router = NULL;
+       struct batadv_neigh_node *router, *best_router = NULL;
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct hlist_head *head;
        struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL;
-       int best_tq = 0;
 
        head = &tt_global_entry->orig_list;
        hlist_for_each_entry_rcu(orig_entry, head, list) {
@@ -966,64 +1389,104 @@ batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry)
                if (!router)
                        continue;
 
-               if (router->tq_avg > best_tq) {
-                       best_entry = orig_entry;
-                       best_tq = router->tq_avg;
+               if (best_router &&
+                   bao->bat_neigh_cmp(router, best_router) <= 0) {
+                       batadv_neigh_node_free_ref(router);
+                       continue;
                }
 
-               batadv_neigh_node_free_ref(router);
+               /* release the refcount for the "old" best */
+               if (best_router)
+                       batadv_neigh_node_free_ref(best_router);
+
+               best_entry = orig_entry;
+               best_router = router;
        }
 
+       if (best_router)
+               batadv_neigh_node_free_ref(best_router);
+
        return best_entry;
 }
 
 /* batadv_tt_global_print_entry - print all orig nodes who announce the address
  * for this global entry
+ * @bat_priv: the bat priv with all the soft interface information
  * @tt_global_entry: global translation table entry to be printed
  * @seq: debugfs table seq_file struct
  *
  * This functon assumes the caller holds rcu_read_lock().
  */
 static void
-batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry,
+batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
+                            struct batadv_tt_global_entry *tt_global_entry,
                             struct seq_file *seq)
 {
-       struct hlist_head *head;
        struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
        struct batadv_tt_common_entry *tt_common_entry;
-       uint16_t flags;
+       struct batadv_orig_node_vlan *vlan;
+       struct hlist_head *head;
        uint8_t last_ttvn;
+       uint16_t flags;
 
        tt_common_entry = &tt_global_entry->common;
        flags = tt_common_entry->flags;
 
-       best_entry = batadv_transtable_best_orig(tt_global_entry);
+       best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
        if (best_entry) {
+               vlan = batadv_orig_node_vlan_get(best_entry->orig_node,
+                                                tt_common_entry->vid);
+               if (!vlan) {
+                       seq_printf(seq,
+                                  " * Cannot retrieve VLAN %d for originator %pM\n",
+                                  BATADV_PRINT_VID(tt_common_entry->vid),
+                                  best_entry->orig_node->orig);
+                       goto print_list;
+               }
+
                last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn);
                seq_printf(seq,
-                          " %c %pM  (%3u) via %pM     (%3u)   (%#.4x) [%c%c%c]\n",
+                          " %c %pM %4i   (%3u) via %pM     (%3u)   (%#.8x) [%c%c%c]\n",
                           '*', tt_global_entry->common.addr,
+                          BATADV_PRINT_VID(tt_global_entry->common.vid),
                           best_entry->ttvn, best_entry->orig_node->orig,
-                          last_ttvn, best_entry->orig_node->tt_crc,
+                          last_ttvn, vlan->tt.crc,
                           (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
                           (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
                           (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
+
+               batadv_orig_node_vlan_free_ref(vlan);
        }
 
+print_list:
        head = &tt_global_entry->orig_list;
 
        hlist_for_each_entry_rcu(orig_entry, head, list) {
                if (best_entry == orig_entry)
                        continue;
 
+               vlan = batadv_orig_node_vlan_get(orig_entry->orig_node,
+                                                tt_common_entry->vid);
+               if (!vlan) {
+                       seq_printf(seq,
+                                  " + Cannot retrieve VLAN %d for originator %pM\n",
+                                  BATADV_PRINT_VID(tt_common_entry->vid),
+                                  orig_entry->orig_node->orig);
+                       continue;
+               }
+
                last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn);
-               seq_printf(seq, " %c %pM  (%3u) via %pM     (%3u)   [%c%c%c]\n",
+               seq_printf(seq,
+                          " %c %pM %4d   (%3u) via %pM     (%3u)   (%#.8x) [%c%c%c]\n",
                           '+', tt_global_entry->common.addr,
+                          BATADV_PRINT_VID(tt_global_entry->common.vid),
                           orig_entry->ttvn, orig_entry->orig_node->orig,
-                          last_ttvn,
+                          last_ttvn, vlan->tt.crc,
                           (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
                           (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
                           (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
+
+               batadv_orig_node_vlan_free_ref(vlan);
        }
 }
 
@@ -1045,9 +1508,9 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
        seq_printf(seq,
                   "Globally announced TT entries received via the mesh %s\n",
                   net_dev->name);
-       seq_printf(seq, "       %-13s %s       %-15s %s (%-6s) %s\n",
-                  "Client", "(TTVN)", "Originator", "(Curr TTVN)", "CRC",
-                  "Flags");
+       seq_printf(seq, "       %-13s  %s  %s       %-15s %s (%-10s) %s\n",
+                  "Client", "VID", "(TTVN)", "Originator", "(Curr TTVN)",
+                  "CRC", "Flags");
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
@@ -1058,7 +1521,7 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
                        tt_global = container_of(tt_common_entry,
                                                 struct batadv_tt_global_entry,
                                                 common);
-                       batadv_tt_global_print_entry(tt_global, seq);
+                       batadv_tt_global_print_entry(bat_priv, tt_global, seq);
                }
                rcu_read_unlock();
        }
@@ -1080,6 +1543,8 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
        head = &tt_global_entry->orig_list;
        hlist_for_each_entry_safe(orig_entry, safe, head, list) {
                hlist_del_rcu(&orig_entry->list);
+               batadv_tt_global_size_dec(orig_entry->orig_node,
+                                         tt_global_entry->common.vid);
                batadv_tt_orig_list_entry_free_ref(orig_entry);
        }
        spin_unlock_bh(&tt_global_entry->list_lock);
@@ -1094,16 +1559,21 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
        struct hlist_head *head;
        struct hlist_node *safe;
        struct batadv_tt_orig_list_entry *orig_entry;
+       unsigned short vid;
 
        spin_lock_bh(&tt_global_entry->list_lock);
        head = &tt_global_entry->orig_list;
        hlist_for_each_entry_safe(orig_entry, safe, head, list) {
                if (orig_entry->orig_node == orig_node) {
+                       vid = tt_global_entry->common.vid;
                        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Deleting %pM from global tt entry %pM: %s\n",
+                                  "Deleting %pM from global tt entry %pM (vid: %d): %s\n",
                                   orig_node->orig,
-                                  tt_global_entry->common.addr, message);
+                                  tt_global_entry->common.addr,
+                                  BATADV_PRINT_VID(vid), message);
                        hlist_del_rcu(&orig_entry->list);
+                       batadv_tt_global_size_dec(orig_node,
+                                                 tt_global_entry->common.vid);
                        batadv_tt_orig_list_entry_free_ref(orig_entry);
                }
        }
@@ -1150,17 +1620,25 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
                                                orig_node, message);
 }
 
-
-
+/**
+ * batadv_tt_global_del - remove a client from the global table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: an originator serving this client
+ * @addr: the mac address of the client
+ * @vid: VLAN identifier
+ * @message: a message explaining the reason for deleting the client to print
+ *  for debugging purpose
+ * @roaming: true if the deletion has been triggered by a roaming event
+ */
 static void batadv_tt_global_del(struct batadv_priv *bat_priv,
                                 struct batadv_orig_node *orig_node,
-                                const unsigned char *addr,
+                                const unsigned char *addr, unsigned short vid,
                                 const char *message, bool roaming)
 {
        struct batadv_tt_global_entry *tt_global_entry;
        struct batadv_tt_local_entry *local_entry = NULL;
 
-       tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+       tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
        if (!tt_global_entry)
                goto out;
 
@@ -1189,7 +1667,8 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
         *    the global entry, since it is useless now.
         */
        local_entry = batadv_tt_local_hash_find(bat_priv,
-                                               tt_global_entry->common.addr);
+                                               tt_global_entry->common.addr,
+                                               vid);
        if (local_entry) {
                /* local entry exists, case 2: client roamed to us. */
                batadv_tt_global_del_orig_list(tt_global_entry);
@@ -1207,8 +1686,18 @@ out:
                batadv_tt_local_entry_free_ref(local_entry);
 }
 
+/**
+ * batadv_tt_global_del_orig - remove all the TT global entries belonging to the
+ *  given originator matching the provided vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the originator owning the entries to remove
+ * @match_vid: the VLAN identifier to match. If negative all the entries will be
+ *  removed
+ * @message: debug message to print as "reason"
+ */
 void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
                               struct batadv_orig_node *orig_node,
+                              int32_t match_vid,
                               const char *message)
 {
        struct batadv_tt_global_entry *tt_global;
@@ -1218,6 +1707,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
        struct hlist_node *safe;
        struct hlist_head *head;
        spinlock_t *list_lock; /* protects write access to the hash lists */
+       unsigned short vid;
 
        if (!hash)
                return;
@@ -1229,6 +1719,10 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
                spin_lock_bh(list_lock);
                hlist_for_each_entry_safe(tt_common_entry, safe,
                                          head, hash_entry) {
+                       /* remove only matching entries */
+                       if (match_vid >= 0 && tt_common_entry->vid != match_vid)
+                               continue;
+
                        tt_global = container_of(tt_common_entry,
                                                 struct batadv_tt_global_entry,
                                                 common);
@@ -1237,9 +1731,11 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
                                                        orig_node, message);
 
                        if (hlist_empty(&tt_global->orig_list)) {
+                               vid = tt_global->common.vid;
                                batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                          "Deleting global tt entry %pM: %s\n",
-                                          tt_global->common.addr, message);
+                                          "Deleting global tt entry %pM (vid: %d): %s\n",
+                                          tt_global->common.addr,
+                                          BATADV_PRINT_VID(vid), message);
                                hlist_del_rcu(&tt_common_entry->hash_entry);
                                batadv_tt_global_entry_free_ref(tt_global);
                        }
@@ -1297,8 +1793,10 @@ static void batadv_tt_global_purge(struct batadv_priv *bat_priv)
                                continue;
 
                        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Deleting global tt entry (%pM): %s\n",
-                                  tt_global->common.addr, msg);
+                                  "Deleting global tt entry %pM (vid: %d): %s\n",
+                                  tt_global->common.addr,
+                                  BATADV_PRINT_VID(tt_global->common.vid),
+                                  msg);
 
                        hlist_del_rcu(&tt_common->hash_entry);
 
@@ -1357,23 +1855,49 @@ _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry,
        return ret;
 }
 
+/**
+ * batadv_transtable_search - get the mesh destination for a given client
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: mac address of the source client
+ * @addr: mac address of the destination client
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the originator that was selected as destination in the
+ * mesh for contacting the client 'addr', NULL otherwise.
+ * In case of multiple originators serving the same client, the function returns
+ * the best one (best in terms of metric towards the destination node).
+ *
+ * If the two clients are AP isolated the function returns NULL.
+ */
 struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
                                                  const uint8_t *src,
-                                                 const uint8_t *addr)
+                                                 const uint8_t *addr,
+                                                 unsigned short vid)
 {
        struct batadv_tt_local_entry *tt_local_entry = NULL;
        struct batadv_tt_global_entry *tt_global_entry = NULL;
        struct batadv_orig_node *orig_node = NULL;
        struct batadv_tt_orig_list_entry *best_entry;
+       bool ap_isolation_enabled = false;
+       struct batadv_softif_vlan *vlan;
+
+       /* if the AP isolation is requested on a VLAN, then check for its
+        * setting in the proper VLAN private data structure
+        */
+       vlan = batadv_softif_vlan_get(bat_priv, vid);
+       if (vlan) {
+               ap_isolation_enabled = atomic_read(&vlan->ap_isolation);
+               batadv_softif_vlan_free_ref(vlan);
+       }
 
-       if (src && atomic_read(&bat_priv->ap_isolation)) {
-               tt_local_entry = batadv_tt_local_hash_find(bat_priv, src);
+       if (src && ap_isolation_enabled) {
+               tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid);
                if (!tt_local_entry ||
                    (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
                        goto out;
        }
 
-       tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+       tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
        if (!tt_global_entry)
                goto out;
 
@@ -1385,7 +1909,7 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
                goto out;
 
        rcu_read_lock();
-       best_entry = batadv_transtable_best_orig(tt_global_entry);
+       best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
        /* found anything? */
        if (best_entry)
                orig_node = best_entry->orig_node;
@@ -1402,17 +1926,40 @@ out:
        return orig_node;
 }
 
-/* Calculates the checksum of the local table of a given orig_node */
-static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
-                                    struct batadv_orig_node *orig_node)
+/**
+ * batadv_tt_global_crc - calculates the checksum of the local table belonging
+ *  to the given orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator for which the CRC should be computed
+ * @vid: VLAN identifier for which the CRC32 has to be computed
+ *
+ * This function computes the checksum for the global table corresponding to a
+ * specific originator. In particular, the checksum is computed as follows: For
+ * each client connected to the originator the CRC32C of the MAC address and the
+ * VID is computed and then all the CRC32Cs of the various clients are xor'ed
+ * together.
+ *
+ * The idea behind is that CRC32C should be used as much as possible in order to
+ * produce a unique hash of the table, but since the order which is used to feed
+ * the CRC32C function affects the result and since every node in the network
+ * probably sorts the clients differently, the hash function cannot be directly
+ * computed over the entire table. Hence the CRC32C is used only on
+ * the single client entry, while all the results are then xor'ed together
+ * because the XOR operation can combine them all while trying to reduce the
+ * noise as much as possible.
+ *
+ * Returns the checksum of the global table of a given originator.
+ */
+static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
+                                    struct batadv_orig_node *orig_node,
+                                    unsigned short vid)
 {
-       uint16_t total = 0, total_one;
        struct batadv_hashtable *hash = bat_priv->tt.global_hash;
        struct batadv_tt_common_entry *tt_common;
        struct batadv_tt_global_entry *tt_global;
        struct hlist_head *head;
-       uint32_t i;
-       int j;
+       uint32_t i, crc_tmp, crc = 0;
+       uint8_t flags;
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
@@ -1422,6 +1969,12 @@ static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
                        tt_global = container_of(tt_common,
                                                 struct batadv_tt_global_entry,
                                                 common);
+                       /* compute the CRC only for entries belonging to the
+                        * VLAN identified by the vid passed as parameter
+                        */
+                       if (tt_common->vid != vid)
+                               continue;
+
                        /* Roaming clients are in the global table for
                         * consistency only. They don't have to be
                         * taken into account while computing the
@@ -1443,48 +1996,74 @@ static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
                                                             orig_node))
                                continue;
 
-                       total_one = 0;
-                       for (j = 0; j < ETH_ALEN; j++)
-                               total_one = crc16_byte(total_one,
-                                                      tt_common->addr[j]);
-                       total ^= total_one;
+                       crc_tmp = crc32c(0, &tt_common->vid,
+                                        sizeof(tt_common->vid));
+
+                       /* compute the CRC on flags that have to be kept in sync
+                        * among nodes
+                        */
+                       flags = tt_common->flags & BATADV_TT_SYNC_MASK;
+                       crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
+
+                       crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
                }
                rcu_read_unlock();
        }
 
-       return total;
+       return crc;
 }
 
-/* Calculates the checksum of the local table */
-static uint16_t batadv_tt_local_crc(struct batadv_priv *bat_priv)
+/**
+ * batadv_tt_local_crc - calculates the checksum of the local table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: VLAN identifier for which the CRC32 has to be computed
+ *
+ * For details about the computation, please refer to the documentation for
+ * batadv_tt_global_crc().
+ *
+ * Returns the checksum of the local table
+ */
+static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv,
+                                   unsigned short vid)
 {
-       uint16_t total = 0, total_one;
        struct batadv_hashtable *hash = bat_priv->tt.local_hash;
        struct batadv_tt_common_entry *tt_common;
        struct hlist_head *head;
-       uint32_t i;
-       int j;
+       uint32_t i, crc_tmp, crc = 0;
+       uint8_t flags;
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(tt_common, head, hash_entry) {
+                       /* compute the CRC only for entries belonging to the
+                        * VLAN identified by vid
+                        */
+                       if (tt_common->vid != vid)
+                               continue;
+
                        /* not yet committed clients have not to be taken into
                         * account while computing the CRC
                         */
                        if (tt_common->flags & BATADV_TT_CLIENT_NEW)
                                continue;
-                       total_one = 0;
-                       for (j = 0; j < ETH_ALEN; j++)
-                               total_one = crc16_byte(total_one,
-                                                      tt_common->addr[j]);
-                       total ^= total_one;
+
+                       crc_tmp = crc32c(0, &tt_common->vid,
+                                        sizeof(tt_common->vid));
+
+                       /* compute the CRC on flags that have to be kept in sync
+                        * among nodes
+                        */
+                       flags = tt_common->flags & BATADV_TT_SYNC_MASK;
+                       crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
+
+                       crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
                }
                rcu_read_unlock();
        }
 
-       return total;
+       return crc;
 }
 
 static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
@@ -1503,11 +2082,9 @@ static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
 
 static void batadv_tt_save_orig_buffer(struct batadv_priv *bat_priv,
                                       struct batadv_orig_node *orig_node,
-                                      const unsigned char *tt_buff,
-                                      uint8_t tt_num_changes)
+                                      const void *tt_buff,
+                                      uint16_t tt_buff_len)
 {
-       uint16_t tt_buff_len = batadv_tt_len(tt_num_changes);
-
        /* Replace the old buffer only if I received something in the
         * last OGM (the OGM could carry no changes)
         */
@@ -1569,9 +2146,14 @@ unlock:
        return tt_req_node;
 }
 
-/* data_ptr is useless here, but has to be kept to respect the prototype */
-static int batadv_tt_local_valid_entry(const void *entry_ptr,
-                                      const void *data_ptr)
+/**
+ * batadv_tt_local_valid - verify that given tt entry is a valid one
+ * @entry_ptr: to be checked local tt entry
+ * @data_ptr: not used but definition required to satisfy the callback prototype
+ *
+ * Returns 1 if the entry is a valid, 0 otherwise.
+ */
+static int batadv_tt_local_valid(const void *entry_ptr, const void *data_ptr)
 {
        const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
 
@@ -1598,41 +2180,30 @@ static int batadv_tt_global_valid(const void *entry_ptr,
        return batadv_tt_global_entry_has_orig(tt_global_entry, orig_node);
 }
 
-static struct sk_buff *
-batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
-                             struct batadv_hashtable *hash,
-                             struct batadv_priv *bat_priv,
-                             int (*valid_cb)(const void *, const void *),
-                             void *cb_data)
+/**
+ * batadv_tt_tvlv_generate - fill the tvlv buff with the tt entries from the
+ *  specified tt hash
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hash: hash table containing the tt entries
+ * @tt_len: expected tvlv tt data buffer length in number of bytes
+ * @tvlv_buff: pointer to the buffer to fill with the TT data
+ * @valid_cb: function to filter tt change entries
+ * @cb_data: data passed to the filter function as argument
+ */
+static void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv,
+                                   struct batadv_hashtable *hash,
+                                   void *tvlv_buff, uint16_t tt_len,
+                                   int (*valid_cb)(const void *, const void *),
+                                   void *cb_data)
 {
        struct batadv_tt_common_entry *tt_common_entry;
-       struct batadv_tt_query_packet *tt_response;
-       struct batadv_tt_change *tt_change;
+       struct batadv_tvlv_tt_change *tt_change;
        struct hlist_head *head;
-       struct sk_buff *skb = NULL;
-       uint16_t tt_tot, tt_count;
-       ssize_t tt_query_size = sizeof(struct batadv_tt_query_packet);
+       uint16_t tt_tot, tt_num_entries = 0;
        uint32_t i;
-       size_t len;
 
-       if (tt_query_size + tt_len > bat_priv->soft_iface->mtu) {
-               tt_len = bat_priv->soft_iface->mtu - tt_query_size;
-               tt_len -= tt_len % sizeof(struct batadv_tt_change);
-       }
-       tt_tot = tt_len / sizeof(struct batadv_tt_change);
-
-       len = tt_query_size + tt_len;
-       skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
-       if (!skb)
-               goto out;
-
-       skb->priority = TC_PRIO_CONTROL;
-       skb_reserve(skb, ETH_HLEN);
-       tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len);
-       tt_response->ttvn = ttvn;
-
-       tt_change = (struct batadv_tt_change *)(skb->data + tt_query_size);
-       tt_count = 0;
+       tt_tot = batadv_tt_entries(tt_len);
+       tt_change = (struct batadv_tvlv_tt_change *)tvlv_buff;
 
        rcu_read_lock();
        for (i = 0; i < hash->size; i++) {
@@ -1640,7 +2211,7 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
 
                hlist_for_each_entry_rcu(tt_common_entry,
                                         head, hash_entry) {
-                       if (tt_count == tt_tot)
+                       if (tt_tot == tt_num_entries)
                                break;
 
                        if ((valid_cb) && (!valid_cb(tt_common_entry, cb_data)))
@@ -1649,33 +2220,123 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
                        memcpy(tt_change->addr, tt_common_entry->addr,
                               ETH_ALEN);
                        tt_change->flags = tt_common_entry->flags;
+                       tt_change->vid = htons(tt_common_entry->vid);
+                       tt_change->reserved = 0;
 
-                       tt_count++;
+                       tt_num_entries++;
                        tt_change++;
                }
        }
        rcu_read_unlock();
+}
 
-       /* store in the message the number of entries we have successfully
-        * copied
-        */
-       tt_response->tt_data = htons(tt_count);
+/**
+ * batadv_tt_global_check_crc - check if all the CRCs are correct
+ * @orig_node: originator for which the CRCs have to be checked
+ * @tt_vlan: pointer to the first tvlv VLAN entry
+ * @num_vlan: number of tvlv VLAN entries
+ * @create: if true, create VLAN objects if not found
+ *
+ * Return true if all the received CRCs match the locally stored ones, false
+ * otherwise
+ */
+static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
+                                      struct batadv_tvlv_tt_vlan_data *tt_vlan,
+                                      uint16_t num_vlan)
+{
+       struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
+       struct batadv_orig_node_vlan *vlan;
+       int i;
 
-out:
-       return skb;
+       /* check if each received CRC matches the locally stored one */
+       for (i = 0; i < num_vlan; i++) {
+               tt_vlan_tmp = tt_vlan + i;
+
+               /* if orig_node is a backbone node for this VLAN, don't check
+                * the CRC as we ignore all the global entries over it
+                */
+               if (batadv_bla_is_backbone_gw_orig(orig_node->bat_priv,
+                                                  orig_node->orig,
+                                                  ntohs(tt_vlan_tmp->vid)))
+                       continue;
+
+               vlan = batadv_orig_node_vlan_get(orig_node,
+                                                ntohs(tt_vlan_tmp->vid));
+               if (!vlan)
+                       return false;
+
+               if (vlan->tt.crc != ntohl(tt_vlan_tmp->crc))
+                       return false;
+       }
+
+       return true;
+}
+
+/**
+ * batadv_tt_local_update_crc - update all the local CRCs
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_tt_local_update_crc(struct batadv_priv *bat_priv)
+{
+       struct batadv_softif_vlan *vlan;
+
+       /* recompute the global CRC for each VLAN */
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+               vlan->tt.crc = batadv_tt_local_crc(bat_priv, vlan->vid);
+       }
+       rcu_read_unlock();
+}
+
+/**
+ * batadv_tt_global_update_crc - update all the global CRCs for this orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the orig_node for which the CRCs have to be updated
+ */
+static void batadv_tt_global_update_crc(struct batadv_priv *bat_priv,
+                                       struct batadv_orig_node *orig_node)
+{
+       struct batadv_orig_node_vlan *vlan;
+       uint32_t crc;
+
+       /* recompute the global CRC for each VLAN */
+       rcu_read_lock();
+       list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
+               /* if orig_node is a backbone node for this VLAN, don't compute
+                * the CRC as we ignore all the global entries over it
+                */
+               if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig,
+                                                  vlan->vid))
+                       continue;
+
+               crc = batadv_tt_global_crc(bat_priv, orig_node, vlan->vid);
+               vlan->tt.crc = crc;
+       }
+       rcu_read_unlock();
 }
 
+/**
+ * batadv_send_tt_request - send a TT Request message to a given node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @dst_orig_node: the destination of the message
+ * @ttvn: the version number that the source of the message is looking for
+ * @tt_vlan: pointer to the first tvlv VLAN object to request
+ * @num_vlan: number of tvlv VLAN entries
+ * @full_table: ask for the entire translation table if true, while only for the
+ *  last TT diff otherwise
+ */
 static int batadv_send_tt_request(struct batadv_priv *bat_priv,
                                  struct batadv_orig_node *dst_orig_node,
-                                 uint8_t ttvn, uint16_t tt_crc,
-                                 bool full_table)
+                                 uint8_t ttvn,
+                                 struct batadv_tvlv_tt_vlan_data *tt_vlan,
+                                 uint16_t num_vlan, bool full_table)
 {
-       struct sk_buff *skb = NULL;
-       struct batadv_tt_query_packet *tt_request;
-       struct batadv_hard_iface *primary_if;
+       struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
        struct batadv_tt_req_node *tt_req_node = NULL;
-       int ret = 1;
-       size_t tt_req_len;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan_req;
+       struct batadv_hard_iface *primary_if;
+       bool ret = false;
+       int i, size;
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
        if (!primary_if)
@@ -1688,157 +2349,171 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
        if (!tt_req_node)
                goto out;
 
-       skb = netdev_alloc_skb_ip_align(NULL, sizeof(*tt_request) + ETH_HLEN);
-       if (!skb)
+       size = sizeof(*tvlv_tt_data) + sizeof(*tt_vlan_req) * num_vlan;
+       tvlv_tt_data = kzalloc(size, GFP_ATOMIC);
+       if (!tvlv_tt_data)
                goto out;
 
-       skb->priority = TC_PRIO_CONTROL;
-       skb_reserve(skb, ETH_HLEN);
+       tvlv_tt_data->flags = BATADV_TT_REQUEST;
+       tvlv_tt_data->ttvn = ttvn;
+       tvlv_tt_data->num_vlan = htons(num_vlan);
 
-       tt_req_len = sizeof(*tt_request);
-       tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len);
+       /* send all the CRCs within the request. This is needed by intermediate
+        * nodes to ensure they have the correct table before replying
+        */
+       tt_vlan_req = (struct batadv_tvlv_tt_vlan_data *)(tvlv_tt_data + 1);
+       for (i = 0; i < num_vlan; i++) {
+               tt_vlan_req->vid = tt_vlan->vid;
+               tt_vlan_req->crc = tt_vlan->crc;
 
-       tt_request->header.packet_type = BATADV_TT_QUERY;
-       tt_request->header.version = BATADV_COMPAT_VERSION;
-       memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN);
-       memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN);
-       tt_request->header.ttl = BATADV_TTL;
-       tt_request->ttvn = ttvn;
-       tt_request->tt_data = htons(tt_crc);
-       tt_request->flags = BATADV_TT_REQUEST;
+               tt_vlan_req++;
+               tt_vlan++;
+       }
 
        if (full_table)
-               tt_request->flags |= BATADV_TT_FULL_TABLE;
+               tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n",
-                  dst_orig_node->orig, (full_table ? 'F' : '.'));
+                  dst_orig_node->orig, full_table ? 'F' : '.');
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
-
-       if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL) != NET_XMIT_DROP)
-               ret = 0;
+       batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
+                                dst_orig_node->orig, BATADV_TVLV_TT, 1,
+                                tvlv_tt_data, size);
+       ret = true;
 
 out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
-       if (ret)
-               kfree_skb(skb);
        if (ret && tt_req_node) {
                spin_lock_bh(&bat_priv->tt.req_list_lock);
                list_del(&tt_req_node->list);
                spin_unlock_bh(&bat_priv->tt.req_list_lock);
                kfree(tt_req_node);
        }
+       kfree(tvlv_tt_data);
        return ret;
 }
 
-static bool
-batadv_send_other_tt_response(struct batadv_priv *bat_priv,
-                             struct batadv_tt_query_packet *tt_request)
+/**
+ * batadv_send_other_tt_response - send reply to tt request concerning another
+ *  node's translation table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @req_src: mac address of tt request sender
+ * @req_dst: mac address of tt request recipient
+ *
+ * Returns true if tt request reply was sent, false otherwise.
+ */
+static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv,
+                                         struct batadv_tvlv_tt_data *tt_data,
+                                         uint8_t *req_src, uint8_t *req_dst)
 {
        struct batadv_orig_node *req_dst_orig_node;
        struct batadv_orig_node *res_dst_orig_node = NULL;
-       uint8_t orig_ttvn, req_ttvn, ttvn;
-       int res, ret = false;
-       unsigned char *tt_buff;
-       bool full_table;
-       uint16_t tt_len, tt_tot;
-       struct sk_buff *skb = NULL;
-       struct batadv_tt_query_packet *tt_response;
-       uint8_t *packet_pos;
-       size_t len;
+       struct batadv_tvlv_tt_change *tt_change;
+       struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       bool ret = false, full_table;
+       uint8_t orig_ttvn, req_ttvn;
+       uint16_t tvlv_len;
+       int32_t tt_len;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
                   "Received TT_REQUEST from %pM for ttvn: %u (%pM) [%c]\n",
-                  tt_request->src, tt_request->ttvn, tt_request->dst,
-                  (tt_request->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+                  req_src, tt_data->ttvn, req_dst,
+                  (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
 
        /* Let's get the orig node of the REAL destination */
-       req_dst_orig_node = batadv_orig_hash_find(bat_priv, tt_request->dst);
+       req_dst_orig_node = batadv_orig_hash_find(bat_priv, req_dst);
        if (!req_dst_orig_node)
                goto out;
 
-       res_dst_orig_node = batadv_orig_hash_find(bat_priv, tt_request->src);
+       res_dst_orig_node = batadv_orig_hash_find(bat_priv, req_src);
        if (!res_dst_orig_node)
                goto out;
 
        orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
-       req_ttvn = tt_request->ttvn;
+       req_ttvn = tt_data->ttvn;
 
-       /* I don't have the requested data */
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
+       /* this node doesn't have the requested data */
        if (orig_ttvn != req_ttvn ||
-           tt_request->tt_data != htons(req_dst_orig_node->tt_crc))
+           !batadv_tt_global_check_crc(req_dst_orig_node, tt_vlan,
+                                       ntohs(tt_data->num_vlan)))
                goto out;
 
        /* If the full table has been explicitly requested */
-       if (tt_request->flags & BATADV_TT_FULL_TABLE ||
+       if (tt_data->flags & BATADV_TT_FULL_TABLE ||
            !req_dst_orig_node->tt_buff)
                full_table = true;
        else
                full_table = false;
 
-       /* In this version, fragmentation is not implemented, then
-        * I'll send only one packet with as much TT entries as I can
+       /* TT fragmentation hasn't been implemented yet, so send as many
+        * TT entries fit a single packet as possible only
         */
        if (!full_table) {
                spin_lock_bh(&req_dst_orig_node->tt_buff_lock);
                tt_len = req_dst_orig_node->tt_buff_len;
-               tt_tot = tt_len / sizeof(struct batadv_tt_change);
 
-               len = sizeof(*tt_response) + tt_len;
-               skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
-               if (!skb)
+               tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
+                                                             &tvlv_tt_data,
+                                                             &tt_change,
+                                                             &tt_len);
+               if (!tt_len)
                        goto unlock;
 
-               skb->priority = TC_PRIO_CONTROL;
-               skb_reserve(skb, ETH_HLEN);
-               packet_pos = skb_put(skb, len);
-               tt_response = (struct batadv_tt_query_packet *)packet_pos;
-               tt_response->ttvn = req_ttvn;
-               tt_response->tt_data = htons(tt_tot);
-
-               tt_buff = skb->data + sizeof(*tt_response);
                /* Copy the last orig_node's OGM buffer */
-               memcpy(tt_buff, req_dst_orig_node->tt_buff,
+               memcpy(tt_change, req_dst_orig_node->tt_buff,
                       req_dst_orig_node->tt_buff_len);
-
                spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
        } else {
-               tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size);
-               tt_len *= sizeof(struct batadv_tt_change);
-               ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
-
-               skb = batadv_tt_response_fill_table(tt_len, ttvn,
-                                                   bat_priv->tt.global_hash,
-                                                   bat_priv,
-                                                   batadv_tt_global_valid,
-                                                   req_dst_orig_node);
-               if (!skb)
+               /* allocate the tvlv, put the tt_data and all the tt_vlan_data
+                * in the initial part
+                */
+               tt_len = -1;
+               tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
+                                                             &tvlv_tt_data,
+                                                             &tt_change,
+                                                             &tt_len);
+               if (!tt_len)
                        goto out;
 
-               tt_response = (struct batadv_tt_query_packet *)skb->data;
+               /* fill the rest of the tvlv with the real TT entries */
+               batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.global_hash,
+                                       tt_change, tt_len,
+                                       batadv_tt_global_valid,
+                                       req_dst_orig_node);
+       }
+
+       /* Don't send the response, if larger than fragmented packet. */
+       tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len;
+       if (tt_len > atomic_read(&bat_priv->packet_size_max)) {
+               net_ratelimited_function(batadv_info, bat_priv->soft_iface,
+                                        "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n",
+                                        res_dst_orig_node->orig);
+               goto out;
        }
 
-       tt_response->header.packet_type = BATADV_TT_QUERY;
-       tt_response->header.version = BATADV_COMPAT_VERSION;
-       tt_response->header.ttl = BATADV_TTL;
-       memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN);
-       memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
-       tt_response->flags = BATADV_TT_RESPONSE;
+       tvlv_tt_data->flags = BATADV_TT_RESPONSE;
+       tvlv_tt_data->ttvn = req_ttvn;
 
        if (full_table)
-               tt_response->flags |= BATADV_TT_FULL_TABLE;
+               tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Sending TT_RESPONSE %pM for %pM (ttvn: %u)\n",
-                  res_dst_orig_node->orig, req_dst_orig_node->orig, req_ttvn);
+                  "Sending TT_RESPONSE %pM for %pM [%c] (ttvn: %u)\n",
+                  res_dst_orig_node->orig, req_dst_orig_node->orig,
+                  full_table ? 'F' : '.', req_ttvn);
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
 
-       res = batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL);
-       if (res != NET_XMIT_DROP)
-               ret = true;
+       batadv_tvlv_unicast_send(bat_priv, req_dst_orig_node->orig,
+                                req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
+                                tvlv_len);
 
+       ret = true;
        goto out;
 
 unlock:
@@ -1849,37 +2524,43 @@ out:
                batadv_orig_node_free_ref(res_dst_orig_node);
        if (req_dst_orig_node)
                batadv_orig_node_free_ref(req_dst_orig_node);
-       if (!ret)
-               kfree_skb(skb);
+       kfree(tvlv_tt_data);
        return ret;
 }
 
-static bool
-batadv_send_my_tt_response(struct batadv_priv *bat_priv,
-                          struct batadv_tt_query_packet *tt_request)
+/**
+ * batadv_send_my_tt_response - send reply to tt request concerning this node's
+ *  translation table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @req_src: mac address of tt request sender
+ *
+ * Returns true if tt request reply was sent, false otherwise.
+ */
+static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
+                                      struct batadv_tvlv_tt_data *tt_data,
+                                      uint8_t *req_src)
 {
-       struct batadv_orig_node *orig_node;
+       struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
        struct batadv_hard_iface *primary_if = NULL;
-       uint8_t my_ttvn, req_ttvn, ttvn;
-       int ret = false;
-       unsigned char *tt_buff;
+       struct batadv_tvlv_tt_change *tt_change;
+       struct batadv_orig_node *orig_node;
+       uint8_t my_ttvn, req_ttvn;
+       uint16_t tvlv_len;
        bool full_table;
-       uint16_t tt_len, tt_tot;
-       struct sk_buff *skb = NULL;
-       struct batadv_tt_query_packet *tt_response;
-       uint8_t *packet_pos;
-       size_t len;
+       int32_t tt_len;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
                   "Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n",
-                  tt_request->src, tt_request->ttvn,
-                  (tt_request->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+                  req_src, tt_data->ttvn,
+                  (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
 
+       spin_lock_bh(&bat_priv->tt.commit_lock);
 
        my_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
-       req_ttvn = tt_request->ttvn;
+       req_ttvn = tt_data->ttvn;
 
-       orig_node = batadv_orig_hash_find(bat_priv, tt_request->src);
+       orig_node = batadv_orig_hash_find(bat_priv, req_src);
        if (!orig_node)
                goto out;
 
@@ -1890,103 +2571,104 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
        /* If the full table has been explicitly requested or the gap
         * is too big send the whole local translation table
         */
-       if (tt_request->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn ||
+       if (tt_data->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn ||
            !bat_priv->tt.last_changeset)
                full_table = true;
        else
                full_table = false;
 
-       /* In this version, fragmentation is not implemented, then
-        * I'll send only one packet with as much TT entries as I can
+       /* TT fragmentation hasn't been implemented yet, so send as many
+        * TT entries fit a single packet as possible only
         */
        if (!full_table) {
                spin_lock_bh(&bat_priv->tt.last_changeset_lock);
-               tt_len = bat_priv->tt.last_changeset_len;
-               tt_tot = tt_len / sizeof(struct batadv_tt_change);
 
-               len = sizeof(*tt_response) + tt_len;
-               skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
-               if (!skb)
+               tt_len = bat_priv->tt.last_changeset_len;
+               tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
+                                                            &tvlv_tt_data,
+                                                            &tt_change,
+                                                            &tt_len);
+               if (!tt_len)
                        goto unlock;
 
-               skb->priority = TC_PRIO_CONTROL;
-               skb_reserve(skb, ETH_HLEN);
-               packet_pos = skb_put(skb, len);
-               tt_response = (struct batadv_tt_query_packet *)packet_pos;
-               tt_response->ttvn = req_ttvn;
-               tt_response->tt_data = htons(tt_tot);
-
-               tt_buff = skb->data + sizeof(*tt_response);
-               memcpy(tt_buff, bat_priv->tt.last_changeset,
+               /* Copy the last orig_node's OGM buffer */
+               memcpy(tt_change, bat_priv->tt.last_changeset,
                       bat_priv->tt.last_changeset_len);
                spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
        } else {
-               tt_len = (uint16_t)atomic_read(&bat_priv->tt.local_entry_num);
-               tt_len *= sizeof(struct batadv_tt_change);
-               ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
-
-               skb = batadv_tt_response_fill_table(tt_len, ttvn,
-                                                   bat_priv->tt.local_hash,
-                                                   bat_priv,
-                                                   batadv_tt_local_valid_entry,
-                                                   NULL);
-               if (!skb)
+               req_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
+
+               /* allocate the tvlv, put the tt_data and all the tt_vlan_data
+                * in the initial part
+                */
+               tt_len = -1;
+               tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
+                                                            &tvlv_tt_data,
+                                                            &tt_change,
+                                                            &tt_len);
+               if (!tt_len)
                        goto out;
 
-               tt_response = (struct batadv_tt_query_packet *)skb->data;
+               /* fill the rest of the tvlv with the real TT entries */
+               batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.local_hash,
+                                       tt_change, tt_len,
+                                       batadv_tt_local_valid, NULL);
        }
 
-       tt_response->header.packet_type = BATADV_TT_QUERY;
-       tt_response->header.version = BATADV_COMPAT_VERSION;
-       tt_response->header.ttl = BATADV_TTL;
-       memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN);
-       memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
-       tt_response->flags = BATADV_TT_RESPONSE;
+       tvlv_tt_data->flags = BATADV_TT_RESPONSE;
+       tvlv_tt_data->ttvn = req_ttvn;
 
        if (full_table)
-               tt_response->flags |= BATADV_TT_FULL_TABLE;
+               tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Sending TT_RESPONSE to %pM [%c]\n",
-                  orig_node->orig,
-                  (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+                  "Sending TT_RESPONSE to %pM [%c] (ttvn: %u)\n",
+                  orig_node->orig, full_table ? 'F' : '.', req_ttvn);
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
-               ret = true;
+       batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
+                                req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
+                                tvlv_len);
+
        goto out;
 
 unlock:
        spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
 out:
+       spin_unlock_bh(&bat_priv->tt.commit_lock);
        if (orig_node)
                batadv_orig_node_free_ref(orig_node);
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
-       if (!ret)
-               kfree_skb(skb);
-       /* This packet was for me, so it doesn't need to be re-routed */
+       kfree(tvlv_tt_data);
+       /* The packet was for this host, so it doesn't need to be re-routed */
        return true;
 }
 
-bool batadv_send_tt_response(struct batadv_priv *bat_priv,
-                            struct batadv_tt_query_packet *tt_request)
+/**
+ * batadv_send_tt_response - send reply to tt request
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @req_src: mac address of tt request sender
+ * @req_dst: mac address of tt request recipient
+ *
+ * Returns true if tt request reply was sent, false otherwise.
+ */
+static bool batadv_send_tt_response(struct batadv_priv *bat_priv,
+                                   struct batadv_tvlv_tt_data *tt_data,
+                                   uint8_t *req_src, uint8_t *req_dst)
 {
-       if (batadv_is_my_mac(bat_priv, tt_request->dst)) {
-               /* don't answer backbone gws! */
-               if (batadv_bla_is_backbone_gw_orig(bat_priv, tt_request->src))
-                       return true;
-
-               return batadv_send_my_tt_response(bat_priv, tt_request);
-       } else {
-               return batadv_send_other_tt_response(bat_priv, tt_request);
-       }
+       if (batadv_is_my_mac(bat_priv, req_dst))
+               return batadv_send_my_tt_response(bat_priv, tt_data, req_src);
+       else
+               return batadv_send_other_tt_response(bat_priv, tt_data,
+                                                    req_src, req_dst);
 }
 
 static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
                                      struct batadv_orig_node *orig_node,
-                                     struct batadv_tt_change *tt_change,
+                                     struct batadv_tvlv_tt_change *tt_change,
                                      uint16_t tt_num_changes, uint8_t ttvn)
 {
        int i;
@@ -1997,11 +2679,13 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
                        roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM;
                        batadv_tt_global_del(bat_priv, orig_node,
                                             (tt_change + i)->addr,
+                                            ntohs((tt_change + i)->vid),
                                             "tt removed by changes",
                                             roams);
                } else {
                        if (!batadv_tt_global_add(bat_priv, orig_node,
                                                  (tt_change + i)->addr,
+                                                 ntohs((tt_change + i)->vid),
                                                  (tt_change + i)->flags, ttvn))
                                /* In case of problem while storing a
                                 * global_entry, we stop the updating
@@ -2016,21 +2700,22 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
 }
 
 static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
-                                 struct batadv_tt_query_packet *tt_response)
+                                 struct batadv_tvlv_tt_change *tt_change,
+                                 uint8_t ttvn, uint8_t *resp_src,
+                                 uint16_t num_entries)
 {
        struct batadv_orig_node *orig_node;
 
-       orig_node = batadv_orig_hash_find(bat_priv, tt_response->src);
+       orig_node = batadv_orig_hash_find(bat_priv, resp_src);
        if (!orig_node)
                goto out;
 
        /* Purge the old table first.. */
-       batadv_tt_global_del_orig(bat_priv, orig_node, "Received full table");
+       batadv_tt_global_del_orig(bat_priv, orig_node, -1,
+                                 "Received full table");
 
-       _batadv_tt_update_changes(bat_priv, orig_node,
-                                 (struct batadv_tt_change *)(tt_response + 1),
-                                 ntohs(tt_response->tt_data),
-                                 tt_response->ttvn);
+       _batadv_tt_update_changes(bat_priv, orig_node, tt_change, num_entries,
+                                 ttvn);
 
        spin_lock_bh(&orig_node->tt_buff_lock);
        kfree(orig_node->tt_buff);
@@ -2038,7 +2723,7 @@ static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
        orig_node->tt_buff = NULL;
        spin_unlock_bh(&orig_node->tt_buff_lock);
 
-       atomic_set(&orig_node->last_ttvn, tt_response->ttvn);
+       atomic_set(&orig_node->last_ttvn, ttvn);
 
 out:
        if (orig_node)
@@ -2048,22 +2733,31 @@ out:
 static void batadv_tt_update_changes(struct batadv_priv *bat_priv,
                                     struct batadv_orig_node *orig_node,
                                     uint16_t tt_num_changes, uint8_t ttvn,
-                                    struct batadv_tt_change *tt_change)
+                                    struct batadv_tvlv_tt_change *tt_change)
 {
        _batadv_tt_update_changes(bat_priv, orig_node, tt_change,
                                  tt_num_changes, ttvn);
 
-       batadv_tt_save_orig_buffer(bat_priv, orig_node,
-                                  (unsigned char *)tt_change, tt_num_changes);
+       batadv_tt_save_orig_buffer(bat_priv, orig_node, tt_change,
+                                  batadv_tt_len(tt_num_changes));
        atomic_set(&orig_node->last_ttvn, ttvn);
 }
 
-bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr)
+/**
+ * batadv_is_my_client - check if a client is served by the local node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac adress of the client to check
+ * @vid: VLAN identifier
+ *
+ * Returns true if the client is served by this node, false otherwise.
+ */
+bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr,
+                        unsigned short vid)
 {
        struct batadv_tt_local_entry *tt_local_entry;
        bool ret = false;
 
-       tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+       tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
        if (!tt_local_entry)
                goto out;
        /* Check if the client has been logically deleted (but is kept for
@@ -2079,72 +2773,68 @@ out:
        return ret;
 }
 
-void batadv_handle_tt_response(struct batadv_priv *bat_priv,
-                              struct batadv_tt_query_packet *tt_response)
+/**
+ * batadv_handle_tt_response - process incoming tt reply
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @resp_src: mac address of tt reply sender
+ * @num_entries: number of tt change entries appended to the tt data
+ */
+static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
+                                     struct batadv_tvlv_tt_data *tt_data,
+                                     uint8_t *resp_src, uint16_t num_entries)
 {
        struct batadv_tt_req_node *node, *safe;
        struct batadv_orig_node *orig_node = NULL;
-       struct batadv_tt_change *tt_change;
+       struct batadv_tvlv_tt_change *tt_change;
+       uint8_t *tvlv_ptr = (uint8_t *)tt_data;
+       uint16_t change_offset;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
                   "Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n",
-                  tt_response->src, tt_response->ttvn,
-                  ntohs(tt_response->tt_data),
-                  (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
-
-       /* we should have never asked a backbone gw */
-       if (batadv_bla_is_backbone_gw_orig(bat_priv, tt_response->src))
-               goto out;
+                  resp_src, tt_data->ttvn, num_entries,
+                  (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
 
-       orig_node = batadv_orig_hash_find(bat_priv, tt_response->src);
+       orig_node = batadv_orig_hash_find(bat_priv, resp_src);
        if (!orig_node)
                goto out;
 
-       if (tt_response->flags & BATADV_TT_FULL_TABLE) {
-               batadv_tt_fill_gtable(bat_priv, tt_response);
+       spin_lock_bh(&orig_node->tt_lock);
+
+       change_offset = sizeof(struct batadv_tvlv_tt_vlan_data);
+       change_offset *= ntohs(tt_data->num_vlan);
+       change_offset += sizeof(*tt_data);
+       tvlv_ptr += change_offset;
+
+       tt_change = (struct batadv_tvlv_tt_change *)tvlv_ptr;
+       if (tt_data->flags & BATADV_TT_FULL_TABLE) {
+               batadv_tt_fill_gtable(bat_priv, tt_change, tt_data->ttvn,
+                                     resp_src, num_entries);
        } else {
-               tt_change = (struct batadv_tt_change *)(tt_response + 1);
-               batadv_tt_update_changes(bat_priv, orig_node,
-                                        ntohs(tt_response->tt_data),
-                                        tt_response->ttvn, tt_change);
+               batadv_tt_update_changes(bat_priv, orig_node, num_entries,
+                                        tt_data->ttvn, tt_change);
        }
 
+       /* Recalculate the CRC for this orig_node and store it */
+       batadv_tt_global_update_crc(bat_priv, orig_node);
+
+       spin_unlock_bh(&orig_node->tt_lock);
+
        /* Delete the tt_req_node from pending tt_requests list */
        spin_lock_bh(&bat_priv->tt.req_list_lock);
        list_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
-               if (!batadv_compare_eth(node->addr, tt_response->src))
+               if (!batadv_compare_eth(node->addr, resp_src))
                        continue;
                list_del(&node->list);
                kfree(node);
        }
-       spin_unlock_bh(&bat_priv->tt.req_list_lock);
 
-       /* Recalculate the CRC for this orig_node and store it */
-       orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
+       spin_unlock_bh(&bat_priv->tt.req_list_lock);
 out:
        if (orig_node)
                batadv_orig_node_free_ref(orig_node);
 }
 
-int batadv_tt_init(struct batadv_priv *bat_priv)
-{
-       int ret;
-
-       ret = batadv_tt_local_init(bat_priv);
-       if (ret < 0)
-               return ret;
-
-       ret = batadv_tt_global_init(bat_priv);
-       if (ret < 0)
-               return ret;
-
-       INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge);
-       queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
-                          msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
-
-       return 1;
-}
-
 static void batadv_tt_roam_list_free(struct batadv_priv *bat_priv)
 {
        struct batadv_tt_roam_node *node, *safe;
@@ -2225,14 +2915,28 @@ unlock:
        return ret;
 }
 
+/**
+ * batadv_send_roam_adv - send a roaming advertisement message
+ * @bat_priv: the bat priv with all the soft interface information
+ * @client: mac address of the roaming client
+ * @vid: VLAN identifier
+ * @orig_node: message destination
+ *
+ * Send a ROAMING_ADV message to the node which was previously serving this
+ * client. This is done to inform the node that from now on all traffic destined
+ * for this particular roamed client has to be forwarded to the sender of the
+ * roaming message.
+ */
 static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
+                                unsigned short vid,
                                 struct batadv_orig_node *orig_node)
 {
-       struct sk_buff *skb = NULL;
-       struct batadv_roam_adv_packet *roam_adv_packet;
-       int ret = 1;
        struct batadv_hard_iface *primary_if;
-       size_t len = sizeof(*roam_adv_packet);
+       struct batadv_tvlv_roam_adv tvlv_roam;
+
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
 
        /* before going on we have to check whether the client has
         * already roamed to us too many times
@@ -2240,40 +2944,22 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
        if (!batadv_tt_check_roam_count(bat_priv, client))
                goto out;
 
-       skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
-       if (!skb)
-               goto out;
-
-       skb->priority = TC_PRIO_CONTROL;
-       skb_reserve(skb, ETH_HLEN);
-
-       roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len);
-
-       roam_adv_packet->header.packet_type = BATADV_ROAM_ADV;
-       roam_adv_packet->header.version = BATADV_COMPAT_VERSION;
-       roam_adv_packet->header.ttl = BATADV_TTL;
-       roam_adv_packet->reserved = 0;
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-       memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
-       batadv_hardif_free_ref(primary_if);
-       memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN);
-       memcpy(roam_adv_packet->client, client, ETH_ALEN);
-
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Sending ROAMING_ADV to %pM (client %pM)\n",
-                  orig_node->orig, client);
+                  "Sending ROAMING_ADV to %pM (client %pM, vid: %d)\n",
+                  orig_node->orig, client, BATADV_PRINT_VID(vid));
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
-               ret = 0;
+       memcpy(tvlv_roam.client, client, sizeof(tvlv_roam.client));
+       tvlv_roam.vid = htons(vid);
+
+       batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
+                                orig_node->orig, BATADV_TVLV_ROAM, 1,
+                                &tvlv_roam, sizeof(tvlv_roam));
 
 out:
-       if (ret && skb)
-               kfree_skb(skb);
-       return;
+       if (primary_if)
+               batadv_hardif_free_ref(primary_if);
 }
 
 static void batadv_tt_purge(struct work_struct *work)
@@ -2286,7 +2972,7 @@ static void batadv_tt_purge(struct work_struct *work)
        priv_tt = container_of(delayed_work, struct batadv_priv_tt, work);
        bat_priv = container_of(priv_tt, struct batadv_priv, tt);
 
-       batadv_tt_local_purge(bat_priv);
+       batadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT);
        batadv_tt_global_purge(bat_priv);
        batadv_tt_req_purge(bat_priv);
        batadv_tt_roam_purge(bat_priv);
@@ -2297,6 +2983,9 @@ static void batadv_tt_purge(struct work_struct *work)
 
 void batadv_tt_free(struct batadv_priv *bat_priv)
 {
+       batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_TT, 1);
+       batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_TT, 1);
+
        cancel_delayed_work_sync(&bat_priv->tt.work);
 
        batadv_tt_local_table_free(bat_priv);
@@ -2308,19 +2997,25 @@ void batadv_tt_free(struct batadv_priv *bat_priv)
        kfree(bat_priv->tt.last_changeset);
 }
 
-/* This function will enable or disable the specified flags for all the entries
- * in the given hash table and returns the number of modified entries
+/**
+ * batadv_tt_local_set_flags - set or unset the specified flags on the local
+ *  table and possibly count them in the TT size
+ * @bat_priv: the bat priv with all the soft interface information
+ * @flags: the flag to switch
+ * @enable: whether to set or unset the flag
+ * @count: whether to increase the TT size by the number of changed entries
  */
-static uint16_t batadv_tt_set_flags(struct batadv_hashtable *hash,
-                                   uint16_t flags, bool enable)
+static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv,
+                                     uint16_t flags, bool enable, bool count)
 {
-       uint32_t i;
+       struct batadv_hashtable *hash = bat_priv->tt.local_hash;
+       struct batadv_tt_common_entry *tt_common_entry;
        uint16_t changed_num = 0;
        struct hlist_head *head;
-       struct batadv_tt_common_entry *tt_common_entry;
+       uint32_t i;
 
        if (!hash)
-               goto out;
+               return;
 
        for (i = 0; i < hash->size; i++) {
                head = &hash->table[i];
@@ -2338,11 +3033,15 @@ static uint16_t batadv_tt_set_flags(struct batadv_hashtable *hash,
                                tt_common_entry->flags &= ~flags;
                        }
                        changed_num++;
+
+                       if (!count)
+                               continue;
+
+                       batadv_tt_local_size_inc(bat_priv,
+                                                tt_common_entry->vid);
                }
                rcu_read_unlock();
        }
-out:
-       return changed_num;
 }
 
 /* Purge out all the tt local entries marked with BATADV_TT_CLIENT_PENDING */
@@ -2370,10 +3069,11 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
                                continue;
 
                        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "Deleting local tt entry (%pM): pending\n",
-                                  tt_common->addr);
+                                  "Deleting local tt entry (%pM, vid: %d): pending\n",
+                                  tt_common->addr,
+                                  BATADV_PRINT_VID(tt_common->vid));
 
-                       atomic_dec(&bat_priv->tt.local_entry_num);
+                       batadv_tt_local_size_dec(bat_priv, tt_common->vid);
                        hlist_del_rcu(&tt_common->hash_entry);
                        tt_local = container_of(tt_common,
                                                struct batadv_tt_local_entry,
@@ -2384,22 +3084,25 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
        }
 }
 
-static int batadv_tt_commit_changes(struct batadv_priv *bat_priv,
-                                   unsigned char **packet_buff,
-                                   int *packet_buff_len, int packet_min_len)
+/**
+ * batadv_tt_local_commit_changes_nolock - commit all pending local tt changes
+ *  which have been queued in the time since the last commit
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Caller must hold tt->commit_lock.
+ */
+static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
 {
-       uint16_t changed_num = 0;
-
-       if (atomic_read(&bat_priv->tt.local_changes) < 1)
-               return -ENOENT;
+       if (atomic_read(&bat_priv->tt.local_changes) < 1) {
+               if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
+                       batadv_tt_tvlv_container_update(bat_priv);
+               return;
+       }
 
-       changed_num = batadv_tt_set_flags(bat_priv->tt.local_hash,
-                                         BATADV_TT_CLIENT_NEW, false);
+       batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true);
 
-       /* all reset entries have to be counted as local entries */
-       atomic_add(changed_num, &bat_priv->tt.local_entry_num);
        batadv_tt_local_purge_pending_clients(bat_priv);
-       bat_priv->tt.local_crc = batadv_tt_local_crc(bat_priv);
+       batadv_tt_local_update_crc(bat_priv);
 
        /* Increment the TTVN only once per OGM interval */
        atomic_inc(&bat_priv->tt.vn);
@@ -2409,49 +3112,38 @@ static int batadv_tt_commit_changes(struct batadv_priv *bat_priv,
 
        /* reset the sending counter */
        atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
-
-       return batadv_tt_changes_fill_buff(bat_priv, packet_buff,
-                                          packet_buff_len, packet_min_len);
+       batadv_tt_tvlv_container_update(bat_priv);
 }
 
-/* when calling this function (hard_iface == primary_if) has to be true */
-int batadv_tt_append_diff(struct batadv_priv *bat_priv,
-                         unsigned char **packet_buff, int *packet_buff_len,
-                         int packet_min_len)
+/**
+ * batadv_tt_local_commit_changes - commit all pending local tt changes which
+ *  have been queued in the time since the last commit
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
 {
-       int tt_num_changes;
-
-       /* if at least one change happened */
-       tt_num_changes = batadv_tt_commit_changes(bat_priv, packet_buff,
-                                                 packet_buff_len,
-                                                 packet_min_len);
-
-       /* if the changes have been sent often enough */
-       if ((tt_num_changes < 0) &&
-           (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))) {
-               batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len,
-                                             packet_min_len, packet_min_len);
-               tt_num_changes = 0;
-       }
-
-       return tt_num_changes;
+       spin_lock_bh(&bat_priv->tt.commit_lock);
+       batadv_tt_local_commit_changes_nolock(bat_priv);
+       spin_unlock_bh(&bat_priv->tt.commit_lock);
 }
 
 bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
-                          uint8_t *dst)
+                          uint8_t *dst, unsigned short vid)
 {
        struct batadv_tt_local_entry *tt_local_entry = NULL;
        struct batadv_tt_global_entry *tt_global_entry = NULL;
+       struct batadv_softif_vlan *vlan;
        bool ret = false;
 
-       if (!atomic_read(&bat_priv->ap_isolation))
+       vlan = batadv_softif_vlan_get(bat_priv, vid);
+       if (!vlan || !atomic_read(&vlan->ap_isolation))
                goto out;
 
-       tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst);
+       tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid);
        if (!tt_local_entry)
                goto out;
 
-       tt_global_entry = batadv_tt_global_hash_find(bat_priv, src);
+       tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid);
        if (!tt_global_entry)
                goto out;
 
@@ -2461,6 +3153,8 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
        ret = true;
 
 out:
+       if (vlan)
+               batadv_softif_vlan_free_ref(vlan);
        if (tt_global_entry)
                batadv_tt_global_entry_free_ref(tt_global_entry);
        if (tt_local_entry)
@@ -2468,19 +3162,29 @@ out:
        return ret;
 }
 
-void batadv_tt_update_orig(struct batadv_priv *bat_priv,
-                          struct batadv_orig_node *orig_node,
-                          const unsigned char *tt_buff, uint8_t tt_num_changes,
-                          uint8_t ttvn, uint16_t tt_crc)
+/**
+ * batadv_tt_update_orig - update global translation table with new tt
+ *  information received via ogms
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @tt_vlan: pointer to the first tvlv VLAN entry
+ * @tt_num_vlan: number of tvlv VLAN entries
+ * @tt_change: pointer to the first entry in the TT buffer
+ * @tt_num_changes: number of tt changes inside the tt buffer
+ * @ttvn: translation table version number of this changeset
+ * @tt_crc: crc32 checksum of orig node's translation table
+ */
+static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
+                                 struct batadv_orig_node *orig_node,
+                                 const void *tt_buff, uint16_t tt_num_vlan,
+                                 struct batadv_tvlv_tt_change *tt_change,
+                                 uint16_t tt_num_changes, uint8_t ttvn)
 {
        uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
        bool full_table = true;
-       struct batadv_tt_change *tt_change;
-
-       /* don't care about a backbone gateways updates. */
-       if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
-               return;
 
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff;
        /* orig table not initialised AND first diff is in the OGM OR the ttvn
         * increased by one -> we can apply the attached changes
         */
@@ -2496,7 +3200,9 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
                        goto request_table;
                }
 
-               tt_change = (struct batadv_tt_change *)tt_buff;
+               spin_lock_bh(&orig_node->tt_lock);
+
+               tt_change = (struct batadv_tvlv_tt_change *)tt_buff;
                batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes,
                                         ttvn, tt_change);
 
@@ -2504,7 +3210,9 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
                 * prefer to recompute it to spot any possible inconsistency
                 * in the global table
                 */
-               orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
+               batadv_tt_global_update_crc(bat_priv, orig_node);
+
+               spin_unlock_bh(&orig_node->tt_lock);
 
                /* The ttvn alone is not enough to guarantee consistency
                 * because a single value could represent different states
@@ -2515,37 +3223,46 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
                 * checking the CRC value is mandatory to detect the
                 * inconsistency
                 */
-               if (orig_node->tt_crc != tt_crc)
+               if (!batadv_tt_global_check_crc(orig_node, tt_vlan,
+                                               tt_num_vlan))
                        goto request_table;
        } else {
                /* if we missed more than one change or our tables are not
                 * in sync anymore -> request fresh tt data
                 */
                if (!orig_node->tt_initialised || ttvn != orig_ttvn ||
-                   orig_node->tt_crc != tt_crc) {
+                   !batadv_tt_global_check_crc(orig_node, tt_vlan,
+                                               tt_num_vlan)) {
 request_table:
                        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                                  "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u crc: %#.4x last_crc: %#.4x num_changes: %u)\n",
-                                  orig_node->orig, ttvn, orig_ttvn, tt_crc,
-                                  orig_node->tt_crc, tt_num_changes);
+                                  "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u num_changes: %u)\n",
+                                  orig_node->orig, ttvn, orig_ttvn,
+                                  tt_num_changes);
                        batadv_send_tt_request(bat_priv, orig_node, ttvn,
-                                              tt_crc, full_table);
+                                              tt_vlan, tt_num_vlan,
+                                              full_table);
                        return;
                }
        }
 }
 
-/* returns true whether we know that the client has moved from its old
- * originator to another one. This entry is kept is still kept for consistency
- * purposes
+/**
+ * batadv_tt_global_client_is_roaming - check if a client is marked as roaming
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the client to check
+ * @vid: VLAN identifier
+ *
+ * Returns true if we know that the client has moved from its old originator
+ * to another one. This entry is still kept for consistency purposes and will be
+ * deleted later by a DEL or because of timeout
  */
 bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
-                                       uint8_t *addr)
+                                       uint8_t *addr, unsigned short vid)
 {
        struct batadv_tt_global_entry *tt_global_entry;
        bool ret = false;
 
-       tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+       tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
        if (!tt_global_entry)
                goto out;
 
@@ -2558,19 +3275,20 @@ out:
 /**
  * batadv_tt_local_client_is_roaming - tells whether the client is roaming
  * @bat_priv: the bat priv with all the soft interface information
- * @addr: the MAC address of the local client to query
+ * @addr: the mac address of the local client to query
+ * @vid: VLAN identifier
  *
  * Returns true if the local client is known to be roaming (it is not served by
  * this node anymore) or not. If yes, the client is still present in the table
  * to keep the latter consistent with the node TTVN
  */
 bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
-                                      uint8_t *addr)
+                                      uint8_t *addr, unsigned short vid)
 {
        struct batadv_tt_local_entry *tt_local_entry;
        bool ret = false;
 
-       tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+       tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
        if (!tt_local_entry)
                goto out;
 
@@ -2582,26 +3300,268 @@ out:
 
 bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
                                          struct batadv_orig_node *orig_node,
-                                         const unsigned char *addr)
+                                         const unsigned char *addr,
+                                         unsigned short vid)
 {
        bool ret = false;
 
-       /* if the originator is a backbone node (meaning it belongs to the same
-        * LAN of this node) the temporary client must not be added because to
-        * reach such destination the node must use the LAN instead of the mesh
-        */
-       if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
-               goto out;
-
-       if (!batadv_tt_global_add(bat_priv, orig_node, addr,
+       if (!batadv_tt_global_add(bat_priv, orig_node, addr, vid,
                                  BATADV_TT_CLIENT_TEMP,
                                  atomic_read(&orig_node->last_ttvn)))
                goto out;
 
        batadv_dbg(BATADV_DBG_TT, bat_priv,
-                  "Added temporary global client (addr: %pM orig: %pM)\n",
-                  addr, orig_node->orig);
+                  "Added temporary global client (addr: %pM, vid: %d, orig: %pM)\n",
+                  addr, BATADV_PRINT_VID(vid), orig_node->orig);
        ret = true;
 out:
        return ret;
 }
+
+/**
+ * batadv_tt_local_resize_to_mtu - resize the local translation table fit the
+ *  maximum packet size that can be transported through the mesh
+ * @soft_iface: netdev struct of the mesh interface
+ *
+ * Remove entries older than 'timeout' and half timeout if more entries need
+ * to be removed.
+ */
+void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface)
+{
+       struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+       int packet_size_max = atomic_read(&bat_priv->packet_size_max);
+       int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2;
+       bool reduced = false;
+
+       spin_lock_bh(&bat_priv->tt.commit_lock);
+
+       while (true) {
+               table_size = batadv_tt_local_table_transmit_size(bat_priv);
+               if (packet_size_max >= table_size)
+                       break;
+
+               batadv_tt_local_purge(bat_priv, timeout);
+               batadv_tt_local_purge_pending_clients(bat_priv);
+
+               timeout /= 2;
+               reduced = true;
+               net_ratelimited_function(batadv_info, soft_iface,
+                                        "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n",
+                                        packet_size_max);
+       }
+
+       /* commit these changes immediately, to avoid synchronization problem
+        * with the TTVN
+        */
+       if (reduced)
+               batadv_tt_local_commit_changes_nolock(bat_priv);
+
+       spin_unlock_bh(&bat_priv->tt.commit_lock);
+}
+
+/**
+ * batadv_tt_tvlv_ogm_handler_v1 - process incoming tt tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+                                         struct batadv_orig_node *orig,
+                                         uint8_t flags, void *tvlv_value,
+                                         uint16_t tvlv_value_len)
+{
+       struct batadv_tvlv_tt_vlan_data *tt_vlan;
+       struct batadv_tvlv_tt_change *tt_change;
+       struct batadv_tvlv_tt_data *tt_data;
+       uint16_t num_entries, num_vlan;
+
+       if (tvlv_value_len < sizeof(*tt_data))
+               return;
+
+       tt_data = (struct batadv_tvlv_tt_data *)tvlv_value;
+       tvlv_value_len -= sizeof(*tt_data);
+
+       num_vlan = ntohs(tt_data->num_vlan);
+
+       if (tvlv_value_len < sizeof(*tt_vlan) * num_vlan)
+               return;
+
+       tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
+       tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan);
+       tvlv_value_len -= sizeof(*tt_vlan) * num_vlan;
+
+       num_entries = batadv_tt_entries(tvlv_value_len);
+
+       batadv_tt_update_orig(bat_priv, orig, tt_vlan, num_vlan, tt_change,
+                             num_entries, tt_data->ttvn);
+}
+
+/**
+ * batadv_tt_tvlv_unicast_handler_v1 - process incoming (unicast) tt tvlv
+ *  container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: mac address of tt tvlv sender
+ * @dst: mac address of tt tvlv recipient
+ * @tvlv_value: tvlv buffer containing the tt data
+ * @tvlv_value_len: tvlv buffer length
+ *
+ * Returns NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS
+ * otherwise.
+ */
+static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
+                                            uint8_t *src, uint8_t *dst,
+                                            void *tvlv_value,
+                                            uint16_t tvlv_value_len)
+{
+       struct batadv_tvlv_tt_data *tt_data;
+       uint16_t tt_vlan_len, tt_num_entries;
+       char tt_flag;
+       bool ret;
+
+       if (tvlv_value_len < sizeof(*tt_data))
+               return NET_RX_SUCCESS;
+
+       tt_data = (struct batadv_tvlv_tt_data *)tvlv_value;
+       tvlv_value_len -= sizeof(*tt_data);
+
+       tt_vlan_len = sizeof(struct batadv_tvlv_tt_vlan_data);
+       tt_vlan_len *= ntohs(tt_data->num_vlan);
+
+       if (tvlv_value_len < tt_vlan_len)
+               return NET_RX_SUCCESS;
+
+       tvlv_value_len -= tt_vlan_len;
+       tt_num_entries = batadv_tt_entries(tvlv_value_len);
+
+       switch (tt_data->flags & BATADV_TT_DATA_TYPE_MASK) {
+       case BATADV_TT_REQUEST:
+               batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
+
+               /* If this node cannot provide a TT response the tt_request is
+                * forwarded
+                */
+               ret = batadv_send_tt_response(bat_priv, tt_data, src, dst);
+               if (!ret) {
+                       if (tt_data->flags & BATADV_TT_FULL_TABLE)
+                               tt_flag = 'F';
+                       else
+                               tt_flag = '.';
+
+                       batadv_dbg(BATADV_DBG_TT, bat_priv,
+                                  "Routing TT_REQUEST to %pM [%c]\n",
+                                  dst, tt_flag);
+                       /* tvlv API will re-route the packet */
+                       return NET_RX_DROP;
+               }
+               break;
+       case BATADV_TT_RESPONSE:
+               batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
+
+               if (batadv_is_my_mac(bat_priv, dst)) {
+                       batadv_handle_tt_response(bat_priv, tt_data,
+                                                 src, tt_num_entries);
+                       return NET_RX_SUCCESS;
+               }
+
+               if (tt_data->flags & BATADV_TT_FULL_TABLE)
+                       tt_flag =  'F';
+               else
+                       tt_flag = '.';
+
+               batadv_dbg(BATADV_DBG_TT, bat_priv,
+                          "Routing TT_RESPONSE to %pM [%c]\n", dst, tt_flag);
+
+               /* tvlv API will re-route the packet */
+               return NET_RX_DROP;
+       }
+
+       return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_roam_tvlv_unicast_handler_v1 - process incoming tt roam tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: mac address of tt tvlv sender
+ * @dst: mac address of tt tvlv recipient
+ * @tvlv_value: tvlv buffer containing the tt data
+ * @tvlv_value_len: tvlv buffer length
+ *
+ * Returns NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS
+ * otherwise.
+ */
+static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
+                                              uint8_t *src, uint8_t *dst,
+                                              void *tvlv_value,
+                                              uint16_t tvlv_value_len)
+{
+       struct batadv_tvlv_roam_adv *roaming_adv;
+       struct batadv_orig_node *orig_node = NULL;
+
+       /* If this node is not the intended recipient of the
+        * roaming advertisement the packet is forwarded
+        * (the tvlv API will re-route the packet).
+        */
+       if (!batadv_is_my_mac(bat_priv, dst))
+               return NET_RX_DROP;
+
+       if (tvlv_value_len < sizeof(*roaming_adv))
+               goto out;
+
+       orig_node = batadv_orig_hash_find(bat_priv, src);
+       if (!orig_node)
+               goto out;
+
+       batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
+       roaming_adv = (struct batadv_tvlv_roam_adv *)tvlv_value;
+
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+                  "Received ROAMING_ADV from %pM (client %pM)\n",
+                  src, roaming_adv->client);
+
+       batadv_tt_global_add(bat_priv, orig_node, roaming_adv->client,
+                            ntohs(roaming_adv->vid), BATADV_TT_CLIENT_ROAM,
+                            atomic_read(&orig_node->last_ttvn) + 1);
+
+out:
+       if (orig_node)
+               batadv_orig_node_free_ref(orig_node);
+       return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_tt_init - initialise the translation table internals
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Return 0 on success or negative error number in case of failure.
+ */
+int batadv_tt_init(struct batadv_priv *bat_priv)
+{
+       int ret;
+
+       /* synchronized flags must be remote */
+       BUILD_BUG_ON(!(BATADV_TT_SYNC_MASK & BATADV_TT_REMOTE_MASK));
+
+       ret = batadv_tt_local_init(bat_priv);
+       if (ret < 0)
+               return ret;
+
+       ret = batadv_tt_global_init(bat_priv);
+       if (ret < 0)
+               return ret;
+
+       batadv_tvlv_handler_register(bat_priv, batadv_tt_tvlv_ogm_handler_v1,
+                                    batadv_tt_tvlv_unicast_handler_v1,
+                                    BATADV_TVLV_TT, 1, BATADV_NO_FLAGS);
+
+       batadv_tvlv_handler_register(bat_priv, NULL,
+                                    batadv_roam_tvlv_unicast_handler_v1,
+                                    BATADV_TVLV_ROAM, 1, BATADV_NO_FLAGS);
+
+       INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge);
+       queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
+                          msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
+
+       return 1;
+}
index 659a3bb..026b1ff 100644 (file)
 #ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
 #define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
 
-int batadv_tt_len(int changes_num);
 int batadv_tt_init(struct batadv_priv *bat_priv);
-void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
-                        int ifindex);
+bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
+                        unsigned short vid, int ifindex);
 uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
-                               const uint8_t *addr, const char *message,
-                               bool roaming);
+                               const uint8_t *addr, unsigned short vid,
+                               const char *message, bool roaming);
 int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset);
-void batadv_tt_global_add_orig(struct batadv_priv *bat_priv,
-                              struct batadv_orig_node *orig_node,
-                              const unsigned char *tt_buff, int tt_buff_len);
-int batadv_tt_global_add(struct batadv_priv *bat_priv,
-                        struct batadv_orig_node *orig_node,
-                        const unsigned char *addr, uint16_t flags,
-                        uint8_t ttvn);
 int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
 void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
                               struct batadv_orig_node *orig_node,
-                              const char *message);
+                              int32_t match_vid, const char *message);
 struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
                                                  const uint8_t *src,
-                                                 const uint8_t *addr);
+                                                 const uint8_t *addr,
+                                                 unsigned short vid);
 void batadv_tt_free(struct batadv_priv *bat_priv);
-bool batadv_send_tt_response(struct batadv_priv *bat_priv,
-                            struct batadv_tt_query_packet *tt_request);
-bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr);
-void batadv_handle_tt_response(struct batadv_priv *bat_priv,
-                              struct batadv_tt_query_packet *tt_response);
+bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr,
+                        unsigned short vid);
 bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
-                          uint8_t *dst);
-void batadv_tt_update_orig(struct batadv_priv *bat_priv,
-                          struct batadv_orig_node *orig_node,
-                          const unsigned char *tt_buff, uint8_t tt_num_changes,
-                          uint8_t ttvn, uint16_t tt_crc);
-int batadv_tt_append_diff(struct batadv_priv *bat_priv,
-                         unsigned char **packet_buff, int *packet_buff_len,
-                         int packet_min_len);
+                          uint8_t *dst, unsigned short vid);
+void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv);
 bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
-                                       uint8_t *addr);
+                                       uint8_t *addr, unsigned short vid);
 bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
-                                      uint8_t *addr);
+                                      uint8_t *addr, unsigned short vid);
+void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface);
 bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
                                          struct batadv_orig_node *orig_node,
-                                         const unsigned char *addr);
+                                         const unsigned char *addr,
+                                         unsigned short vid);
 
 #endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
index b2c94e1..91dd369 100644 (file)
 #include "bitarray.h"
 #include <linux/kernel.h>
 
-/**
- * Maximum overhead for the encapsulation for a payload packet
- */
-#define BATADV_HEADER_LEN \
-       (ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \
-                       sizeof(struct batadv_bcast_packet)))
-
 #ifdef CONFIG_BATMAN_ADV_DAT
 
 /* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed,
 
 #endif /* CONFIG_BATMAN_ADV_DAT */
 
+/**
+ * BATADV_TT_REMOTE_MASK - bitmask selecting the flags that are sent over the
+ *  wire only
+ */
+#define BATADV_TT_REMOTE_MASK  0x00FF
+
+/**
+ * BATADV_TT_SYNC_MASK - bitmask of the flags that need to be kept in sync
+ *  among the nodes. These flags are used to compute the global/local CRC
+ */
+#define BATADV_TT_SYNC_MASK    0x00F0
+
 /**
  * struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data
  * @ogm_buff: buffer holding the OGM packet
@@ -60,7 +65,6 @@ struct batadv_hard_iface_bat_iv {
  * @if_num: identificator of the interface
  * @if_status: status of the interface for batman-adv
  * @net_dev: pointer to the net_device
- * @frag_seqno: last fragment sequence number sent by this interface
  * @num_bcasts: number of payload re-broadcasts on this interface (ARQ)
  * @hardif_obj: kobject of the per interface sysfs "mesh" directory
  * @refcount: number of contexts the object is used
@@ -76,7 +80,6 @@ struct batadv_hard_iface {
        int16_t if_num;
        char if_status;
        struct net_device *net_dev;
-       atomic_t frag_seqno;
        uint8_t num_bcasts;
        struct kobject *hardif_obj;
        atomic_t refcount;
@@ -87,29 +90,98 @@ struct batadv_hard_iface {
        struct work_struct cleanup_work;
 };
 
+/**
+ * struct batadv_frag_table_entry - head in the fragment buffer table
+ * @head: head of list with fragments
+ * @lock: lock to protect the list of fragments
+ * @timestamp: time (jiffie) of last received fragment
+ * @seqno: sequence number of the fragments in the list
+ * @size: accumulated size of packets in list
+ */
+struct batadv_frag_table_entry {
+       struct hlist_head head;
+       spinlock_t lock; /* protects head */
+       unsigned long timestamp;
+       uint16_t seqno;
+       uint16_t size;
+};
+
+/**
+ * struct batadv_frag_list_entry - entry in a list of fragments
+ * @list: list node information
+ * @skb: fragment
+ * @no: fragment number in the set
+ */
+struct batadv_frag_list_entry {
+       struct hlist_node list;
+       struct sk_buff *skb;
+       uint8_t no;
+};
+
+/**
+ * struct batadv_vlan_tt - VLAN specific TT attributes
+ * @crc: CRC32 checksum of the entries belonging to this vlan
+ * @num_entries: number of TT entries for this VLAN
+ */
+struct batadv_vlan_tt {
+       uint32_t crc;
+       atomic_t num_entries;
+};
+
+/**
+ * batadv_orig_node_vlan - VLAN specific data per orig_node
+ * @vid: the VLAN identifier
+ * @tt: VLAN specific TT attributes
+ * @list: list node for orig_node::vlan_list
+ * @refcount: number of context where this object is currently in use
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_orig_node_vlan {
+       unsigned short vid;
+       struct batadv_vlan_tt tt;
+       struct list_head list;
+       atomic_t refcount;
+       struct rcu_head rcu;
+};
+
+/**
+ * struct batadv_orig_bat_iv - B.A.T.M.A.N. IV private orig_node members
+ * @bcast_own: bitfield containing the number of our OGMs this orig_node
+ *  rebroadcasted "back" to us (relative to last_real_seqno)
+ * @bcast_own_sum: counted result of bcast_own
+ * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum,
+ *  neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count
+ */
+struct batadv_orig_bat_iv {
+       unsigned long *bcast_own;
+       uint8_t *bcast_own_sum;
+       /* ogm_cnt_lock protects: bcast_own, bcast_own_sum,
+        * neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count
+        */
+       spinlock_t ogm_cnt_lock;
+};
+
 /**
  * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh
  * @orig: originator ethernet address
  * @primary_addr: hosts primary interface address
  * @router: router that should be used to reach this originator
  * @batadv_dat_addr_t:  address of the orig node in the distributed hash
- * @bcast_own: bitfield containing the number of our OGMs this orig_node
- *  rebroadcasted "back" to us (relative to last_real_seqno)
- * @bcast_own_sum: counted result of bcast_own
  * @last_seen: time when last packet from this node was received
  * @bcast_seqno_reset: time when the broadcast seqno window was reset
  * @batman_seqno_reset: time when the batman seqno window was reset
- * @gw_flags: flags related to gateway class
- * @flags: for now only VIS_SERVER flag
+ * @capabilities: announced capabilities of this originator
  * @last_ttvn: last seen translation table version number
- * @tt_crc: CRC of the translation table
  * @tt_buff: last tt changeset this node received from the orig node
  * @tt_buff_len: length of the last tt changeset this node received from the
  *  orig node
  * @tt_buff_lock: lock that protects tt_buff and tt_buff_len
- * @tt_size: number of global TT entries announced by the orig node
  * @tt_initialised: bool keeping track of whether or not this node have received
  *  any translation table information from the orig node yet
+ * @tt_lock: prevents from updating the table while reading it. Table update is
+ *  made up by two operations (data structure update and metdata -CRC/TTVN-
+ *  recalculation) and they have to be executed atomically in order to avoid
+ *  another thread to read the table/metadata between those.
  * @last_real_seqno: last and best known sequence number
  * @last_ttl: ttl of last received packet
  * @bcast_bits: bitfield containing the info which payload broadcast originated
@@ -117,14 +189,9 @@ struct batadv_hard_iface {
  *  last_bcast_seqno)
  * @last_bcast_seqno: last broadcast sequence number received by this host
  * @neigh_list: list of potential next hop neighbor towards this orig node
- * @frag_list: fragmentation buffer list for fragment re-assembly
- * @last_frag_packet: time when last fragmented packet from this node was
- *  received
  * @neigh_list_lock: lock protecting neigh_list, router and bonding_list
  * @hash_entry: hlist node for batadv_priv::orig_hash
  * @bat_priv: pointer to soft_iface this orig node belongs to
- * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum,
- *  neigh_node->real_bits & neigh_node->real_packet_count
  * @bcast_seqno_lock: lock protecting bcast_bits & last_bcast_seqno
  * @bond_candidates: how many candidates are available
  * @bond_list: list of bonding candidates
@@ -134,6 +201,11 @@ struct batadv_hard_iface {
  * @out_coding_list: list of nodes that can hear this orig
  * @in_coding_list_lock: protects in_coding_list
  * @out_coding_list_lock: protects out_coding_list
+ * @fragments: array with heads for fragment chains
+ * @vlan_list: a list of orig_node_vlan structs, one per VLAN served by the
+ *  originator represented by this object
+ * @vlan_list_lock: lock protecting vlan_list
+ * @bat_iv: B.A.T.M.A.N. IV private structure
  */
 struct batadv_orig_node {
        uint8_t orig[ETH_ALEN];
@@ -142,35 +214,26 @@ struct batadv_orig_node {
 #ifdef CONFIG_BATMAN_ADV_DAT
        batadv_dat_addr_t dat_addr;
 #endif
-       unsigned long *bcast_own;
-       uint8_t *bcast_own_sum;
        unsigned long last_seen;
        unsigned long bcast_seqno_reset;
        unsigned long batman_seqno_reset;
-       uint8_t gw_flags;
-       uint8_t flags;
+       uint8_t capabilities;
        atomic_t last_ttvn;
-       uint16_t tt_crc;
        unsigned char *tt_buff;
        int16_t tt_buff_len;
        spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */
-       atomic_t tt_size;
        bool tt_initialised;
+       /* prevents from changing the table while reading it */
+       spinlock_t tt_lock;
        uint32_t last_real_seqno;
        uint8_t last_ttl;
        DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
        uint32_t last_bcast_seqno;
        struct hlist_head neigh_list;
-       struct list_head frag_list;
-       unsigned long last_frag_packet;
        /* neigh_list_lock protects: neigh_list, router & bonding_list */
        spinlock_t neigh_list_lock;
        struct hlist_node hash_entry;
        struct batadv_priv *bat_priv;
-       /* ogm_cnt_lock protects: bcast_own, bcast_own_sum,
-        * neigh_node->real_bits & neigh_node->real_packet_count
-        */
-       spinlock_t ogm_cnt_lock;
        /* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */
        spinlock_t bcast_seqno_lock;
        atomic_t bond_candidates;
@@ -183,12 +246,28 @@ struct batadv_orig_node {
        spinlock_t in_coding_list_lock; /* Protects in_coding_list */
        spinlock_t out_coding_list_lock; /* Protects out_coding_list */
 #endif
+       struct batadv_frag_table_entry fragments[BATADV_FRAG_BUFFER_COUNT];
+       struct list_head vlan_list;
+       spinlock_t vlan_list_lock; /* protects vlan_list */
+       struct batadv_orig_bat_iv bat_iv;
+};
+
+/**
+ * enum batadv_orig_capabilities - orig node capabilities
+ * @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled
+ * @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled
+ */
+enum batadv_orig_capabilities {
+       BATADV_ORIG_CAPA_HAS_DAT = BIT(0),
+       BATADV_ORIG_CAPA_HAS_NC = BIT(1),
 };
 
 /**
  * struct batadv_gw_node - structure for orig nodes announcing gw capabilities
  * @list: list node for batadv_priv_gw::list
  * @orig_node: pointer to corresponding orig node
+ * @bandwidth_down: advertised uplink download bandwidth
+ * @bandwidth_up: advertised uplink upload bandwidth
  * @deleted: this struct is scheduled for deletion
  * @refcount: number of contexts the object is used
  * @rcu: struct used for freeing in an RCU-safe manner
@@ -196,46 +275,57 @@ struct batadv_orig_node {
 struct batadv_gw_node {
        struct hlist_node list;
        struct batadv_orig_node *orig_node;
+       uint32_t bandwidth_down;
+       uint32_t bandwidth_up;
        unsigned long deleted;
        atomic_t refcount;
        struct rcu_head rcu;
 };
 
 /**
- * struct batadv_neigh_node - structure for single hop neighbors
- * @list: list node for batadv_orig_node::neigh_list
- * @addr: mac address of neigh node
+ * struct batadv_neigh_bat_iv - B.A.T.M.A.N. IV specific structure for single
+ *  hop neighbors
  * @tq_recv: ring buffer of received TQ values from this neigh node
  * @tq_index: ring buffer index
  * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv)
- * @last_ttl: last received ttl from this neigh node
- * @bonding_list: list node for batadv_orig_node::bond_list
- * @last_seen: when last packet via this neighbor was received
  * @real_bits: bitfield containing the number of OGMs received from this neigh
  *  node (relative to orig_node->last_real_seqno)
  * @real_packet_count: counted result of real_bits
+ * @lq_update_lock: lock protecting tq_recv & tq_index
+ */
+struct batadv_neigh_bat_iv {
+       uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE];
+       uint8_t tq_index;
+       uint8_t tq_avg;
+       DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
+       uint8_t real_packet_count;
+       spinlock_t lq_update_lock; /* protects tq_recv & tq_index */
+};
+
+/**
+ * struct batadv_neigh_node - structure for single hops neighbors
+ * @list: list node for batadv_orig_node::neigh_list
  * @orig_node: pointer to corresponding orig_node
+ * @addr: the MAC address of the neighboring interface
  * @if_incoming: pointer to incoming hard interface
- * @lq_update_lock: lock protecting tq_recv & tq_index
+ * @last_seen: when last packet via this neighbor was received
+ * @last_ttl: last received ttl from this neigh node
+ * @bonding_list: list node for batadv_orig_node::bond_list
  * @refcount: number of contexts the object is used
  * @rcu: struct used for freeing in an RCU-safe manner
+ * @bat_iv: B.A.T.M.A.N. IV private structure
  */
 struct batadv_neigh_node {
        struct hlist_node list;
+       struct batadv_orig_node *orig_node;
        uint8_t addr[ETH_ALEN];
-       uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE];
-       uint8_t tq_index;
-       uint8_t tq_avg;
+       struct batadv_hard_iface *if_incoming;
+       unsigned long last_seen;
        uint8_t last_ttl;
        struct list_head bonding_list;
-       unsigned long last_seen;
-       DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
-       uint8_t real_packet_count;
-       struct batadv_orig_node *orig_node;
-       struct batadv_hard_iface *if_incoming;
-       spinlock_t lq_update_lock; /* protects tq_recv & tq_index */
        atomic_t refcount;
        struct rcu_head rcu;
+       struct batadv_neigh_bat_iv bat_iv;
 };
 
 /**
@@ -265,6 +355,12 @@ struct batadv_bcast_duplist_entry {
  * @BATADV_CNT_MGMT_TX_BYTES: transmitted routing protocol traffic bytes counter
  * @BATADV_CNT_MGMT_RX: received routing protocol traffic packet counter
  * @BATADV_CNT_MGMT_RX_BYTES: received routing protocol traffic bytes counter
+ * @BATADV_CNT_FRAG_TX: transmitted fragment traffic packet counter
+ * @BATADV_CNT_FRAG_TX_BYTES: transmitted fragment traffic bytes counter
+ * @BATADV_CNT_FRAG_RX: received fragment traffic packet counter
+ * @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter
+ * @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter
+ * @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter
  * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter
  * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter
  * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter
@@ -302,6 +398,12 @@ enum batadv_counters {
        BATADV_CNT_MGMT_TX_BYTES,
        BATADV_CNT_MGMT_RX,
        BATADV_CNT_MGMT_RX_BYTES,
+       BATADV_CNT_FRAG_TX,
+       BATADV_CNT_FRAG_TX_BYTES,
+       BATADV_CNT_FRAG_RX,
+       BATADV_CNT_FRAG_RX_BYTES,
+       BATADV_CNT_FRAG_FWD,
+       BATADV_CNT_FRAG_FWD_BYTES,
        BATADV_CNT_TT_REQUEST_TX,
        BATADV_CNT_TT_REQUEST_RX,
        BATADV_CNT_TT_RESPONSE_TX,
@@ -343,11 +445,14 @@ enum batadv_counters {
  * @changes_list_lock: lock protecting changes_list
  * @req_list_lock: lock protecting req_list
  * @roam_list_lock: lock protecting roam_list
- * @local_entry_num: number of entries in the local hash table
- * @local_crc: Checksum of the local table, recomputed before sending a new OGM
  * @last_changeset: last tt changeset this host has generated
  * @last_changeset_len: length of last tt changeset this host has generated
  * @last_changeset_lock: lock protecting last_changeset & last_changeset_len
+ * @commit_lock: prevents from executing a local TT commit while reading the
+ *  local table. The local TT commit is made up by two operations (data
+ *  structure update and metdata -CRC/TTVN- recalculation) and they have to be
+ *  executed atomically in order to avoid another thread to read the
+ *  table/metadata between those.
  * @work: work queue callback item for translation table purging
  */
 struct batadv_priv_tt {
@@ -362,12 +467,12 @@ struct batadv_priv_tt {
        spinlock_t changes_list_lock; /* protects changes */
        spinlock_t req_list_lock; /* protects req_list */
        spinlock_t roam_list_lock; /* protects roam_list */
-       atomic_t local_entry_num;
-       uint16_t local_crc;
        unsigned char *last_changeset;
        int16_t last_changeset_len;
        /* protects last_changeset & last_changeset_len */
        spinlock_t last_changeset_lock;
+       /* prevents from executing a commit while reading the table */
+       spinlock_t commit_lock;
        struct delayed_work work;
 };
 
@@ -420,31 +525,31 @@ struct batadv_priv_debug_log {
  * @list: list of available gateway nodes
  * @list_lock: lock protecting gw_list & curr_gw
  * @curr_gw: pointer to currently selected gateway node
+ * @bandwidth_down: advertised uplink download bandwidth (if gw_mode server)
+ * @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server)
  * @reselect: bool indicating a gateway re-selection is in progress
  */
 struct batadv_priv_gw {
        struct hlist_head list;
        spinlock_t list_lock; /* protects gw_list & curr_gw */
        struct batadv_gw_node __rcu *curr_gw;  /* rcu protected pointer */
+       atomic_t bandwidth_down;
+       atomic_t bandwidth_up;
        atomic_t reselect;
 };
 
 /**
- * struct batadv_priv_vis - per mesh interface vis data
- * @send_list: list of batadv_vis_info packets to sent
- * @hash: hash table containing vis data from other nodes in the network
- * @hash_lock: lock protecting the hash table
- * @list_lock: lock protecting my_info::recv_list
- * @work: work queue callback item for vis packet sending
- * @my_info: holds this node's vis data sent on a regular basis
+ * struct batadv_priv_tvlv - per mesh interface tvlv data
+ * @container_list: list of registered tvlv containers to be sent with each OGM
+ * @handler_list: list of the various tvlv content handlers
+ * @container_list_lock: protects tvlv container list access
+ * @handler_list_lock: protects handler list access
  */
-struct batadv_priv_vis {
-       struct list_head send_list;
-       struct batadv_hashtable *hash;
-       spinlock_t hash_lock; /* protects hash */
-       spinlock_t list_lock; /* protects my_info::recv_list */
-       struct delayed_work work;
-       struct batadv_vis_info *my_info;
+struct batadv_priv_tvlv {
+       struct hlist_head container_list;
+       struct hlist_head handler_list;
+       spinlock_t container_list_lock; /* protects container_list */
+       spinlock_t handler_list_lock; /* protects handler_list */
 };
 
 /**
@@ -490,6 +595,26 @@ struct batadv_priv_nc {
        struct batadv_hashtable *decoding_hash;
 };
 
+/**
+ * struct batadv_softif_vlan - per VLAN attributes set
+ * @vid: VLAN identifier
+ * @kobj: kobject for sysfs vlan subdirectory
+ * @ap_isolation: AP isolation state
+ * @tt: TT private attributes (VLAN specific)
+ * @list: list node for bat_priv::softif_vlan_list
+ * @refcount: number of context where this object is currently in use
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_softif_vlan {
+       unsigned short vid;
+       struct kobject *kobj;
+       atomic_t ap_isolation;          /* boolean */
+       struct batadv_vlan_tt tt;
+       struct hlist_node list;
+       atomic_t refcount;
+       struct rcu_head rcu;
+};
+
 /**
  * struct batadv_priv - per mesh interface data
  * @mesh_state: current status of the mesh (inactive/active/deactivating)
@@ -499,15 +624,15 @@ struct batadv_priv_nc {
  * @aggregated_ogms: bool indicating whether OGM aggregation is enabled
  * @bonding: bool indicating whether traffic bonding is enabled
  * @fragmentation: bool indicating whether traffic fragmentation is enabled
- * @ap_isolation: bool indicating whether ap isolation is enabled
+ * @packet_size_max: max packet size that can be transmitted via
+ *  multiple fragmented skbs or a single frame if fragmentation is disabled
+ * @frag_seqno: incremental counter to identify chains of egress fragments
  * @bridge_loop_avoidance: bool indicating whether bridge loop avoidance is
  *  enabled
  * @distributed_arp_table: bool indicating whether distributed ARP table is
  *  enabled
- * @vis_mode: vis operation: client or server (see batadv_vis_packettype)
  * @gw_mode: gateway operation: off, client or server (see batadv_gw_modes)
  * @gw_sel_class: gateway selection class (applies if gw_mode client)
- * @gw_bandwidth: gateway announced bandwidth (applies if gw_mode server)
  * @orig_interval: OGM broadcast interval in milliseconds
  * @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop
  * @log_level: configured log level (see batadv_dbg_level)
@@ -527,11 +652,14 @@ struct batadv_priv_nc {
  * @primary_if: one of the hard interfaces assigned to this mesh interface
  *  becomes the primary interface
  * @bat_algo_ops: routing algorithm used by this mesh interface
+ * @softif_vlan_list: a list of softif_vlan structs, one per VLAN created on top
+ *  of the mesh interface represented by this object
+ * @softif_vlan_list_lock: lock protecting softif_vlan_list
  * @bla: bridge loope avoidance data
  * @debug_log: holding debug logging relevant data
  * @gw: gateway data
  * @tt: translation table data
- * @vis: vis data
+ * @tvlv: type-version-length-value data
  * @dat: distributed arp table data
  * @network_coding: bool indicating whether network coding is enabled
  * @batadv_priv_nc: network coding data
@@ -544,17 +672,16 @@ struct batadv_priv {
        atomic_t aggregated_ogms;
        atomic_t bonding;
        atomic_t fragmentation;
-       atomic_t ap_isolation;
+       atomic_t packet_size_max;
+       atomic_t frag_seqno;
 #ifdef CONFIG_BATMAN_ADV_BLA
        atomic_t bridge_loop_avoidance;
 #endif
 #ifdef CONFIG_BATMAN_ADV_DAT
        atomic_t distributed_arp_table;
 #endif
-       atomic_t vis_mode;
        atomic_t gw_mode;
        atomic_t gw_sel_class;
-       atomic_t gw_bandwidth;
        atomic_t orig_interval;
        atomic_t hop_penalty;
 #ifdef CONFIG_BATMAN_ADV_DEBUG
@@ -575,6 +702,8 @@ struct batadv_priv {
        struct work_struct cleanup_work;
        struct batadv_hard_iface __rcu *primary_if;  /* rcu protected pointer */
        struct batadv_algo_ops *bat_algo_ops;
+       struct hlist_head softif_vlan_list;
+       spinlock_t softif_vlan_list_lock; /* protects softif_vlan_list */
 #ifdef CONFIG_BATMAN_ADV_BLA
        struct batadv_priv_bla bla;
 #endif
@@ -583,7 +712,7 @@ struct batadv_priv {
 #endif
        struct batadv_priv_gw gw;
        struct batadv_priv_tt tt;
-       struct batadv_priv_vis vis;
+       struct batadv_priv_tvlv tvlv;
 #ifdef CONFIG_BATMAN_ADV_DAT
        struct batadv_priv_dat dat;
 #endif
@@ -620,7 +749,7 @@ struct batadv_socket_client {
 struct batadv_socket_packet {
        struct list_head list;
        size_t icmp_len;
-       struct batadv_icmp_packet_rr icmp_packet;
+       uint8_t icmp_packet[BATADV_ICMP_MAX_PACKET_SIZE];
 };
 
 /**
@@ -677,6 +806,7 @@ struct batadv_bla_claim {
 /**
  * struct batadv_tt_common_entry - tt local & tt global common data
  * @addr: mac address of non-mesh client
+ * @vid: VLAN identifier
  * @hash_entry: hlist node for batadv_priv_tt::local_hash or for
  *  batadv_priv_tt::global_hash
  * @flags: various state handling flags (see batadv_tt_client_flags)
@@ -686,6 +816,7 @@ struct batadv_bla_claim {
  */
 struct batadv_tt_common_entry {
        uint8_t addr[ETH_ALEN];
+       unsigned short vid;
        struct hlist_node hash_entry;
        uint16_t flags;
        unsigned long added_at;
@@ -740,7 +871,7 @@ struct batadv_tt_orig_list_entry {
  */
 struct batadv_tt_change_node {
        struct list_head list;
-       struct batadv_tt_change change;
+       struct batadv_tvlv_tt_change change;
 };
 
 /**
@@ -865,78 +996,6 @@ struct batadv_forw_packet {
        struct batadv_hard_iface *if_incoming;
 };
 
-/**
- * struct batadv_frag_packet_list_entry - storage for fragment packet
- * @list: list node for orig_node::frag_list
- * @seqno: sequence number of the fragment
- * @skb: fragment's skb buffer
- */
-struct batadv_frag_packet_list_entry {
-       struct list_head list;
-       uint16_t seqno;
-       struct sk_buff *skb;
-};
-
-/**
- * struct batadv_vis_info - local data for vis information
- * @first_seen: timestamp used for purging stale vis info entries
- * @recv_list: List of server-neighbors we have received this packet from. This
- *  packet should not be re-forward to them again. List elements are struct
- *  batadv_vis_recvlist_node
- * @send_list: list of packets to be forwarded
- * @refcount: number of contexts the object is used
- * @hash_entry: hlist node for batadv_priv_vis::hash
- * @bat_priv: pointer to soft_iface this orig node belongs to
- * @skb_packet: contains the vis packet
- */
-struct batadv_vis_info {
-       unsigned long first_seen;
-       struct list_head recv_list;
-       struct list_head send_list;
-       struct kref refcount;
-       struct hlist_node hash_entry;
-       struct batadv_priv *bat_priv;
-       struct sk_buff *skb_packet;
-} __packed;
-
-/**
- * struct batadv_vis_info_entry - contains link information for vis
- * @src: source MAC of the link, all zero for local TT entry
- * @dst: destination MAC of the link, client mac address for local TT entry
- * @quality: transmission quality of the link, or 0 for local TT entry
- */
-struct batadv_vis_info_entry {
-       uint8_t  src[ETH_ALEN];
-       uint8_t  dest[ETH_ALEN];
-       uint8_t  quality;
-} __packed;
-
-/**
- * struct batadv_vis_recvlist_node - list entry for batadv_vis_info::recv_list
- * @list: list node for batadv_vis_info::recv_list
- * @mac: MAC address of the originator from where the vis_info was received
- */
-struct batadv_vis_recvlist_node {
-       struct list_head list;
-       uint8_t mac[ETH_ALEN];
-};
-
-/**
- * struct batadv_vis_if_list_entry - auxiliary data for vis data generation
- * @addr: MAC address of the interface
- * @primary: true if this interface is the primary interface
- * @list: list node the interface list
- *
- * While scanning for vis-entries of a particular vis-originator
- * this list collects its interfaces to create a subgraph/cluster
- * out of them later
- */
-struct batadv_vis_if_list_entry {
-       uint8_t addr[ETH_ALEN];
-       bool primary;
-       struct hlist_node list;
-};
-
 /**
  * struct batadv_algo_ops - mesh algorithm callbacks
  * @list: list node for the batadv_algo_list
@@ -948,6 +1007,16 @@ struct batadv_vis_if_list_entry {
  * @bat_primary_iface_set: called when primary interface is selected / changed
  * @bat_ogm_schedule: prepare a new outgoing OGM for the send queue
  * @bat_ogm_emit: send scheduled OGM
+ * @bat_neigh_cmp: compare the metrics of two neighbors
+ * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or
+ *  better than neigh2 from the metric prospective
+ * @bat_orig_print: print the originator table (optional)
+ * @bat_orig_free: free the resources allocated by the routing algorithm for an
+ *  orig_node object
+ * @bat_orig_add_if: ask the routing algorithm to apply the needed changes to
+ *  the orig_node due to a new hard-interface being added into the mesh
+ * @bat_orig_del_if: ask the routing algorithm to apply the needed changes to
+ *  the orig_node due to an hard-interface being removed from the mesh
  */
 struct batadv_algo_ops {
        struct hlist_node list;
@@ -958,6 +1027,17 @@ struct batadv_algo_ops {
        void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface);
        void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface);
        void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
+       int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1,
+                            struct batadv_neigh_node *neigh2);
+       bool (*bat_neigh_is_equiv_or_better)(struct batadv_neigh_node *neigh1,
+                                            struct batadv_neigh_node *neigh2);
+       /* orig_node handling API */
+       void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq);
+       void (*bat_orig_free)(struct batadv_orig_node *orig_node);
+       int (*bat_orig_add_if)(struct batadv_orig_node *orig_node,
+                              int max_if_num);
+       int (*bat_orig_del_if)(struct batadv_orig_node *orig_node,
+                              int max_if_num, int del_if_num);
 };
 
 /**
@@ -965,6 +1045,7 @@ struct batadv_algo_ops {
  * is used to stored ARP entries needed for the global DAT cache
  * @ip: the IPv4 corresponding to this DAT/ARP entry
  * @mac_addr: the MAC address associated to the stored IPv4
+ * @vid: the vlan ID associated to this entry
  * @last_update: time in jiffies when this entry was refreshed last time
  * @hash_entry: hlist node for batadv_priv_dat::hash
  * @refcount: number of contexts the object is used
@@ -973,6 +1054,7 @@ struct batadv_algo_ops {
 struct batadv_dat_entry {
        __be32 ip;
        uint8_t mac_addr[ETH_ALEN];
+       unsigned short vid;
        unsigned long last_update;
        struct hlist_node hash_entry;
        atomic_t refcount;
@@ -992,4 +1074,60 @@ struct batadv_dat_candidate {
        struct batadv_orig_node *orig_node;
 };
 
+/**
+ * struct batadv_tvlv_container - container for tvlv appended to OGMs
+ * @list: hlist node for batadv_priv_tvlv::container_list
+ * @tvlv_hdr: tvlv header information needed to construct the tvlv
+ * @value_len: length of the buffer following this struct which contains
+ *  the actual tvlv payload
+ * @refcount: number of contexts the object is used
+ */
+struct batadv_tvlv_container {
+       struct hlist_node list;
+       struct batadv_tvlv_hdr tvlv_hdr;
+       atomic_t refcount;
+};
+
+/**
+ * struct batadv_tvlv_handler - handler for specific tvlv type and version
+ * @list: hlist node for batadv_priv_tvlv::handler_list
+ * @ogm_handler: handler callback which is given the tvlv payload to process on
+ *  incoming OGM packets
+ * @unicast_handler: handler callback which is given the tvlv payload to process
+ *  on incoming unicast tvlv packets
+ * @type: tvlv type this handler feels responsible for
+ * @version: tvlv version this handler feels responsible for
+ * @flags: tvlv handler flags
+ * @refcount: number of contexts the object is used
+ * @rcu: struct used for freeing in an RCU-safe manner
+ */
+struct batadv_tvlv_handler {
+       struct hlist_node list;
+       void (*ogm_handler)(struct batadv_priv *bat_priv,
+                           struct batadv_orig_node *orig,
+                           uint8_t flags,
+                           void *tvlv_value, uint16_t tvlv_value_len);
+       int (*unicast_handler)(struct batadv_priv *bat_priv,
+                              uint8_t *src, uint8_t *dst,
+                              void *tvlv_value, uint16_t tvlv_value_len);
+       uint8_t type;
+       uint8_t version;
+       uint8_t flags;
+       atomic_t refcount;
+       struct rcu_head rcu;
+};
+
+/**
+ * enum batadv_tvlv_handler_flags - tvlv handler flags definitions
+ * @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call
+ *  this handler even if its type was not found (with no data)
+ * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks
+ *  a handler as being called, so it won't be called if the
+ *  BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+ */
+enum batadv_tvlv_handler_flags {
+       BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+       BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+};
+
 #endif /* _NET_BATMAN_ADV_TYPES_H_ */
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c
deleted file mode 100644 (file)
index 48b31d3..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors:
- *
- * Andreas Langer
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include "main.h"
-#include "unicast.h"
-#include "send.h"
-#include "soft-interface.h"
-#include "gateway_client.h"
-#include "originator.h"
-#include "hash.h"
-#include "translation-table.h"
-#include "routing.h"
-#include "hard-interface.h"
-
-
-static struct sk_buff *
-batadv_frag_merge_packet(struct list_head *head,
-                        struct batadv_frag_packet_list_entry *tfp,
-                        struct sk_buff *skb)
-{
-       struct batadv_unicast_frag_packet *up;
-       struct sk_buff *tmp_skb;
-       struct batadv_unicast_packet *unicast_packet;
-       int hdr_len = sizeof(*unicast_packet);
-       int uni_diff = sizeof(*up) - hdr_len;
-       uint8_t *packet_pos;
-
-       up = (struct batadv_unicast_frag_packet *)skb->data;
-       /* set skb to the first part and tmp_skb to the second part */
-       if (up->flags & BATADV_UNI_FRAG_HEAD) {
-               tmp_skb = tfp->skb;
-       } else {
-               tmp_skb = skb;
-               skb = tfp->skb;
-       }
-
-       if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0)
-               goto err;
-
-       skb_pull(tmp_skb, sizeof(*up));
-       if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0)
-               goto err;
-
-       /* move free entry to end */
-       tfp->skb = NULL;
-       tfp->seqno = 0;
-       list_move_tail(&tfp->list, head);
-
-       memcpy(skb_put(skb, tmp_skb->len), tmp_skb->data, tmp_skb->len);
-       kfree_skb(tmp_skb);
-
-       memmove(skb->data + uni_diff, skb->data, hdr_len);
-       packet_pos = skb_pull(skb, uni_diff);
-       unicast_packet = (struct batadv_unicast_packet *)packet_pos;
-       unicast_packet->header.packet_type = BATADV_UNICAST;
-
-       return skb;
-
-err:
-       /* free buffered skb, skb will be freed later */
-       kfree_skb(tfp->skb);
-       return NULL;
-}
-
-static void batadv_frag_create_entry(struct list_head *head,
-                                    struct sk_buff *skb)
-{
-       struct batadv_frag_packet_list_entry *tfp;
-       struct batadv_unicast_frag_packet *up;
-
-       up = (struct batadv_unicast_frag_packet *)skb->data;
-
-       /* free and oldest packets stand at the end */
-       tfp = list_entry((head)->prev, typeof(*tfp), list);
-       kfree_skb(tfp->skb);
-
-       tfp->seqno = ntohs(up->seqno);
-       tfp->skb = skb;
-       list_move(&tfp->list, head);
-       return;
-}
-
-static int batadv_frag_create_buffer(struct list_head *head)
-{
-       int i;
-       struct batadv_frag_packet_list_entry *tfp;
-
-       for (i = 0; i < BATADV_FRAG_BUFFER_SIZE; i++) {
-               tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC);
-               if (!tfp) {
-                       batadv_frag_list_free(head);
-                       return -ENOMEM;
-               }
-               tfp->skb = NULL;
-               tfp->seqno = 0;
-               INIT_LIST_HEAD(&tfp->list);
-               list_add(&tfp->list, head);
-       }
-
-       return 0;
-}
-
-static struct batadv_frag_packet_list_entry *
-batadv_frag_search_packet(struct list_head *head,
-                         const struct batadv_unicast_frag_packet *up)
-{
-       struct batadv_frag_packet_list_entry *tfp;
-       struct batadv_unicast_frag_packet *tmp_up = NULL;
-       bool is_head_tmp, is_head;
-       uint16_t search_seqno;
-
-       if (up->flags & BATADV_UNI_FRAG_HEAD)
-               search_seqno = ntohs(up->seqno)+1;
-       else
-               search_seqno = ntohs(up->seqno)-1;
-
-       is_head = up->flags & BATADV_UNI_FRAG_HEAD;
-
-       list_for_each_entry(tfp, head, list) {
-               if (!tfp->skb)
-                       continue;
-
-               if (tfp->seqno == ntohs(up->seqno))
-                       goto mov_tail;
-
-               tmp_up = (struct batadv_unicast_frag_packet *)tfp->skb->data;
-
-               if (tfp->seqno == search_seqno) {
-                       is_head_tmp = tmp_up->flags & BATADV_UNI_FRAG_HEAD;
-                       if (is_head_tmp != is_head)
-                               return tfp;
-                       else
-                               goto mov_tail;
-               }
-       }
-       return NULL;
-
-mov_tail:
-       list_move_tail(&tfp->list, head);
-       return NULL;
-}
-
-void batadv_frag_list_free(struct list_head *head)
-{
-       struct batadv_frag_packet_list_entry *pf, *tmp_pf;
-
-       if (!list_empty(head)) {
-               list_for_each_entry_safe(pf, tmp_pf, head, list) {
-                       kfree_skb(pf->skb);
-                       list_del(&pf->list);
-                       kfree(pf);
-               }
-       }
-       return;
-}
-
-/* frag_reassemble_skb():
- * returns NET_RX_DROP if the operation failed - skb is left intact
- * returns NET_RX_SUCCESS if the fragment was buffered (skb_new will be NULL)
- * or the skb could be reassembled (skb_new will point to the new packet and
- * skb was freed)
- */
-int batadv_frag_reassemble_skb(struct sk_buff *skb,
-                              struct batadv_priv *bat_priv,
-                              struct sk_buff **new_skb)
-{
-       struct batadv_orig_node *orig_node;
-       struct batadv_frag_packet_list_entry *tmp_frag_entry;
-       int ret = NET_RX_DROP;
-       struct batadv_unicast_frag_packet *unicast_packet;
-
-       unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
-       *new_skb = NULL;
-
-       orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->orig);
-       if (!orig_node)
-               goto out;
-
-       orig_node->last_frag_packet = jiffies;
-
-       if (list_empty(&orig_node->frag_list) &&
-           batadv_frag_create_buffer(&orig_node->frag_list)) {
-               pr_debug("couldn't create frag buffer\n");
-               goto out;
-       }
-
-       tmp_frag_entry = batadv_frag_search_packet(&orig_node->frag_list,
-                                                  unicast_packet);
-
-       if (!tmp_frag_entry) {
-               batadv_frag_create_entry(&orig_node->frag_list, skb);
-               ret = NET_RX_SUCCESS;
-               goto out;
-       }
-
-       *new_skb = batadv_frag_merge_packet(&orig_node->frag_list,
-                                           tmp_frag_entry, skb);
-       /* if not, merge failed */
-       if (*new_skb)
-               ret = NET_RX_SUCCESS;
-
-out:
-       if (orig_node)
-               batadv_orig_node_free_ref(orig_node);
-       return ret;
-}
-
-int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv,
-                        struct batadv_hard_iface *hard_iface,
-                        const uint8_t dstaddr[])
-{
-       struct batadv_unicast_packet tmp_uc, *unicast_packet;
-       struct batadv_hard_iface *primary_if;
-       struct sk_buff *frag_skb;
-       struct batadv_unicast_frag_packet *frag1, *frag2;
-       int uc_hdr_len = sizeof(*unicast_packet);
-       int ucf_hdr_len = sizeof(*frag1);
-       int data_len = skb->len - uc_hdr_len;
-       int large_tail = 0, ret = NET_RX_DROP;
-       uint16_t seqno;
-
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto dropped;
-
-       frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len);
-       if (!frag_skb)
-               goto dropped;
-
-       skb->priority = TC_PRIO_CONTROL;
-       skb_reserve(frag_skb, ucf_hdr_len);
-
-       unicast_packet = (struct batadv_unicast_packet *)skb->data;
-       memcpy(&tmp_uc, unicast_packet, uc_hdr_len);
-       skb_split(skb, frag_skb, data_len / 2 + uc_hdr_len);
-
-       if (batadv_skb_head_push(skb, ucf_hdr_len - uc_hdr_len) < 0 ||
-           batadv_skb_head_push(frag_skb, ucf_hdr_len) < 0)
-               goto drop_frag;
-
-       frag1 = (struct batadv_unicast_frag_packet *)skb->data;
-       frag2 = (struct batadv_unicast_frag_packet *)frag_skb->data;
-
-       memcpy(frag1, &tmp_uc, sizeof(tmp_uc));
-
-       frag1->header.ttl--;
-       frag1->header.version = BATADV_COMPAT_VERSION;
-       frag1->header.packet_type = BATADV_UNICAST_FRAG;
-
-       memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
-       memcpy(frag2, frag1, sizeof(*frag2));
-
-       if (data_len & 1)
-               large_tail = BATADV_UNI_FRAG_LARGETAIL;
-
-       frag1->flags = BATADV_UNI_FRAG_HEAD | large_tail;
-       frag2->flags = large_tail;
-
-       seqno = atomic_add_return(2, &hard_iface->frag_seqno);
-       frag1->seqno = htons(seqno - 1);
-       frag2->seqno = htons(seqno);
-
-       batadv_send_skb_packet(skb, hard_iface, dstaddr);
-       batadv_send_skb_packet(frag_skb, hard_iface, dstaddr);
-       ret = NET_RX_SUCCESS;
-       goto out;
-
-drop_frag:
-       kfree_skb(frag_skb);
-dropped:
-       kfree_skb(skb);
-out:
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-       return ret;
-}
-
-/**
- * batadv_unicast_push_and_fill_skb - extends the buffer and initializes the
- * common fields for unicast packets
- * @skb: packet
- * @hdr_size: amount of bytes to push at the beginning of the skb
- * @orig_node: the destination node
- *
- * Returns false if the buffer extension was not possible or true otherwise
- */
-static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size,
-                                            struct batadv_orig_node *orig_node)
-{
-       struct batadv_unicast_packet *unicast_packet;
-       uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
-
-       if (batadv_skb_head_push(skb, hdr_size) < 0)
-               return false;
-
-       unicast_packet = (struct batadv_unicast_packet *)skb->data;
-       unicast_packet->header.version = BATADV_COMPAT_VERSION;
-       /* batman packet type: unicast */
-       unicast_packet->header.packet_type = BATADV_UNICAST;
-       /* set unicast ttl */
-       unicast_packet->header.ttl = BATADV_TTL;
-       /* copy the destination for faster routing */
-       memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
-       /* set the destination tt version number */
-       unicast_packet->ttvn = ttvn;
-
-       return true;
-}
-
-/**
- * batadv_unicast_prepare_skb - encapsulate an skb with a unicast header
- * @skb: the skb containing the payload to encapsulate
- * @orig_node: the destination node
- *
- * Returns false if the payload could not be encapsulated or true otherwise.
- *
- * This call might reallocate skb data.
- */
-static bool batadv_unicast_prepare_skb(struct sk_buff *skb,
-                                      struct batadv_orig_node *orig_node)
-{
-       size_t uni_size = sizeof(struct batadv_unicast_packet);
-       return batadv_unicast_push_and_fill_skb(skb, uni_size, orig_node);
-}
-
-/**
- * batadv_unicast_4addr_prepare_skb - encapsulate an skb with a unicast4addr
- * header
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: the skb containing the payload to encapsulate
- * @orig_node: the destination node
- * @packet_subtype: the batman 4addr packet subtype to use
- *
- * Returns false if the payload could not be encapsulated or true otherwise.
- *
- * This call might reallocate skb data.
- */
-bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv,
-                                     struct sk_buff *skb,
-                                     struct batadv_orig_node *orig,
-                                     int packet_subtype)
-{
-       struct batadv_hard_iface *primary_if;
-       struct batadv_unicast_4addr_packet *unicast_4addr_packet;
-       bool ret = false;
-
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-
-       /* pull the header space and fill the unicast_packet substructure.
-        * We can do that because the first member of the unicast_4addr_packet
-        * is of type struct unicast_packet
-        */
-       if (!batadv_unicast_push_and_fill_skb(skb,
-                                             sizeof(*unicast_4addr_packet),
-                                             orig))
-               goto out;
-
-       unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
-       unicast_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR;
-       memcpy(unicast_4addr_packet->src, primary_if->net_dev->dev_addr,
-              ETH_ALEN);
-       unicast_4addr_packet->subtype = packet_subtype;
-       unicast_4addr_packet->reserved = 0;
-
-       ret = true;
-out:
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-       return ret;
-}
-
-/**
- * batadv_unicast_generic_send_skb - send an skb as unicast
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: payload to send
- * @packet_type: the batman unicast packet type to use
- * @packet_subtype: the batman packet subtype. It is ignored if packet_type is
- *                 not BATADV_UNICAT_4ADDR
- *
- * Returns 1 in case of error or 0 otherwise
- */
-int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv,
-                                   struct sk_buff *skb, int packet_type,
-                                   int packet_subtype)
-{
-       struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
-       struct batadv_unicast_packet *unicast_packet;
-       struct batadv_orig_node *orig_node;
-       struct batadv_neigh_node *neigh_node;
-       int data_len = skb->len;
-       int ret = NET_RX_DROP;
-       unsigned int dev_mtu, header_len;
-
-       /* get routing information */
-       if (is_multicast_ether_addr(ethhdr->h_dest)) {
-               orig_node = batadv_gw_get_selected_orig(bat_priv);
-               if (orig_node)
-                       goto find_router;
-       }
-
-       /* check for tt host - increases orig_node refcount.
-        * returns NULL in case of AP isolation
-        */
-       orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
-                                            ethhdr->h_dest);
-
-find_router:
-       /* find_router():
-        *  - if orig_node is NULL it returns NULL
-        *  - increases neigh_nodes refcount if found.
-        */
-       neigh_node = batadv_find_router(bat_priv, orig_node, NULL);
-
-       if (!neigh_node)
-               goto out;
-
-       switch (packet_type) {
-       case BATADV_UNICAST:
-               if (!batadv_unicast_prepare_skb(skb, orig_node))
-                       goto out;
-
-               header_len = sizeof(struct batadv_unicast_packet);
-               break;
-       case BATADV_UNICAST_4ADDR:
-               if (!batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
-                                                     packet_subtype))
-                       goto out;
-
-               header_len = sizeof(struct batadv_unicast_4addr_packet);
-               break;
-       default:
-               /* this function supports UNICAST and UNICAST_4ADDR only. It
-                * should never be invoked with any other packet type
-                */
-               goto out;
-       }
-
-       ethhdr = (struct ethhdr *)(skb->data + header_len);
-       unicast_packet = (struct batadv_unicast_packet *)skb->data;
-
-       /* inform the destination node that we are still missing a correct route
-        * for this client. The destination will receive this packet and will
-        * try to reroute it because the ttvn contained in the header is less
-        * than the current one
-        */
-       if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest))
-               unicast_packet->ttvn = unicast_packet->ttvn - 1;
-
-       dev_mtu = neigh_node->if_incoming->net_dev->mtu;
-       /* fragmentation mechanism only works for UNICAST (now) */
-       if (packet_type == BATADV_UNICAST &&
-           atomic_read(&bat_priv->fragmentation) &&
-           data_len + sizeof(*unicast_packet) > dev_mtu) {
-               /* send frag skb decreases ttl */
-               unicast_packet->header.ttl++;
-               ret = batadv_frag_send_skb(skb, bat_priv,
-                                          neigh_node->if_incoming,
-                                          neigh_node->addr);
-               goto out;
-       }
-
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
-               ret = 0;
-
-out:
-       if (neigh_node)
-               batadv_neigh_node_free_ref(neigh_node);
-       if (orig_node)
-               batadv_orig_node_free_ref(orig_node);
-       if (ret == NET_RX_DROP)
-               kfree_skb(skb);
-       return ret;
-}
diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h
deleted file mode 100644 (file)
index 429cf8a..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors:
- *
- * Andreas Langer
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef _NET_BATMAN_ADV_UNICAST_H_
-#define _NET_BATMAN_ADV_UNICAST_H_
-
-#include "packet.h"
-
-#define BATADV_FRAG_TIMEOUT 10000 /* purge frag list entries after time in ms */
-#define BATADV_FRAG_BUFFER_SIZE 6 /* number of list elements in buffer */
-
-int batadv_frag_reassemble_skb(struct sk_buff *skb,
-                              struct batadv_priv *bat_priv,
-                              struct sk_buff **new_skb);
-void batadv_frag_list_free(struct list_head *head);
-int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv,
-                        struct batadv_hard_iface *hard_iface,
-                        const uint8_t dstaddr[]);
-bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv,
-                                     struct sk_buff *skb,
-                                     struct batadv_orig_node *orig_node,
-                                     int packet_subtype);
-int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv,
-                                   struct sk_buff *skb, int packet_type,
-                                   int packet_subtype);
-
-
-/**
- * batadv_unicast_send_skb - send the skb encapsulated in a unicast packet
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: the payload to send
- */
-static inline int batadv_unicast_send_skb(struct batadv_priv *bat_priv,
-                                         struct sk_buff *skb)
-{
-       return batadv_unicast_generic_send_skb(bat_priv, skb, BATADV_UNICAST,
-                                              0);
-}
-
-/**
- * batadv_unicast_send_skb - send the skb encapsulated in a unicast4addr packet
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: the payload to send
- * @packet_subtype: the batman 4addr packet subtype to use
- */
-static inline int batadv_unicast_4addr_send_skb(struct batadv_priv *bat_priv,
-                                               struct sk_buff *skb,
-                                               int packet_subtype)
-{
-       return batadv_unicast_generic_send_skb(bat_priv, skb,
-                                              BATADV_UNICAST_4ADDR,
-                                              packet_subtype);
-}
-
-static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu)
-{
-       const struct batadv_unicast_frag_packet *unicast_packet;
-       int uneven_correction = 0;
-       unsigned int merged_size;
-
-       unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
-
-       if (unicast_packet->flags & BATADV_UNI_FRAG_LARGETAIL) {
-               if (unicast_packet->flags & BATADV_UNI_FRAG_HEAD)
-                       uneven_correction = 1;
-               else
-                       uneven_correction = -1;
-       }
-
-       merged_size = (skb->len - sizeof(*unicast_packet)) * 2;
-       merged_size += sizeof(struct batadv_unicast_packet) + uneven_correction;
-
-       return merged_size <= mtu;
-}
-
-#endif /* _NET_BATMAN_ADV_UNICAST_H_ */
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
deleted file mode 100644 (file)
index d8ea31a..0000000
+++ /dev/null
@@ -1,938 +0,0 @@
-/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors:
- *
- * Simon Wunderlich
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include "main.h"
-#include "send.h"
-#include "translation-table.h"
-#include "vis.h"
-#include "soft-interface.h"
-#include "hard-interface.h"
-#include "hash.h"
-#include "originator.h"
-
-#define BATADV_MAX_VIS_PACKET_SIZE 1000
-
-/* hash class keys */
-static struct lock_class_key batadv_vis_hash_lock_class_key;
-
-/* free the info */
-static void batadv_free_info(struct kref *ref)
-{
-       struct batadv_vis_info *info;
-       struct batadv_priv *bat_priv;
-       struct batadv_vis_recvlist_node *entry, *tmp;
-
-       info = container_of(ref, struct batadv_vis_info, refcount);
-       bat_priv = info->bat_priv;
-
-       list_del_init(&info->send_list);
-       spin_lock_bh(&bat_priv->vis.list_lock);
-       list_for_each_entry_safe(entry, tmp, &info->recv_list, list) {
-               list_del(&entry->list);
-               kfree(entry);
-       }
-
-       spin_unlock_bh(&bat_priv->vis.list_lock);
-       kfree_skb(info->skb_packet);
-       kfree(info);
-}
-
-/* Compare two vis packets, used by the hashing algorithm */
-static int batadv_vis_info_cmp(const struct hlist_node *node, const void *data2)
-{
-       const struct batadv_vis_info *d1, *d2;
-       const struct batadv_vis_packet *p1, *p2;
-
-       d1 = container_of(node, struct batadv_vis_info, hash_entry);
-       d2 = data2;
-       p1 = (struct batadv_vis_packet *)d1->skb_packet->data;
-       p2 = (struct batadv_vis_packet *)d2->skb_packet->data;
-       return batadv_compare_eth(p1->vis_orig, p2->vis_orig);
-}
-
-/* hash function to choose an entry in a hash table of given size
- * hash algorithm from http://en.wikipedia.org/wiki/Hash_table
- */
-static uint32_t batadv_vis_info_choose(const void *data, uint32_t size)
-{
-       const struct batadv_vis_info *vis_info = data;
-       const struct batadv_vis_packet *packet;
-       const unsigned char *key;
-       uint32_t hash = 0;
-       size_t i;
-
-       packet = (struct batadv_vis_packet *)vis_info->skb_packet->data;
-       key = packet->vis_orig;
-       for (i = 0; i < ETH_ALEN; i++) {
-               hash += key[i];
-               hash += (hash << 10);
-               hash ^= (hash >> 6);
-       }
-
-       hash += (hash << 3);
-       hash ^= (hash >> 11);
-       hash += (hash << 15);
-
-       return hash % size;
-}
-
-static struct batadv_vis_info *
-batadv_vis_hash_find(struct batadv_priv *bat_priv, const void *data)
-{
-       struct batadv_hashtable *hash = bat_priv->vis.hash;
-       struct hlist_head *head;
-       struct batadv_vis_info *vis_info, *vis_info_tmp = NULL;
-       uint32_t index;
-
-       if (!hash)
-               return NULL;
-
-       index = batadv_vis_info_choose(data, hash->size);
-       head = &hash->table[index];
-
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(vis_info, head, hash_entry) {
-               if (!batadv_vis_info_cmp(&vis_info->hash_entry, data))
-                       continue;
-
-               vis_info_tmp = vis_info;
-               break;
-       }
-       rcu_read_unlock();
-
-       return vis_info_tmp;
-}
-
-/* insert interface to the list of interfaces of one originator, if it
- * does not already exist in the list
- */
-static void batadv_vis_data_insert_interface(const uint8_t *interface,
-                                            struct hlist_head *if_list,
-                                            bool primary)
-{
-       struct batadv_vis_if_list_entry *entry;
-
-       hlist_for_each_entry(entry, if_list, list) {
-               if (batadv_compare_eth(entry->addr, interface))
-                       return;
-       }
-
-       /* it's a new address, add it to the list */
-       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
-       if (!entry)
-               return;
-       memcpy(entry->addr, interface, ETH_ALEN);
-       entry->primary = primary;
-       hlist_add_head(&entry->list, if_list);
-}
-
-static void batadv_vis_data_read_prim_sec(struct seq_file *seq,
-                                         const struct hlist_head *if_list)
-{
-       struct batadv_vis_if_list_entry *entry;
-
-       hlist_for_each_entry(entry, if_list, list) {
-               if (entry->primary)
-                       seq_puts(seq, "PRIMARY, ");
-               else
-                       seq_printf(seq,  "SEC %pM, ", entry->addr);
-       }
-}
-
-/* read an entry  */
-static ssize_t
-batadv_vis_data_read_entry(struct seq_file *seq,
-                          const struct batadv_vis_info_entry *entry,
-                          const uint8_t *src, bool primary)
-{
-       if (primary && entry->quality == 0)
-               return seq_printf(seq, "TT %pM, ", entry->dest);
-       else if (batadv_compare_eth(entry->src, src))
-               return seq_printf(seq, "TQ %pM %d, ", entry->dest,
-                                 entry->quality);
-
-       return 0;
-}
-
-static void
-batadv_vis_data_insert_interfaces(struct hlist_head *list,
-                                 struct batadv_vis_packet *packet,
-                                 struct batadv_vis_info_entry *entries)
-{
-       int i;
-
-       for (i = 0; i < packet->entries; i++) {
-               if (entries[i].quality == 0)
-                       continue;
-
-               if (batadv_compare_eth(entries[i].src, packet->vis_orig))
-                       continue;
-
-               batadv_vis_data_insert_interface(entries[i].src, list, false);
-       }
-}
-
-static void batadv_vis_data_read_entries(struct seq_file *seq,
-                                        struct hlist_head *list,
-                                        struct batadv_vis_packet *packet,
-                                        struct batadv_vis_info_entry *entries)
-{
-       int i;
-       struct batadv_vis_if_list_entry *entry;
-
-       hlist_for_each_entry(entry, list, list) {
-               seq_printf(seq, "%pM,", entry->addr);
-
-               for (i = 0; i < packet->entries; i++)
-                       batadv_vis_data_read_entry(seq, &entries[i],
-                                                  entry->addr, entry->primary);
-
-               /* add primary/secondary records */
-               if (batadv_compare_eth(entry->addr, packet->vis_orig))
-                       batadv_vis_data_read_prim_sec(seq, list);
-
-               seq_puts(seq, "\n");
-       }
-}
-
-static void batadv_vis_seq_print_text_bucket(struct seq_file *seq,
-                                            const struct hlist_head *head)
-{
-       struct batadv_vis_info *info;
-       struct batadv_vis_packet *packet;
-       uint8_t *entries_pos;
-       struct batadv_vis_info_entry *entries;
-       struct batadv_vis_if_list_entry *entry;
-       struct hlist_node *n;
-
-       HLIST_HEAD(vis_if_list);
-
-       hlist_for_each_entry_rcu(info, head, hash_entry) {
-               packet = (struct batadv_vis_packet *)info->skb_packet->data;
-               entries_pos = (uint8_t *)packet + sizeof(*packet);
-               entries = (struct batadv_vis_info_entry *)entries_pos;
-
-               batadv_vis_data_insert_interface(packet->vis_orig, &vis_if_list,
-                                                true);
-               batadv_vis_data_insert_interfaces(&vis_if_list, packet,
-                                                 entries);
-               batadv_vis_data_read_entries(seq, &vis_if_list, packet,
-                                            entries);
-
-               hlist_for_each_entry_safe(entry, n, &vis_if_list, list) {
-                       hlist_del(&entry->list);
-                       kfree(entry);
-               }
-       }
-}
-
-int batadv_vis_seq_print_text(struct seq_file *seq, void *offset)
-{
-       struct batadv_hard_iface *primary_if;
-       struct hlist_head *head;
-       struct net_device *net_dev = (struct net_device *)seq->private;
-       struct batadv_priv *bat_priv = netdev_priv(net_dev);
-       struct batadv_hashtable *hash = bat_priv->vis.hash;
-       uint32_t i;
-       int ret = 0;
-       int vis_server = atomic_read(&bat_priv->vis_mode);
-
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-
-       if (vis_server == BATADV_VIS_TYPE_CLIENT_UPDATE)
-               goto out;
-
-       spin_lock_bh(&bat_priv->vis.hash_lock);
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-               batadv_vis_seq_print_text_bucket(seq, head);
-       }
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-
-out:
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-       return ret;
-}
-
-/* add the info packet to the send list, if it was not
- * already linked in.
- */
-static void batadv_send_list_add(struct batadv_priv *bat_priv,
-                                struct batadv_vis_info *info)
-{
-       if (list_empty(&info->send_list)) {
-               kref_get(&info->refcount);
-               list_add_tail(&info->send_list, &bat_priv->vis.send_list);
-       }
-}
-
-/* delete the info packet from the send list, if it was
- * linked in.
- */
-static void batadv_send_list_del(struct batadv_vis_info *info)
-{
-       if (!list_empty(&info->send_list)) {
-               list_del_init(&info->send_list);
-               kref_put(&info->refcount, batadv_free_info);
-       }
-}
-
-/* tries to add one entry to the receive list. */
-static void batadv_recv_list_add(struct batadv_priv *bat_priv,
-                                struct list_head *recv_list, const char *mac)
-{
-       struct batadv_vis_recvlist_node *entry;
-
-       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
-       if (!entry)
-               return;
-
-       memcpy(entry->mac, mac, ETH_ALEN);
-       spin_lock_bh(&bat_priv->vis.list_lock);
-       list_add_tail(&entry->list, recv_list);
-       spin_unlock_bh(&bat_priv->vis.list_lock);
-}
-
-/* returns 1 if this mac is in the recv_list */
-static int batadv_recv_list_is_in(struct batadv_priv *bat_priv,
-                                 const struct list_head *recv_list,
-                                 const char *mac)
-{
-       const struct batadv_vis_recvlist_node *entry;
-
-       spin_lock_bh(&bat_priv->vis.list_lock);
-       list_for_each_entry(entry, recv_list, list) {
-               if (batadv_compare_eth(entry->mac, mac)) {
-                       spin_unlock_bh(&bat_priv->vis.list_lock);
-                       return 1;
-               }
-       }
-       spin_unlock_bh(&bat_priv->vis.list_lock);
-       return 0;
-}
-
-/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old,
- * broken.. ). vis hash must be locked outside.  is_new is set when the packet
- * is newer than old entries in the hash.
- */
-static struct batadv_vis_info *
-batadv_add_packet(struct batadv_priv *bat_priv,
-                 struct batadv_vis_packet *vis_packet, int vis_info_len,
-                 int *is_new, int make_broadcast)
-{
-       struct batadv_vis_info *info, *old_info;
-       struct batadv_vis_packet *search_packet, *old_packet;
-       struct batadv_vis_info search_elem;
-       struct batadv_vis_packet *packet;
-       struct sk_buff *tmp_skb;
-       int hash_added;
-       size_t len;
-       size_t max_entries;
-
-       *is_new = 0;
-       /* sanity check */
-       if (!bat_priv->vis.hash)
-               return NULL;
-
-       /* see if the packet is already in vis_hash */
-       search_elem.skb_packet = dev_alloc_skb(sizeof(*search_packet));
-       if (!search_elem.skb_packet)
-               return NULL;
-       len = sizeof(*search_packet);
-       tmp_skb = search_elem.skb_packet;
-       search_packet = (struct batadv_vis_packet *)skb_put(tmp_skb, len);
-
-       memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN);
-       old_info = batadv_vis_hash_find(bat_priv, &search_elem);
-       kfree_skb(search_elem.skb_packet);
-
-       if (old_info) {
-               tmp_skb = old_info->skb_packet;
-               old_packet = (struct batadv_vis_packet *)tmp_skb->data;
-               if (!batadv_seq_after(ntohl(vis_packet->seqno),
-                                     ntohl(old_packet->seqno))) {
-                       if (old_packet->seqno == vis_packet->seqno) {
-                               batadv_recv_list_add(bat_priv,
-                                                    &old_info->recv_list,
-                                                    vis_packet->sender_orig);
-                               return old_info;
-                       } else {
-                               /* newer packet is already in hash. */
-                               return NULL;
-                       }
-               }
-               /* remove old entry */
-               batadv_hash_remove(bat_priv->vis.hash, batadv_vis_info_cmp,
-                                  batadv_vis_info_choose, old_info);
-               batadv_send_list_del(old_info);
-               kref_put(&old_info->refcount, batadv_free_info);
-       }
-
-       info = kmalloc(sizeof(*info), GFP_ATOMIC);
-       if (!info)
-               return NULL;
-
-       len = sizeof(*packet) + vis_info_len;
-       info->skb_packet = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
-       if (!info->skb_packet) {
-               kfree(info);
-               return NULL;
-       }
-       info->skb_packet->priority = TC_PRIO_CONTROL;
-       skb_reserve(info->skb_packet, ETH_HLEN);
-       packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len);
-
-       kref_init(&info->refcount);
-       INIT_LIST_HEAD(&info->send_list);
-       INIT_LIST_HEAD(&info->recv_list);
-       info->first_seen = jiffies;
-       info->bat_priv = bat_priv;
-       memcpy(packet, vis_packet, len);
-
-       /* initialize and add new packet. */
-       *is_new = 1;
-
-       /* Make it a broadcast packet, if required */
-       if (make_broadcast)
-               memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN);
-
-       /* repair if entries is longer than packet. */
-       max_entries = vis_info_len / sizeof(struct batadv_vis_info_entry);
-       if (packet->entries > max_entries)
-               packet->entries = max_entries;
-
-       batadv_recv_list_add(bat_priv, &info->recv_list, packet->sender_orig);
-
-       /* try to add it */
-       hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp,
-                                    batadv_vis_info_choose, info,
-                                    &info->hash_entry);
-       if (hash_added != 0) {
-               /* did not work (for some reason) */
-               kref_put(&info->refcount, batadv_free_info);
-               info = NULL;
-       }
-
-       return info;
-}
-
-/* handle the server sync packet, forward if needed. */
-void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv,
-                                      struct batadv_vis_packet *vis_packet,
-                                      int vis_info_len)
-{
-       struct batadv_vis_info *info;
-       int is_new, make_broadcast;
-       int vis_server = atomic_read(&bat_priv->vis_mode);
-
-       make_broadcast = (vis_server == BATADV_VIS_TYPE_SERVER_SYNC);
-
-       spin_lock_bh(&bat_priv->vis.hash_lock);
-       info = batadv_add_packet(bat_priv, vis_packet, vis_info_len,
-                                &is_new, make_broadcast);
-       if (!info)
-               goto end;
-
-       /* only if we are server ourselves and packet is newer than the one in
-        * hash.
-        */
-       if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC && is_new)
-               batadv_send_list_add(bat_priv, info);
-end:
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-}
-
-/* handle an incoming client update packet and schedule forward if needed. */
-void batadv_receive_client_update_packet(struct batadv_priv *bat_priv,
-                                        struct batadv_vis_packet *vis_packet,
-                                        int vis_info_len)
-{
-       struct batadv_vis_info *info;
-       struct batadv_vis_packet *packet;
-       int is_new;
-       int vis_server = atomic_read(&bat_priv->vis_mode);
-       int are_target = 0;
-
-       /* clients shall not broadcast. */
-       if (is_broadcast_ether_addr(vis_packet->target_orig))
-               return;
-
-       /* Are we the target for this VIS packet? */
-       if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC   &&
-           batadv_is_my_mac(bat_priv, vis_packet->target_orig))
-               are_target = 1;
-
-       spin_lock_bh(&bat_priv->vis.hash_lock);
-       info = batadv_add_packet(bat_priv, vis_packet, vis_info_len,
-                                &is_new, are_target);
-
-       if (!info)
-               goto end;
-       /* note that outdated packets will be dropped at this point. */
-
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
-       /* send only if we're the target server or ... */
-       if (are_target && is_new) {
-               packet->vis_type = BATADV_VIS_TYPE_SERVER_SYNC; /* upgrade! */
-               batadv_send_list_add(bat_priv, info);
-
-               /* ... we're not the recipient (and thus need to forward). */
-       } else if (!batadv_is_my_mac(bat_priv, packet->target_orig)) {
-               batadv_send_list_add(bat_priv, info);
-       }
-
-end:
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-}
-
-/* Walk the originators and find the VIS server with the best tq. Set the packet
- * address to its address and return the best_tq.
- *
- * Must be called with the originator hash locked
- */
-static int batadv_find_best_vis_server(struct batadv_priv *bat_priv,
-                                      struct batadv_vis_info *info)
-{
-       struct batadv_hashtable *hash = bat_priv->orig_hash;
-       struct batadv_neigh_node *router;
-       struct hlist_head *head;
-       struct batadv_orig_node *orig_node;
-       struct batadv_vis_packet *packet;
-       int best_tq = -1;
-       uint32_t i;
-
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       router = batadv_orig_node_get_router(orig_node);
-                       if (!router)
-                               continue;
-
-                       if ((orig_node->flags & BATADV_VIS_SERVER) &&
-                           (router->tq_avg > best_tq)) {
-                               best_tq = router->tq_avg;
-                               memcpy(packet->target_orig, orig_node->orig,
-                                      ETH_ALEN);
-                       }
-                       batadv_neigh_node_free_ref(router);
-               }
-               rcu_read_unlock();
-       }
-
-       return best_tq;
-}
-
-/* Return true if the vis packet is full. */
-static bool batadv_vis_packet_full(const struct batadv_vis_info *info)
-{
-       const struct batadv_vis_packet *packet;
-       size_t num;
-
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-       num = BATADV_MAX_VIS_PACKET_SIZE / sizeof(struct batadv_vis_info_entry);
-
-       if (num < packet->entries + 1)
-               return true;
-       return false;
-}
-
-/* generates a packet of own vis data,
- * returns 0 on success, -1 if no packet could be generated
- */
-static int batadv_generate_vis_packet(struct batadv_priv *bat_priv)
-{
-       struct batadv_hashtable *hash = bat_priv->orig_hash;
-       struct hlist_head *head;
-       struct batadv_orig_node *orig_node;
-       struct batadv_neigh_node *router;
-       struct batadv_vis_info *info = bat_priv->vis.my_info;
-       struct batadv_vis_packet *packet;
-       struct batadv_vis_info_entry *entry;
-       struct batadv_tt_common_entry *tt_common_entry;
-       uint8_t *packet_pos;
-       int best_tq = -1;
-       uint32_t i;
-
-       info->first_seen = jiffies;
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-       packet->vis_type = atomic_read(&bat_priv->vis_mode);
-
-       memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN);
-       packet->header.ttl = BATADV_TTL;
-       packet->seqno = htonl(ntohl(packet->seqno) + 1);
-       packet->entries = 0;
-       packet->reserved = 0;
-       skb_trim(info->skb_packet, sizeof(*packet));
-
-       if (packet->vis_type == BATADV_VIS_TYPE_CLIENT_UPDATE) {
-               best_tq = batadv_find_best_vis_server(bat_priv, info);
-
-               if (best_tq < 0)
-                       return best_tq;
-       }
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       router = batadv_orig_node_get_router(orig_node);
-                       if (!router)
-                               continue;
-
-                       if (!batadv_compare_eth(router->addr, orig_node->orig))
-                               goto next;
-
-                       if (router->if_incoming->if_status != BATADV_IF_ACTIVE)
-                               goto next;
-
-                       if (router->tq_avg < 1)
-                               goto next;
-
-                       /* fill one entry into buffer. */
-                       packet_pos = skb_put(info->skb_packet, sizeof(*entry));
-                       entry = (struct batadv_vis_info_entry *)packet_pos;
-                       memcpy(entry->src,
-                              router->if_incoming->net_dev->dev_addr,
-                              ETH_ALEN);
-                       memcpy(entry->dest, orig_node->orig, ETH_ALEN);
-                       entry->quality = router->tq_avg;
-                       packet->entries++;
-
-next:
-                       batadv_neigh_node_free_ref(router);
-
-                       if (batadv_vis_packet_full(info))
-                               goto unlock;
-               }
-               rcu_read_unlock();
-       }
-
-       hash = bat_priv->tt.local_hash;
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(tt_common_entry, head,
-                                        hash_entry) {
-                       packet_pos = skb_put(info->skb_packet, sizeof(*entry));
-                       entry = (struct batadv_vis_info_entry *)packet_pos;
-                       memset(entry->src, 0, ETH_ALEN);
-                       memcpy(entry->dest, tt_common_entry->addr, ETH_ALEN);
-                       entry->quality = 0; /* 0 means TT */
-                       packet->entries++;
-
-                       if (batadv_vis_packet_full(info))
-                               goto unlock;
-               }
-               rcu_read_unlock();
-       }
-
-       return 0;
-
-unlock:
-       rcu_read_unlock();
-       return 0;
-}
-
-/* free old vis packets. Must be called with this vis_hash_lock
- * held
- */
-static void batadv_purge_vis_packets(struct batadv_priv *bat_priv)
-{
-       uint32_t i;
-       struct batadv_hashtable *hash = bat_priv->vis.hash;
-       struct hlist_node *node_tmp;
-       struct hlist_head *head;
-       struct batadv_vis_info *info;
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               hlist_for_each_entry_safe(info, node_tmp,
-                                         head, hash_entry) {
-                       /* never purge own data. */
-                       if (info == bat_priv->vis.my_info)
-                               continue;
-
-                       if (batadv_has_timed_out(info->first_seen,
-                                                BATADV_VIS_TIMEOUT)) {
-                               hlist_del(&info->hash_entry);
-                               batadv_send_list_del(info);
-                               kref_put(&info->refcount, batadv_free_info);
-                       }
-               }
-       }
-}
-
-static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
-                                       struct batadv_vis_info *info)
-{
-       struct batadv_hashtable *hash = bat_priv->orig_hash;
-       struct hlist_head *head;
-       struct batadv_orig_node *orig_node;
-       struct batadv_vis_packet *packet;
-       struct sk_buff *skb;
-       uint32_t i, res;
-
-
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
-       /* send to all routers in range. */
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       /* if it's a vis server and reachable, send it. */
-                       if (!(orig_node->flags & BATADV_VIS_SERVER))
-                               continue;
-
-                       /* don't send it if we already received the packet from
-                        * this node.
-                        */
-                       if (batadv_recv_list_is_in(bat_priv, &info->recv_list,
-                                                  orig_node->orig))
-                               continue;
-
-                       memcpy(packet->target_orig, orig_node->orig, ETH_ALEN);
-                       skb = skb_clone(info->skb_packet, GFP_ATOMIC);
-                       if (!skb)
-                               continue;
-
-                       res = batadv_send_skb_to_orig(skb, orig_node, NULL);
-                       if (res == NET_XMIT_DROP)
-                               kfree_skb(skb);
-               }
-               rcu_read_unlock();
-       }
-}
-
-static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv,
-                                     struct batadv_vis_info *info)
-{
-       struct batadv_orig_node *orig_node;
-       struct sk_buff *skb;
-       struct batadv_vis_packet *packet;
-
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
-       orig_node = batadv_orig_hash_find(bat_priv, packet->target_orig);
-       if (!orig_node)
-               goto out;
-
-       skb = skb_clone(info->skb_packet, GFP_ATOMIC);
-       if (!skb)
-               goto out;
-
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
-               kfree_skb(skb);
-
-out:
-       if (orig_node)
-               batadv_orig_node_free_ref(orig_node);
-}
-
-/* only send one vis packet. called from batadv_send_vis_packets() */
-static void batadv_send_vis_packet(struct batadv_priv *bat_priv,
-                                  struct batadv_vis_info *info)
-{
-       struct batadv_hard_iface *primary_if;
-       struct batadv_vis_packet *packet;
-
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
-
-       packet = (struct batadv_vis_packet *)info->skb_packet->data;
-       if (packet->header.ttl < 2) {
-               pr_debug("Error - can't send vis packet: ttl exceeded\n");
-               goto out;
-       }
-
-       memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
-       packet->header.ttl--;
-
-       if (is_broadcast_ether_addr(packet->target_orig))
-               batadv_broadcast_vis_packet(bat_priv, info);
-       else
-               batadv_unicast_vis_packet(bat_priv, info);
-       packet->header.ttl++; /* restore TTL */
-
-out:
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-}
-
-/* called from timer; send (and maybe generate) vis packet. */
-static void batadv_send_vis_packets(struct work_struct *work)
-{
-       struct delayed_work *delayed_work;
-       struct batadv_priv *bat_priv;
-       struct batadv_priv_vis *priv_vis;
-       struct batadv_vis_info *info;
-
-       delayed_work = container_of(work, struct delayed_work, work);
-       priv_vis = container_of(delayed_work, struct batadv_priv_vis, work);
-       bat_priv = container_of(priv_vis, struct batadv_priv, vis);
-       spin_lock_bh(&bat_priv->vis.hash_lock);
-       batadv_purge_vis_packets(bat_priv);
-
-       if (batadv_generate_vis_packet(bat_priv) == 0) {
-               /* schedule if generation was successful */
-               batadv_send_list_add(bat_priv, bat_priv->vis.my_info);
-       }
-
-       while (!list_empty(&bat_priv->vis.send_list)) {
-               info = list_first_entry(&bat_priv->vis.send_list,
-                                       typeof(*info), send_list);
-
-               kref_get(&info->refcount);
-               spin_unlock_bh(&bat_priv->vis.hash_lock);
-
-               batadv_send_vis_packet(bat_priv, info);
-
-               spin_lock_bh(&bat_priv->vis.hash_lock);
-               batadv_send_list_del(info);
-               kref_put(&info->refcount, batadv_free_info);
-       }
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-
-       queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work,
-                          msecs_to_jiffies(BATADV_VIS_INTERVAL));
-}
-
-/* init the vis server. this may only be called when if_list is already
- * initialized (e.g. bat0 is initialized, interfaces have been added)
- */
-int batadv_vis_init(struct batadv_priv *bat_priv)
-{
-       struct batadv_vis_packet *packet;
-       int hash_added;
-       unsigned int len;
-       unsigned long first_seen;
-       struct sk_buff *tmp_skb;
-
-       if (bat_priv->vis.hash)
-               return 0;
-
-       spin_lock_bh(&bat_priv->vis.hash_lock);
-
-       bat_priv->vis.hash = batadv_hash_new(256);
-       if (!bat_priv->vis.hash) {
-               pr_err("Can't initialize vis_hash\n");
-               goto err;
-       }
-
-       batadv_hash_set_lock_class(bat_priv->vis.hash,
-                                  &batadv_vis_hash_lock_class_key);
-
-       bat_priv->vis.my_info = kmalloc(BATADV_MAX_VIS_PACKET_SIZE, GFP_ATOMIC);
-       if (!bat_priv->vis.my_info)
-               goto err;
-
-       len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN;
-       bat_priv->vis.my_info->skb_packet = netdev_alloc_skb_ip_align(NULL,
-                                                                     len);
-       if (!bat_priv->vis.my_info->skb_packet)
-               goto free_info;
-
-       bat_priv->vis.my_info->skb_packet->priority = TC_PRIO_CONTROL;
-       skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN);
-       tmp_skb = bat_priv->vis.my_info->skb_packet;
-       packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet));
-
-       /* prefill the vis info */
-       first_seen = jiffies - msecs_to_jiffies(BATADV_VIS_INTERVAL);
-       bat_priv->vis.my_info->first_seen = first_seen;
-       INIT_LIST_HEAD(&bat_priv->vis.my_info->recv_list);
-       INIT_LIST_HEAD(&bat_priv->vis.my_info->send_list);
-       kref_init(&bat_priv->vis.my_info->refcount);
-       bat_priv->vis.my_info->bat_priv = bat_priv;
-       packet->header.version = BATADV_COMPAT_VERSION;
-       packet->header.packet_type = BATADV_VIS;
-       packet->header.ttl = BATADV_TTL;
-       packet->seqno = 0;
-       packet->reserved = 0;
-       packet->entries = 0;
-
-       INIT_LIST_HEAD(&bat_priv->vis.send_list);
-
-       hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp,
-                                    batadv_vis_info_choose,
-                                    bat_priv->vis.my_info,
-                                    &bat_priv->vis.my_info->hash_entry);
-       if (hash_added != 0) {
-               pr_err("Can't add own vis packet into hash\n");
-               /* not in hash, need to remove it manually. */
-               kref_put(&bat_priv->vis.my_info->refcount, batadv_free_info);
-               goto err;
-       }
-
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-
-       INIT_DELAYED_WORK(&bat_priv->vis.work, batadv_send_vis_packets);
-       queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work,
-                          msecs_to_jiffies(BATADV_VIS_INTERVAL));
-
-       return 0;
-
-free_info:
-       kfree(bat_priv->vis.my_info);
-       bat_priv->vis.my_info = NULL;
-err:
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-       batadv_vis_quit(bat_priv);
-       return -ENOMEM;
-}
-
-/* Decrease the reference count on a hash item info */
-static void batadv_free_info_ref(struct hlist_node *node, void *arg)
-{
-       struct batadv_vis_info *info;
-
-       info = container_of(node, struct batadv_vis_info, hash_entry);
-       batadv_send_list_del(info);
-       kref_put(&info->refcount, batadv_free_info);
-}
-
-/* shutdown vis-server */
-void batadv_vis_quit(struct batadv_priv *bat_priv)
-{
-       if (!bat_priv->vis.hash)
-               return;
-
-       cancel_delayed_work_sync(&bat_priv->vis.work);
-
-       spin_lock_bh(&bat_priv->vis.hash_lock);
-       /* properly remove, kill timers ... */
-       batadv_hash_delete(bat_priv->vis.hash, batadv_free_info_ref, NULL);
-       bat_priv->vis.hash = NULL;
-       bat_priv->vis.my_info = NULL;
-       spin_unlock_bh(&bat_priv->vis.hash_lock);
-}
diff --git a/net/batman-adv/vis.h b/net/batman-adv/vis.h
deleted file mode 100644 (file)
index ad92b0e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors:
- *
- * Simon Wunderlich, Marek Lindner
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef _NET_BATMAN_ADV_VIS_H_
-#define _NET_BATMAN_ADV_VIS_H_
-
-/* timeout of vis packets in milliseconds */
-#define BATADV_VIS_TIMEOUT             200000
-
-int batadv_vis_seq_print_text(struct seq_file *seq, void *offset);
-void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv,
-                                      struct batadv_vis_packet *vis_packet,
-                                      int vis_info_len);
-void batadv_receive_client_update_packet(struct batadv_priv *bat_priv,
-                                        struct batadv_vis_packet *vis_packet,
-                                        int vis_info_len);
-int batadv_vis_init(struct batadv_priv *bat_priv);
-void batadv_vis_quit(struct batadv_priv *bat_priv);
-
-#endif /* _NET_BATMAN_ADV_VIS_H_ */
index dea6a28..6a791e7 100644 (file)
@@ -11,3 +11,5 @@ obj-$(CONFIG_BT_HIDP) += hidp/
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
        hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
        a2mp.o amp.o
+
+subdir-ccflags-y += -D__CHECK_ENDIAN__
index 17f33a6..efcd108 100644 (file)
@@ -15,8 +15,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "a2mp.h"
+#include "amp.h"
 
 /* Global AMP Manager list */
 LIST_HEAD(amp_mgr_list);
@@ -75,33 +76,26 @@ u8 __next_ident(struct amp_mgr *mgr)
        return mgr->ident;
 }
 
-static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
-{
-       cl->id = 0;
-       cl->type = 0;
-       cl->status = 1;
-}
-
 /* hci_dev_list shall be locked */
-static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl)
 {
-       int i = 0;
        struct hci_dev *hdev;
+       int i = 1;
 
-       __a2mp_cl_bredr(cl);
+       cl[0].id = AMP_ID_BREDR;
+       cl[0].type = AMP_TYPE_BREDR;
+       cl[0].status = AMP_STATUS_BLUETOOTH_ONLY;
 
        list_for_each_entry(hdev, &hci_dev_list, list) {
-               /* Iterate through AMP controllers */
-               if (hdev->id == HCI_BREDR_ID)
-                       continue;
-
-               /* Starting from second entry */
-               if (++i >= num_ctrl)
-                       return;
-
-               cl[i].id = hdev->id;
-               cl[i].type = hdev->amp_type;
-               cl[i].status = hdev->amp_status;
+               if (hdev->dev_type == HCI_AMP) {
+                       cl[i].id = hdev->id;
+                       cl[i].type = hdev->amp_type;
+                       if (test_bit(HCI_UP, &hdev->flags))
+                               cl[i].status = hdev->amp_status;
+                       else
+                               cl[i].status = AMP_STATUS_POWERED_DOWN;
+                       i++;
+               }
        }
 }
 
@@ -129,6 +123,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
        struct a2mp_discov_rsp *rsp;
        u16 ext_feat;
        u8 num_ctrl;
+       struct hci_dev *hdev;
 
        if (len < sizeof(*req))
                return -EINVAL;
@@ -152,7 +147,14 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
 
        read_lock(&hci_dev_list_lock);
 
-       num_ctrl = __hci_num_ctrl();
+       /* at minimum the BR/EDR needs to be listed */
+       num_ctrl = 1;
+
+       list_for_each_entry(hdev, &hci_dev_list, list) {
+               if (hdev->dev_type == HCI_AMP)
+                       num_ctrl++;
+       }
+
        len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
        rsp = kmalloc(len, GFP_ATOMIC);
        if (!rsp) {
@@ -163,7 +165,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
        rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
        rsp->ext_feat = 0;
 
-       __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+       __a2mp_add_cl(mgr, rsp->cl);
 
        read_unlock(&hci_dev_list_lock);
 
@@ -208,7 +210,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
                BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type,
                       cl->status);
 
-               if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) {
+               if (cl->id != AMP_ID_BREDR && cl->type != AMP_TYPE_BREDR) {
                        struct a2mp_info_req req;
 
                        found = true;
@@ -344,7 +346,7 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
        tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
 
        hdev = hci_dev_get(req->id);
-       if (!hdev || hdev->amp_type == HCI_BREDR || tmp) {
+       if (!hdev || hdev->amp_type == AMP_TYPE_BREDR || tmp) {
                struct a2mp_amp_assoc_rsp rsp;
                rsp.id = req->id;
 
@@ -451,7 +453,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
        rsp.remote_id = req->local_id;
 
        hdev = hci_dev_get(req->remote_id);
-       if (!hdev || hdev->amp_type != HCI_AMP) {
+       if (!hdev || hdev->amp_type == AMP_TYPE_BREDR) {
                rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
                goto send_rsp;
        }
@@ -535,7 +537,8 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
                goto send_rsp;
        }
 
-       hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst);
+       hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+                                      &mgr->l2cap_conn->hcon->dst);
        if (!hcon) {
                BT_ERR("No phys link exist");
                rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS;
@@ -669,7 +672,8 @@ static void a2mp_chan_close_cb(struct l2cap_chan *chan)
        l2cap_chan_put(chan);
 }
 
-static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state)
+static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
+                                     int err)
 {
        struct amp_mgr *mgr = chan->data;
 
@@ -706,6 +710,9 @@ static struct l2cap_ops a2mp_chan_ops = {
        .teardown = l2cap_chan_no_teardown,
        .ready = l2cap_chan_no_ready,
        .defer = l2cap_chan_no_defer,
+       .resume = l2cap_chan_no_resume,
+       .set_shutdown = l2cap_chan_no_set_shutdown,
+       .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
 };
 
 static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
@@ -829,6 +836,9 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
 {
        struct amp_mgr *mgr;
 
+       if (conn->hcon->type != ACL_LINK)
+               return NULL;
+
        mgr = amp_mgr_create(conn, false);
        if (!mgr) {
                BT_ERR("Could not create AMP manager");
@@ -871,7 +881,7 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
        rsp.id = hdev->id;
        rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
 
-       if (hdev->amp_type != HCI_BREDR) {
+       if (hdev->amp_type != AMP_TYPE_BREDR) {
                rsp.status = 0;
                rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
                rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
diff --git a/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h
new file mode 100644 (file)
index 0000000..487b54c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+   Copyright (c) 2010,2011 Code Aurora Forum.  All rights reserved.
+   Copyright (c) 2011,2012 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 as published by the Free Software Foundation.
+
+   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.
+*/
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+#include <net/bluetooth/l2cap.h>
+
+#define A2MP_FEAT_EXT  0x8000
+
+enum amp_mgr_state {
+       READ_LOC_AMP_INFO,
+       READ_LOC_AMP_ASSOC,
+       READ_LOC_AMP_ASSOC_FINAL,
+       WRITE_REMOTE_AMP_ASSOC,
+};
+
+struct amp_mgr {
+       struct list_head        list;
+       struct l2cap_conn       *l2cap_conn;
+       struct l2cap_chan       *a2mp_chan;
+       struct l2cap_chan       *bredr_chan;
+       struct kref             kref;
+       __u8                    ident;
+       __u8                    handle;
+       unsigned long           state;
+       unsigned long           flags;
+
+       struct list_head        amp_ctrls;
+       struct mutex            amp_ctrls_lock;
+};
+
+struct a2mp_cmd {
+       __u8    code;
+       __u8    ident;
+       __le16  len;
+       __u8    data[0];
+} __packed;
+
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ         0x01
+struct a2mp_cmd_rej {
+       __le16  reason;
+       __u8    data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ        0x02
+struct a2mp_discov_req {
+       __le16  mtu;
+       __le16  ext_feat;
+} __packed;
+
+struct a2mp_cl {
+       __u8    id;
+       __u8    type;
+       __u8    status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP        0x03
+struct a2mp_discov_rsp {
+       __le16     mtu;
+       __le16     ext_feat;
+       struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY       0x04
+#define A2MP_CHANGE_RSP          0x05
+
+#define A2MP_GETINFO_REQ         0x06
+struct a2mp_info_req {
+       __u8       id;
+} __packed;
+
+#define A2MP_GETINFO_RSP         0x07
+struct a2mp_info_rsp {
+       __u8    id;
+       __u8    status;
+       __le32  total_bw;
+       __le32  max_bw;
+       __le32  min_latency;
+       __le16  pal_cap;
+       __le16  assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ     0x08
+struct a2mp_amp_assoc_req {
+       __u8    id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP     0x09
+struct a2mp_amp_assoc_rsp {
+       __u8    id;
+       __u8    status;
+       __u8    amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ  0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+       __u8    local_id;
+       __u8    remote_id;
+       __u8    amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP  0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+       __u8    local_id;
+       __u8    remote_id;
+       __u8    status;
+} __packed;
+
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS                    0x00
+#define A2MP_STATUS_INVALID_CTRL_ID            0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS    0x02
+#define A2MP_STATUS_COLLISION_OCCURED          0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD          0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS           0x05
+#define A2MP_STATUS_SECURITY_VIOLATION         0x06
+
+extern struct list_head amp_mgr_list;
+extern struct mutex amp_mgr_list_lock;
+
+struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+u8 __next_ident(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+                                      struct sk_buff *skb);
+struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
+void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
+void a2mp_discover_amp(struct l2cap_chan *chan);
+void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
+void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
+
+#endif /* __A2MP_H */
index 9096137..f6a1671 100644 (file)
 /* Bluetooth address family and sockets. */
 
 #include <linux/module.h>
+#include <linux/debugfs.h>
 #include <asm/ioctls.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <linux/proc_fs.h>
 
-#define VERSION "2.16"
+#define VERSION "2.17"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
@@ -221,12 +222,12 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (flags & (MSG_OOB))
                return -EOPNOTSUPP;
 
-       msg->msg_namelen = 0;
-
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb) {
-               if (sk->sk_shutdown & RCV_SHUTDOWN)
+               if (sk->sk_shutdown & RCV_SHUTDOWN) {
+                       msg->msg_namelen = 0;
                        return 0;
+               }
                return err;
        }
 
@@ -238,9 +239,16 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        skb_reset_transport_header(skb);
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-       if (err == 0)
+       if (err == 0) {
                sock_recv_ts_and_drops(msg, sk, skb);
 
+               if (bt_sk(sk)->skb_msg_name)
+                       bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
+                                               &msg->msg_namelen);
+               else
+                       msg->msg_namelen = 0;
+       }
+
        skb_free_datagram(sk, skb);
 
        return err ? : copied;
@@ -490,6 +498,7 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 }
 EXPORT_SYMBOL(bt_sock_ioctl);
 
+/* This function expects the sk lock to be held when called */
 int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
 {
        DECLARE_WAITQUEUE(wait, current);
@@ -525,6 +534,46 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
 }
 EXPORT_SYMBOL(bt_sock_wait_state);
 
+/* This function expects the sk lock to be held when called */
+int bt_sock_wait_ready(struct sock *sk, unsigned long flags)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long timeo;
+       int err = 0;
+
+       BT_DBG("sk %p", sk);
+
+       timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+
+       add_wait_queue(sk_sleep(sk), &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags)) {
+               if (!timeo) {
+                       err = -EAGAIN;
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       err = sock_intr_errno(timeo);
+                       break;
+               }
+
+               release_sock(sk);
+               timeo = schedule_timeout(timeo);
+               lock_sock(sk);
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               err = sock_error(sk);
+               if (err)
+                       break;
+       }
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(sk_sleep(sk), &wait);
+
+       return err;
+}
+EXPORT_SYMBOL(bt_sock_wait_ready);
+
 #ifdef CONFIG_PROC_FS
 struct bt_seq_state {
        struct bt_sock_list *l;
@@ -563,7 +612,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
        struct bt_sock_list *l = s->l;
 
        if (v == SEQ_START_TOKEN) {
-               seq_puts(seq ,"sk               RefCnt Rmem   Wmem   User   Inode  Src Dst Parent");
+               seq_puts(seq ,"sk               RefCnt Rmem   Wmem   User   Inode  Parent");
 
                if (l->custom_seq_show) {
                        seq_putc(seq, ' ');
@@ -576,15 +625,13 @@ static int bt_seq_show(struct seq_file *seq, void *v)
                struct bt_sock *bt = bt_sk(sk);
 
                seq_printf(seq,
-                          "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu",
+                          "%pK %-6d %-6u %-6u %-6u %-6lu %-6lu",
                           sk,
                           atomic_read(&sk->sk_refcnt),
                           sk_rmem_alloc_get(sk),
                           sk_wmem_alloc_get(sk),
                           from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
                           sock_i_ino(sk),
-                          &bt->src,
-                          &bt->dst,
                           bt->parent? sock_i_ino(bt->parent): 0LU);
 
                if (l->custom_seq_show) {
@@ -662,12 +709,17 @@ static struct net_proto_family bt_sock_family_ops = {
        .create = bt_sock_create,
 };
 
+struct dentry *bt_debugfs;
+EXPORT_SYMBOL_GPL(bt_debugfs);
+
 static int __init bt_init(void)
 {
        int err;
 
        BT_INFO("Core ver %s", VERSION);
 
+       bt_debugfs = debugfs_create_dir("bluetooth", NULL);
+
        err = bt_sysfs_init();
        if (err < 0)
                return err;
@@ -708,7 +760,6 @@ error:
 
 static void __exit bt_exit(void)
 {
-
        sco_exit();
 
        l2cap_exit();
@@ -718,6 +769,8 @@ static void __exit bt_exit(void)
        sock_unregister(PF_BLUETOOTH);
 
        bt_sysfs_cleanup();
+
+       debugfs_remove_recursive(bt_debugfs);
 }
 
 subsys_initcall(bt_init);
index d459ed4..bb39509 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
 #include <crypto/hash.h>
 
+#include "a2mp.h"
+#include "amp.h"
+
 /* Remote AMP Controllers interface */
 void amp_ctrl_get(struct amp_ctrl *ctrl)
 {
@@ -110,7 +111,7 @@ static u8 __next_handle(struct amp_mgr *mgr)
 struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
                             u8 remote_id, bool out)
 {
-       bdaddr_t *dst = mgr->l2cap_conn->dst;
+       bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
        struct hci_conn *hcon;
 
        hcon = hci_conn_add(hdev, AMP_LINK, dst);
@@ -409,7 +410,8 @@ void amp_create_logical_link(struct l2cap_chan *chan)
        struct hci_cp_create_accept_logical_link cp;
        struct hci_dev *hdev;
 
-       BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst);
+       BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon,
+              &chan->conn->hcon->dst);
 
        if (!hs_hcon)
                return;
diff --git a/net/bluetooth/amp.h b/net/bluetooth/amp.h
new file mode 100644 (file)
index 0000000..7ea3db7
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+   Copyright (c) 2011,2012 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 as published by the Free Software Foundation.
+
+   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.
+*/
+
+#ifndef __AMP_H
+#define __AMP_H
+
+struct amp_ctrl {
+       struct list_head        list;
+       struct kref             kref;
+       __u8                    id;
+       __u16                   assoc_len_so_far;
+       __u16                   assoc_rem_len;
+       __u16                   assoc_len;
+       __u8                    *assoc;
+};
+
+int amp_ctrl_put(struct amp_ctrl *ctrl);
+void amp_ctrl_get(struct amp_ctrl *ctrl);
+struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
+struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
+void amp_ctrl_list_flush(struct amp_mgr *mgr);
+
+struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
+                            u8 remote_id, bool out);
+
+int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
+
+void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
+void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
+void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
+void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
+                                  struct hci_conn *hcon);
+void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+                       struct hci_conn *hcon);
+void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+                       struct hci_conn *hcon);
+void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
+void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
+void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
+void amp_create_logical_link(struct l2cap_chan *chan);
+void amp_disconnect_logical_link(struct hci_chan *hchan);
+void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
+
+#endif /* __AMP_H */
index e430b1a..a841d3e 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/hci_core.h>
 
 #include "bnep.h"
@@ -510,20 +511,13 @@ static int bnep_session(void *arg)
 
 static struct device *bnep_get_device(struct bnep_session *session)
 {
-       bdaddr_t *src = &bt_sk(session->sock->sk)->src;
-       bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
-       struct hci_dev *hdev;
        struct hci_conn *conn;
 
-       hdev = hci_get_route(dst, src);
-       if (!hdev)
+       conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
+       if (!conn)
                return NULL;
 
-       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
-
-       hci_dev_put(hdev);
-
-       return conn ? &conn->dev : NULL;
+       return &conn->dev;
 }
 
 static struct device_type bnep_type = {
@@ -539,8 +533,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 
        BT_DBG("");
 
-       baswap((void *) dst, &bt_sk(sock->sk)->dst);
-       baswap((void *) src, &bt_sk(sock->sk)->src);
+       baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
+       baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
 
        /* session struct allocated as private part of net_device */
        dev = alloc_netdev(sizeof(struct bnep_session),
index e0a6ebf..67fe5e8 100644 (file)
@@ -340,20 +340,20 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
 
        down_write(&cmtp_session_sem);
 
-       s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
+       s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst);
        if (s && s->state == BT_CONNECTED) {
                err = -EEXIST;
                goto failed;
        }
 
-       bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
+       bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst);
 
        session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
                                        l2cap_pi(sock->sk)->chan->imtu);
 
        BT_DBG("mtu %d", session->mtu);
 
-       sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst);
+       sprintf(session->name, "%pMR", &session->bdaddr);
 
        session->sock  = sock;
        session->state = BT_CONFIG;
index f081712..ba5366c 100644 (file)
@@ -28,8 +28,9 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
+#include "a2mp.h"
 
 struct sco_param {
        u16 pkt_type;
@@ -49,30 +50,6 @@ static const struct sco_param sco_param_wideband[] = {
        { EDR_ESCO_MASK | ESCO_EV3,   0x0008 }, /* T1 */
 };
 
-static void hci_le_create_connection(struct hci_conn *conn)
-{
-       struct hci_dev *hdev = conn->hdev;
-       struct hci_cp_le_create_conn cp;
-
-       conn->state = BT_CONNECT;
-       conn->out = true;
-       conn->link_mode |= HCI_LM_MASTER;
-       conn->sec_level = BT_SECURITY_LOW;
-
-       memset(&cp, 0, sizeof(cp));
-       cp.scan_interval = __constant_cpu_to_le16(0x0060);
-       cp.scan_window = __constant_cpu_to_le16(0x0030);
-       bacpy(&cp.peer_addr, &conn->dst);
-       cp.peer_addr_type = conn->dst_type;
-       cp.conn_interval_min = __constant_cpu_to_le16(0x0028);
-       cp.conn_interval_max = __constant_cpu_to_le16(0x0038);
-       cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
-       cp.min_ce_len = __constant_cpu_to_le16(0x0000);
-       cp.max_ce_len = __constant_cpu_to_le16(0x0000);
-
-       hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
-}
-
 static void hci_le_create_connection_cancel(struct hci_conn *conn)
 {
        hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
@@ -340,8 +317,10 @@ static void hci_conn_timeout(struct work_struct *work)
 }
 
 /* Enter sniff mode */
-static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+static void hci_conn_idle(struct work_struct *work)
 {
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            idle_work.work);
        struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("hcon %p mode %d", conn, conn->mode);
@@ -375,21 +354,12 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
        }
 }
 
-static void hci_conn_idle(unsigned long arg)
-{
-       struct hci_conn *conn = (void *) arg;
-
-       BT_DBG("hcon %p mode %d", conn, conn->mode);
-
-       hci_conn_enter_sniff_mode(conn);
-}
-
-static void hci_conn_auto_accept(unsigned long arg)
+static void hci_conn_auto_accept(struct work_struct *work)
 {
-       struct hci_conn *conn = (void *) arg;
-       struct hci_dev *hdev = conn->hdev;
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                            auto_accept_work.work);
 
-       hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
+       hci_send_cmd(conn->hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
                     &conn->dst);
 }
 
@@ -404,6 +374,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
                return NULL;
 
        bacpy(&conn->dst, dst);
+       bacpy(&conn->src, &hdev->bdaddr);
        conn->hdev  = hdev;
        conn->type  = type;
        conn->mode  = HCI_CM_ACTIVE;
@@ -437,9 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        INIT_LIST_HEAD(&conn->chan_list);
 
        INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
-       setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
-       setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
-                   (unsigned long) conn);
+       INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
+       INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
 
        atomic_set(&conn->refcnt, 0);
 
@@ -460,11 +430,9 @@ int hci_conn_del(struct hci_conn *conn)
 
        BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
 
-       del_timer(&conn->idle_timer);
-
        cancel_delayed_work_sync(&conn->disc_work);
-
-       del_timer(&conn->auto_accept_timer);
+       cancel_delayed_work_sync(&conn->auto_accept_work);
+       cancel_delayed_work_sync(&conn->idle_work);
 
        if (conn->type == ACL_LINK) {
                struct hci_conn *sco = conn->link;
@@ -518,6 +486,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
        list_for_each_entry(d, &hci_dev_list, list) {
                if (!test_bit(HCI_UP, &d->flags) ||
                    test_bit(HCI_RAW, &d->flags) ||
+                   test_bit(HCI_USER_CHANNEL, &d->dev_flags) ||
                    d->dev_type != HCI_BREDR)
                        continue;
 
@@ -545,34 +514,124 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 }
 EXPORT_SYMBOL(hci_get_route);
 
+static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
+{
+       struct hci_conn *conn;
+
+       if (status == 0)
+               return;
+
+       BT_ERR("HCI request failed to create LE connection: status 0x%2.2x",
+              status);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+       if (!conn)
+               goto done;
+
+       conn->state = BT_CLOSED;
+
+       mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+                           status);
+
+       hci_proto_connect_cfm(conn, status);
+
+       hci_conn_del(conn);
+
+done:
+       hci_dev_unlock(hdev);
+}
+
+static int hci_create_le_conn(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_le_create_conn cp;
+       struct hci_request req;
+       int err;
+
+       hci_req_init(&req, hdev);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
+       cp.scan_window = cpu_to_le16(hdev->le_scan_window);
+       bacpy(&cp.peer_addr, &conn->dst);
+       cp.peer_addr_type = conn->dst_type;
+       cp.own_address_type = conn->src_type;
+       cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval);
+       cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval);
+       cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
+       cp.min_ce_len = __constant_cpu_to_le16(0x0000);
+       cp.max_ce_len = __constant_cpu_to_le16(0x0000);
+
+       hci_req_add(&req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+
+       err = hci_req_run(&req, create_le_conn_complete);
+       if (err) {
+               hci_conn_del(conn);
+               return err;
+       }
+
+       return 0;
+}
+
 static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                    u8 dst_type, u8 sec_level, u8 auth_type)
 {
-       struct hci_conn *le;
+       struct hci_conn *conn;
+       int err;
 
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags))
+       if (test_bit(HCI_ADVERTISING, &hdev->flags))
                return ERR_PTR(-ENOTSUPP);
 
-       le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
-       if (!le) {
-               le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
-               if (le)
-                       return ERR_PTR(-EBUSY);
+       /* Some devices send ATT messages as soon as the physical link is
+        * established. To be able to handle these ATT messages, the user-
+        * space first establishes the connection and then starts the pairing
+        * process.
+        *
+        * So if a hci_conn object already exists for the following connection
+        * attempt, we simply update pending_sec_level and auth_type fields
+        * and return the object found.
+        */
+       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+       if (conn) {
+               conn->pending_sec_level = sec_level;
+               conn->auth_type = auth_type;
+               goto done;
+       }
 
-               le = hci_conn_add(hdev, LE_LINK, dst);
-               if (!le)
-                       return ERR_PTR(-ENOMEM);
+       /* Since the controller supports only one LE connection attempt at a
+        * time, we return -EBUSY if there is any connection attempt running.
+        */
+       conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+       if (conn)
+               return ERR_PTR(-EBUSY);
 
-               le->dst_type = bdaddr_to_le(dst_type);
-               hci_le_create_connection(le);
-       }
+       conn = hci_conn_add(hdev, LE_LINK, dst);
+       if (!conn)
+               return ERR_PTR(-ENOMEM);
 
-       le->pending_sec_level = sec_level;
-       le->auth_type = auth_type;
+       if (dst_type == BDADDR_LE_PUBLIC)
+               conn->dst_type = ADDR_LE_DEV_PUBLIC;
+       else
+               conn->dst_type = ADDR_LE_DEV_RANDOM;
 
-       hci_conn_hold(le);
+       conn->src_type = hdev->own_addr_type;
 
-       return le;
+       conn->state = BT_CONNECT;
+       conn->out = true;
+       conn->link_mode |= HCI_LM_MASTER;
+       conn->sec_level = BT_SECURITY_LOW;
+       conn->pending_sec_level = sec_level;
+       conn->auth_type = auth_type;
+
+       err = hci_create_le_conn(conn);
+       if (err)
+               return ERR_PTR(err);
+
+done:
+       hci_conn_hold(conn);
+       return conn;
 }
 
 static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
@@ -580,6 +639,9 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
 {
        struct hci_conn *acl;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return ERR_PTR(-ENOTSUPP);
+
        acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
        if (!acl) {
                acl = hci_conn_add(hdev, ACL_LINK, dst);
@@ -846,8 +908,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
 
 timer:
        if (hdev->idle_timeout > 0)
-               mod_timer(&conn->idle_timer,
-                         jiffies + msecs_to_jiffies(hdev->idle_timeout));
+               queue_delayed_work(hdev->workqueue, &conn->idle_work,
+                                  msecs_to_jiffies(hdev->idle_timeout));
 }
 
 /* Drop all connection on the device */
index 1b66547..6ccc4eb 100644 (file)
 
 #include <linux/export.h>
 #include <linux/idr.h>
+#include <linux/rfkill.h>
+#include <linux/debugfs.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+static void hci_rx_work(struct work_struct *work);
+static void hci_cmd_work(struct work_struct *work);
+static void hci_tx_work(struct work_struct *work);
+
+/* HCI device list */
+LIST_HEAD(hci_dev_list);
+DEFINE_RWLOCK(hci_dev_list_lock);
+
+/* HCI callback list */
+LIST_HEAD(hci_cb_list);
+DEFINE_RWLOCK(hci_cb_list_lock);
+
+/* HCI ID Numbering */
+static DEFINE_IDA(hci_index_ida);
+
+/* ---- HCI notifications ---- */
+
+static void hci_notify(struct hci_dev *hdev, int event)
+{
+       hci_sock_dev_event(hdev, event);
+}
+
+/* ---- HCI debugfs entries ---- */
+
+static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
+                            size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       struct sk_buff *skb;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+       int err;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags))
+               return -EALREADY;
+
+       hci_req_lock(hdev);
+       if (enable)
+               skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       else
+               skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       change_bit(HCI_DUT_MODE, &hdev->dev_flags);
+
+       return count;
+}
+
+static const struct file_operations dut_mode_fops = {
+       .open           = simple_open,
+       .read           = dut_mode_read,
+       .write          = dut_mode_write,
+       .llseek         = default_llseek,
+};
+
+static int features_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       u8 p;
+
+       hci_dev_lock(hdev);
+       for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
+               seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
+                          hdev->features[p][0], hdev->features[p][1],
+                          hdev->features[p][2], hdev->features[p][3],
+                          hdev->features[p][4], hdev->features[p][5],
+                          hdev->features[p][6], hdev->features[p][7]);
+       }
+       if (lmp_le_capable(hdev))
+               seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+                          hdev->le_features[0], hdev->le_features[1],
+                          hdev->le_features[2], hdev->le_features[3],
+                          hdev->le_features[4], hdev->le_features[5],
+                          hdev->le_features[6], hdev->le_features[7]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int features_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, features_show, inode->i_private);
+}
+
+static const struct file_operations features_fops = {
+       .open           = features_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int blacklist_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->blacklist, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int blacklist_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blacklist_show, inode->i_private);
+}
+
+static const struct file_operations blacklist_fops = {
+       .open           = blacklist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int uuids_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bt_uuid *uuid;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               u8 i, val[16];
+
+               /* The Bluetooth UUID values are stored in big endian,
+                * but with reversed byte order. So convert them into
+                * the right order for the %pUb modifier.
+                */
+               for (i = 0; i < 16; i++)
+                       val[i] = uuid->uuid[15 - i];
+
+               seq_printf(f, "%pUb\n", val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int uuids_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, uuids_show, inode->i_private);
+}
+
+static const struct file_operations uuids_fops = {
+       .open           = uuids_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int inquiry_cache_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct discovery_state *cache = &hdev->discovery;
+       struct inquiry_entry *e;
+
+       hci_dev_lock(hdev);
+
+       list_for_each_entry(e, &cache->all, all) {
+               struct inquiry_data *data = &e->data;
+               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+                          &data->bdaddr,
+                          data->pscan_rep_mode, data->pscan_period_mode,
+                          data->pscan_mode, data->dev_class[2],
+                          data->dev_class[1], data->dev_class[0],
+                          __le16_to_cpu(data->clock_offset),
+                          data->rssi, data->ssp_mode, e->timestamp);
+       }
+
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, inquiry_cache_show, inode->i_private);
+}
+
+static const struct file_operations inquiry_cache_fops = {
+       .open           = inquiry_cache_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int link_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
+
+       hci_dev_lock(hdev);
+       list_for_each_safe(p, n, &hdev->link_keys) {
+               struct link_key *key = list_entry(p, struct link_key, list);
+               seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
+                          HCI_LINK_KEY_SIZE, key->val, key->pin_len);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int link_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, link_keys_show, inode->i_private);
+}
+
+static const struct file_operations link_keys_fops = {
+       .open           = link_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+       .open           = simple_open,
+       .read           = use_debug_keys_read,
+       .llseek         = default_llseek,
+};
+
+static int dev_class_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
+                  hdev->dev_class[1], hdev->dev_class[0]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int dev_class_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dev_class_show, inode->i_private);
+}
+
+static const struct file_operations dev_class_fops = {
+       .open           = dev_class_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int voice_setting_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->voice_setting;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
+                       NULL, "0x%4.4llx\n");
+
+static int auto_accept_delay_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       hdev->auto_accept_delay = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->auto_accept_delay;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+                       auto_accept_delay_set, "%llu\n");
+
+static int ssp_debug_mode_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+       struct sk_buff *skb;
+       __u8 mode;
+       int err;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       hci_req_lock(hdev);
+       mode = val;
+       skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode),
+                            &mode, HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       hci_dev_lock(hdev);
+       hdev->ssp_debug_mode = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int ssp_debug_mode_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->ssp_debug_mode;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
+                       ssp_debug_mode_set, "%llu\n");
+
+static int idle_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && (val < 500 || val > 3600000))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->idle_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int idle_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->idle_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+                       idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+                       sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+                       sniff_max_interval_set, "%llu\n");
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->static_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+       .open           = static_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int own_address_type_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->own_addr_type = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int own_address_type_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->own_addr_type;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get,
+                       own_address_type_set, "%llu\n");
+
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
+
+       hci_dev_lock(hdev);
+       list_for_each_safe(p, n, &hdev->link_keys) {
+               struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+               seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n",
+                          &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+                          ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+                          8, ltk->rand, 16, ltk->val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+       .open           = long_term_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_min_interval = val;
+       hci_dev_unlock(hdev);
 
-#include <linux/rfkill.h>
+       return 0;
+}
 
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
+static int conn_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
 
-static void hci_rx_work(struct work_struct *work);
-static void hci_cmd_work(struct work_struct *work);
-static void hci_tx_work(struct work_struct *work);
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_min_interval;
+       hci_dev_unlock(hdev);
 
-/* HCI device list */
-LIST_HEAD(hci_dev_list);
-DEFINE_RWLOCK(hci_dev_list_lock);
+       return 0;
+}
 
-/* HCI callback list */
-LIST_HEAD(hci_cb_list);
-DEFINE_RWLOCK(hci_cb_list_lock);
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+                       conn_min_interval_set, "%llu\n");
 
-/* HCI ID Numbering */
-static DEFINE_IDA(hci_index_ida);
+static int conn_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
 
-/* ---- HCI notifications ---- */
+       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+               return -EINVAL;
 
-static void hci_notify(struct hci_dev *hdev, int event)
+       hci_dev_lock(hdev);
+       hdev->le_conn_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
 {
-       hci_sock_dev_event(hdev, event);
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
 }
 
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+                       conn_max_interval_set, "%llu\n");
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -307,11 +888,23 @@ static void amp_init(struct hci_request *req)
        /* Read Local Version */
        hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
 
+       /* Read Local Supported Commands */
+       hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
+
+       /* Read Local Supported Features */
+       hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+
        /* Read Local AMP Info */
        hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
 
        /* Read Data Blk size */
        hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
+
+       /* Read Flow Control Mode */
+       hci_req_add(req, HCI_OP_READ_FLOW_CONTROL_MODE, 0, NULL);
+
+       /* Read Location Data */
+       hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
 }
 
 static void hci_init1_req(struct hci_request *req, unsigned long opt)
@@ -341,6 +934,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
 
 static void bredr_setup(struct hci_request *req)
 {
+       struct hci_dev *hdev = req->hdev;
+
        __le16 param;
        __u8 flt_type;
 
@@ -356,6 +951,12 @@ static void bredr_setup(struct hci_request *req)
        /* Read Voice Setting */
        hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL);
 
+       /* Read Number of Supported IAC */
+       hci_req_add(req, HCI_OP_READ_NUM_SUPPORTED_IAC, 0, NULL);
+
+       /* Read Current IAC LAP */
+       hci_req_add(req, HCI_OP_READ_CURRENT_IAC_LAP, 0, NULL);
+
        /* Clear Event Filters */
        flt_type = HCI_FLT_CLEAR_ALL;
        hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
@@ -364,8 +965,10 @@ static void bredr_setup(struct hci_request *req)
        param = __constant_cpu_to_le16(0x7d00);
        hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
 
-       /* Read page scan parameters */
-       if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) {
+       /* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2,
+        * but it does not support page scan related HCI commands.
+        */
+       if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) {
                hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
                hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL);
        }
@@ -519,6 +1122,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
 
        if (lmp_bredr_capable(hdev))
                bredr_setup(req);
+       else
+               clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
 
        if (lmp_le_capable(hdev))
                le_setup(req);
@@ -532,6 +1137,14 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
                hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
        if (lmp_ssp_capable(hdev)) {
+               /* When SSP is available, then the host features page
+                * should also be available as well. However some
+                * controllers list the max_page as 0 as long as SSP
+                * has not been enabled. To achieve proper debugging
+                * output, force the minimum max_page to 1 at least.
+                */
+               hdev->max_page = 0x01;
+
                if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
                        u8 mode = 0x01;
                        hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
@@ -607,6 +1220,34 @@ static void hci_set_le_support(struct hci_request *req)
                            &cp);
 }
 
+static void hci_set_event_mask_page_2(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       u8 events[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       /* If Connectionless Slave Broadcast master role is supported
+        * enable all necessary events for it.
+        */
+       if (hdev->features[2][0] & 0x01) {
+               events[1] |= 0x40;      /* Triggered Clock Capture */
+               events[1] |= 0x80;      /* Synchronization Train Complete */
+               events[2] |= 0x10;      /* Slave Page Response Timeout */
+               events[2] |= 0x20;      /* CSB Channel Map Change */
+       }
+
+       /* If Connectionless Slave Broadcast slave role is supported
+        * enable all necessary events for it.
+        */
+       if (hdev->features[2][0] & 0x02) {
+               events[2] |= 0x01;      /* Synchronization Train Received */
+               events[2] |= 0x02;      /* CSB Receive */
+               events[2] |= 0x04;      /* CSB Timeout */
+               events[2] |= 0x08;      /* Truncated Page Complete */
+       }
+
+       hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
+}
+
 static void hci_init3_req(struct hci_request *req, unsigned long opt)
 {
        struct hci_dev *hdev = req->hdev;
@@ -634,8 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                hci_setup_link_policy(req);
 
        if (lmp_le_capable(hdev)) {
+               /* If the controller has a public BD_ADDR, then by
+                * default use that one. If this is a LE only
+                * controller without one, default to the random
+                * address.
+                */
+               if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+                       hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+
                hci_set_le_support(req);
-               hci_update_ad(req);
        }
 
        /* Read features beyond page 1 if available */
@@ -648,6 +1298,19 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
        }
 }
 
+static void hci_init4_req(struct hci_request *req, unsigned long opt)
+{
+       struct hci_dev *hdev = req->hdev;
+
+       /* Set event mask page 2 if the HCI command for it is supported */
+       if (hdev->commands[22] & 0x04)
+               hci_set_event_mask_page_2(req);
+
+       /* Check for Synchronization Train support */
+       if (hdev->features[2][0] & 0x04)
+               hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
+}
+
 static int __hci_init(struct hci_dev *hdev)
 {
        int err;
@@ -656,6 +1319,14 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
+       /* The Device Under Test (DUT) mode is special and available for
+        * all controller types. So just create it early on.
+        */
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+                                   &dut_mode_fops);
+       }
+
        /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
         * BR/EDR/LE type controllers. AMP controllers only need the
         * first stage init.
@@ -667,7 +1338,75 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
-       return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+       err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       /* Only create debugfs entries during the initial setup
+        * phase and not every time the controller gets powered on.
+        */
+       if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+               return 0;
+
+       debugfs_create_file("features", 0444, hdev->debugfs, hdev,
+                           &features_fops);
+       debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
+                          &hdev->manufacturer);
+       debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
+       debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+       debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
+                           &blacklist_fops);
+       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+
+       if (lmp_bredr_capable(hdev)) {
+               debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
+                                   hdev, &inquiry_cache_fops);
+               debugfs_create_file("link_keys", 0400, hdev->debugfs,
+                                   hdev, &link_keys_fops);
+               debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+                                   hdev, &use_debug_keys_fops);
+               debugfs_create_file("dev_class", 0444, hdev->debugfs,
+                                   hdev, &dev_class_fops);
+               debugfs_create_file("voice_setting", 0444, hdev->debugfs,
+                                   hdev, &voice_setting_fops);
+       }
+
+       if (lmp_ssp_capable(hdev)) {
+               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+                                   hdev, &auto_accept_delay_fops);
+               debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
+                                   hdev, &ssp_debug_mode_fops);
+       }
+
+       if (lmp_sniff_capable(hdev)) {
+               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+                                   hdev, &idle_timeout_fops);
+               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_min_interval_fops);
+               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_max_interval_fops);
+       }
+
+       if (lmp_le_capable(hdev)) {
+               debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+                                 &hdev->le_white_list_size);
+               debugfs_create_file("static_address", 0444, hdev->debugfs,
+                                  hdev, &static_address_fops);
+               debugfs_create_file("own_address_type", 0644, hdev->debugfs,
+                                   hdev, &own_address_type_fops);
+               debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
+                                   hdev, &long_term_keys_fops);
+               debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
+                                   hdev, &conn_min_interval_fops);
+               debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
+                                   hdev, &conn_max_interval_fops);
+       }
+
+       return 0;
 }
 
 static void hci_scan_req(struct hci_request *req, unsigned long opt)
@@ -984,6 +1723,21 @@ int hci_inquiry(void __user *arg)
        if (!hdev)
                return -ENODEV;
 
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (hdev->dev_type != HCI_BREDR) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        hci_dev_lock(hdev);
        if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
            inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
@@ -1043,100 +1797,10 @@ done:
        return err;
 }
 
-static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
-{
-       u8 ad_len = 0, flags = 0;
-       size_t name_len;
-
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
-               flags |= LE_AD_GENERAL;
-
-       if (!lmp_bredr_capable(hdev))
-               flags |= LE_AD_NO_BREDR;
-
-       if (lmp_le_br_capable(hdev))
-               flags |= LE_AD_SIM_LE_BREDR_CTRL;
-
-       if (lmp_host_le_br_capable(hdev))
-               flags |= LE_AD_SIM_LE_BREDR_HOST;
-
-       if (flags) {
-               BT_DBG("adv flags 0x%02x", flags);
-
-               ptr[0] = 2;
-               ptr[1] = EIR_FLAGS;
-               ptr[2] = flags;
-
-               ad_len += 3;
-               ptr += 3;
-       }
-
-       if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
-               ptr[0] = 2;
-               ptr[1] = EIR_TX_POWER;
-               ptr[2] = (u8) hdev->adv_tx_power;
-
-               ad_len += 3;
-               ptr += 3;
-       }
-
-       name_len = strlen(hdev->dev_name);
-       if (name_len > 0) {
-               size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
-
-               if (name_len > max_len) {
-                       name_len = max_len;
-                       ptr[1] = EIR_NAME_SHORT;
-               } else
-                       ptr[1] = EIR_NAME_COMPLETE;
-
-               ptr[0] = name_len + 1;
-
-               memcpy(ptr + 2, hdev->dev_name, name_len);
-
-               ad_len += (name_len + 2);
-               ptr += (name_len + 2);
-       }
-
-       return ad_len;
-}
-
-void hci_update_ad(struct hci_request *req)
-{
-       struct hci_dev *hdev = req->hdev;
-       struct hci_cp_le_set_adv_data cp;
-       u8 len;
-
-       if (!lmp_le_capable(hdev))
-               return;
-
-       memset(&cp, 0, sizeof(cp));
-
-       len = create_ad(hdev, cp.data);
-
-       if (hdev->adv_data_len == len &&
-           memcmp(cp.data, hdev->adv_data, len) == 0)
-               return;
-
-       memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
-       hdev->adv_data_len = len;
-
-       cp.length = len;
-
-       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
-}
-
-/* ---- HCI ioctl helpers ---- */
-
-int hci_dev_open(__u16 dev)
+static int hci_dev_do_open(struct hci_dev *hdev)
 {
-       struct hci_dev *hdev;
        int ret = 0;
 
-       hdev = hci_dev_get(dev);
-       if (!hdev)
-               return -ENODEV;
-
        BT_DBG("%s %p", hdev->name, hdev);
 
        hci_req_lock(hdev);
@@ -1146,13 +1810,29 @@ int hci_dev_open(__u16 dev)
                goto done;
        }
 
-       /* Check for rfkill but allow the HCI setup stage to proceed
-        * (which in itself doesn't cause any RF activity).
-        */
-       if (test_bit(HCI_RFKILLED, &hdev->dev_flags) &&
-           !test_bit(HCI_SETUP, &hdev->dev_flags)) {
-               ret = -ERFKILL;
-               goto done;
+       if (!test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               /* Check for rfkill but allow the HCI setup stage to
+                * proceed (which in itself doesn't cause any RF activity).
+                */
+               if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+                       ret = -ERFKILL;
+                       goto done;
+               }
+
+               /* Check for valid public address or a configured static
+                * random adddress, but let the HCI setup proceed to
+                * be able to determine if there is a public address
+                * or not.
+                *
+                * This check is only valid for BR/EDR controllers
+                * since AMP controllers do not have an address.
+                */
+               if (hdev->dev_type == HCI_BREDR &&
+                   !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
+                   !bacmp(&hdev->static_addr, BDADDR_ANY)) {
+                       ret = -EADDRNOTAVAIL;
+                       goto done;
+               }
        }
 
        if (test_bit(HCI_UP, &hdev->flags)) {
@@ -1172,16 +1852,11 @@ int hci_dev_open(__u16 dev)
                ret = hdev->setup(hdev);
 
        if (!ret) {
-               /* Treat all non BR/EDR controllers as raw devices if
-                * enable_hs is not set.
-                */
-               if (hdev->dev_type != HCI_BREDR && !enable_hs)
-                       set_bit(HCI_RAW, &hdev->flags);
-
                if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
                        set_bit(HCI_RAW, &hdev->flags);
 
-               if (!test_bit(HCI_RAW, &hdev->flags))
+               if (!test_bit(HCI_RAW, &hdev->flags) &&
+                   !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
                        ret = __hci_init(hdev);
        }
 
@@ -1192,7 +1867,8 @@ int hci_dev_open(__u16 dev)
                set_bit(HCI_UP, &hdev->flags);
                hci_notify(hdev, HCI_DEV_UP);
                if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
-                   mgmt_valid_hdev(hdev)) {
+                   !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+                   hdev->dev_type == HCI_BREDR) {
                        hci_dev_lock(hdev);
                        mgmt_powered(hdev, 1);
                        hci_dev_unlock(hdev);
@@ -1220,10 +1896,41 @@ int hci_dev_open(__u16 dev)
 
 done:
        hci_req_unlock(hdev);
-       hci_dev_put(hdev);
        return ret;
 }
 
+/* ---- HCI ioctl helpers ---- */
+
+int hci_dev_open(__u16 dev)
+{
+       struct hci_dev *hdev;
+       int err;
+
+       hdev = hci_dev_get(dev);
+       if (!hdev)
+               return -ENODEV;
+
+       /* We need to ensure that no other power on/off work is pending
+        * before proceeding to call hci_dev_do_open. This is
+        * particularly important if the setup procedure has not yet
+        * completed.
+        */
+       if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+               cancel_delayed_work(&hdev->power_off);
+
+       /* After this call it is guaranteed that the setup procedure
+        * has finished. This means that error conditions like RFKILL
+        * or no valid public or static random address apply.
+        */
+       flush_workqueue(hdev->req_workqueue);
+
+       err = hci_dev_do_open(hdev);
+
+       hci_dev_put(hdev);
+
+       return err;
+}
+
 static int hci_dev_do_close(struct hci_dev *hdev)
 {
        BT_DBG("%s %p", hdev->name, hdev);
@@ -1247,6 +1954,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
                cancel_delayed_work(&hdev->discov_off);
                hdev->discov_timeout = 0;
                clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
        }
 
        if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
@@ -1268,6 +1976,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        skb_queue_purge(&hdev->cmd_q);
        atomic_set(&hdev->cmd_cnt, 1);
        if (!test_bit(HCI_RAW, &hdev->flags) &&
+           !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
            test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
                set_bit(HCI_INIT, &hdev->flags);
                __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
@@ -1300,15 +2009,16 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        hdev->flags = 0;
        hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
 
-       if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
-           mgmt_valid_hdev(hdev)) {
-               hci_dev_lock(hdev);
-               mgmt_powered(hdev, 0);
-               hci_dev_unlock(hdev);
+       if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+               if (hdev->dev_type == HCI_BREDR) {
+                       hci_dev_lock(hdev);
+                       mgmt_powered(hdev, 0);
+                       hci_dev_unlock(hdev);
+               }
        }
 
        /* Controller radio is available but is currently powered down */
-       hdev->amp_status = 0;
+       hdev->amp_status = AMP_STATUS_POWERED_DOWN;
 
        memset(hdev->eir, 0, sizeof(hdev->eir));
        memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
@@ -1328,11 +2038,17 @@ int hci_dev_close(__u16 dev)
        if (!hdev)
                return -ENODEV;
 
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               err = -EBUSY;
+               goto done;
+       }
+
        if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
                cancel_delayed_work(&hdev->power_off);
 
        err = hci_dev_do_close(hdev);
 
+done:
        hci_dev_put(hdev);
        return err;
 }
@@ -1348,8 +2064,15 @@ int hci_dev_reset(__u16 dev)
 
        hci_req_lock(hdev);
 
-       if (!test_bit(HCI_UP, &hdev->flags))
+       if (!test_bit(HCI_UP, &hdev->flags)) {
+               ret = -ENETDOWN;
+               goto done;
+       }
+
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               ret = -EBUSY;
                goto done;
+       }
 
        /* Drop queues */
        skb_queue_purge(&hdev->rx_q);
@@ -1384,10 +2107,15 @@ int hci_dev_reset_stat(__u16 dev)
        if (!hdev)
                return -ENODEV;
 
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               ret = -EBUSY;
+               goto done;
+       }
+
        memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
 
+done:
        hci_dev_put(hdev);
-
        return ret;
 }
 
@@ -1404,6 +2132,21 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
        if (!hdev)
                return -ENODEV;
 
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (hdev->dev_type != HCI_BREDR) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        switch (cmd) {
        case HCISETAUTH:
                err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
@@ -1462,6 +2205,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
                break;
        }
 
+done:
        hci_dev_put(hdev);
        return err;
 }
@@ -1534,7 +2278,7 @@ int hci_get_dev_info(void __user *arg)
 
        strcpy(di.name, hdev->name);
        di.bdaddr   = hdev->bdaddr;
-       di.type     = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
+       di.type     = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4);
        di.flags    = hdev->flags;
        di.pkt_type = hdev->pkt_type;
        if (lmp_bredr_capable(hdev)) {
@@ -1570,13 +2314,16 @@ static int hci_rfkill_set_block(void *data, bool blocked)
 
        BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
 
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+               return -EBUSY;
+
        if (blocked) {
                set_bit(HCI_RFKILLED, &hdev->dev_flags);
                if (!test_bit(HCI_SETUP, &hdev->dev_flags))
                        hci_dev_do_close(hdev);
        } else {
                clear_bit(HCI_RFKILLED, &hdev->dev_flags);
-}
+       }
 
        return 0;
 }
@@ -1592,13 +2339,20 @@ static void hci_power_on(struct work_struct *work)
 
        BT_DBG("%s", hdev->name);
 
-       err = hci_dev_open(hdev->id);
+       err = hci_dev_do_open(hdev);
        if (err < 0) {
                mgmt_set_powered_failed(hdev, err);
                return;
        }
 
-       if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+       /* During the HCI setup phase, a few error conditions are
+        * ignored and they need to be checked now. If they are still
+        * valid, it is important to turn the device back off.
+        */
+       if (test_bit(HCI_RFKILLED, &hdev->dev_flags) ||
+           (hdev->dev_type == HCI_BREDR &&
+            !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
+            !bacmp(&hdev->static_addr, BDADDR_ANY))) {
                clear_bit(HCI_AUTO_OFF, &hdev->dev_flags);
                hci_dev_do_close(hdev);
        } else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
@@ -1623,19 +2377,12 @@ static void hci_power_off(struct work_struct *work)
 static void hci_discov_off(struct work_struct *work)
 {
        struct hci_dev *hdev;
-       u8 scan = SCAN_PAGE;
 
        hdev = container_of(work, struct hci_dev, discov_off.work);
 
        BT_DBG("%s", hdev->name);
 
-       hci_dev_lock(hdev);
-
-       hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-
-       hdev->discov_timeout = 0;
-
-       hci_dev_unlock(hdev);
+       mgmt_discoverable_timeout(hdev);
 }
 
 int hci_uuids_clear(struct hci_dev *hdev)
@@ -1958,13 +2705,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
        return 0;
 }
 
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
+                                        bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *b;
 
-       list_for_each_entry(b, &hdev->blacklist, list)
-               if (bacmp(bdaddr, &b->bdaddr) == 0)
+       list_for_each_entry(b, &hdev->blacklist, list) {
+               if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
                        return b;
+       }
 
        return NULL;
 }
@@ -1974,9 +2723,7 @@ int hci_blacklist_clear(struct hci_dev *hdev)
        struct list_head *p, *n;
 
        list_for_each_safe(p, n, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(p, struct bdaddr_list, list);
+               struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
 
                list_del(p);
                kfree(b);
@@ -1989,10 +2736,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+       if (!bacmp(bdaddr, BDADDR_ANY))
                return -EBADF;
 
-       if (hci_blacklist_lookup(hdev, bdaddr))
+       if (hci_blacklist_lookup(hdev, bdaddr, type))
                return -EEXIST;
 
        entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
@@ -2000,6 +2747,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
                return -ENOMEM;
 
        bacpy(&entry->bdaddr, bdaddr);
+       entry->bdaddr_type = type;
 
        list_add(&entry->list, &hdev->blacklist);
 
@@ -2010,10 +2758,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+       if (!bacmp(bdaddr, BDADDR_ANY))
                return hci_blacklist_clear(hdev);
 
-       entry = hci_blacklist_lookup(hdev, bdaddr);
+       entry = hci_blacklist_lookup(hdev, bdaddr, type);
        if (!entry)
                return -ENOENT;
 
@@ -2111,13 +2859,19 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
        hdev->esco_type = (ESCO_HV1);
        hdev->link_mode = (HCI_LM_ACCEPT);
-       hdev->io_capability = 0x03; /* No Input No Output */
+       hdev->num_iac = 0x01;           /* One IAC support is mandatory */
+       hdev->io_capability = 0x03;     /* No Input No Output */
        hdev->inq_tx_power = HCI_TX_POWER_INVALID;
        hdev->adv_tx_power = HCI_TX_POWER_INVALID;
 
        hdev->sniff_max_interval = 800;
        hdev->sniff_min_interval = 80;
 
+       hdev->le_scan_interval = 0x0060;
+       hdev->le_scan_window = 0x0030;
+       hdev->le_conn_min_interval = 0x0028;
+       hdev->le_conn_max_interval = 0x0038;
+
        mutex_init(&hdev->lock);
        mutex_init(&hdev->req_lock);
 
@@ -2206,7 +2960,12 @@ int hci_register_dev(struct hci_dev *hdev)
                goto err;
        }
 
-       error = hci_add_sysfs(hdev);
+       if (!IS_ERR_OR_NULL(bt_debugfs))
+               hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
+
+       dev_set_name(&hdev->dev, "%s", hdev->name);
+
+       error = device_add(&hdev->dev);
        if (error < 0)
                goto err_wqueue;
 
@@ -2224,9 +2983,14 @@ int hci_register_dev(struct hci_dev *hdev)
                set_bit(HCI_RFKILLED, &hdev->dev_flags);
 
        set_bit(HCI_SETUP, &hdev->dev_flags);
+       set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
 
-       if (hdev->dev_type != HCI_AMP)
-               set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+       if (hdev->dev_type == HCI_BREDR) {
+               /* Assume BR/EDR support until proven otherwise (such as
+                * through reading supported features during init.
+                */
+               set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+       }
 
        write_lock(&hci_dev_list_lock);
        list_add(&hdev->list, &hci_dev_list);
@@ -2289,7 +3053,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
                rfkill_destroy(hdev->rfkill);
        }
 
-       hci_del_sysfs(hdev);
+       device_del(&hdev->dev);
+
+       debugfs_remove_recursive(hdev->debugfs);
 
        destroy_workqueue(hdev->workqueue);
        destroy_workqueue(hdev->req_workqueue);
@@ -2325,9 +3091,8 @@ int hci_resume_dev(struct hci_dev *hdev)
 EXPORT_SYMBOL(hci_resume_dev);
 
 /* Receive frame from HCI drivers */
-int hci_recv_frame(struct sk_buff *skb)
+int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
        if (!hdev || (!test_bit(HCI_UP, &hdev->flags)
                      && !test_bit(HCI_INIT, &hdev->flags))) {
                kfree_skb(skb);
@@ -2386,7 +3151,6 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
                scb->expect = hlen;
                scb->pkt_type = type;
 
-               skb->dev = (void *) hdev;
                hdev->reassembly[index] = skb;
        }
 
@@ -2446,7 +3210,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
                        /* Complete frame */
 
                        bt_cb(skb)->pkt_type = type;
-                       hci_recv_frame(skb);
+                       hci_recv_frame(hdev, skb);
 
                        hdev->reassembly[index] = NULL;
                        return remain;
@@ -2537,15 +3301,8 @@ int hci_unregister_cb(struct hci_cb *cb)
 }
 EXPORT_SYMBOL(hci_unregister_cb);
 
-static int hci_send_frame(struct sk_buff *skb)
+static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
-       if (!hdev) {
-               kfree_skb(skb);
-               return -ENODEV;
-       }
-
        BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
        /* Time stamp */
@@ -2562,7 +3319,8 @@ static int hci_send_frame(struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
-       return hdev->send(skb);
+       if (hdev->send(hdev, skb) < 0)
+               BT_ERR("%s sending frame failed", hdev->name);
 }
 
 void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
@@ -2625,7 +3383,6 @@ static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
        BT_DBG("skb len %d", skb->len);
 
        bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
-       skb->dev = (void *) hdev;
 
        return skb;
 }
@@ -2769,7 +3526,6 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
                do {
                        skb = list; list = list->next;
 
-                       skb->dev = (void *) hdev;
                        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
                        hci_add_acl_hdr(skb, conn->handle, flags);
 
@@ -2788,8 +3544,6 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
 
        BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
 
-       skb->dev = (void *) hdev;
-
        hci_queue_acl(chan, &chan->data_q, skb, flags);
 
        queue_work(hdev->workqueue, &hdev->tx_work);
@@ -2810,7 +3564,6 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
        skb_reset_transport_header(skb);
        memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
 
-       skb->dev = (void *) hdev;
        bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
 
        skb_queue_tail(&conn->data_q, skb);
@@ -3075,7 +3828,7 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
                        hci_conn_enter_active_mode(chan->conn,
                                                   bt_cb(skb)->force_active);
 
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        hdev->acl_last_tx = jiffies;
 
                        hdev->acl_cnt--;
@@ -3127,7 +3880,7 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
                        hci_conn_enter_active_mode(chan->conn,
                                                   bt_cb(skb)->force_active);
 
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        hdev->acl_last_tx = jiffies;
 
                        hdev->block_cnt -= blocks;
@@ -3180,7 +3933,7 @@ static void hci_sched_sco(struct hci_dev *hdev)
        while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
                while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
                        BT_DBG("skb %p len %d", skb, skb->len);
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
 
                        conn->sent++;
                        if (conn->sent == ~0)
@@ -3204,7 +3957,7 @@ static void hci_sched_esco(struct hci_dev *hdev)
                                                     &quote))) {
                while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
                        BT_DBG("skb %p len %d", skb, skb->len);
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
 
                        conn->sent++;
                        if (conn->sent == ~0)
@@ -3246,7 +3999,7 @@ static void hci_sched_le(struct hci_dev *hdev)
 
                        skb = skb_dequeue(&chan->data_q);
 
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        hdev->le_last_tx = jiffies;
 
                        cnt--;
@@ -3272,19 +4025,17 @@ static void hci_tx_work(struct work_struct *work)
        BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
               hdev->sco_cnt, hdev->le_cnt);
 
-       /* Schedule queues and send stuff to HCI driver */
-
-       hci_sched_acl(hdev);
-
-       hci_sched_sco(hdev);
-
-       hci_sched_esco(hdev);
-
-       hci_sched_le(hdev);
+       if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               /* Schedule queues and send stuff to HCI driver */
+               hci_sched_acl(hdev);
+               hci_sched_sco(hdev);
+               hci_sched_esco(hdev);
+               hci_sched_le(hdev);
+       }
 
        /* Send next queued raw (unknown type) packet */
        while ((skb = skb_dequeue(&hdev->raw_q)))
-               hci_send_frame(skb);
+               hci_send_frame(hdev, skb);
 }
 
 /* ----- HCI RX task (incoming data processing) ----- */
@@ -3471,7 +4222,8 @@ static void hci_rx_work(struct work_struct *work)
                        hci_send_to_sock(hdev, skb);
                }
 
-               if (test_bit(HCI_RAW, &hdev->flags)) {
+               if (test_bit(HCI_RAW, &hdev->flags) ||
+                   test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
                        kfree_skb(skb);
                        continue;
                }
@@ -3526,10 +4278,10 @@ static void hci_cmd_work(struct work_struct *work)
 
                kfree_skb(hdev->sent_cmd);
 
-               hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
+               hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
                if (hdev->sent_cmd) {
                        atomic_dec(&hdev->cmd_cnt);
-                       hci_send_frame(skb);
+                       hci_send_frame(hdev, skb);
                        if (test_bit(HCI_RESET, &hdev->flags))
                                del_timer(&hdev->cmd_timer);
                        else
@@ -3541,15 +4293,3 @@ static void hci_cmd_work(struct work_struct *work)
                }
        }
 }
-
-u8 bdaddr_to_le(u8 bdaddr_type)
-{
-       switch (bdaddr_type) {
-       case BDADDR_LE_PUBLIC:
-               return ADDR_LE_DEV_PUBLIC;
-
-       default:
-               /* Fallback to LE Random address type */
-               return ADDR_LE_DEV_RANDOM;
-       }
-}
index 8db3e89..5935f74 100644 (file)
@@ -29,8 +29,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "a2mp.h"
+#include "amp.h"
 
 /* Handle HCI Event packets */
 
@@ -194,6 +195,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
 
        memset(hdev->adv_data, 0, sizeof(hdev->adv_data));
        hdev->adv_data_len = 0;
+
+       memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data));
+       hdev->scan_rsp_data_len = 0;
+
+       hdev->ssp_debug_mode = 0;
 }
 
 static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -297,6 +303,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
                goto done;
        }
 
+       /* We need to ensure that we set this back on if someone changed
+        * the scan mode through a raw HCI socket.
+        */
+       set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
        old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
        old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
 
@@ -304,11 +315,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
                set_bit(HCI_ISCAN, &hdev->flags);
                if (!old_iscan)
                        mgmt_discoverable(hdev, 1);
-               if (hdev->discov_timeout > 0) {
-                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
-                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
-                                          to);
-               }
        } else if (old_iscan)
                mgmt_discoverable(hdev, 0);
 
@@ -412,6 +418,21 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev,
                hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
 }
 
+static void hci_cc_read_num_supported_iac(struct hci_dev *hdev,
+                                         struct sk_buff *skb)
+{
+       struct hci_rp_read_num_supported_iac *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hdev->num_iac = rp->num_iac;
+
+       BT_DBG("%s num iac %d", hdev->name, hdev->num_iac);
+}
+
 static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -449,14 +470,13 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
        if (rp->status)
                return;
 
-       hdev->hci_ver = rp->hci_ver;
-       hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
-       hdev->lmp_ver = rp->lmp_ver;
-       hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
-       hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
-
-       BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name,
-              hdev->manufacturer, hdev->hci_ver, hdev->hci_rev);
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               hdev->hci_ver = rp->hci_ver;
+               hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
+               hdev->lmp_ver = rp->lmp_ver;
+               hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
+               hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
+       }
 }
 
 static void hci_cc_read_local_commands(struct hci_dev *hdev,
@@ -536,7 +556,8 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
        if (rp->status)
                return;
 
-       hdev->max_page = rp->max_page;
+       if (hdev->max_page < rp->max_page)
+               hdev->max_page = rp->max_page;
 
        if (rp->page < HCI_MAX_PAGES)
                memcpy(hdev->features[rp->page], rp->features, 8);
@@ -913,17 +934,9 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
 
        if (!status) {
                if (*sent)
-                       set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+                       set_bit(HCI_ADVERTISING, &hdev->dev_flags);
                else
-                       clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
-       }
-
-       if (!test_bit(HCI_INIT, &hdev->flags)) {
-               struct hci_request req;
-
-               hci_req_init(&req, hdev);
-               hci_update_ad(&req);
-               hci_req_run(&req, NULL);
+                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
        }
 
        hci_dev_unlock(hdev);
@@ -994,20 +1007,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
                return;
 
        if (!status) {
-               if (sent->le)
+               if (sent->le) {
                        hdev->features[1][0] |= LMP_HOST_LE;
-               else
+                       set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+               } else {
                        hdev->features[1][0] &= ~LMP_HOST_LE;
+                       clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+               }
 
                if (sent->simul)
                        hdev->features[1][0] |= LMP_HOST_LE_BREDR;
                else
                        hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
        }
-
-       if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
-           !test_bit(HCI_INIT, &hdev->flags))
-               mgmt_le_enable_complete(hdev, sent->le, status);
 }
 
 static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
@@ -1291,9 +1304,11 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
                goto unlock;
 
        if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
-               struct hci_cp_auth_requested cp;
-               cp.handle = __cpu_to_le16(conn->handle);
-               hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
+               struct hci_cp_auth_requested auth_cp;
+
+               auth_cp.handle = __cpu_to_le16(conn->handle);
+               hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
+                            sizeof(auth_cp), &auth_cp);
        }
 
 unlock:
@@ -1465,33 +1480,6 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
        hci_dev_unlock(hdev);
 }
 
-static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
-{
-       struct hci_conn *conn;
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       if (status) {
-               hci_dev_lock(hdev);
-
-               conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
-               if (!conn) {
-                       hci_dev_unlock(hdev);
-                       return;
-               }
-
-               BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn);
-
-               conn->state = BT_CLOSED;
-               mgmt_connect_failed(hdev, &conn->dst, conn->type,
-                                   conn->dst_type, status);
-               hci_proto_connect_cfm(conn, status);
-               hci_conn_del(conn);
-
-               hci_dev_unlock(hdev);
-       }
-}
-
 static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
 {
        struct hci_cp_create_phy_link *cp;
@@ -1706,7 +1694,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                                      &flags);
 
        if ((mask & HCI_LM_ACCEPT) &&
-           !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
+           !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) {
                /* Connection accepted */
                struct inquiry_entry *ie;
                struct hci_conn *conn;
@@ -1821,10 +1809,25 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 
        if (ev->status == 0) {
-               if (conn->type == ACL_LINK && conn->flush_key)
+               u8 type = conn->type;
+
+               if (type == ACL_LINK && conn->flush_key)
                        hci_remove_link_key(hdev, &conn->dst);
                hci_proto_disconn_cfm(conn, ev->reason);
                hci_conn_del(conn);
+
+               /* Re-enable advertising if necessary, since it might
+                * have been disabled by the connection. From the
+                * HCI_LE_Set_Advertise_Enable command description in
+                * the core specification (v4.0):
+                * "The Controller shall continue advertising until the Host
+                * issues an LE_Set_Advertise_Enable command with
+                * Advertising_Enable set to 0x00 (Advertising is disabled)
+                * or until a connection is created or until the Advertising
+                * is timed out due to Directed Advertising."
+                */
+               if (type == LE_LINK)
+                       mgmt_reenable_advertising(hdev);
        }
 
 unlock:
@@ -2139,6 +2142,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_write_voice_setting(hdev, skb);
                break;
 
+       case HCI_OP_READ_NUM_SUPPORTED_IAC:
+               hci_cc_read_num_supported_iac(hdev, skb);
+               break;
+
        case HCI_OP_WRITE_SSP_MODE:
                hci_cc_write_ssp_mode(hdev, skb);
                break;
@@ -2342,10 +2349,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_disconnect(hdev, ev->status);
                break;
 
-       case HCI_OP_LE_CREATE_CONN:
-               hci_cs_le_create_conn(hdev, ev->status);
-               break;
-
        case HCI_OP_CREATE_PHY_LINK:
                hci_cs_create_phylink(hdev, ev->status);
                break;
@@ -2548,7 +2551,6 @@ static void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                conn->mode = ev->mode;
-               conn->interval = __le16_to_cpu(ev->interval);
 
                if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND,
                                        &conn->flags)) {
@@ -2930,6 +2932,23 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static inline size_t eir_get_length(u8 *eir, size_t eir_len)
+{
+       size_t parsed = 0;
+
+       while (parsed < eir_len) {
+               u8 field_len = eir[0];
+
+               if (field_len == 0)
+                       return parsed;
+
+               parsed += field_len + 1;
+               eir += field_len + 1;
+       }
+
+       return eir_len;
+}
+
 static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                                            struct sk_buff *skb)
 {
@@ -3170,7 +3189,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
 
                if (hdev->auto_accept_delay > 0) {
                        int delay = msecs_to_jiffies(hdev->auto_accept_delay);
-                       mod_timer(&conn->auto_accept_timer, jiffies + delay);
+                       queue_delayed_work(conn->hdev->workqueue,
+                                          &conn->auto_accept_work, delay);
                        goto unlock;
                }
 
@@ -3485,6 +3505,17 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                conn->dst_type = ev->bdaddr_type;
 
+               /* The advertising parameters for own address type
+                * define which source address and source address
+                * type this connections has.
+                */
+               if (bacmp(&conn->src, BDADDR_ANY)) {
+                       conn->src_type = ADDR_LE_DEV_PUBLIC;
+               } else {
+                       bacpy(&conn->src, &hdev->static_addr);
+                       conn->src_type = ADDR_LE_DEV_RANDOM;
+               }
+
                if (ev->role == LE_CONN_ROLE_MASTER) {
                        conn->out = true;
                        conn->link_mode |= HCI_LM_MASTER;
@@ -3640,8 +3671,8 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
        skb_pull(skb, HCI_EVENT_HDR_SIZE);
 
        if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
-               struct hci_command_hdr *hdr = (void *) hdev->sent_cmd->data;
-               u16 opcode = __le16_to_cpu(hdr->opcode);
+               struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
+               u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
 
                hci_req_cmd_complete(hdev, opcode, 0);
        }
index 9bd7d95..71f0be1 100644 (file)
@@ -66,6 +66,46 @@ static struct bt_sock_list hci_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock)
 };
 
+static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb)
+{
+       struct hci_filter *flt;
+       int flt_type, flt_event;
+
+       /* Apply filter */
+       flt = &hci_pi(sk)->filter;
+
+       if (bt_cb(skb)->pkt_type == HCI_VENDOR_PKT)
+               flt_type = 0;
+       else
+               flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
+
+       if (!test_bit(flt_type, &flt->type_mask))
+               return true;
+
+       /* Extra filter for event packets only */
+       if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT)
+               return false;
+
+       flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
+
+       if (!hci_test_bit(flt_event, &flt->event_mask))
+               return true;
+
+       /* Check filter only when opcode is set */
+       if (!flt->opcode)
+               return false;
+
+       if (flt_event == HCI_EV_CMD_COMPLETE &&
+           flt->opcode != get_unaligned((__le16 *)(skb->data + 3)))
+               return true;
+
+       if (flt_event == HCI_EV_CMD_STATUS &&
+           flt->opcode != get_unaligned((__le16 *)(skb->data + 4)))
+               return true;
+
+       return false;
+}
+
 /* Send frame to RAW socket */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
 {
@@ -77,7 +117,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
        read_lock(&hci_sk_list.lock);
 
        sk_for_each(sk, &hci_sk_list.head) {
-               struct hci_filter *flt;
                struct sk_buff *nskb;
 
                if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
@@ -87,31 +126,19 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
                if (skb->sk == sk)
                        continue;
 
-               if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
-                       continue;
-
-               /* Apply filter */
-               flt = &hci_pi(sk)->filter;
-
-               if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ?
-                             0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS),
-                             &flt->type_mask))
-                       continue;
-
-               if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) {
-                       int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
-
-                       if (!hci_test_bit(evt, &flt->event_mask))
+               if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
+                       if (is_filtered_packet(sk, skb))
                                continue;
-
-                       if (flt->opcode &&
-                           ((evt == HCI_EV_CMD_COMPLETE &&
-                             flt->opcode !=
-                             get_unaligned((__le16 *)(skb->data + 3))) ||
-                            (evt == HCI_EV_CMD_STATUS &&
-                             flt->opcode !=
-                             get_unaligned((__le16 *)(skb->data + 4)))))
+               } else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+                       if (!bt_cb(skb)->incoming)
+                               continue;
+                       if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
+                           bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+                           bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
                                continue;
+               } else {
+                       /* Don't send frame to other channel types */
+                       continue;
                }
 
                if (!skb_copy) {
@@ -360,7 +387,6 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
        __net_timestamp(skb);
 
        bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
-       skb->dev = (void *) hdev;
        hci_send_to_sock(hdev, skb);
        kfree_skb(skb);
 }
@@ -426,6 +452,12 @@ static int hci_sock_release(struct socket *sock)
        bt_sock_unlink(&hci_sk_list, sk);
 
        if (hdev) {
+               if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+                       mgmt_index_added(hdev);
+                       clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+                       hci_dev_close(hdev->id);
+               }
+
                atomic_dec(&hdev->promisc);
                hci_dev_put(hdev);
        }
@@ -449,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_add(hdev, &bdaddr, 0);
+       err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -466,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_del(hdev, &bdaddr, 0);
+       err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -482,6 +514,12 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        if (!hdev)
                return -EBADFD;
 
+       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+               return -EBUSY;
+
+       if (hdev->dev_type != HCI_BREDR)
+               return -EOPNOTSUPP;
+
        switch (cmd) {
        case HCISETRAW:
                if (!capable(CAP_NET_ADMIN))
@@ -512,23 +550,29 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
                return hci_sock_blacklist_del(hdev, (void __user *) arg);
-
-       default:
-               if (hdev->ioctl)
-                       return hdev->ioctl(hdev, cmd, arg);
-               return -EINVAL;
        }
+
+       return -ENOIOCTLCMD;
 }
 
 static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
                          unsigned long arg)
 {
-       struct sock *sk = sock->sk;
        void __user *argp = (void __user *) arg;
+       struct sock *sk = sock->sk;
        int err;
 
        BT_DBG("cmd %x arg %lx", cmd, arg);
 
+       lock_sock(sk);
+
+       if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
+               err = -EBADFD;
+               goto done;
+       }
+
+       release_sock(sk);
+
        switch (cmd) {
        case HCIGETDEVLIST:
                return hci_get_dev_list(argp);
@@ -573,13 +617,15 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
 
        case HCIINQUIRY:
                return hci_inquiry(argp);
-
-       default:
-               lock_sock(sk);
-               err = hci_sock_bound_ioctl(sk, cmd, arg);
-               release_sock(sk);
-               return err;
        }
+
+       lock_sock(sk);
+
+       err = hci_sock_bound_ioctl(sk, cmd, arg);
+
+done:
+       release_sock(sk);
+       return err;
 }
 
 static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
@@ -629,6 +675,56 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
                hci_pi(sk)->hdev = hdev;
                break;
 
+       case HCI_CHANNEL_USER:
+               if (hci_pi(sk)->hdev) {
+                       err = -EALREADY;
+                       goto done;
+               }
+
+               if (haddr.hci_dev == HCI_DEV_NONE) {
+                       err = -EINVAL;
+                       goto done;
+               }
+
+               if (!capable(CAP_NET_ADMIN)) {
+                       err = -EPERM;
+                       goto done;
+               }
+
+               hdev = hci_dev_get(haddr.hci_dev);
+               if (!hdev) {
+                       err = -ENODEV;
+                       goto done;
+               }
+
+               if (test_bit(HCI_UP, &hdev->flags) ||
+                   test_bit(HCI_INIT, &hdev->flags) ||
+                   test_bit(HCI_SETUP, &hdev->dev_flags)) {
+                       err = -EBUSY;
+                       hci_dev_put(hdev);
+                       goto done;
+               }
+
+               if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+                       err = -EUSERS;
+                       hci_dev_put(hdev);
+                       goto done;
+               }
+
+               mgmt_index_removed(hdev);
+
+               err = hci_dev_open(hdev->id);
+               if (err) {
+                       clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+                       hci_dev_put(hdev);
+                       goto done;
+               }
+
+               atomic_inc(&hdev->promisc);
+
+               hci_pi(sk)->hdev = hdev;
+               break;
+
        case HCI_CHANNEL_CONTROL:
                if (haddr.hci_dev != HCI_DEV_NONE) {
                        err = -EINVAL;
@@ -677,22 +773,30 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
 {
        struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
        struct sock *sk = sock->sk;
-       struct hci_dev *hdev = hci_pi(sk)->hdev;
+       struct hci_dev *hdev;
+       int err = 0;
 
        BT_DBG("sock %p sk %p", sock, sk);
 
-       if (!hdev)
-               return -EBADFD;
+       if (peer)
+               return -EOPNOTSUPP;
 
        lock_sock(sk);
 
+       hdev = hci_pi(sk)->hdev;
+       if (!hdev) {
+               err = -EBADFD;
+               goto done;
+       }
+
        *addr_len = sizeof(*haddr);
        haddr->hci_family = AF_BLUETOOTH;
        haddr->hci_dev    = hdev->id;
-       haddr->hci_channel= 0;
+       haddr->hci_channel= hci_pi(sk)->channel;
 
+done:
        release_sock(sk);
-       return 0;
+       return err;
 }
 
 static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
@@ -767,6 +871,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        case HCI_CHANNEL_RAW:
                hci_sock_cmsg(sk, msg, skb);
                break;
+       case HCI_CHANNEL_USER:
        case HCI_CHANNEL_CONTROL:
        case HCI_CHANNEL_MONITOR:
                sock_recv_timestamp(msg, sk, skb);
@@ -801,6 +906,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        switch (hci_pi(sk)->channel) {
        case HCI_CHANNEL_RAW:
+       case HCI_CHANNEL_USER:
                break;
        case HCI_CHANNEL_CONTROL:
                err = mgmt_control(sk, msg, len);
@@ -835,9 +941,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        bt_cb(skb)->pkt_type = *((unsigned char *) skb->data);
        skb_pull(skb, 1);
-       skb->dev = (void *) hdev;
 
-       if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+       if (hci_pi(sk)->channel == HCI_CHANNEL_RAW &&
+           bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
                u16 opcode = get_unaligned_le16(skb->data);
                u16 ogf = hci_opcode_ogf(opcode);
                u16 ocf = hci_opcode_ocf(opcode);
@@ -868,6 +974,14 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                        goto drop;
                }
 
+               if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
+                   bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
+                   bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+                   bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+                       err = -EINVAL;
+                       goto drop;
+               }
+
                skb_queue_tail(&hdev->raw_q, skb);
                queue_work(hdev->workqueue, &hdev->tx_work);
        }
@@ -895,7 +1009,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
        lock_sock(sk);
 
        if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
-               err = -EINVAL;
+               err = -EBADFD;
                goto done;
        }
 
@@ -981,7 +1095,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
        lock_sock(sk);
 
        if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
-               err = -EINVAL;
+               err = -EBADFD;
                goto done;
        }
 
index edf623a..0b61250 100644 (file)
@@ -1,17 +1,12 @@
 /* Bluetooth HCI driver model support. */
 
-#include <linux/debugfs.h>
 #include <linux/module.h>
-#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
 static struct class *bt_class;
 
-struct dentry *bt_debugfs;
-EXPORT_SYMBOL_GPL(bt_debugfs);
-
 static inline char *link_typetostr(int type)
 {
        switch (type) {
@@ -42,29 +37,15 @@ static ssize_t show_link_address(struct device *dev,
        return sprintf(buf, "%pMR\n", &conn->dst);
 }
 
-static ssize_t show_link_features(struct device *dev,
-                                 struct device_attribute *attr, char *buf)
-{
-       struct hci_conn *conn = to_hci_conn(dev);
-
-       return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
-                      conn->features[0][0], conn->features[0][1],
-                      conn->features[0][2], conn->features[0][3],
-                      conn->features[0][4], conn->features[0][5],
-                      conn->features[0][6], conn->features[0][7]);
-}
-
 #define LINK_ATTR(_name, _mode, _show, _store) \
 struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
 
 static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
 static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
-static LINK_ATTR(features, S_IRUGO, show_link_features, NULL);
 
 static struct attribute *bt_link_attrs[] = {
        &link_attr_type.attr,
        &link_attr_address.attr,
-       &link_attr_features.attr,
        NULL
 };
 
@@ -150,28 +131,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
        hci_dev_put(hdev);
 }
 
-static inline char *host_bustostr(int bus)
-{
-       switch (bus) {
-       case HCI_VIRTUAL:
-               return "VIRTUAL";
-       case HCI_USB:
-               return "USB";
-       case HCI_PCCARD:
-               return "PCCARD";
-       case HCI_UART:
-               return "UART";
-       case HCI_RS232:
-               return "RS232";
-       case HCI_PCI:
-               return "PCI";
-       case HCI_SDIO:
-               return "SDIO";
-       default:
-               return "UNKNOWN";
-       }
-}
-
 static inline char *host_typetostr(int type)
 {
        switch (type) {
@@ -184,13 +143,6 @@ static inline char *host_typetostr(int type)
        }
 }
 
-static ssize_t show_bus(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%s\n", host_bustostr(hdev->bus));
-}
-
 static ssize_t show_type(struct device *dev,
                         struct device_attribute *attr, char *buf)
 {
@@ -212,14 +164,6 @@ static ssize_t show_name(struct device *dev,
        return sprintf(buf, "%s\n", name);
 }
 
-static ssize_t show_class(struct device *dev,
-                         struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
-                      hdev->dev_class[1], hdev->dev_class[0]);
-}
-
 static ssize_t show_address(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
@@ -227,150 +171,14 @@ static ssize_t show_address(struct device *dev,
        return sprintf(buf, "%pMR\n", &hdev->bdaddr);
 }
 
-static ssize_t show_features(struct device *dev,
-                            struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-
-       return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
-                      hdev->features[0][0], hdev->features[0][1],
-                      hdev->features[0][2], hdev->features[0][3],
-                      hdev->features[0][4], hdev->features[0][5],
-                      hdev->features[0][6], hdev->features[0][7]);
-}
-
-static ssize_t show_manufacturer(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->manufacturer);
-}
-
-static ssize_t show_hci_version(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->hci_ver);
-}
-
-static ssize_t show_hci_revision(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->hci_rev);
-}
-
-static ssize_t show_idle_timeout(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->idle_timeout);
-}
-
-static ssize_t store_idle_timeout(struct device *dev,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       unsigned int val;
-       int rv;
-
-       rv = kstrtouint(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val != 0 && (val < 500 || val > 3600000))
-               return -EINVAL;
-
-       hdev->idle_timeout = val;
-
-       return count;
-}
-
-static ssize_t show_sniff_max_interval(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->sniff_max_interval);
-}
-
-static ssize_t store_sniff_max_interval(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       u16 val;
-       int rv;
-
-       rv = kstrtou16(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
-               return -EINVAL;
-
-       hdev->sniff_max_interval = val;
-
-       return count;
-}
-
-static ssize_t show_sniff_min_interval(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       return sprintf(buf, "%d\n", hdev->sniff_min_interval);
-}
-
-static ssize_t store_sniff_min_interval(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
-{
-       struct hci_dev *hdev = to_hci_dev(dev);
-       u16 val;
-       int rv;
-
-       rv = kstrtou16(buf, 0, &val);
-       if (rv < 0)
-               return rv;
-
-       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
-               return -EINVAL;
-
-       hdev->sniff_min_interval = val;
-
-       return count;
-}
-
-static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL);
 static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-static DEVICE_ATTR(class, S_IRUGO, show_class, NULL);
 static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
-static DEVICE_ATTR(features, S_IRUGO, show_features, NULL);
-static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL);
-static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL);
-static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL);
-
-static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
-                  show_idle_timeout, store_idle_timeout);
-static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
-                  show_sniff_max_interval, store_sniff_max_interval);
-static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
-                  show_sniff_min_interval, store_sniff_min_interval);
 
 static struct attribute *bt_host_attrs[] = {
-       &dev_attr_bus.attr,
        &dev_attr_type.attr,
        &dev_attr_name.attr,
-       &dev_attr_class.attr,
        &dev_attr_address.attr,
-       &dev_attr_features.attr,
-       &dev_attr_manufacturer.attr,
-       &dev_attr_hci_version.attr,
-       &dev_attr_hci_revision.attr,
-       &dev_attr_idle_timeout.attr,
-       &dev_attr_sniff_max_interval.attr,
-       &dev_attr_sniff_min_interval.attr,
        NULL
 };
 
@@ -396,141 +204,6 @@ static struct device_type bt_host = {
        .release = bt_host_release,
 };
 
-static int inquiry_cache_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct discovery_state *cache = &hdev->discovery;
-       struct inquiry_entry *e;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(e, &cache->all, all) {
-               struct inquiry_data *data = &e->data;
-               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
-                          &data->bdaddr,
-                          data->pscan_rep_mode, data->pscan_period_mode,
-                          data->pscan_mode, data->dev_class[2],
-                          data->dev_class[1], data->dev_class[0],
-                          __le16_to_cpu(data->clock_offset),
-                          data->rssi, data->ssp_mode, e->timestamp);
-       }
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int inquiry_cache_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, inquiry_cache_show, inode->i_private);
-}
-
-static const struct file_operations inquiry_cache_fops = {
-       .open           = inquiry_cache_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int blacklist_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bdaddr_list *b;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(b, &hdev->blacklist, list)
-               seq_printf(f, "%pMR\n", &b->bdaddr);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int blacklist_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, blacklist_show, inode->i_private);
-}
-
-static const struct file_operations blacklist_fops = {
-       .open           = blacklist_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static void print_bt_uuid(struct seq_file *f, u8 *uuid)
-{
-       u32 data0, data5;
-       u16 data1, data2, data3, data4;
-
-       data5 = get_unaligned_le32(uuid);
-       data4 = get_unaligned_le16(uuid + 4);
-       data3 = get_unaligned_le16(uuid + 6);
-       data2 = get_unaligned_le16(uuid + 8);
-       data1 = get_unaligned_le16(uuid + 10);
-       data0 = get_unaligned_le32(uuid + 12);
-
-       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
-                  data0, data1, data2, data3, data4, data5);
-}
-
-static int uuids_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bt_uuid *uuid;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(uuid, &hdev->uuids, list)
-               print_bt_uuid(f, uuid->uuid);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int uuids_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, uuids_show, inode->i_private);
-}
-
-static const struct file_operations uuids_fops = {
-       .open           = uuids_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int auto_accept_delay_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-
-       hdev->auto_accept_delay = val;
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int auto_accept_delay_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-
-       *val = hdev->auto_accept_delay;
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
-                       auto_accept_delay_set, "%llu\n");
-
 void hci_init_sysfs(struct hci_dev *hdev)
 {
        struct device *dev = &hdev->dev;
@@ -542,52 +215,8 @@ void hci_init_sysfs(struct hci_dev *hdev)
        device_initialize(dev);
 }
 
-int hci_add_sysfs(struct hci_dev *hdev)
-{
-       struct device *dev = &hdev->dev;
-       int err;
-
-       BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
-       dev_set_name(dev, "%s", hdev->name);
-
-       err = device_add(dev);
-       if (err < 0)
-               return err;
-
-       if (!bt_debugfs)
-               return 0;
-
-       hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
-       if (!hdev->debugfs)
-               return 0;
-
-       debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
-                           hdev, &inquiry_cache_fops);
-
-       debugfs_create_file("blacklist", 0444, hdev->debugfs,
-                           hdev, &blacklist_fops);
-
-       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
-
-       debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev,
-                           &auto_accept_delay_fops);
-       return 0;
-}
-
-void hci_del_sysfs(struct hci_dev *hdev)
-{
-       BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
-       debugfs_remove_recursive(hdev->debugfs);
-
-       device_del(&hdev->dev);
-}
-
 int __init bt_sysfs_init(void)
 {
-       bt_debugfs = debugfs_create_dir("bluetooth", NULL);
-
        bt_class = class_create(THIS_MODULE, "bluetooth");
 
        return PTR_ERR_OR_ZERO(bt_class);
@@ -596,6 +225,4 @@ int __init bt_sysfs_init(void)
 void bt_sysfs_cleanup(void)
 {
        class_destroy(bt_class);
-
-       debugfs_remove_recursive(bt_debugfs);
 }
index bdc35a7..292e619 100644 (file)
@@ -767,10 +767,10 @@ static int hidp_setup_hid(struct hidp_session *session,
        strncpy(hid->name, req->name, sizeof(req->name) - 1);
 
        snprintf(hid->phys, sizeof(hid->phys), "%pMR",
-                &bt_sk(session->ctrl_sock->sk)->src);
+                &l2cap_pi(session->ctrl_sock->sk)->chan->src);
 
        snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
-                &bt_sk(session->ctrl_sock->sk)->dst);
+                &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
 
        hid->dev.parent = &session->conn->hcon->dev;
        hid->ll_driver = &hidp_hid_driver;
@@ -1283,23 +1283,29 @@ static int hidp_session_thread(void *arg)
 static int hidp_verify_sockets(struct socket *ctrl_sock,
                               struct socket *intr_sock)
 {
+       struct l2cap_chan *ctrl_chan, *intr_chan;
        struct bt_sock *ctrl, *intr;
        struct hidp_session *session;
 
        if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock))
                return -EINVAL;
 
+       ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan;
+       intr_chan = l2cap_pi(intr_sock->sk)->chan;
+
+       if (bacmp(&ctrl_chan->src, &intr_chan->src) ||
+           bacmp(&ctrl_chan->dst, &intr_chan->dst))
+               return -ENOTUNIQ;
+
        ctrl = bt_sk(ctrl_sock->sk);
        intr = bt_sk(intr_sock->sk);
 
-       if (bacmp(&ctrl->src, &intr->src) || bacmp(&ctrl->dst, &intr->dst))
-               return -ENOTUNIQ;
        if (ctrl->sk.sk_state != BT_CONNECTED ||
            intr->sk.sk_state != BT_CONNECTED)
                return -EBADFD;
 
        /* early session check, we check again during session registration */
-       session = hidp_session_find(&ctrl->dst);
+       session = hidp_session_find(&ctrl_chan->dst);
        if (session) {
                hidp_session_put(session);
                return -EEXIST;
@@ -1332,7 +1338,7 @@ int hidp_connection_add(struct hidp_connadd_req *req,
        if (!conn)
                return -EBADFD;
 
-       ret = hidp_session_new(&session, &bt_sk(ctrl_sock->sk)->dst, ctrl_sock,
+       ret = hidp_session_new(&session, &chan->dst, ctrl_sock,
                               intr_sock, req, conn);
        if (ret)
                goto out_conn;
index 9e6cc35..ab52414 100644 (file)
@@ -182,7 +182,7 @@ struct hidp_session {
 };
 
 /* HIDP init defines */
-extern int __init hidp_init_sockets(void);
-extern void __exit hidp_cleanup_sockets(void);
+int __init hidp_init_sockets(void);
+void __exit hidp_cleanup_sockets(void);
 
 #endif /* __HIDP_H */
index 11b5d09..4af3821 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "smp.h"
+#include "a2mp.h"
+#include "amp.h"
 
 bool disable_ertm;
 
-static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
+static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
+static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
 
 static LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
@@ -58,6 +59,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
 static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
                     struct sk_buff_head *skbs, u8 event);
 
+static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type)
+{
+       if (hcon->type == LE_LINK) {
+               if (type == ADDR_LE_DEV_PUBLIC)
+                       return BDADDR_LE_PUBLIC;
+               else
+                       return BDADDR_LE_RANDOM;
+       }
+
+       return BDADDR_BREDR;
+}
+
 /* ---- L2CAP channels ---- */
 
 static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
@@ -148,7 +161,7 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
        struct l2cap_chan *c;
 
        list_for_each_entry(c, &chan_list, global_l) {
-               if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
+               if (c->sport == psm && !bacmp(&c->src, src))
                        return c;
        }
        return NULL;
@@ -210,38 +223,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
        return 0;
 }
 
-static void __l2cap_state_change(struct l2cap_chan *chan, int state)
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
 {
        BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
               state_to_string(state));
 
        chan->state = state;
-       chan->ops->state_change(chan, state);
-}
-
-static void l2cap_state_change(struct l2cap_chan *chan, int state)
-{
-       struct sock *sk = chan->sk;
-
-       lock_sock(sk);
-       __l2cap_state_change(chan, state);
-       release_sock(sk);
+       chan->ops->state_change(chan, state, 0);
 }
 
-static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
+static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
+                                               int state, int err)
 {
-       struct sock *sk = chan->sk;
-
-       sk->sk_err = err;
+       chan->state = state;
+       chan->ops->state_change(chan, chan->state, err);
 }
 
 static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
 {
-       struct sock *sk = chan->sk;
-
-       lock_sock(sk);
-       __l2cap_chan_set_err(chan, err);
-       release_sock(sk);
+       chan->ops->state_change(chan, chan->state, err);
 }
 
 static void __set_retrans_timer(struct l2cap_chan *chan)
@@ -620,10 +620,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 void l2cap_chan_close(struct l2cap_chan *chan, int reason)
 {
        struct l2cap_conn *conn = chan->conn;
-       struct sock *sk = chan->sk;
 
-       BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state),
-              sk);
+       BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
 
        switch (chan->state) {
        case BT_LISTEN:
@@ -634,7 +632,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
        case BT_CONFIG:
                if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
                    conn->hcon->type == ACL_LINK) {
-                       __set_chan_timer(chan, sk->sk_sndtimeo);
+                       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
                        l2cap_send_disconn_req(chan, reason);
                } else
                        l2cap_chan_del(chan, reason);
@@ -646,10 +644,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
                        struct l2cap_conn_rsp rsp;
                        __u16 result;
 
-                       if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+                       if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
                                result = L2CAP_CR_SEC_BLOCK;
                        else
                                result = L2CAP_CR_BAD_PSM;
+
                        l2cap_state_change(chan, BT_DISCONN);
 
                        rsp.scid   = cpu_to_le16(chan->dcid);
@@ -676,7 +675,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
 
 static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
 {
-       if (chan->chan_type == L2CAP_CHAN_RAW) {
+       switch (chan->chan_type) {
+       case L2CAP_CHAN_RAW:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
                        return HCI_AT_DEDICATED_BONDING_MITM;
@@ -685,15 +685,29 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                default:
                        return HCI_AT_NO_BONDING;
                }
-       } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
-               if (chan->sec_level == BT_SECURITY_LOW)
-                       chan->sec_level = BT_SECURITY_SDP;
-
+               break;
+       case L2CAP_CHAN_CONN_LESS:
+               if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_3DSP)) {
+                       if (chan->sec_level == BT_SECURITY_LOW)
+                               chan->sec_level = BT_SECURITY_SDP;
+               }
                if (chan->sec_level == BT_SECURITY_HIGH)
                        return HCI_AT_NO_BONDING_MITM;
                else
                        return HCI_AT_NO_BONDING;
-       } else {
+               break;
+       case L2CAP_CHAN_CONN_ORIENTED:
+               if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
+                       if (chan->sec_level == BT_SECURITY_LOW)
+                               chan->sec_level = BT_SECURITY_SDP;
+
+                       if (chan->sec_level == BT_SECURITY_HIGH)
+                               return HCI_AT_NO_BONDING_MITM;
+                       else
+                               return HCI_AT_NO_BONDING;
+               }
+               /* fall through */
+       default:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
                        return HCI_AT_GENERAL_BONDING_MITM;
@@ -702,6 +716,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                default:
                        return HCI_AT_NO_BONDING;
                }
+               break;
        }
 }
 
@@ -1015,14 +1030,29 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
 static bool __amp_capable(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
+       struct hci_dev *hdev;
+       bool amp_available = false;
 
-       if (enable_hs &&
-           hci_amp_capable() &&
-           chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
-           conn->fixed_chan_mask & L2CAP_FC_A2MP)
-               return true;
-       else
+       if (!conn->hs_enabled)
                return false;
+
+       if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
+               return false;
+
+       read_lock(&hci_dev_list_lock);
+       list_for_each_entry(hdev, &hci_dev_list, list) {
+               if (hdev->amp_type != AMP_TYPE_BREDR &&
+                   test_bit(HCI_UP, &hdev->flags)) {
+                       amp_available = true;
+                       break;
+               }
+       }
+       read_unlock(&hci_dev_list_lock);
+
+       if (chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED)
+               return amp_available;
+
+       return false;
 }
 
 static bool l2cap_check_efs(struct l2cap_chan *chan)
@@ -1186,7 +1216,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
 
 static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
 {
-       struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
        struct l2cap_disconn_req req;
 
@@ -1209,10 +1238,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
        l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
                       sizeof(req), &req);
 
-       lock_sock(sk);
-       __l2cap_state_change(chan, BT_DISCONN);
-       __l2cap_chan_set_err(chan, err);
-       release_sock(sk);
+       l2cap_state_change_and_error(chan, BT_DISCONN, err);
 }
 
 /* ---- L2CAP connections ---- */
@@ -1225,8 +1251,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
        mutex_lock(&conn->chan_lock);
 
        list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
-               struct sock *sk = chan->sk;
-
                l2cap_chan_lock(chan);
 
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1258,19 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        rsp.dcid = cpu_to_le16(chan->scid);
 
                        if (l2cap_chan_check_security(chan)) {
-                               lock_sock(sk);
-                               if (test_bit(BT_SK_DEFER_SETUP,
-                                            &bt_sk(sk)->flags)) {
+                               if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
                                        rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
                                        chan->ops->defer(chan);
 
                                } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
                                        rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
                                }
-                               release_sock(sk);
                        } else {
                                rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
                                rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -1309,8 +1330,6 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
        read_lock(&chan_list_lock);
 
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                if (state && c->state != state)
                        continue;
 
@@ -1319,16 +1338,16 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
                        int src_any, dst_any;
 
                        /* Exact match. */
-                       src_match = !bacmp(&bt_sk(sk)->src, src);
-                       dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+                       src_match = !bacmp(&c->src, src);
+                       dst_match = !bacmp(&c->dst, dst);
                        if (src_match && dst_match) {
                                read_unlock(&chan_list_lock);
                                return c;
                        }
 
                        /* Closest match */
-                       src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
-                       dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+                       src_any = !bacmp(&c->src, BDADDR_ANY);
+                       dst_any = !bacmp(&c->dst, BDADDR_ANY);
                        if ((src_match && dst_any) || (src_any && dst_match) ||
                            (src_any && dst_any))
                                c1 = c;
@@ -1342,14 +1361,15 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
 
 static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 {
-       struct sock *parent;
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan, *pchan;
+       u8 dst_type;
 
        BT_DBG("");
 
        /* Check if we have socket listening on cid */
        pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
-                                         conn->src, conn->dst);
+                                         &hcon->src, &hcon->dst);
        if (!pchan)
                return;
 
@@ -1357,9 +1377,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
                return;
 
-       parent = pchan->sk;
+       dst_type = bdaddr_type(hcon, hcon->dst_type);
+
+       /* If device is blocked, do not create a channel for it */
+       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
+               return;
 
-       lock_sock(parent);
+       l2cap_chan_lock(pchan);
 
        chan = pchan->ops->new_connection(pchan);
        if (!chan)
@@ -1367,13 +1391,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
        chan->dcid = L2CAP_CID_ATT;
 
-       bacpy(&bt_sk(chan->sk)->src, conn->src);
-       bacpy(&bt_sk(chan->sk)->dst, conn->dst);
+       bacpy(&chan->src, &hcon->src);
+       bacpy(&chan->dst, &hcon->dst);
+       chan->src_type = bdaddr_type(hcon, hcon->src_type);
+       chan->dst_type = dst_type;
 
        __l2cap_chan_add(conn, chan);
 
 clean:
-       release_sock(parent);
+       l2cap_chan_unlock(pchan);
 }
 
 static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1408,12 +1434,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                                l2cap_chan_ready(chan);
 
                } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       struct sock *sk = chan->sk;
-                       __clear_chan_timer(chan);
-                       lock_sock(sk);
-                       __l2cap_state_change(chan, BT_CONNECTED);
-                       sk->sk_state_change(sk);
-                       release_sock(sk);
+                       l2cap_chan_ready(chan);
 
                } else if (chan->state == BT_CONNECT) {
                        l2cap_do_start(chan);
@@ -1633,11 +1654,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
                break;
        }
 
-       conn->src = &hcon->hdev->bdaddr;
-       conn->dst = &hcon->dst;
-
        conn->feat_mask = 0;
 
+       if (hcon->type == ACL_LINK)
+               conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+                                           &hcon->hdev->dev_flags);
+
        spin_lock_init(&conn->lock);
        mutex_init(&conn->chan_lock);
 
@@ -1688,8 +1710,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
        read_lock(&chan_list_lock);
 
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                if (state && c->state != state)
                        continue;
 
@@ -1698,16 +1718,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
                        int src_any, dst_any;
 
                        /* Exact match. */
-                       src_match = !bacmp(&bt_sk(sk)->src, src);
-                       dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+                       src_match = !bacmp(&c->src, src);
+                       dst_match = !bacmp(&c->dst, dst);
                        if (src_match && dst_match) {
                                read_unlock(&chan_list_lock);
                                return c;
                        }
 
                        /* Closest match */
-                       src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
-                       dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+                       src_any = !bacmp(&c->src, BDADDR_ANY);
+                       dst_any = !bacmp(&c->dst, BDADDR_ANY);
                        if ((src_match && dst_any) || (src_any && dst_match) ||
                            (src_any && dst_any))
                                c1 = c;
@@ -1722,18 +1742,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
 int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                       bdaddr_t *dst, u8 dst_type)
 {
-       struct sock *sk = chan->sk;
-       bdaddr_t *src = &bt_sk(sk)->src;
        struct l2cap_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev *hdev;
        __u8 auth_type;
        int err;
 
-       BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst,
+       BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
               dst_type, __le16_to_cpu(psm));
 
-       hdev = hci_get_route(dst, src);
+       hdev = hci_get_route(dst, &chan->src);
        if (!hdev)
                return -EHOSTUNREACH;
 
@@ -1790,9 +1808,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        }
 
        /* Set destination address and psm */
-       lock_sock(sk);
-       bacpy(&bt_sk(sk)->dst, dst);
-       release_sock(sk);
+       bacpy(&chan->dst, dst);
+       chan->dst_type = dst_type;
 
        chan->psm = psm;
        chan->dcid = cid;
@@ -1825,7 +1842,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        }
 
        /* Update source addr of the socket */
-       bacpy(src, conn->src);
+       bacpy(&chan->src, &hcon->src);
+       chan->src_type = bdaddr_type(hcon, hcon->src_type);
 
        l2cap_chan_unlock(chan);
        l2cap_chan_add(conn, chan);
@@ -1835,7 +1853,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        hci_conn_drop(hcon);
 
        l2cap_state_change(chan, BT_CONNECT);
-       __set_chan_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
 
        if (hcon->state == BT_CONNECTED) {
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1855,38 +1873,6 @@ done:
        return err;
 }
 
-int __l2cap_wait_ack(struct sock *sk)
-{
-       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-       DECLARE_WAITQUEUE(wait, current);
-       int err = 0;
-       int timeo = HZ/5;
-
-       add_wait_queue(sk_sleep(sk), &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-       while (chan->unacked_frames > 0 && chan->conn) {
-               if (!timeo)
-                       timeo = HZ/5;
-
-               if (signal_pending(current)) {
-                       err = sock_intr_errno(timeo);
-                       break;
-               }
-
-               release_sock(sk);
-               timeo = schedule_timeout(timeo);
-               lock_sock(sk);
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               err = sock_error(sk);
-               if (err)
-                       break;
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(sk_sleep(sk), &wait);
-       return err;
-}
-
 static void l2cap_monitor_timeout(struct work_struct *work)
 {
        struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -2263,7 +2249,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
        struct l2cap_hdr *lh;
 
-       BT_DBG("chan %p len %zu priority %u", chan, len, priority);
+       BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan,
+              __le16_to_cpu(chan->psm), len, priority);
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
 
@@ -2278,7 +2265,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
        lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
-       put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE));
+       put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
 
        err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
        if (unlikely(err < 0)) {
@@ -2829,17 +2816,16 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
        mutex_lock(&conn->chan_lock);
 
        list_for_each_entry(chan, &conn->chan_l, list) {
-               struct sock *sk = chan->sk;
                if (chan->chan_type != L2CAP_CHAN_RAW)
                        continue;
 
-               /* Don't send frame to the socket it came from */
-               if (skb->sk == sk)
+               /* Don't send frame to the channel it came from */
+               if (bt_cb(skb)->chan == chan)
                        continue;
+
                nskb = skb_clone(skb, GFP_KERNEL);
                if (!nskb)
                        continue;
-
                if (chan->ops->recv(chan, nskb))
                        kfree_skb(nskb);
        }
@@ -3046,8 +3032,8 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
 
        skb_queue_head_init(&chan->tx_q);
 
-       chan->local_amp_id = 0;
-       chan->move_id = 0;
+       chan->local_amp_id = AMP_ID_BREDR;
+       chan->move_id = AMP_ID_BREDR;
        chan->move_state = L2CAP_MOVE_STABLE;
        chan->move_role = L2CAP_MOVE_ROLE_NONE;
 
@@ -3087,20 +3073,20 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
        }
 }
 
-static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
+static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
 {
-       return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+       return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
 }
 
-static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
+static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
 {
-       return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+       return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
 }
 
 static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
                                      struct l2cap_conf_rfc *rfc)
 {
-       if (chan->local_amp_id && chan->hs_hcon) {
+       if (chan->local_amp_id != AMP_ID_BREDR && chan->hs_hcon) {
                u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
 
                /* Class 1 devices have must have ERTM timeouts
@@ -3138,7 +3124,7 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
 static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
 {
        if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
-           __l2cap_ews_supported(chan)) {
+           __l2cap_ews_supported(chan->conn)) {
                /* use extended control field */
                set_bit(FLAG_EXT_CTRL, &chan->flags);
                chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
@@ -3168,7 +3154,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
                if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
                        break;
 
-               if (__l2cap_efs_supported(chan))
+               if (__l2cap_efs_supported(chan->conn))
                        set_bit(FLAG_EFS_ENABLE, &chan->flags);
 
                /* fall through */
@@ -3320,7 +3306,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                        break;
 
                case L2CAP_CONF_EWS:
-                       if (!enable_hs)
+                       if (!chan->conn->hs_enabled)
                                return -ECONNREFUSED;
 
                        set_bit(FLAG_EXT_CTRL, &chan->flags);
@@ -3352,7 +3338,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                }
 
                if (remote_efs) {
-                       if (__l2cap_efs_supported(chan))
+                       if (__l2cap_efs_supported(chan->conn))
                                set_bit(FLAG_EFS_ENABLE, &chan->flags);
                        else
                                return -ECONNREFUSED;
@@ -3718,7 +3704,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
        struct l2cap_chan *chan = NULL, *pchan;
-       struct sock *parent, *sk = NULL;
        int result, status = L2CAP_CS_NO_INFO;
 
        u16 dcid = 0, scid = __le16_to_cpu(req->scid);
@@ -3727,16 +3712,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
 
        /* Check if we have socket listening on psm */
-       pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst);
+       pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
+                                        &conn->hcon->dst);
        if (!pchan) {
                result = L2CAP_CR_BAD_PSM;
                goto sendresp;
        }
 
-       parent = pchan->sk;
-
        mutex_lock(&conn->chan_lock);
-       lock_sock(parent);
+       l2cap_chan_lock(pchan);
 
        /* Check if the ACL is secure enough (if not SDP) */
        if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
@@ -3756,8 +3740,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        if (!chan)
                goto response;
 
-       sk = chan->sk;
-
        /* For certain devices (ex: HID mouse), support for authentication,
         * pairing and bonding is optional. For such devices, inorder to avoid
         * the ACL alive for too long after L2CAP disconnection, reset the ACL
@@ -3765,8 +3747,10 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
         */
        conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
 
-       bacpy(&bt_sk(sk)->src, conn->src);
-       bacpy(&bt_sk(sk)->dst, conn->dst);
+       bacpy(&chan->src, &conn->hcon->src);
+       bacpy(&chan->dst, &conn->hcon->dst);
+       chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
+       chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
        chan->psm  = psm;
        chan->dcid = scid;
        chan->local_amp_id = amp_id;
@@ -3775,14 +3759,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        dcid = chan->scid;
 
-       __set_chan_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
 
        chan->ident = cmd->ident;
 
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
                if (l2cap_chan_check_security(chan)) {
-                       if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
-                               __l2cap_state_change(chan, BT_CONNECT2);
+                       if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
+                               l2cap_state_change(chan, BT_CONNECT2);
                                result = L2CAP_CR_PEND;
                                status = L2CAP_CS_AUTHOR_PEND;
                                chan->ops->defer(chan);
@@ -3791,28 +3775,28 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
                                 * The connection will succeed after the
                                 * physical link is up.
                                 */
-                               if (amp_id) {
-                                       __l2cap_state_change(chan, BT_CONNECT2);
-                                       result = L2CAP_CR_PEND;
-                               } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                               if (amp_id == AMP_ID_BREDR) {
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        result = L2CAP_CR_SUCCESS;
+                               } else {
+                                       l2cap_state_change(chan, BT_CONNECT2);
+                                       result = L2CAP_CR_PEND;
                                }
                                status = L2CAP_CS_NO_INFO;
                        }
                } else {
-                       __l2cap_state_change(chan, BT_CONNECT2);
+                       l2cap_state_change(chan, BT_CONNECT2);
                        result = L2CAP_CR_PEND;
                        status = L2CAP_CS_AUTHEN_PEND;
                }
        } else {
-               __l2cap_state_change(chan, BT_CONNECT2);
+               l2cap_state_change(chan, BT_CONNECT2);
                result = L2CAP_CR_PEND;
                status = L2CAP_CS_NO_INFO;
        }
 
 response:
-       release_sock(parent);
+       l2cap_chan_unlock(pchan);
        mutex_unlock(&conn->chan_lock);
 
 sendresp:
@@ -3894,13 +3878,13 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
        if (scid) {
                chan = __l2cap_get_chan_by_scid(conn, scid);
                if (!chan) {
-                       err = -EFAULT;
+                       err = -EBADSLT;
                        goto unlock;
                }
        } else {
                chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
                if (!chan) {
-                       err = -EFAULT;
+                       err = -EBADSLT;
                        goto unlock;
                }
        }
@@ -3968,6 +3952,18 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
                                            L2CAP_CONF_SUCCESS, flags), data);
 }
 
+static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
+                                  u16 scid, u16 dcid)
+{
+       struct l2cap_cmd_rej_cid rej;
+
+       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+       rej.scid = __cpu_to_le16(scid);
+       rej.dcid = __cpu_to_le16(dcid);
+
+       l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
+}
+
 static inline int l2cap_config_req(struct l2cap_conn *conn,
                                   struct l2cap_cmd_hdr *cmd, u16 cmd_len,
                                   u8 *data)
@@ -3987,18 +3983,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
        BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
 
        chan = l2cap_get_chan_by_scid(conn, dcid);
-       if (!chan)
-               return -ENOENT;
+       if (!chan) {
+               cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
+               return 0;
+       }
 
        if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
-               struct l2cap_cmd_rej_cid rej;
-
-               rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
-               rej.scid = cpu_to_le16(chan->scid);
-               rej.dcid = cpu_to_le16(chan->dcid);
-
-               l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
-                              sizeof(rej), &rej);
+               cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+                                      chan->dcid);
                goto unlock;
        }
 
@@ -4201,7 +4193,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        struct l2cap_disconn_rsp rsp;
        u16 dcid, scid;
        struct l2cap_chan *chan;
-       struct sock *sk;
 
        if (cmd_len != sizeof(*req))
                return -EPROTO;
@@ -4216,20 +4207,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        chan = __l2cap_get_chan_by_scid(conn, dcid);
        if (!chan) {
                mutex_unlock(&conn->chan_lock);
+               cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
                return 0;
        }
 
        l2cap_chan_lock(chan);
 
-       sk = chan->sk;
-
        rsp.dcid = cpu_to_le16(chan->scid);
        rsp.scid = cpu_to_le16(chan->dcid);
        l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
 
-       lock_sock(sk);
-       sk->sk_shutdown = SHUTDOWN_MASK;
-       release_sock(sk);
+       chan->ops->set_shutdown(chan);
 
        l2cap_chan_hold(chan);
        l2cap_chan_del(chan, ECONNRESET);
@@ -4306,7 +4294,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
                if (!disable_ertm)
                        feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
                                | L2CAP_FEAT_FCS;
-               if (enable_hs)
+               if (conn->hs_enabled)
                        feat_mask |= L2CAP_FEAT_EXT_FLOW
                                | L2CAP_FEAT_EXT_WINDOW;
 
@@ -4317,7 +4305,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
                u8 buf[12];
                struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
 
-               if (enable_hs)
+               if (conn->hs_enabled)
                        l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
                else
                        l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
@@ -4414,7 +4402,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
        if (cmd_len != sizeof(*req))
                return -EPROTO;
 
-       if (!enable_hs)
+       if (!conn->hs_enabled)
                return -EINVAL;
 
        psm = le16_to_cpu(req->psm);
@@ -4423,7 +4411,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
        BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
 
        /* For controller id 0 make BR/EDR connection */
-       if (req->amp_id == HCI_BREDR_ID) {
+       if (req->amp_id == AMP_ID_BREDR) {
                l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
                              req->amp_id);
                return 0;
@@ -4445,10 +4433,13 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
                struct amp_mgr *mgr = conn->hcon->amp_mgr;
                struct hci_conn *hs_hcon;
 
-               hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
+               hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+                                                 &conn->hcon->dst);
                if (!hs_hcon) {
                        hci_dev_put(hdev);
-                       return -EFAULT;
+                       cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+                                              chan->dcid);
+                       return 0;
                }
 
                BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
@@ -4472,7 +4463,7 @@ error:
        l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
                       sizeof(rsp), &rsp);
 
-       return -EFAULT;
+       return 0;
 }
 
 static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
@@ -4658,7 +4649,7 @@ void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
 
        if (chan->state != BT_CONNECTED) {
                /* Ignore logical link if channel is on BR/EDR */
-               if (chan->local_amp_id)
+               if (chan->local_amp_id != AMP_ID_BREDR)
                        l2cap_logical_finish_create(chan, hchan);
        } else {
                l2cap_logical_finish_move(chan, hchan);
@@ -4669,7 +4660,7 @@ void l2cap_move_start(struct l2cap_chan *chan)
 {
        BT_DBG("chan %p", chan);
 
-       if (chan->local_amp_id == HCI_BREDR_ID) {
+       if (chan->local_amp_id == AMP_ID_BREDR) {
                if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
                        return;
                chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
@@ -4726,7 +4717,7 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
                               sizeof(rsp), &rsp);
 
                if (result == L2CAP_CR_SUCCESS) {
-                       __l2cap_state_change(chan, BT_CONFIG);
+                       l2cap_state_change(chan, BT_CONFIG);
                        set_bit(CONF_REQ_SENT, &chan->conf_state);
                        l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
                                       L2CAP_CONF_REQ,
@@ -4841,7 +4832,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 
        BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
 
-       if (!enable_hs)
+       if (!conn->hs_enabled)
                return -EINVAL;
 
        chan = l2cap_get_chan_by_dcid(conn, icid);
@@ -4868,7 +4859,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
                goto send_move_response;
        }
 
-       if (req->dest_amp_id) {
+       if (req->dest_amp_id != AMP_ID_BREDR) {
                struct hci_dev *hdev;
                hdev = hci_dev_get(req->dest_amp_id);
                if (!hdev || hdev->dev_type != HCI_AMP ||
@@ -4888,7 +4879,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
         */
        if ((__chan_is_moving(chan) ||
             chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
-           bacmp(conn->src, conn->dst) > 0) {
+           bacmp(&conn->hcon->src, &conn->hcon->dst) > 0) {
                result = L2CAP_MR_COLLISION;
                goto send_move_response;
        }
@@ -4898,7 +4889,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
        chan->move_id = req->dest_amp_id;
        icid = chan->dcid;
 
-       if (!req->dest_amp_id) {
+       if (req->dest_amp_id == AMP_ID_BREDR) {
                /* Moving to BR/EDR */
                if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                        chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
@@ -5090,7 +5081,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
        if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
                if (result == L2CAP_MC_CONFIRMED) {
                        chan->local_amp_id = chan->move_id;
-                       if (!chan->local_amp_id)
+                       if (chan->local_amp_id == AMP_ID_BREDR)
                                __release_logical_link(chan);
                } else {
                        chan->move_id = chan->local_amp_id;
@@ -5130,7 +5121,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
        if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
                chan->local_amp_id = chan->move_id;
 
-               if (!chan->local_amp_id && chan->hs_hchan)
+               if (chan->local_amp_id == AMP_ID_BREDR && chan->hs_hchan)
                        __release_logical_link(chan);
 
                l2cap_move_done(chan);
@@ -5222,7 +5213,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
 
        case L2CAP_CONN_RSP:
        case L2CAP_CREATE_CHAN_RSP:
-               err = l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
+               l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CONF_REQ:
@@ -5230,7 +5221,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_CONF_RSP:
-               err = l2cap_config_rsp(conn, cmd, cmd_len, data);
+               l2cap_config_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_DISCONN_REQ:
@@ -5238,7 +5229,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_DISCONN_RSP:
-               err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
+               l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_ECHO_REQ:
@@ -5253,7 +5244,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_INFO_RSP:
-               err = l2cap_information_rsp(conn, cmd, cmd_len, data);
+               l2cap_information_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CREATE_CHAN_REQ:
@@ -5265,7 +5256,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_MOVE_CHAN_RSP:
-               err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
+               l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_MOVE_CHAN_CFM:
@@ -5273,7 +5264,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_MOVE_CHAN_CFM_RSP:
-               err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
+               l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
                break;
 
        default:
@@ -5307,51 +5298,48 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
 static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
                                        struct sk_buff *skb)
 {
-       u8 *data = skb->data;
-       int len = skb->len;
-       struct l2cap_cmd_hdr cmd;
+       struct hci_conn *hcon = conn->hcon;
+       struct l2cap_cmd_hdr *cmd;
+       u16 len;
        int err;
 
-       l2cap_raw_recv(conn, skb);
+       if (hcon->type != LE_LINK)
+               goto drop;
 
-       while (len >= L2CAP_CMD_HDR_SIZE) {
-               u16 cmd_len;
-               memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
-               data += L2CAP_CMD_HDR_SIZE;
-               len  -= L2CAP_CMD_HDR_SIZE;
+       if (skb->len < L2CAP_CMD_HDR_SIZE)
+               goto drop;
 
-               cmd_len = le16_to_cpu(cmd.len);
+       cmd = (void *) skb->data;
+       skb_pull(skb, L2CAP_CMD_HDR_SIZE);
 
-               BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
-                      cmd.ident);
+       len = le16_to_cpu(cmd->len);
 
-               if (cmd_len > len || !cmd.ident) {
-                       BT_DBG("corrupted command");
-                       break;
-               }
+       BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident);
 
-               err = l2cap_le_sig_cmd(conn, &cmd, data);
-               if (err) {
-                       struct l2cap_cmd_rej_unk rej;
+       if (len != skb->len || !cmd->ident) {
+               BT_DBG("corrupted command");
+               goto drop;
+       }
 
-                       BT_ERR("Wrong link type (%d)", err);
+       err = l2cap_le_sig_cmd(conn, cmd, skb->data);
+       if (err) {
+               struct l2cap_cmd_rej_unk rej;
 
-                       /* FIXME: Map err to a valid reason */
-                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
-                       l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
-                                      sizeof(rej), &rej);
-               }
+               BT_ERR("Wrong link type (%d)", err);
 
-               data += cmd_len;
-               len  -= cmd_len;
+               rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+               l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
+                              sizeof(rej), &rej);
        }
 
+drop:
        kfree_skb(skb);
 }
 
 static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                                     struct sk_buff *skb)
 {
+       struct hci_conn *hcon = conn->hcon;
        u8 *data = skb->data;
        int len = skb->len;
        struct l2cap_cmd_hdr cmd;
@@ -5359,6 +5347,9 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 
        l2cap_raw_recv(conn, skb);
 
+       if (hcon->type != ACL_LINK)
+               goto drop;
+
        while (len >= L2CAP_CMD_HDR_SIZE) {
                u16 cmd_len;
                memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
@@ -5381,7 +5372,6 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 
                        BT_ERR("Wrong link type (%d)", err);
 
-                       /* FIXME: Map err to a valid reason */
                        rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
                        l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
                                       sizeof(rej), &rej);
@@ -5391,6 +5381,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                len  -= cmd_len;
        }
 
+drop:
        kfree_skb(skb);
 }
 
@@ -5787,7 +5778,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
                               struct sk_buff *skb, u8 event)
 {
        int err = 0;
-       bool skb_in_use = 0;
+       bool skb_in_use = false;
 
        BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
               event);
@@ -5808,7 +5799,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
                                                           control->txseq);
 
                        chan->buffer_seq = chan->expected_tx_seq;
-                       skb_in_use = 1;
+                       skb_in_use = true;
 
                        err = l2cap_reassemble_sdu(chan, skb, control);
                        if (err)
@@ -5844,7 +5835,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
                         * current frame is stored for later use.
                         */
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5922,7 +5913,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
 {
        int err = 0;
        u16 txseq = control->txseq;
-       bool skb_in_use = 0;
+       bool skb_in_use = false;
 
        BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
               event);
@@ -5934,7 +5925,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
                        /* Keep frame for reassembly later */
                        l2cap_pass_to_tx(chan, control);
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5945,7 +5936,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
 
                        l2cap_pass_to_tx(chan, control);
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5960,7 +5951,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
                         * the missing frames.
                         */
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5974,7 +5965,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
                         * SREJ'd frames.
                         */
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -6383,9 +6374,13 @@ done:
 static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
                                  struct sk_buff *skb)
 {
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan;
 
-       chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
+       if (hcon->type != ACL_LINK)
+               goto drop;
+
+       chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
        if (!chan)
                goto drop;
 
@@ -6397,6 +6392,10 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
        if (chan->imtu < skb->len)
                goto drop;
 
+       /* Store remote BD_ADDR and PSM for msg_name */
+       bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
+       bt_cb(skb)->psm = psm;
+
        if (!chan->ops->recv(chan, skb))
                return;
 
@@ -6407,15 +6406,22 @@ drop:
 static void l2cap_att_channel(struct l2cap_conn *conn,
                              struct sk_buff *skb)
 {
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan;
 
+       if (hcon->type != LE_LINK)
+               goto drop;
+
        chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
-                                        conn->src, conn->dst);
+                                        &hcon->src, &hcon->dst);
        if (!chan)
                goto drop;
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
+       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
+               goto drop;
+
        if (chan->imtu < skb->len)
                goto drop;
 
@@ -6444,9 +6450,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("len %d, cid 0x%4.4x", len, cid);
 
        switch (cid) {
-       case L2CAP_CID_LE_SIGNALING:
-               l2cap_le_sig_channel(conn, skb);
-               break;
        case L2CAP_CID_SIGNALING:
                l2cap_sig_channel(conn, skb);
                break;
@@ -6461,6 +6464,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                l2cap_att_channel(conn, skb);
                break;
 
+       case L2CAP_CID_LE_SIGNALING:
+               l2cap_le_sig_channel(conn, skb);
+               break;
+
        case L2CAP_CID_SMP:
                if (smp_sig_channel(conn, skb))
                        l2cap_conn_del(conn->hcon, EACCES);
@@ -6484,17 +6491,15 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
        /* Find listening sockets and check their link_mode */
        read_lock(&chan_list_lock);
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                if (c->state != BT_LISTEN)
                        continue;
 
-               if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
+               if (!bacmp(&c->src, &hdev->bdaddr)) {
                        lm1 |= HCI_LM_ACCEPT;
                        if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
                                lm1 |= HCI_LM_MASTER;
                        exact++;
-               } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+               } else if (!bacmp(&c->src, BDADDR_ANY)) {
                        lm2 |= HCI_LM_ACCEPT;
                        if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
                                lm2 |= HCI_LM_MASTER;
@@ -6600,11 +6605,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 
                if (!status && (chan->state == BT_CONNECTED ||
                                chan->state == BT_CONFIG)) {
-                       struct sock *sk = chan->sk;
-
-                       clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
-                       sk->sk_state_change(sk);
-
+                       chan->ops->resume(chan);
                        l2cap_check_encryption(chan, encrypt);
                        l2cap_chan_unlock(chan);
                        continue;
@@ -6617,32 +6618,26 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                        }
                } else if (chan->state == BT_CONNECT2) {
-                       struct sock *sk = chan->sk;
                        struct l2cap_conn_rsp rsp;
                        __u16 res, stat;
 
-                       lock_sock(sk);
-
                        if (!status) {
-                               if (test_bit(BT_SK_DEFER_SETUP,
-                                            &bt_sk(sk)->flags)) {
+                               if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        res = L2CAP_CR_PEND;
                                        stat = L2CAP_CS_AUTHOR_PEND;
                                        chan->ops->defer(chan);
                                } else {
-                                       __l2cap_state_change(chan, BT_CONFIG);
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        res = L2CAP_CR_SUCCESS;
                                        stat = L2CAP_CS_NO_INFO;
                                }
                        } else {
-                               __l2cap_state_change(chan, BT_DISCONN);
+                               l2cap_state_change(chan, BT_DISCONN);
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                                res = L2CAP_CR_SEC_BLOCK;
                                stat = L2CAP_CS_NO_INFO;
                        }
 
-                       release_sock(sk);
-
                        rsp.scid   = cpu_to_le16(chan->dcid);
                        rsp.dcid   = cpu_to_le16(chan->scid);
                        rsp.result = cpu_to_le16(res);
@@ -6759,9 +6754,13 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
                conn->rx_len -= skb->len;
 
                if (!conn->rx_len) {
-                       /* Complete frame received */
-                       l2cap_recv_frame(conn, conn->rx_skb);
+                       /* Complete frame received. l2cap_recv_frame
+                        * takes ownership of the skb so set the global
+                        * rx_skb pointer to NULL first.
+                        */
+                       struct sk_buff *rx_skb = conn->rx_skb;
                        conn->rx_skb = NULL;
+                       l2cap_recv_frame(conn, rx_skb);
                }
                break;
        }
@@ -6778,10 +6777,8 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
        read_lock(&chan_list_lock);
 
        list_for_each_entry(c, &chan_list, global_l) {
-               struct sock *sk = c->sk;
-
                seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
-                          &bt_sk(sk)->src, &bt_sk(sk)->dst,
+                          &c->src, &c->dst,
                           c->state, __le16_to_cpu(c->psm),
                           c->scid, c->dcid, c->imtu, c->omtu,
                           c->sec_level, c->mode);
@@ -6814,12 +6811,11 @@ int __init l2cap_init(void)
        if (err < 0)
                return err;
 
-       if (bt_debugfs) {
-               l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
-                                                   NULL, &l2cap_debugfs_fops);
-               if (!l2cap_debugfs)
-                       BT_ERR("Failed to create L2CAP debug file");
-       }
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
+                                           NULL, &l2cap_debugfs_fops);
 
        return 0;
 }
index 0098af8..7cc24d2 100644 (file)
@@ -32,7 +32,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
 
 static struct bt_sock_list l2cap_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
@@ -68,6 +69,18 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
+       if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
+               return -EINVAL;
+
+       if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Connection oriented channels are not supported on LE */
+               if (la.l2_psm)
+                       return -EINVAL;
+               /* We only allow ATT user space socket */
+               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+       }
+
        lock_sock(sk);
 
        if (sk->sk_state != BT_OPEN) {
@@ -99,11 +112,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (err < 0)
                goto done;
 
-       if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
-           __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
-               chan->sec_level = BT_SECURITY_SDP;
+       switch (chan->chan_type) {
+       case L2CAP_CHAN_CONN_LESS:
+               if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_3DSP)
+                       chan->sec_level = BT_SECURITY_SDP;
+               break;
+       case L2CAP_CHAN_CONN_ORIENTED:
+               if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
+                   __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
+                       chan->sec_level = BT_SECURITY_SDP;
+               break;
+       }
 
-       bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+       bacpy(&chan->src, &la.l2_bdaddr);
+       chan->src_type = la.l2_bdaddr_type;
 
        chan->state = BT_BOUND;
        sk->sk_state = BT_BOUND;
@@ -134,6 +156,47 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
        if (la.l2_cid && la.l2_psm)
                return -EINVAL;
 
+       if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
+               return -EINVAL;
+
+       /* Check that the socket wasn't bound to something that
+        * conflicts with the address given to connect(). If chan->src
+        * is BDADDR_ANY it means bind() was never used, in which case
+        * chan->src_type and la.l2_bdaddr_type do not need to match.
+        */
+       if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
+           bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Old user space versions will try to incorrectly bind
+                * the ATT socket using BDADDR_BREDR. We need to accept
+                * this and fix up the source address type only when
+                * both the source CID and destination CID indicate
+                * ATT. Anything else is an invalid combination.
+                */
+               if (chan->scid != L2CAP_CID_ATT ||
+                   la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+
+               /* We don't have the hdev available here to make a
+                * better decision on random vs public, but since all
+                * user space versions that exhibit this issue anyway do
+                * not support random local addresses assuming public
+                * here is good enough.
+                */
+               chan->src_type = BDADDR_LE_PUBLIC;
+       }
+
+       if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
+               return -EINVAL;
+
+       if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+               /* Connection oriented channels are not supported on LE */
+               if (la.l2_psm)
+                       return -EINVAL;
+               /* We only allow ATT user space socket */
+               if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+                       return -EINVAL;
+       }
+
        err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
                                 &la.l2_bdaddr, la.l2_bdaddr_type);
        if (err)
@@ -265,12 +328,14 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
 
        if (peer) {
                la->l2_psm = chan->psm;
-               bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
+               bacpy(&la->l2_bdaddr, &chan->dst);
                la->l2_cid = cpu_to_le16(chan->dcid);
+               la->l2_bdaddr_type = chan->dst_type;
        } else {
                la->l2_psm = chan->sport;
-               bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
+               bacpy(&la->l2_bdaddr, &chan->src);
                la->l2_cid = cpu_to_le16(chan->scid);
+               la->l2_bdaddr_type = chan->src_type;
        }
 
        return 0;
@@ -445,11 +510,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_CHANNEL_POLICY:
-               if (!enable_hs) {
-                       err = -ENOPROTOOPT;
-                       break;
-               }
-
                if (put_user(chan->chan_policy, (u32 __user *) optval))
                        err = -EFAULT;
                break;
@@ -665,10 +725,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                        break;
                }
 
-               if (opt)
+               if (opt) {
                        set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
-               else
+                       set_bit(FLAG_DEFER_SETUP, &chan->flags);
+               } else {
                        clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+                       clear_bit(FLAG_DEFER_SETUP, &chan->flags);
+               }
                break;
 
        case BT_FLUSHABLE:
@@ -683,7 +746,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                }
 
                if (opt == BT_FLUSHABLE_OFF) {
-                       struct l2cap_conn *conn = chan->conn;
+                       conn = chan->conn;
                        /* proceed further only when we have l2cap_conn and
                           No Flush support in the LM */
                        if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
@@ -720,11 +783,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_CHANNEL_POLICY:
-               if (!enable_hs) {
-                       err = -ENOPROTOOPT;
-                       break;
-               }
-
                if (get_user(opt, (u32 __user *) optval)) {
                        err = -EFAULT;
                        break;
@@ -777,6 +835,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (sk->sk_state != BT_CONNECTED)
                return -ENOTCONN;
 
+       lock_sock(sk);
+       err = bt_sock_wait_ready(sk, msg->msg_flags);
+       release_sock(sk);
+       if (err)
+               return err;
+
        l2cap_chan_lock(chan);
        err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
        l2cap_chan_unlock(chan);
@@ -799,8 +863,8 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                pi->chan->state = BT_CONFIG;
 
                __l2cap_connect_rsp_defer(pi->chan);
-               release_sock(sk);
-               return 0;
+               err = 0;
+               goto done;
        }
 
        release_sock(sk);
@@ -856,6 +920,38 @@ static void l2cap_sock_kill(struct sock *sk)
        sock_put(sk);
 }
 
+static int __l2cap_wait_ack(struct sock *sk)
+{
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+       DECLARE_WAITQUEUE(wait, current);
+       int err = 0;
+       int timeo = HZ/5;
+
+       add_wait_queue(sk_sleep(sk), &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (chan->unacked_frames > 0 && chan->conn) {
+               if (!timeo)
+                       timeo = HZ/5;
+
+               if (signal_pending(current)) {
+                       err = sock_intr_errno(timeo);
+                       break;
+               }
+
+               release_sock(sk);
+               timeo = schedule_timeout(timeo);
+               lock_sock(sk);
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               err = sock_error(sk);
+               if (err)
+                       break;
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(sk_sleep(sk), &wait);
+       return err;
+}
+
 static int l2cap_sock_shutdown(struct socket *sock, int how)
 {
        struct sock *sk = sock->sk;
@@ -946,6 +1042,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 {
        struct sock *sk, *parent = chan->data;
 
+       lock_sock(parent);
+
        /* Check for backlog size */
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -963,18 +1061,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
 
        bt_accept_enqueue(parent, sk);
 
+       release_sock(parent);
+
        return l2cap_pi(sk)->chan;
 }
 
 static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
 {
-       int err;
        struct sock *sk = chan->data;
-       struct l2cap_pinfo *pi = l2cap_pi(sk);
+       int err;
 
        lock_sock(sk);
 
-       if (pi->rx_busy_skb) {
+       if (l2cap_pi(sk)->rx_busy_skb) {
                err = -ENOMEM;
                goto done;
        }
@@ -990,9 +1089,9 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
         * acked and reassembled until there is buffer space
         * available.
         */
-       if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) {
-               pi->rx_busy_skb = skb;
-               l2cap_chan_busy(pi->chan, 1);
+       if (err < 0 && chan->mode == L2CAP_MODE_ERTM) {
+               l2cap_pi(sk)->rx_busy_skb = skb;
+               l2cap_chan_busy(chan, 1);
                err = 0;
        }
 
@@ -1050,26 +1149,33 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
        release_sock(sk);
 }
 
-static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state)
+static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
+                                      int err)
 {
        struct sock *sk = chan->data;
 
        sk->sk_state = state;
+
+       if (err)
+               sk->sk_err = err;
 }
 
 static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
                                               unsigned long len, int nb)
 {
+       struct sock *sk = chan->data;
        struct sk_buff *skb;
        int err;
 
        l2cap_chan_unlock(chan);
-       skb = bt_skb_send_alloc(chan->sk, len, nb, &err);
+       skb = bt_skb_send_alloc(sk, len, nb, &err);
        l2cap_chan_lock(chan);
 
        if (!skb)
                return ERR_PTR(err);
 
+       bt_cb(skb)->chan = chan;
+
        return skb;
 }
 
@@ -1095,11 +1201,39 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 
 static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
 {
-       struct sock *sk = chan->data;
-       struct sock *parent = bt_sk(sk)->parent;
+       struct sock *parent, *sk = chan->data;
+
+       lock_sock(sk);
 
+       parent = bt_sk(sk)->parent;
        if (parent)
                parent->sk_data_ready(parent, 0);
+
+       release_sock(sk);
+}
+
+static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
+       sk->sk_state_change(sk);
+}
+
+static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       lock_sock(sk);
+       sk->sk_shutdown = SHUTDOWN_MASK;
+       release_sock(sk);
+}
+
+static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
+{
+       struct sock *sk = chan->data;
+
+       return sk->sk_sndtimeo;
 }
 
 static struct l2cap_ops l2cap_chan_ops = {
@@ -1111,6 +1245,9 @@ static struct l2cap_ops l2cap_chan_ops = {
        .state_change   = l2cap_sock_state_change_cb,
        .ready          = l2cap_sock_ready_cb,
        .defer          = l2cap_sock_defer_cb,
+       .resume         = l2cap_sock_resume_cb,
+       .set_shutdown   = l2cap_sock_set_shutdown_cb,
+       .get_sndtimeo   = l2cap_sock_get_sndtimeo_cb,
        .alloc_skb      = l2cap_sock_alloc_skb_cb,
 };
 
@@ -1120,6 +1257,7 @@ static void l2cap_sock_destruct(struct sock *sk)
 
        if (l2cap_pi(sk)->chan)
                l2cap_chan_put(l2cap_pi(sk)->chan);
+
        if (l2cap_pi(sk)->rx_busy_skb) {
                kfree_skb(l2cap_pi(sk)->rx_busy_skb);
                l2cap_pi(sk)->rx_busy_skb = NULL;
@@ -1129,10 +1267,22 @@ static void l2cap_sock_destruct(struct sock *sk)
        skb_queue_purge(&sk->sk_write_queue);
 }
 
+static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name,
+                              int *msg_namelen)
+{
+       struct sockaddr_l2 *la = (struct sockaddr_l2 *) msg_name;
+
+       memset(la, 0, sizeof(struct sockaddr_l2));
+       la->l2_family = AF_BLUETOOTH;
+       la->l2_psm = bt_cb(skb)->psm;
+       bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
+
+       *msg_namelen = sizeof(struct sockaddr_l2);
+}
+
 static void l2cap_sock_init(struct sock *sk, struct sock *parent)
 {
-       struct l2cap_pinfo *pi = l2cap_pi(sk);
-       struct l2cap_chan *chan = pi->chan;
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
 
        BT_DBG("sk %p", sk);
 
@@ -1156,13 +1306,13 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
 
                security_sk_clone(parent, sk);
        } else {
-
                switch (sk->sk_type) {
                case SOCK_RAW:
                        chan->chan_type = L2CAP_CHAN_RAW;
                        break;
                case SOCK_DGRAM:
                        chan->chan_type = L2CAP_CHAN_CONN_LESS;
+                       bt_sk(sk)->skb_msg_name = l2cap_skb_msg_name;
                        break;
                case SOCK_SEQPACKET:
                case SOCK_STREAM:
@@ -1224,8 +1374,6 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 
        l2cap_chan_hold(chan);
 
-       chan->sk = sk;
-
        l2cap_pi(sk)->chan = chan;
 
        return sk;
index fedc539..074d836 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/smp.h>
 
-bool enable_hs;
+#include "smp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  3
+#define MGMT_REVISION  4
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -76,6 +75,10 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_BLOCK_DEVICE,
        MGMT_OP_UNBLOCK_DEVICE,
        MGMT_OP_SET_DEVICE_ID,
+       MGMT_OP_SET_ADVERTISING,
+       MGMT_OP_SET_BREDR,
+       MGMT_OP_SET_STATIC_ADDRESS,
+       MGMT_OP_SET_SCAN_PARAMS,
 };
 
 static const u16 mgmt_events[] = {
@@ -181,11 +184,6 @@ static u8 mgmt_status_table[] = {
        MGMT_STATUS_CONNECT_FAILED,     /* MAC Connection Failed */
 };
 
-bool mgmt_valid_hdev(struct hci_dev *hdev)
-{
-       return hdev->dev_type == HCI_BREDR;
-}
-
 static u8 mgmt_status(u8 hci_status)
 {
        if (hci_status < ARRAY_SIZE(mgmt_status_table))
@@ -321,10 +319,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
 
        count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
-               if (!mgmt_valid_hdev(d))
-                       continue;
-
-               count++;
+               if (d->dev_type == HCI_BREDR)
+                       count++;
        }
 
        rp_len = sizeof(*rp) + (2 * count);
@@ -339,11 +335,13 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
                if (test_bit(HCI_SETUP, &d->dev_flags))
                        continue;
 
-               if (!mgmt_valid_hdev(d))
+               if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
                        continue;
 
-               rp->index[count++] = cpu_to_le16(d->id);
-               BT_DBG("Added hci%u", d->id);
+               if (d->dev_type == HCI_BREDR) {
+                       rp->index[count++] = cpu_to_le16(d->id);
+                       BT_DBG("Added hci%u", d->id);
+               }
        }
 
        rp->num_controllers = cpu_to_le16(count);
@@ -366,9 +364,6 @@ static u32 get_supported_settings(struct hci_dev *hdev)
        settings |= MGMT_SETTING_POWERED;
        settings |= MGMT_SETTING_PAIRABLE;
 
-       if (lmp_ssp_capable(hdev))
-               settings |= MGMT_SETTING_SSP;
-
        if (lmp_bredr_capable(hdev)) {
                settings |= MGMT_SETTING_CONNECTABLE;
                if (hdev->hci_ver >= BLUETOOTH_VER_1_2)
@@ -376,13 +371,17 @@ static u32 get_supported_settings(struct hci_dev *hdev)
                settings |= MGMT_SETTING_DISCOVERABLE;
                settings |= MGMT_SETTING_BREDR;
                settings |= MGMT_SETTING_LINK_SECURITY;
-       }
 
-       if (enable_hs)
-               settings |= MGMT_SETTING_HS;
+               if (lmp_ssp_capable(hdev)) {
+                       settings |= MGMT_SETTING_SSP;
+                       settings |= MGMT_SETTING_HS;
+               }
+       }
 
-       if (lmp_le_capable(hdev))
+       if (lmp_le_capable(hdev)) {
                settings |= MGMT_SETTING_LE;
+               settings |= MGMT_SETTING_ADVERTISING;
+       }
 
        return settings;
 }
@@ -406,7 +405,7 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
                settings |= MGMT_SETTING_PAIRABLE;
 
-       if (lmp_bredr_capable(hdev))
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                settings |= MGMT_SETTING_BREDR;
 
        if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
@@ -421,6 +420,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
                settings |= MGMT_SETTING_HS;
 
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+               settings |= MGMT_SETTING_ADVERTISING;
+
        return settings;
 }
 
@@ -534,6 +536,156 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
        return ptr;
 }
 
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+               if (cmd->opcode == opcode)
+                       return cmd;
+       }
+
+       return NULL;
+}
+
+static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+       u8 ad_len = 0;
+       size_t name_len;
+
+       name_len = strlen(hdev->dev_name);
+       if (name_len > 0) {
+               size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+               if (name_len > max_len) {
+                       name_len = max_len;
+                       ptr[1] = EIR_NAME_SHORT;
+               } else
+                       ptr[1] = EIR_NAME_COMPLETE;
+
+               ptr[0] = name_len + 1;
+
+               memcpy(ptr + 2, hdev->dev_name, name_len);
+
+               ad_len += (name_len + 2);
+               ptr += (name_len + 2);
+       }
+
+       return ad_len;
+}
+
+static void update_scan_rsp_data(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_scan_rsp_data cp;
+       u8 len;
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       len = create_scan_rsp_data(hdev, cp.data);
+
+       if (hdev->scan_rsp_data_len == len &&
+           memcmp(cp.data, hdev->scan_rsp_data, len) == 0)
+               return;
+
+       memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+       hdev->scan_rsp_data_len = len;
+
+       cp.length = len;
+
+       hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 get_adv_discov_flags(struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+
+       /* If there's a pending mgmt command the flags will not yet have
+        * their final values, so check for this first.
+        */
+       cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+       if (cmd) {
+               struct mgmt_mode *cp = cmd->param;
+               if (cp->val == 0x01)
+                       return LE_AD_GENERAL;
+               else if (cp->val == 0x02)
+                       return LE_AD_LIMITED;
+       } else {
+               if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+                       return LE_AD_LIMITED;
+               else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+                       return LE_AD_GENERAL;
+       }
+
+       return 0;
+}
+
+static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
+{
+       u8 ad_len = 0, flags = 0;
+
+       flags |= get_adv_discov_flags(hdev);
+
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               if (lmp_le_br_capable(hdev))
+                       flags |= LE_AD_SIM_LE_BREDR_CTRL;
+               if (lmp_host_le_br_capable(hdev))
+                       flags |= LE_AD_SIM_LE_BREDR_HOST;
+       } else {
+               flags |= LE_AD_NO_BREDR;
+       }
+
+       if (flags) {
+               BT_DBG("adv flags 0x%02x", flags);
+
+               ptr[0] = 2;
+               ptr[1] = EIR_FLAGS;
+               ptr[2] = flags;
+
+               ad_len += 3;
+               ptr += 3;
+       }
+
+       if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+               ptr[0] = 2;
+               ptr[1] = EIR_TX_POWER;
+               ptr[2] = (u8) hdev->adv_tx_power;
+
+               ad_len += 3;
+               ptr += 3;
+       }
+
+       return ad_len;
+}
+
+static void update_adv_data(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_data cp;
+       u8 len;
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       len = create_adv_data(hdev, cp.data);
+
+       if (hdev->adv_data_len == len &&
+           memcmp(cp.data, hdev->adv_data, len) == 0)
+               return;
+
+       memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+       hdev->adv_data_len = len;
+
+       cp.length = len;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
 static void create_eir(struct hci_dev *hdev, u8 *data)
 {
        u8 *ptr = data;
@@ -632,6 +784,9 @@ static void update_class(struct hci_request *req)
        if (!hdev_is_powered(hdev))
                return;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
        if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
                return;
 
@@ -639,6 +794,9 @@ static void update_class(struct hci_request *req)
        cod[1] = hdev->major_class;
        cod[2] = get_service_classes(hdev);
 
+       if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+               cod[1] |= 0x20;
+
        if (memcmp(cod, hdev->dev_class, 3) == 0)
                return;
 
@@ -763,18 +921,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
        }
 }
 
-static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
-{
-       struct pending_cmd *cmd;
-
-       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-               if (cmd->opcode == opcode)
-                       return cmd;
-       }
-
-       return NULL;
-}
-
 static void mgmt_pending_remove(struct pending_cmd *cmd)
 {
        list_del(&cmd->list);
@@ -804,6 +950,12 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
 
        hci_dev_lock(hdev);
 
+       if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+                                MGMT_STATUS_BUSY);
+               goto failed;
+       }
+
        if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
                cancel_delayed_work(&hdev->power_off);
 
@@ -820,12 +972,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
-                                MGMT_STATUS_BUSY);
-               goto failed;
-       }
-
        cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
@@ -883,27 +1029,141 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip)
        return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
 }
 
+struct cmd_lookup {
+       struct sock *sk;
+       struct hci_dev *hdev;
+       u8 mgmt_status;
+};
+
+static void settings_rsp(struct pending_cmd *cmd, void *data)
+{
+       struct cmd_lookup *match = data;
+
+       send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
+
+       list_del(&cmd->list);
+
+       if (match->sk == NULL) {
+               match->sk = cmd->sk;
+               sock_hold(match->sk);
+       }
+
+       mgmt_pending_free(cmd);
+}
+
+static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
+{
+       u8 *status = data;
+
+       cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+       mgmt_pending_remove(cmd);
+}
+
+static u8 mgmt_bredr_support(struct hci_dev *hdev)
+{
+       if (!lmp_bredr_capable(hdev))
+               return MGMT_STATUS_NOT_SUPPORTED;
+       else if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return MGMT_STATUS_REJECTED;
+       else
+               return MGMT_STATUS_SUCCESS;
+}
+
+static u8 mgmt_le_support(struct hci_dev *hdev)
+{
+       if (!lmp_le_capable(hdev))
+               return MGMT_STATUS_NOT_SUPPORTED;
+       else if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return MGMT_STATUS_REJECTED;
+       else
+               return MGMT_STATUS_SUCCESS;
+}
+
+static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
+{
+       struct pending_cmd *cmd;
+       struct mgmt_mode *cp;
+       struct hci_request req;
+       bool changed;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+               cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+               goto remove_cmd;
+       }
+
+       cp = cmd->param;
+       if (cp->val) {
+               changed = !test_and_set_bit(HCI_DISCOVERABLE,
+                                           &hdev->dev_flags);
+
+               if (hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+                                          to);
+               }
+       } else {
+               changed = test_and_clear_bit(HCI_DISCOVERABLE,
+                                            &hdev->dev_flags);
+       }
+
+       send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
+
+       if (changed)
+               new_settings(hdev, cmd->sk);
+
+       /* When the discoverable mode gets changed, make sure
+        * that class of device has the limited discoverable
+        * bit correctly set.
+        */
+       hci_req_init(&req, hdev);
+       update_class(&req);
+       hci_req_run(&req, NULL);
+
+remove_cmd:
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                            u16 len)
 {
        struct mgmt_cp_set_discoverable *cp = data;
        struct pending_cmd *cmd;
+       struct hci_request req;
        u16 timeout;
        u8 scan;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       if (!lmp_bredr_capable(hdev))
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+           !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
-                                MGMT_STATUS_NOT_SUPPORTED);
+                                 MGMT_STATUS_REJECTED);
 
-       if (cp->val != 0x00 && cp->val != 0x01)
+       if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                  MGMT_STATUS_INVALID_PARAMS);
 
        timeout = __le16_to_cpu(cp->timeout);
-       if (!cp->val && timeout > 0)
+
+       /* Disabling discoverable requires that no timeout is set,
+        * and enabling limited discoverable requires a timeout.
+        */
+       if ((cp->val == 0x00 && timeout > 0) ||
+           (cp->val == 0x02 && timeout == 0))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                  MGMT_STATUS_INVALID_PARAMS);
 
@@ -931,6 +1191,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (!hdev_is_powered(hdev)) {
                bool changed = false;
 
+               /* Setting limited discoverable when powered off is
+                * not a valid operation since it requires a timeout
+                * and so no need to check HCI_LIMITED_DISCOVERABLE.
+                */
                if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
                        change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
                        changed = true;
@@ -946,16 +1210,20 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
-               if (hdev->discov_timeout > 0) {
-                       cancel_delayed_work(&hdev->discov_off);
-                       hdev->discov_timeout = 0;
-               }
+       /* If the current mode is the same, then just update the timeout
+        * value with the new value. And if only the timeout gets updated,
+        * then no need for any HCI transactions.
+        */
+       if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) &&
+           (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE,
+                                         &hdev->dev_flags)) {
+               cancel_delayed_work(&hdev->discov_off);
+               hdev->discov_timeout = timeout;
 
-               if (cp->val && timeout > 0) {
-                       hdev->discov_timeout = timeout;
+               if (cp->val && hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
                        queue_delayed_work(hdev->workqueue, &hdev->discov_off,
-                               msecs_to_jiffies(hdev->discov_timeout * 1000));
+                                          to);
                }
 
                err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -968,20 +1236,66 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       /* Cancel any potential discoverable timeout that might be
+        * still active and store new timeout value. The arming of
+        * the timeout happens in the complete handler.
+        */
+       cancel_delayed_work(&hdev->discov_off);
+       hdev->discov_timeout = timeout;
+
+       /* Limited discoverable mode */
+       if (cp->val == 0x02)
+               set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       else
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+
+       hci_req_init(&req, hdev);
+
+       /* The procedure for LE-only controllers is much simpler - just
+        * update the advertising data.
+        */
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               goto update_ad;
+
        scan = SCAN_PAGE;
 
-       if (cp->val)
+       if (cp->val) {
+               struct hci_cp_write_current_iac_lap hci_cp;
+
+               if (cp->val == 0x02) {
+                       /* Limited discoverable mode */
+                       hci_cp.num_iac = 2;
+                       hci_cp.iac_lap[0] = 0x00;       /* LIAC */
+                       hci_cp.iac_lap[1] = 0x8b;
+                       hci_cp.iac_lap[2] = 0x9e;
+                       hci_cp.iac_lap[3] = 0x33;       /* GIAC */
+                       hci_cp.iac_lap[4] = 0x8b;
+                       hci_cp.iac_lap[5] = 0x9e;
+               } else {
+                       /* General discoverable mode */
+                       hci_cp.num_iac = 1;
+                       hci_cp.iac_lap[0] = 0x33;       /* GIAC */
+                       hci_cp.iac_lap[1] = 0x8b;
+                       hci_cp.iac_lap[2] = 0x9e;
+               }
+
+               hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+                           (hci_cp.num_iac * 3) + 1, &hci_cp);
+
                scan |= SCAN_INQUIRY;
-       else
-               cancel_delayed_work(&hdev->discov_off);
+       } else {
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       }
+
+       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
+
+update_ad:
+       update_adv_data(&req);
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       err = hci_req_run(&req, set_discoverable_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
-       if (cp->val)
-               hdev->discov_timeout = timeout;
-
 failed:
        hci_dev_unlock(hdev);
        return err;
@@ -993,6 +1307,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
        struct hci_cp_write_page_scan_activity acp;
        u8 type;
 
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
        if (hdev->hci_ver < BLUETOOTH_VER_1_2)
                return;
 
@@ -1019,9 +1336,55 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
                hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
 }
 
+static u8 get_adv_type(struct hci_dev *hdev)
+{
+       struct pending_cmd *cmd;
+       bool connectable;
+
+       /* If there's a pending mgmt command the flag will not yet have
+        * it's final value, so check for this first.
+        */
+       cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+       if (cmd) {
+               struct mgmt_mode *cp = cmd->param;
+               connectable = !!cp->val;
+       } else {
+               connectable = test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       }
+
+       return connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
+}
+
+static void enable_advertising(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_param cp;
+       u8 enable = 0x01;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.min_interval = __constant_cpu_to_le16(0x0800);
+       cp.max_interval = __constant_cpu_to_le16(0x0800);
+       cp.type = get_adv_type(hdev);
+       cp.own_address_type = hdev->own_addr_type;
+       cp.channel_map = 0x07;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static void disable_advertising(struct hci_request *req)
+{
+       u8 enable = 0x00;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
 static void set_connectable_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
+       struct mgmt_mode *cp;
+       bool changed;
 
        BT_DBG("status 0x%02x", status);
 
@@ -1031,14 +1394,56 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
        if (!cmd)
                goto unlock;
 
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+               cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+               goto remove_cmd;
+       }
+
+       cp = cmd->param;
+       if (cp->val)
+               changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+
        send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
 
+       if (changed)
+               new_settings(hdev, cmd->sk);
+
+remove_cmd:
        mgmt_pending_remove(cmd);
 
 unlock:
        hci_dev_unlock(hdev);
 }
 
+static int set_connectable_update_settings(struct hci_dev *hdev,
+                                          struct sock *sk, u8 val)
+{
+       bool changed = false;
+       int err;
+
+       if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               changed = true;
+
+       if (val) {
+               set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       } else {
+               clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+               clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+       }
+
+       err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
+       if (err < 0)
+               return err;
+
+       if (changed)
+               return new_settings(hdev, sk);
+
+       return 0;
+}
+
 static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                           u16 len)
 {
@@ -1050,9 +1455,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
-       if (!lmp_bredr_capable(hdev))
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+           !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
-                                 MGMT_STATUS_NOT_SUPPORTED);
+                                 MGMT_STATUS_REJECTED);
 
        if (cp->val != 0x00 && cp->val != 0x01)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
@@ -1061,25 +1467,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
-               bool changed = false;
-
-               if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-
-               if (cp->val) {
-                       set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-               } else {
-                       clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-               }
-
-               err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
-               if (err < 0)
-                       goto failed;
-
-               if (changed)
-                       err = new_settings(hdev, sk);
-
+               err = set_connectable_update_settings(hdev, sk, cp->val);
                goto failed;
        }
 
@@ -1090,30 +1478,37 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
-       if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
-               err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
-               goto failed;
-       }
-
        cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
-       if (cp->val) {
-               scan = SCAN_PAGE;
-       } else {
-               scan = 0;
+       hci_req_init(&req, hdev);
 
-               if (test_bit(HCI_ISCAN, &hdev->flags) &&
-                   hdev->discov_timeout > 0)
-                       cancel_delayed_work(&hdev->discov_off);
-       }
+       /* If BR/EDR is not enabled and we disable advertising as a
+        * by-product of disabling connectable, we need to update the
+        * advertising flags.
+        */
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               if (!cp->val) {
+                       clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+               }
+               update_adv_data(&req);
+       } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
+               if (cp->val) {
+                       scan = SCAN_PAGE;
+               } else {
+                       scan = 0;
 
-       hci_req_init(&req, hdev);
+                       if (test_bit(HCI_ISCAN, &hdev->flags) &&
+                           hdev->discov_timeout > 0)
+                               cancel_delayed_work(&hdev->discov_off);
+               }
 
-       hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+               hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       }
 
        /* If we're going from non-connectable to connectable or
         * vice-versa when fast connectable is enabled ensure that fast
@@ -1124,9 +1519,20 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
                write_fast_connectable(&req, false);
 
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) &&
+           hci_conn_num(hdev, LE_LINK) == 0) {
+               disable_advertising(&req);
+               enable_advertising(&req);
+       }
+
        err = hci_req_run(&req, set_connectable_complete);
-       if (err < 0)
+       if (err < 0) {
                mgmt_pending_remove(cmd);
+               if (err == -ENODATA)
+                       err = set_connectable_update_settings(hdev, sk,
+                                                             cp->val);
+               goto failed;
+       }
 
 failed:
        hci_dev_unlock(hdev);
@@ -1137,6 +1543,7 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
                        u16 len)
 {
        struct mgmt_mode *cp = data;
+       bool changed;
        int err;
 
        BT_DBG("request for %s", hdev->name);
@@ -1148,17 +1555,18 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (cp->val)
-               set_bit(HCI_PAIRABLE, &hdev->dev_flags);
+               changed = !test_and_set_bit(HCI_PAIRABLE, &hdev->dev_flags);
        else
-               clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
 
        err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
        if (err < 0)
-               goto failed;
+               goto unlock;
 
-       err = new_settings(hdev, sk);
+       if (changed)
+               err = new_settings(hdev, sk);
 
-failed:
+unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -1168,14 +1576,15 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
-       u8 val;
+       u8 val, status;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       if (!lmp_bredr_capable(hdev))
+       status = mgmt_bredr_support(hdev);
+       if (status)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
-                                 MGMT_STATUS_NOT_SUPPORTED);
+                                 status);
 
        if (cp->val != 0x00 && cp->val != 0x01)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
@@ -1236,11 +1645,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
        struct mgmt_mode *cp = data;
        struct pending_cmd *cmd;
-       u8 val;
+       u8 status;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
+       status = mgmt_bredr_support(hdev);
+       if (status)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status);
+
        if (!lmp_ssp_capable(hdev))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
                                  MGMT_STATUS_NOT_SUPPORTED);
@@ -1251,14 +1664,20 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        hci_dev_lock(hdev);
 
-       val = !!cp->val;
-
        if (!hdev_is_powered(hdev)) {
-               bool changed = false;
+               bool changed;
 
-               if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
-                       change_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
-                       changed = true;
+               if (cp->val) {
+                       changed = !test_and_set_bit(HCI_SSP_ENABLED,
+                                                   &hdev->dev_flags);
+               } else {
+                       changed = test_and_clear_bit(HCI_SSP_ENABLED,
+                                                    &hdev->dev_flags);
+                       if (!changed)
+                               changed = test_and_clear_bit(HCI_HS_ENABLED,
+                                                            &hdev->dev_flags);
+                       else
+                               clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
                }
 
                err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
@@ -1271,13 +1690,14 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
+       if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) ||
+           mgmt_pending_find(MGMT_OP_SET_HS, hdev)) {
                err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
                                 MGMT_STATUS_BUSY);
                goto failed;
        }
 
-       if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
+       if (!!cp->val == test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
                err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
                goto failed;
        }
@@ -1288,7 +1708,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto failed;
        }
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
+       err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val);
        if (err < 0) {
                mgmt_pending_remove(cmd);
                goto failed;
@@ -1302,23 +1722,90 @@ failed:
 static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
        struct mgmt_mode *cp = data;
+       bool changed;
+       u8 status;
+       int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       if (!enable_hs)
+       status = mgmt_bredr_support(hdev);
+       if (status)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
+
+       if (!lmp_ssp_capable(hdev))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+                                 MGMT_STATUS_REJECTED);
+
        if (cp->val != 0x00 && cp->val != 0x01)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
                                  MGMT_STATUS_INVALID_PARAMS);
 
-       if (cp->val)
-               set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
-       else
-               clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+       hci_dev_lock(hdev);
+
+       if (cp->val) {
+               changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+       } else {
+               if (hdev_is_powered(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+                                        MGMT_STATUS_REJECTED);
+                       goto unlock;
+               }
+
+               changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+       }
+
+       err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (changed)
+               err = new_settings(hdev, sk);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
 
-       return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
+static void le_enable_complete(struct hci_dev *hdev, u8 status)
+{
+       struct cmd_lookup match = { NULL, hdev };
+
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+
+               mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
+                                    &mgmt_err);
+               return;
+       }
+
+       mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
+
+       new_settings(hdev, match.sk);
+
+       if (match.sk)
+               sock_put(match.sk);
+
+       /* Make sure the controller has a good default for
+        * advertising data. Restrict the update to when LE
+        * has actually been enabled. During power on, the
+        * update in powered_update_hci will take care of it.
+        */
+       if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+               struct hci_request req;
+
+               hci_dev_lock(hdev);
+
+               hci_req_init(&req, hdev);
+               update_adv_data(&req);
+               update_scan_rsp_data(&req);
+               hci_req_run(&req, NULL);
+
+               hci_dev_unlock(hdev);
+       }
 }
 
 static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -1326,6 +1813,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
        struct mgmt_mode *cp = data;
        struct hci_cp_write_le_host_supported hci_cp;
        struct pending_cmd *cmd;
+       struct hci_request req;
        int err;
        u8 val, enabled;
 
@@ -1340,7 +1828,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                                  MGMT_STATUS_INVALID_PARAMS);
 
        /* LE-only devices do not allow toggling LE on/off */
-       if (!lmp_bredr_capable(hdev))
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
                                  MGMT_STATUS_REJECTED);
 
@@ -1357,6 +1845,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                        changed = true;
                }
 
+               if (!val && test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+                       changed = true;
+               }
+
                err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
                if (err < 0)
                        goto unlock;
@@ -1367,7 +1860,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto unlock;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
+       if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) ||
+           mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
                err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
                                 MGMT_STATUS_BUSY);
                goto unlock;
@@ -1379,15 +1873,22 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto unlock;
        }
 
+       hci_req_init(&req, hdev);
+
        memset(&hci_cp, 0, sizeof(hci_cp));
 
        if (val) {
                hci_cp.le = val;
                hci_cp.simul = lmp_le_br_capable(hdev);
+       } else {
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+                       disable_advertising(&req);
        }
 
-       err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
-                          &hci_cp);
+       hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
+                   &hci_cp);
+
+       err = hci_req_run(&req, le_enable_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -1706,6 +2207,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
        u16 key_count, expected_len;
        int i;
 
+       BT_DBG("request for %s", hdev->name);
+
+       if (!lmp_bredr_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
        key_count = __le16_to_cpu(cp->key_count);
 
        expected_len = sizeof(*cp) + key_count *
@@ -2515,8 +3022,11 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
                update_eir(&req);
        }
 
+       /* The name is stored in the scan response data and so
+        * no need to udpate the advertising data here.
+        */
        if (lmp_le_capable(hdev))
-               hci_update_ad(&req);
+               update_scan_rsp_data(&req);
 
        err = hci_req_run(&req, set_name_complete);
        if (err < 0)
@@ -2685,6 +3195,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
        struct hci_request req;
        /* General inquiry access code (GIAC) */
        u8 lap[3] = { 0x33, 0x8b, 0x9e };
+       u8 status;
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -2721,9 +3232,10 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_BREDR:
-               if (!lmp_bredr_capable(hdev)) {
+               status = mgmt_bredr_support(hdev);
+               if (status) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                        MGMT_STATUS_NOT_SUPPORTED);
+                                        status);
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
@@ -2745,22 +3257,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 
        case DISCOV_TYPE_LE:
        case DISCOV_TYPE_INTERLEAVED:
-               if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+               status = mgmt_le_support(hdev);
+               if (status) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                        MGMT_STATUS_NOT_SUPPORTED);
+                                        status);
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
 
                if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
-                   !lmp_bredr_capable(hdev)) {
+                   !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_NOT_SUPPORTED);
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
 
-               if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_REJECTED);
                        mgmt_pending_remove(cmd);
@@ -2778,6 +3291,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                param_cp.type = LE_SCAN_ACTIVE;
                param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
                param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+               param_cp.own_address_type = hdev->own_addr_type;
                hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
                            &param_cp);
 
@@ -3065,6 +3579,186 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
        return err;
 }
 
+static void set_advertising_complete(struct hci_dev *hdev, u8 status)
+{
+       struct cmd_lookup match = { NULL, hdev };
+
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+
+               mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev,
+                                    cmd_status_rsp, &mgmt_err);
+               return;
+       }
+
+       mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
+                            &match);
+
+       new_settings(hdev, match.sk);
+
+       if (match.sk)
+               sock_put(match.sk);
+}
+
+static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
+                          u16 len)
+{
+       struct mgmt_mode *cp = data;
+       struct pending_cmd *cmd;
+       struct hci_request req;
+       u8 val, enabled, status;
+       int err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       status = mgmt_le_support(hdev);
+       if (status)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+                                 status);
+
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       val = !!cp->val;
+       enabled = test_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
+       /* The following conditions are ones which mean that we should
+        * not do any HCI communication but directly send a mgmt
+        * response to user space (after toggling the flag if
+        * necessary).
+        */
+       if (!hdev_is_powered(hdev) || val == enabled ||
+           hci_conn_num(hdev, LE_LINK) > 0) {
+               bool changed = false;
+
+               if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+                       change_bit(HCI_ADVERTISING, &hdev->dev_flags);
+                       changed = true;
+               }
+
+               err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev);
+               if (err < 0)
+                       goto unlock;
+
+               if (changed)
+                       err = new_settings(hdev, sk);
+
+               goto unlock;
+       }
+
+       if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
+           mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+                                MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hci_req_init(&req, hdev);
+
+       if (val)
+               enable_advertising(&req);
+       else
+               disable_advertising(&req);
+
+       err = hci_req_run(&req, set_advertising_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int set_static_address(struct sock *sk, struct hci_dev *hdev,
+                             void *data, u16 len)
+{
+       struct mgmt_cp_set_static_address *cp = data;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       if (hdev_is_powered(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+                                 MGMT_STATUS_REJECTED);
+
+       if (bacmp(&cp->bdaddr, BDADDR_ANY)) {
+               if (!bacmp(&cp->bdaddr, BDADDR_NONE))
+                       return cmd_status(sk, hdev->id,
+                                         MGMT_OP_SET_STATIC_ADDRESS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+
+               /* Two most significant bits shall be set */
+               if ((cp->bdaddr.b[5] & 0xc0) != 0xc0)
+                       return cmd_status(sk, hdev->id,
+                                         MGMT_OP_SET_STATIC_ADDRESS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       hci_dev_lock(hdev);
+
+       bacpy(&hdev->static_addr, &cp->bdaddr);
+
+       err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
+                          void *data, u16 len)
+{
+       struct mgmt_cp_set_scan_params *cp = data;
+       __u16 interval, window;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       interval = __le16_to_cpu(cp->interval);
+
+       if (interval < 0x0004 || interval > 0x4000)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       window = __le16_to_cpu(cp->window);
+
+       if (window < 0x0004 || window > 0x4000)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       if (window > interval)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       hdev->le_scan_interval = interval;
+       hdev->le_scan_window = window;
+
+       err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
 static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
@@ -3108,7 +3802,8 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
 
        BT_DBG("%s", hdev->name);
 
-       if (!lmp_bredr_capable(hdev) || hdev->hci_ver < BLUETOOTH_VER_1_2)
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) ||
+           hdev->hci_ver < BLUETOOTH_VER_1_2)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
@@ -3124,41 +3819,183 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_REJECTED);
 
-       hci_dev_lock(hdev);
+       hci_dev_lock(hdev);
+
+       if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+                                MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) {
+               err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE,
+                                       hdev);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev,
+                              data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hci_req_init(&req, hdev);
+
+       write_fast_connectable(&req, cp->val);
+
+       err = hci_req_run(&req, fast_connectable_complete);
+       if (err < 0) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+                                MGMT_STATUS_FAILED);
+               mgmt_pending_remove(cmd);
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static void set_bredr_scan(struct hci_request *req)
+{
+       struct hci_dev *hdev = req->hdev;
+       u8 scan = 0;
+
+       /* Ensure that fast connectable is disabled. This function will
+        * not do anything if the page scan parameters are already what
+        * they should be.
+        */
+       write_fast_connectable(req, false);
+
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               scan |= SCAN_PAGE;
+       if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+               scan |= SCAN_INQUIRY;
+
+       if (scan)
+               hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
+static void set_bredr_complete(struct hci_dev *hdev, u8 status)
+{
+       struct pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+
+               /* We need to restore the flag if related HCI commands
+                * failed.
+                */
+               clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
+               cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+       } else {
+               send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev);
+               new_settings(hdev, cmd->sk);
+       }
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
+{
+       struct mgmt_mode *cp = data;
+       struct pending_cmd *cmd;
+       struct hci_request req;
+       int err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+                                 MGMT_STATUS_REJECTED);
+
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
+               goto unlock;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               if (!cp->val) {
+                       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+                       clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+                       clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+                       clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
+                       clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+               }
+
+               change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
+               err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
+               if (err < 0)
+                       goto unlock;
+
+               err = new_settings(hdev, sk);
+               goto unlock;
+       }
 
-       if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-                                MGMT_STATUS_BUSY);
+       /* Reject disabling when powered on */
+       if (!cp->val) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+                                MGMT_STATUS_REJECTED);
                goto unlock;
        }
 
-       if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) {
-               err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE,
-                                       hdev);
+       if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+                                MGMT_STATUS_BUSY);
                goto unlock;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev,
-                              data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_BREDR, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto unlock;
        }
 
+       /* We need to flip the bit already here so that update_adv_data
+        * generates the correct flags.
+        */
+       set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
        hci_req_init(&req, hdev);
 
-       write_fast_connectable(&req, cp->val);
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               set_bredr_scan(&req);
 
-       err = hci_req_run(&req, fast_connectable_complete);
-       if (err < 0) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
-                                MGMT_STATUS_FAILED);
+       /* Since only the advertising data flags will change, there
+        * is no need to update the scan response data.
+        */
+       update_adv_data(&req);
+
+       err = hci_req_run(&req, set_bredr_complete);
+       if (err < 0)
                mgmt_pending_remove(cmd);
-       }
 
 unlock:
        hci_dev_unlock(hdev);
-
        return err;
 }
 
@@ -3180,6 +4017,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
        u16 key_count, expected_len;
        int i, err;
 
+       BT_DBG("request for %s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
        key_count = __le16_to_cpu(cp->key_count);
 
        expected_len = sizeof(*cp) + key_count *
@@ -3208,15 +4051,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 
        for (i = 0; i < key_count; i++) {
                struct mgmt_ltk_info *key = &cp->keys[i];
-               u8 type;
+               u8 type, addr_type;
+
+               if (key->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
 
                if (key->master)
                        type = HCI_SMP_LTK;
                else
                        type = HCI_SMP_LTK_SLAVE;
 
-               hci_add_ltk(hdev, &key->addr.bdaddr,
-                           bdaddr_to_le(key->addr.type),
+               hci_add_ltk(hdev, &key->addr.bdaddr, addr_type,
                            type, 0, key->authenticated, key->val,
                            key->enc_size, key->ediv, key->rand);
        }
@@ -3276,6 +4123,10 @@ static const struct mgmt_handler {
        { block_device,           false, MGMT_BLOCK_DEVICE_SIZE },
        { unblock_device,         false, MGMT_UNBLOCK_DEVICE_SIZE },
        { set_device_id,          false, MGMT_SET_DEVICE_ID_SIZE },
+       { set_advertising,        false, MGMT_SETTING_SIZE },
+       { set_bredr,              false, MGMT_SETTING_SIZE },
+       { set_static_address,     false, MGMT_SET_STATIC_ADDRESS_SIZE },
+       { set_scan_params,        false, MGMT_SET_SCAN_PARAMS_SIZE },
 };
 
 
@@ -3320,6 +4171,13 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                                         MGMT_STATUS_INVALID_INDEX);
                        goto done;
                }
+
+               if (test_bit(HCI_SETUP, &hdev->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+                       err = cmd_status(sk, index, opcode,
+                                        MGMT_STATUS_INVALID_INDEX);
+                       goto done;
+               }
        }
 
        if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
@@ -3365,74 +4223,24 @@ done:
        return err;
 }
 
-static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
-{
-       u8 *status = data;
-
-       cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
-       mgmt_pending_remove(cmd);
-}
-
-int mgmt_index_added(struct hci_dev *hdev)
+void mgmt_index_added(struct hci_dev *hdev)
 {
-       if (!mgmt_valid_hdev(hdev))
-               return -ENOTSUPP;
+       if (hdev->dev_type != HCI_BREDR)
+               return;
 
-       return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+       mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
 }
 
-int mgmt_index_removed(struct hci_dev *hdev)
+void mgmt_index_removed(struct hci_dev *hdev)
 {
        u8 status = MGMT_STATUS_INVALID_INDEX;
 
-       if (!mgmt_valid_hdev(hdev))
-               return -ENOTSUPP;
+       if (hdev->dev_type != HCI_BREDR)
+               return;
 
        mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
 
-       return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
-}
-
-struct cmd_lookup {
-       struct sock *sk;
-       struct hci_dev *hdev;
-       u8 mgmt_status;
-};
-
-static void settings_rsp(struct pending_cmd *cmd, void *data)
-{
-       struct cmd_lookup *match = data;
-
-       send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
-
-       list_del(&cmd->list);
-
-       if (match->sk == NULL) {
-               match->sk = cmd->sk;
-               sock_hold(match->sk);
-       }
-
-       mgmt_pending_free(cmd);
-}
-
-static void set_bredr_scan(struct hci_request *req)
-{
-       struct hci_dev *hdev = req->hdev;
-       u8 scan = 0;
-
-       /* Ensure that fast connectable is disabled. This function will
-        * not do anything if the page scan parameters are already what
-        * they should be.
-        */
-       write_fast_connectable(req, false);
-
-       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-               scan |= SCAN_PAGE;
-       if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-               scan |= SCAN_INQUIRY;
-
-       if (scan)
-               hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+       mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
 }
 
 static void powered_complete(struct hci_dev *hdev, u8 status)
@@ -3483,13 +4291,33 @@ static int powered_update_hci(struct hci_dev *hdev)
                                    sizeof(cp), &cp);
        }
 
+       if (lmp_le_capable(hdev)) {
+               /* Set random address to static address if configured */
+               if (bacmp(&hdev->static_addr, BDADDR_ANY))
+                       hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
+                                   &hdev->static_addr);
+
+               /* Make sure the controller has a good default for
+                * advertising data. This also applies to the case
+                * where BR/EDR was toggled during the AUTO_OFF phase.
+                */
+               if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+                       update_adv_data(&req);
+                       update_scan_rsp_data(&req);
+               }
+
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+                       enable_advertising(&req);
+       }
+
        link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
        if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
                hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE,
                            sizeof(link_sec), &link_sec);
 
        if (lmp_bredr_capable(hdev)) {
-               set_bredr_scan(&req);
+               if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+                       set_bredr_scan(&req);
                update_class(&req);
                update_name(&req);
                update_eir(&req);
@@ -3533,76 +4361,110 @@ new_settings:
        return err;
 }
 
-int mgmt_set_powered_failed(struct hci_dev *hdev, int err)
+void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
 {
        struct pending_cmd *cmd;
        u8 status;
 
        cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        if (err == -ERFKILL)
                status = MGMT_STATUS_RFKILLED;
        else
                status = MGMT_STATUS_FAILED;
 
-       err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
+       cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
 
        mgmt_pending_remove(cmd);
+}
 
-       return err;
+void mgmt_discoverable_timeout(struct hci_dev *hdev)
+{
+       struct hci_request req;
+
+       hci_dev_lock(hdev);
+
+       /* When discoverable timeout triggers, then just make sure
+        * the limited discoverable flag is cleared. Even in the case
+        * of a timeout triggered from general discoverable, it is
+        * safe to unconditionally clear the flag.
+        */
+       clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+       clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+
+       hci_req_init(&req, hdev);
+       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+               u8 scan = SCAN_PAGE;
+               hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
+                           sizeof(scan), &scan);
+       }
+       update_class(&req);
+       update_adv_data(&req);
+       hci_req_run(&req, NULL);
+
+       hdev->discov_timeout = 0;
+
+       new_settings(hdev, NULL);
+
+       hci_dev_unlock(hdev);
 }
 
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
 {
-       struct cmd_lookup match = { NULL, hdev };
-       bool changed = false;
-       int err = 0;
+       bool changed;
+
+       /* Nothing needed here if there's a pending command since that
+        * commands request completion callback takes care of everything
+        * necessary.
+        */
+       if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
+               return;
 
        if (discoverable) {
-               if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-                       changed = true;
+               changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
        } else {
-               if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
-                       changed = true;
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
        }
 
-       mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp,
-                            &match);
-
-       if (changed)
-               err = new_settings(hdev, match.sk);
+       if (changed) {
+               struct hci_request req;
 
-       if (match.sk)
-               sock_put(match.sk);
+               /* In case this change in discoverable was triggered by
+                * a disabling of connectable there could be a need to
+                * update the advertising flags.
+                */
+               hci_req_init(&req, hdev);
+               update_adv_data(&req);
+               hci_req_run(&req, NULL);
 
-       return err;
+               new_settings(hdev, NULL);
+       }
 }
 
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
 {
-       struct pending_cmd *cmd;
-       bool changed = false;
-       int err = 0;
+       bool changed;
 
-       if (connectable) {
-               if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags))
-                       changed = true;
-       }
+       /* Nothing needed here if there's a pending command since that
+        * commands request completion callback takes care of everything
+        * necessary.
+        */
+       if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
+               return;
 
-       cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+       if (connectable)
+               changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
 
        if (changed)
-               err = new_settings(hdev, cmd ? cmd->sk : NULL);
-
-       return err;
+               new_settings(hdev, NULL);
 }
 
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
 {
        u8 mgmt_err = mgmt_status(status);
 
@@ -3613,12 +4475,10 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
        if (scan & SCAN_INQUIRY)
                mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
                                     cmd_status_rsp, &mgmt_err);
-
-       return 0;
 }
 
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
-                     bool persistent)
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                      bool persistent)
 {
        struct mgmt_ev_new_link_key ev;
 
@@ -3631,10 +4491,10 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE);
        ev.key.pin_len = key->pin_len;
 
-       return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
 {
        struct mgmt_ev_new_long_term_key ev;
 
@@ -3653,13 +4513,23 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
        memcpy(ev.key.rand, key->rand, sizeof(key->rand));
        memcpy(ev.key.val, key->val, sizeof(key->val));
 
-       return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev),
-                         NULL);
+       mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
+}
+
+static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
+                                 u8 data_len)
+{
+       eir[eir_len++] = sizeof(type) + data_len;
+       eir[eir_len++] = type;
+       memcpy(&eir[eir_len], data, data_len);
+       eir_len += data_len;
+
+       return eir_len;
 }
 
-int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                         u8 addr_type, u32 flags, u8 *name, u8 name_len,
-                         u8 *dev_class)
+void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                          u8 addr_type, u32 flags, u8 *name, u8 name_len,
+                          u8 *dev_class)
 {
        char buf[512];
        struct mgmt_ev_device_connected *ev = (void *) buf;
@@ -3680,8 +4550,8 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        ev->eir_len = cpu_to_le16(eir_len);
 
-       return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
-                         sizeof(*ev) + eir_len, NULL);
+       mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
+                   sizeof(*ev) + eir_len, NULL);
 }
 
 static void disconnect_rsp(struct pending_cmd *cmd, void *data)
@@ -3719,12 +4589,11 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
        mgmt_pending_remove(cmd);
 }
 
-int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                            u8 link_type, u8 addr_type, u8 reason)
+void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                             u8 link_type, u8 addr_type, u8 reason)
 {
        struct mgmt_ev_device_disconnected ev;
        struct sock *sk = NULL;
-       int err;
 
        mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
 
@@ -3732,45 +4601,39 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.reason = reason;
 
-       err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
-                        sk);
+       mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
 
        if (sk)
                sock_put(sk);
 
        mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
                             hdev);
-
-       return err;
 }
 
-int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                          u8 link_type, u8 addr_type, u8 status)
+void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 link_type, u8 addr_type, u8 status)
 {
        struct mgmt_rp_disconnect rp;
        struct pending_cmd *cmd;
-       int err;
 
        mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
                             hdev);
 
        cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = link_to_bdaddr(link_type, addr_type);
 
-       err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
-int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                       u8 addr_type, u8 status)
+void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                        u8 addr_type, u8 status)
 {
        struct mgmt_ev_connect_failed ev;
 
@@ -3778,10 +4641,10 @@ int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.status = mgmt_status(status);
 
-       return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
 {
        struct mgmt_ev_pin_code_request ev;
 
@@ -3789,52 +4652,45 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
        ev.addr.type = BDADDR_BREDR;
        ev.secure = secure;
 
-       return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
-                         NULL);
+       mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                u8 status)
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                 u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = BDADDR_BREDR;
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 status)
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                     u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        bacpy(&rp.addr.bdaddr, bdaddr);
        rp.addr.type = BDADDR_BREDR;
 
-       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
-                          mgmt_status(status), &rp, sizeof(rp));
+       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+                    mgmt_status(status), &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
-
-       return err;
 }
 
 int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -3936,8 +4792,8 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
        return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, u8 status)
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, u8 status)
 {
        struct mgmt_ev_auth_failed ev;
 
@@ -3945,40 +4801,36 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev.addr.type = link_to_bdaddr(link_type, addr_type);
        ev.status = mgmt_status(status);
 
-       return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
-       bool changed = false;
-       int err = 0;
+       bool changed;
 
        if (status) {
                u8 mgmt_err = mgmt_status(status);
                mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
                                     cmd_status_rsp, &mgmt_err);
-               return 0;
+               return;
        }
 
-       if (test_bit(HCI_AUTH, &hdev->flags)) {
-               if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
-                       changed = true;
-       }
+       if (test_bit(HCI_AUTH, &hdev->flags))
+               changed = !test_and_set_bit(HCI_LINK_SECURITY,
+                                           &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_LINK_SECURITY,
+                                            &hdev->dev_flags);
 
        mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
                             &match);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
+               new_settings(hdev, match.sk);
 
        if (match.sk)
                sock_put(match.sk);
-
-       return err;
 }
 
 static void clear_eir(struct hci_request *req)
@@ -3996,38 +4848,41 @@ static void clear_eir(struct hci_request *req)
        hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
 }
 
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
        struct hci_request req;
        bool changed = false;
-       int err = 0;
 
        if (status) {
                u8 mgmt_err = mgmt_status(status);
 
                if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
-                                                &hdev->dev_flags))
-                       err = new_settings(hdev, NULL);
+                                                &hdev->dev_flags)) {
+                       clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+                       new_settings(hdev, NULL);
+               }
 
                mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp,
                                     &mgmt_err);
-
-               return err;
+               return;
        }
 
        if (enable) {
-               if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-                       changed = true;
+               changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
        } else {
-               if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
-                       changed = true;
+               changed = test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+               if (!changed)
+                       changed = test_and_clear_bit(HCI_HS_ENABLED,
+                                                    &hdev->dev_flags);
+               else
+                       clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
        }
 
        mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
 
        if (changed)
-               err = new_settings(hdev, match.sk);
+               new_settings(hdev, match.sk);
 
        if (match.sk)
                sock_put(match.sk);
@@ -4040,8 +4895,6 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
                clear_eir(&req);
 
        hci_req_run(&req, NULL);
-
-       return err;
 }
 
 static void sk_lookup(struct pending_cmd *cmd, void *data)
@@ -4054,33 +4907,30 @@ static void sk_lookup(struct pending_cmd *cmd, void *data)
        }
 }
 
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
-                                  u8 status)
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+                                   u8 status)
 {
        struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
-       int err = 0;
 
        mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match);
        mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
        mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
 
        if (!status)
-               err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
-                                3, NULL);
+               mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
+                          NULL);
 
        if (match.sk)
                sock_put(match.sk);
-
-       return err;
 }
 
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 {
        struct mgmt_cp_set_local_name ev;
        struct pending_cmd *cmd;
 
        if (status)
-               return 0;
+               return;
 
        memset(&ev, 0, sizeof(ev));
        memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
@@ -4094,96 +4944,54 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
                 * HCI dev don't send any mgmt signals.
                 */
                if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-                       return 0;
+                       return;
        }
 
-       return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
-                         cmd ? cmd->sk : NULL);
+       mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+                  cmd ? cmd->sk : NULL);
 }
 
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                           u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                            u8 *randomizer, u8 status)
 {
        struct pending_cmd *cmd;
-       int err;
 
        BT_DBG("%s status %u", hdev->name, status);
 
        cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
        if (!cmd)
-               return -ENOENT;
+               return;
 
        if (status) {
-               err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                mgmt_status(status));
+               cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+                          mgmt_status(status));
        } else {
                struct mgmt_rp_read_local_oob_data rp;
 
                memcpy(rp.hash, hash, sizeof(rp.hash));
                memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
 
-               err = cmd_complete(cmd->sk, hdev->id,
-                                  MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
-                                  sizeof(rp));
+               cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+                            0, &rp, sizeof(rp));
        }
 
        mgmt_pending_remove(cmd);
-
-       return err;
-}
-
-int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
-{
-       struct cmd_lookup match = { NULL, hdev };
-       bool changed = false;
-       int err = 0;
-
-       if (status) {
-               u8 mgmt_err = mgmt_status(status);
-
-               if (enable && test_and_clear_bit(HCI_LE_ENABLED,
-                                                &hdev->dev_flags))
-                       err = new_settings(hdev, NULL);
-
-               mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
-                                    &mgmt_err);
-
-               return err;
-       }
-
-       if (enable) {
-               if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags))
-                       changed = true;
-       } else {
-               if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags))
-                       changed = true;
-       }
-
-       mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
-
-       if (changed)
-               err = new_settings(hdev, match.sk);
-
-       if (match.sk)
-               sock_put(match.sk);
-
-       return err;
 }
 
-int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                     u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
-                     ssp, u8 *eir, u16 eir_len)
+void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
+                      ssp, u8 *eir, u16 eir_len)
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
        size_t ev_size;
 
        if (!hci_discovery_active(hdev))
-               return -EPERM;
+               return;
 
        /* Leave 5 bytes for a potential CoD field */
        if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
-               return -EINVAL;
+               return;
 
        memset(buf, 0, sizeof(buf));
 
@@ -4205,11 +5013,11 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        ev->eir_len = cpu_to_le16(eir_len);
        ev_size = sizeof(*ev) + eir_len;
 
-       return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
+       mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
 }
 
-int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                    u8 addr_type, s8 rssi, u8 *name, u8 name_len)
+void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                     u8 addr_type, s8 rssi, u8 *name, u8 name_len)
 {
        struct mgmt_ev_device_found *ev;
        char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
@@ -4228,11 +5036,10 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        ev->eir_len = cpu_to_le16(eir_len);
 
-       return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
-                         sizeof(*ev) + eir_len, NULL);
+       mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, sizeof(*ev) + eir_len, NULL);
 }
 
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
+void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
 {
        struct mgmt_ev_discovering ev;
        struct pending_cmd *cmd;
@@ -4256,7 +5063,7 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
        ev.type = hdev->discovery.type;
        ev.discovering = discovering;
 
-       return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
+       mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
 }
 
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
@@ -4287,5 +5094,35 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
                          cmd ? cmd->sk : NULL);
 }
 
-module_param(enable_hs, bool, 0644);
-MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
+static void adv_enable_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("%s status %u", hdev->name, status);
+
+       /* Clear the advertising mgmt setting if we failed to re-enable it */
+       if (status) {
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+               new_settings(hdev, NULL);
+       }
+}
+
+void mgmt_reenable_advertising(struct hci_dev *hdev)
+{
+       struct hci_request req;
+
+       if (hci_conn_num(hdev, LE_LINK) > 0)
+               return;
+
+       if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+               return;
+
+       hci_req_init(&req, hdev);
+       enable_advertising(&req);
+
+       /* If this fails we have no option but to let user space know
+        * that we've disabled advertising.
+        */
+       if (hci_req_run(&req, adv_enable_complete) < 0) {
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+               new_settings(hdev, NULL);
+       }
+}
index ce75211..facd8a7 100644 (file)
@@ -641,13 +641,13 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
 {
        struct rfcomm_session *s;
        struct list_head *p, *n;
-       struct bt_sock *sk;
+       struct l2cap_chan *chan;
        list_for_each_safe(p, n, &session_list) {
                s = list_entry(p, struct rfcomm_session, list);
-               sk = bt_sk(s->sock->sk);
+               chan = l2cap_pi(s->sock->sk)->chan;
 
-               if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) &&
-                               !bacmp(&sk->dst, dst))
+               if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) &&
+                   !bacmp(&chan->dst, dst))
                        return s;
        }
        return NULL;
@@ -734,11 +734,11 @@ failed:
 
 void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)
 {
-       struct sock *sk = s->sock->sk;
+       struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;
        if (src)
-               bacpy(src, &bt_sk(sk)->src);
+               bacpy(src, &chan->src);
        if (dst)
-               bacpy(dst, &bt_sk(sk)->dst);
+               bacpy(dst, &chan->dst);
 }
 
 /* ---- RFCOMM frame sending ---- */
@@ -2115,12 +2115,11 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
        rfcomm_lock();
 
        list_for_each_entry(s, &session_list, list) {
+               struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;
                struct rfcomm_dlc *d;
                list_for_each_entry(d, &s->dlcs, list) {
-                       struct sock *sk = s->sock->sk;
-
                        seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n",
-                                  &bt_sk(sk)->src, &bt_sk(sk)->dst,
+                                  &chan->src, &chan->dst,
                                   d->state, d->dlci, d->mtu,
                                   d->rx_credits, d->tx_credits);
                }
@@ -2158,13 +2157,6 @@ static int __init rfcomm_init(void)
                goto unregister;
        }
 
-       if (bt_debugfs) {
-               rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
-                               bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);
-               if (!rfcomm_dlc_debugfs)
-                       BT_ERR("Failed to create RFCOMM debug file");
-       }
-
        err = rfcomm_init_ttys();
        if (err < 0)
                goto stop;
@@ -2175,6 +2167,13 @@ static int __init rfcomm_init(void)
 
        BT_INFO("RFCOMM ver %s", VERSION);
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
+                                                bt_debugfs, NULL,
+                                                &rfcomm_dlc_debugfs_fops);
+
        return 0;
 
 cleanup:
index 7096cfe..0be7619 100644 (file)
@@ -87,7 +87,8 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
                parent->sk_data_ready(parent, 0);
        } else {
                if (d->state == BT_CONNECTED)
-                       rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL);
+                       rfcomm_session_getaddr(d->session,
+                                              &rfcomm_pi(sk)->src, NULL);
                sk->sk_state_change(sk);
        }
 
@@ -110,7 +111,7 @@ static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
 
        sk_for_each(sk, &rfcomm_sk_list.head) {
                if (rfcomm_pi(sk)->channel == channel &&
-                               !bacmp(&bt_sk(sk)->src, src))
+                               !bacmp(&rfcomm_pi(sk)->src, src))
                        break;
        }
 
@@ -132,11 +133,11 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *
 
                if (rfcomm_pi(sk)->channel == channel) {
                        /* Exact match. */
-                       if (!bacmp(&bt_sk(sk)->src, src))
+                       if (!bacmp(&rfcomm_pi(sk)->src, src))
                                break;
 
                        /* Closest match */
-                       if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+                       if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY))
                                sk1 = sk;
                }
        }
@@ -355,7 +356,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
                err = -EADDRINUSE;
        } else {
                /* Save source address */
-               bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr);
+               bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
                rfcomm_pi(sk)->channel = sa->rc_channel;
                sk->sk_state = BT_BOUND;
        }
@@ -393,13 +394,14 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
        }
 
        sk->sk_state = BT_CONNECT;
-       bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
+       bacpy(&rfcomm_pi(sk)->dst, &sa->rc_bdaddr);
        rfcomm_pi(sk)->channel = sa->rc_channel;
 
        d->sec_level = rfcomm_pi(sk)->sec_level;
        d->role_switch = rfcomm_pi(sk)->role_switch;
 
-       err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+       err = rfcomm_dlc_open(d, &rfcomm_pi(sk)->src, &sa->rc_bdaddr,
+                             sa->rc_channel);
        if (!err)
                err = bt_sock_wait_state(sk, BT_CONNECTED,
                                sock_sndtimeo(sk, flags & O_NONBLOCK));
@@ -429,7 +431,7 @@ static int rfcomm_sock_listen(struct socket *sock, int backlog)
        }
 
        if (!rfcomm_pi(sk)->channel) {
-               bdaddr_t *src = &bt_sk(sk)->src;
+               bdaddr_t *src = &rfcomm_pi(sk)->src;
                u8 channel;
 
                err = -EINVAL;
@@ -530,9 +532,9 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *
        sa->rc_family  = AF_BLUETOOTH;
        sa->rc_channel = rfcomm_pi(sk)->channel;
        if (peer)
-               bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst);
+               bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->dst);
        else
-               bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src);
+               bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->src);
 
        *len = sizeof(struct sockaddr_rc);
        return 0;
@@ -544,7 +546,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        struct sock *sk = sock->sk;
        struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
        struct sk_buff *skb;
-       int sent = 0;
+       int sent;
 
        if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
                return -ENOTCONN;
@@ -559,6 +561,10 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        lock_sock(sk);
 
+       sent = bt_sock_wait_ready(sk, msg->msg_flags);
+       if (sent)
+               goto done;
+
        while (len) {
                size_t size = min_t(size_t, len, d->mtu);
                int err;
@@ -594,6 +600,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                len  -= size;
        }
 
+done:
        release_sock(sk);
 
        return sent;
@@ -950,8 +957,8 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
        bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM);
 
        rfcomm_sock_init(sk, parent);
-       bacpy(&bt_sk(sk)->src, &src);
-       bacpy(&bt_sk(sk)->dst, &dst);
+       bacpy(&rfcomm_pi(sk)->src, &src);
+       bacpy(&rfcomm_pi(sk)->dst, &dst);
        rfcomm_pi(sk)->channel = channel;
 
        sk->sk_state = BT_CONFIG;
@@ -978,7 +985,7 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
 
        sk_for_each(sk, &rfcomm_sk_list.head) {
                seq_printf(f, "%pMR %pMR %d %d\n",
-                          &bt_sk(sk)->src, &bt_sk(sk)->dst,
+                          &rfcomm_pi(sk)->src, &rfcomm_pi(sk)->dst,
                           sk->sk_state, rfcomm_pi(sk)->channel);
        }
 
@@ -1048,15 +1055,15 @@ int __init rfcomm_init_sockets(void)
                goto error;
        }
 
-       if (bt_debugfs) {
-               rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
-                               bt_debugfs, NULL, &rfcomm_sock_debugfs_fops);
-               if (!rfcomm_sock_debugfs)
-                       BT_ERR("Failed to create RFCOMM debug file");
-       }
-
        BT_INFO("RFCOMM socket layer initialized");
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
+                                                 bt_debugfs, NULL,
+                                                 &rfcomm_sock_debugfs_fops);
+
        return 0;
 
 error:
index 96bd388..12a0e51 100644 (file)
@@ -92,9 +92,6 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
        hcon->sco_data = conn;
        conn->hcon = hcon;
 
-       conn->src = &hdev->bdaddr;
-       conn->dst = &hcon->dst;
-
        if (hdev->sco_mtu > 0)
                conn->mtu = hdev->sco_mtu;
        else
@@ -156,16 +153,14 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
 
 static int sco_connect(struct sock *sk)
 {
-       bdaddr_t *src = &bt_sk(sk)->src;
-       bdaddr_t *dst = &bt_sk(sk)->dst;
        struct sco_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev  *hdev;
        int err, type;
 
-       BT_DBG("%pMR -> %pMR", src, dst);
+       BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
 
-       hdev = hci_get_route(dst, src);
+       hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src);
        if (!hdev)
                return -EHOSTUNREACH;
 
@@ -182,7 +177,8 @@ static int sco_connect(struct sock *sk)
                goto done;
        }
 
-       hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting);
+       hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
+                              sco_pi(sk)->setting);
        if (IS_ERR(hcon)) {
                err = PTR_ERR(hcon);
                goto done;
@@ -196,7 +192,7 @@ static int sco_connect(struct sock *sk)
        }
 
        /* Update source addr of the socket */
-       bacpy(src, conn->src);
+       bacpy(&sco_pi(sk)->src, &hcon->src);
 
        err = sco_chan_add(conn, sk, NULL);
        if (err)
@@ -270,7 +266,7 @@ static struct sock *__sco_get_sock_listen_by_addr(bdaddr_t *ba)
                if (sk->sk_state != BT_LISTEN)
                        continue;
 
-               if (!bacmp(&bt_sk(sk)->src, ba))
+               if (!bacmp(&sco_pi(sk)->src, ba))
                        return sk;
        }
 
@@ -291,11 +287,11 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src)
                        continue;
 
                /* Exact match. */
-               if (!bacmp(&bt_sk(sk)->src, src))
+               if (!bacmp(&sco_pi(sk)->src, src))
                        break;
 
                /* Closest match */
-               if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+               if (!bacmp(&sco_pi(sk)->src, BDADDR_ANY))
                        sk1 = sk;
        }
 
@@ -475,7 +471,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
                goto done;
        }
 
-       bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr);
+       bacpy(&sco_pi(sk)->src, &sa->sco_bdaddr);
 
        sk->sk_state = BT_BOUND;
 
@@ -505,7 +501,7 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
        lock_sock(sk);
 
        /* Set destination address and psm */
-       bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr);
+       bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
 
        err = sco_connect(sk);
        if (err)
@@ -522,7 +518,7 @@ done:
 static int sco_sock_listen(struct socket *sock, int backlog)
 {
        struct sock *sk = sock->sk;
-       bdaddr_t *src = &bt_sk(sk)->src;
+       bdaddr_t *src = &sco_pi(sk)->src;
        int err = 0;
 
        BT_DBG("sk %p backlog %d", sk, backlog);
@@ -626,9 +622,9 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len
        *len = sizeof(struct sockaddr_sco);
 
        if (peer)
-               bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst);
+               bacpy(&sa->sco_bdaddr, &sco_pi(sk)->dst);
        else
-               bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src);
+               bacpy(&sa->sco_bdaddr, &sco_pi(sk)->src);
 
        return 0;
 }
@@ -999,7 +995,7 @@ static void sco_conn_ready(struct sco_conn *conn)
        } else {
                sco_conn_lock(conn);
 
-               parent = sco_get_sock_listen(conn->src);
+               parent = sco_get_sock_listen(&conn->hcon->src);
                if (!parent) {
                        sco_conn_unlock(conn);
                        return;
@@ -1017,8 +1013,8 @@ static void sco_conn_ready(struct sco_conn *conn)
 
                sco_sock_init(sk, parent);
 
-               bacpy(&bt_sk(sk)->src, conn->src);
-               bacpy(&bt_sk(sk)->dst, conn->dst);
+               bacpy(&sco_pi(sk)->src, &conn->hcon->src);
+               bacpy(&sco_pi(sk)->dst, &conn->hcon->dst);
 
                hci_conn_hold(conn->hcon);
                __sco_chan_add(conn, sk, parent);
@@ -1051,8 +1047,8 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
                if (sk->sk_state != BT_LISTEN)
                        continue;
 
-               if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
-                   !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+               if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) ||
+                   !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) {
                        lm |= HCI_LM_ACCEPT;
 
                        if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
@@ -1111,8 +1107,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p)
        read_lock(&sco_sk_list.lock);
 
        sk_for_each(sk, &sco_sk_list.head) {
-               seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src,
-                          &bt_sk(sk)->dst, sk->sk_state);
+               seq_printf(f, "%pMR %pMR %d\n", &sco_pi(sk)->src,
+                          &sco_pi(sk)->dst, sk->sk_state);
        }
 
        read_unlock(&sco_sk_list.lock);
@@ -1181,15 +1177,14 @@ int __init sco_init(void)
                goto error;
        }
 
-       if (bt_debugfs) {
-               sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
-                                                 NULL, &sco_debugfs_fops);
-               if (!sco_debugfs)
-                       BT_ERR("Failed to create SCO debug file");
-       }
-
        BT_INFO("SCO socket layer initialized");
 
+       if (IS_ERR_OR_NULL(bt_debugfs))
+               return 0;
+
+       sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
+                                         NULL, &sco_debugfs_fops);
+
        return 0;
 
 error:
index ccad6c1..4b07acb 100644 (file)
@@ -28,7 +28,8 @@
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
 
 #define SMP_TIMEOUT    msecs_to_jiffies(30000)
 
@@ -85,8 +86,8 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
 }
 
 static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
-               u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
-               u8 _rat, bdaddr_t *ra, u8 res[16])
+                 u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
+                 u8 _rat, bdaddr_t *ra, u8 res[16])
 {
        u8 p1[16], p2[16];
        int err;
@@ -126,8 +127,8 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
        return err;
 }
 
-static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
-                       u8 r1[16], u8 r2[16], u8 _r[16])
+static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
+                 u8 r2[16], u8 _r[16])
 {
        int err;
 
@@ -150,7 +151,7 @@ static int smp_rand(u8 *buf)
 }
 
 static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
-                                               u16 dlen, void *data)
+                                    u16 dlen, void *data)
 {
        struct sk_buff *skb;
        struct l2cap_hdr *lh;
@@ -213,9 +214,8 @@ static __u8 seclevel_to_authreq(__u8 sec_level)
 }
 
 static void build_pairing_cmd(struct l2cap_conn *conn,
-                               struct smp_cmd_pairing *req,
-                               struct smp_cmd_pairing *rsp,
-                               __u8 authreq)
+                             struct smp_cmd_pairing *req,
+                             struct smp_cmd_pairing *rsp, __u8 authreq)
 {
        u8 dist_keys = 0;
 
@@ -249,7 +249,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
        struct smp_chan *smp = conn->smp_chan;
 
        if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
-                       (max_key_size < SMP_MIN_ENC_KEY_SIZE))
+           (max_key_size < SMP_MIN_ENC_KEY_SIZE))
                return SMP_ENC_KEY_SIZE;
 
        smp->enc_key_size = max_key_size;
@@ -263,15 +263,15 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
 
        if (send)
                smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
-                                                               &reason);
+                            &reason);
 
-       clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags);
-       mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
-                        hcon->dst_type, HCI_ERROR_AUTH_FAILURE);
+       clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags);
+       mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type,
+                        HCI_ERROR_AUTH_FAILURE);
 
        cancel_delayed_work_sync(&conn->security_timer);
 
-       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
+       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
                smp_chan_destroy(conn);
 }
 
@@ -309,8 +309,8 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        /* If either side has unknown io_caps, use JUST WORKS */
        /* Otherwise, look up method from the table */
        if (!(auth & SMP_AUTH_MITM) ||
-                       local_io > SMP_IO_KEYBOARD_DISPLAY ||
-                       remote_io > SMP_IO_KEYBOARD_DISPLAY)
+           local_io > SMP_IO_KEYBOARD_DISPLAY ||
+           remote_io > SMP_IO_KEYBOARD_DISPLAY)
                method = JUST_WORKS;
        else
                method = gen_method[remote_io][local_io];
@@ -354,10 +354,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        hci_dev_lock(hcon->hdev);
 
        if (method == REQ_PASSKEY)
-               ret = mgmt_user_passkey_request(hcon->hdev, conn->dst,
+               ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type);
        else
-               ret = mgmt_user_confirm_request(hcon->hdev, conn->dst,
+               ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type,
                                                cpu_to_le32(passkey), 0);
 
@@ -386,12 +386,13 @@ static void confirm_work(struct work_struct *work)
        smp->tfm = tfm;
 
        if (conn->hcon->out)
-               ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0,
-                            conn->src, conn->hcon->dst_type, conn->dst, res);
+               ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+                            conn->hcon->src_type, &conn->hcon->src,
+                            conn->hcon->dst_type, &conn->hcon->dst, res);
        else
                ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
-                            conn->hcon->dst_type, conn->dst, 0, conn->src,
-                            res);
+                            conn->hcon->dst_type, &conn->hcon->dst,
+                            conn->hcon->src_type, &conn->hcon->src, res);
        if (ret) {
                reason = SMP_UNSPECIFIED;
                goto error;
@@ -425,11 +426,13 @@ static void random_work(struct work_struct *work)
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
        if (hcon->out)
-               ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
-                            conn->src, hcon->dst_type, conn->dst, res);
+               ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+                            hcon->src_type, &hcon->src,
+                            hcon->dst_type, &hcon->dst, res);
        else
                ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
-                            hcon->dst_type, conn->dst, 0, conn->src, res);
+                            hcon->dst_type, &hcon->dst,
+                            hcon->src_type, &hcon->src, res);
        if (ret) {
                reason = SMP_UNSPECIFIED;
                goto error;
@@ -477,9 +480,9 @@ static void random_work(struct work_struct *work)
                swap128(key, stk);
 
                memset(stk + smp->enc_key_size, 0,
-                               SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
+                      SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
 
-               hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type,
+               hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
                            HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
                            ediv, rand);
        }
@@ -494,7 +497,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
 {
        struct smp_chan *smp;
 
-       smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC);
+       smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
        if (!smp)
                return NULL;
 
@@ -649,7 +652,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
 
        if ((req->auth_req & SMP_AUTH_BONDING) &&
-                       (rsp->auth_req & SMP_AUTH_BONDING))
+           (rsp->auth_req & SMP_AUTH_BONDING))
                auth = SMP_AUTH_BONDING;
 
        auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
@@ -684,7 +687,7 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 
                swap128(smp->prnd, random);
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
-                                                               random);
+                            random);
        } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) {
                queue_work(hdev->workqueue, &smp->confirm);
        } else {
@@ -714,7 +717,7 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        struct smp_ltk *key;
        struct hci_conn *hcon = conn->hcon;
 
-       key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type);
+       key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type);
        if (!key)
                return 0;
 
@@ -728,8 +731,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        hcon->enc_key_size = key->enc_size;
 
        return 1;
-
 }
+
 static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_security_req *rp = (void *) skb->data;
@@ -838,9 +841,9 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        skb_pull(skb, sizeof(*rp));
 
        hci_dev_lock(hdev);
-       authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH);
-       hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
-                   HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size,
+       authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
+       hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1,
+                   authenticated, smp->tk, smp->enc_key_size,
                    rp->ediv, rp->rand);
        smp_distribute_keys(conn, 1);
        hci_dev_unlock(hdev);
@@ -850,16 +853,27 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
 
 int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 {
-       __u8 code = skb->data[0];
-       __u8 reason;
+       struct hci_conn *hcon = conn->hcon;
+       __u8 code, reason;
        int err = 0;
 
-       if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) {
+       if (hcon->type != LE_LINK) {
+               kfree_skb(skb);
+               return 0;
+       }
+
+       if (skb->len < 1) {
+               kfree_skb(skb);
+               return -EILSEQ;
+       }
+
+       if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
                err = -ENOTSUPP;
                reason = SMP_PAIRING_NOTSUPP;
                goto done;
        }
 
+       code = skb->data[0];
        skb_pull(skb, sizeof(code));
 
        /*
@@ -977,7 +991,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
                smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
 
                authenticated = hcon->sec_level == BT_SECURITY_HIGH;
-               hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
+               hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
                            HCI_SMP_LTK_SLAVE, 1, authenticated,
                            enc.ltk, smp->enc_key_size, ediv, ident.rand);
 
@@ -999,10 +1013,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
 
                /* Just public address */
                memset(&addrinfo, 0, sizeof(addrinfo));
-               bacpy(&addrinfo.bdaddr, conn->src);
+               bacpy(&addrinfo.bdaddr, &conn->hcon->src);
 
                smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
-                                                               &addrinfo);
+                            &addrinfo);
 
                *keydist &= ~SMP_DIST_ID_KEY;
        }
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
new file mode 100644 (file)
index 0000000..f8ba07f
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __SMP_H
+#define __SMP_H
+
+struct smp_command_hdr {
+       __u8    code;
+} __packed;
+
+#define SMP_CMD_PAIRING_REQ    0x01
+#define SMP_CMD_PAIRING_RSP    0x02
+struct smp_cmd_pairing {
+       __u8    io_capability;
+       __u8    oob_flag;
+       __u8    auth_req;
+       __u8    max_key_size;
+       __u8    init_key_dist;
+       __u8    resp_key_dist;
+} __packed;
+
+#define SMP_IO_DISPLAY_ONLY    0x00
+#define SMP_IO_DISPLAY_YESNO   0x01
+#define SMP_IO_KEYBOARD_ONLY   0x02
+#define SMP_IO_NO_INPUT_OUTPUT 0x03
+#define SMP_IO_KEYBOARD_DISPLAY        0x04
+
+#define SMP_OOB_NOT_PRESENT    0x00
+#define SMP_OOB_PRESENT                0x01
+
+#define SMP_DIST_ENC_KEY       0x01
+#define SMP_DIST_ID_KEY                0x02
+#define SMP_DIST_SIGN          0x04
+
+#define SMP_AUTH_NONE          0x00
+#define SMP_AUTH_BONDING       0x01
+#define SMP_AUTH_MITM          0x04
+
+#define SMP_CMD_PAIRING_CONFIRM        0x03
+struct smp_cmd_pairing_confirm {
+       __u8    confirm_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_RANDOM 0x04
+struct smp_cmd_pairing_random {
+       __u8    rand_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_FAIL   0x05
+struct smp_cmd_pairing_fail {
+       __u8    reason;
+} __packed;
+
+#define SMP_CMD_ENCRYPT_INFO   0x06
+struct smp_cmd_encrypt_info {
+       __u8    ltk[16];
+} __packed;
+
+#define SMP_CMD_MASTER_IDENT   0x07
+struct smp_cmd_master_ident {
+       __le16  ediv;
+       __u8    rand[8];
+} __packed;
+
+#define SMP_CMD_IDENT_INFO     0x08
+struct smp_cmd_ident_info {
+       __u8    irk[16];
+} __packed;
+
+#define SMP_CMD_IDENT_ADDR_INFO        0x09
+struct smp_cmd_ident_addr_info {
+       __u8    addr_type;
+       bdaddr_t bdaddr;
+} __packed;
+
+#define SMP_CMD_SIGN_INFO      0x0a
+struct smp_cmd_sign_info {
+       __u8    csrk[16];
+} __packed;
+
+#define SMP_CMD_SECURITY_REQ   0x0b
+struct smp_cmd_security_req {
+       __u8    auth_req;
+} __packed;
+
+#define SMP_PASSKEY_ENTRY_FAILED       0x01
+#define SMP_OOB_NOT_AVAIL              0x02
+#define SMP_AUTH_REQUIREMENTS          0x03
+#define SMP_CONFIRM_FAILED             0x04
+#define SMP_PAIRING_NOTSUPP            0x05
+#define SMP_ENC_KEY_SIZE               0x06
+#define SMP_CMD_NOTSUPP                        0x07
+#define SMP_UNSPECIFIED                        0x08
+#define SMP_REPEATED_ATTEMPTS          0x09
+
+#define SMP_MIN_ENC_KEY_SIZE           7
+#define SMP_MAX_ENC_KEY_SIZE           16
+
+#define SMP_FLAG_TK_VALID      1
+#define SMP_FLAG_CFM_PENDING   2
+#define SMP_FLAG_MITM_AUTH     3
+
+struct smp_chan {
+       struct l2cap_conn *conn;
+       u8              preq[7]; /* SMP Pairing Request */
+       u8              prsp[7]; /* SMP Pairing Response */
+       u8              prnd[16]; /* SMP Pairing Random (local) */
+       u8              rrnd[16]; /* SMP Pairing Random (remote) */
+       u8              pcnf[16]; /* SMP Pairing Confirm */
+       u8              tk[16]; /* SMP Temporary Key */
+       u8              enc_key_size;
+       unsigned long   smp_flags;
+       struct crypto_blkcipher *tfm;
+       struct work_struct confirm;
+       struct work_struct random;
+
+};
+
+/* SMP Commands */
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
+int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
+int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
+
+void smp_chan_destroy(struct l2cap_conn *conn);
+
+#endif /* __SMP_H */
index ca04163..e6b7fec 100644 (file)
@@ -64,7 +64,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
                        br_flood_deliver(br, skb, false);
                        goto out;
                }
-               if (br_multicast_rcv(br, NULL, skb)) {
+               if (br_multicast_rcv(br, NULL, skb, vid)) {
                        kfree_skb(skb);
                        goto out;
                }
index ffd5874..33e8f23 100644 (file)
@@ -700,7 +700,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
                vid = nla_get_u16(tb[NDA_VLAN]);
 
-               if (vid >= VLAN_N_VID) {
+               if (!vid || vid >= VLAN_VID_MASK) {
                        pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
                                vid);
                        return -EINVAL;
@@ -794,7 +794,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 
                vid = nla_get_u16(tb[NDA_VLAN]);
 
-               if (vid >= VLAN_N_VID) {
+               if (!vid || vid >= VLAN_VID_MASK) {
                        pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
                                vid);
                        return -EINVAL;
index a2fd37e..7e73c32 100644 (file)
@@ -80,7 +80,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
                br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
 
        if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
-           br_multicast_rcv(br, p, skb))
+           br_multicast_rcv(br, p, skb, vid))
                goto drop;
 
        if (p->state == BR_STATE_LEARNING)
index 85a09bb..b7b1914 100644 (file)
@@ -453,7 +453,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
                err = 0;
 
-               if (!mp->ports && !mp->mglist && mp->timer_armed &&
+               if (!mp->ports && !mp->mglist &&
                    netif_running(br->dev))
                        mod_timer(&mp->timer, jiffies);
                break;
index d1c5786..4c214b2 100644 (file)
@@ -272,7 +272,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
                del_timer(&p->timer);
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
 
-               if (!mp->ports && !mp->mglist && mp->timer_armed &&
+               if (!mp->ports && !mp->mglist &&
                    netif_running(br->dev))
                        mod_timer(&mp->timer, jiffies);
 
@@ -363,7 +363,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
 
-       memcpy(eth->h_source, br->dev->dev_addr, 6);
+       memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN);
        eth->h_dest[0] = 1;
        eth->h_dest[1] = 0;
        eth->h_dest[2] = 0x5e;
@@ -433,7 +433,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
 
-       memcpy(eth->h_source, br->dev->dev_addr, 6);
+       memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN);
        eth->h_proto = htons(ETH_P_IPV6);
        skb_put(skb, sizeof(*eth));
 
@@ -620,7 +620,6 @@ rehash:
 
        mp->br = br;
        mp->addr = *group;
-
        setup_timer(&mp->timer, br_multicast_group_expired,
                    (unsigned long)mp);
 
@@ -660,6 +659,7 @@ static int br_multicast_add_group(struct net_bridge *br,
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       unsigned long now = jiffies;
        int err;
 
        spin_lock(&br->multicast_lock);
@@ -674,6 +674,7 @@ static int br_multicast_add_group(struct net_bridge *br,
 
        if (!port) {
                mp->mglist = true;
+               mod_timer(&mp->timer, now + br->multicast_membership_interval);
                goto out;
        }
 
@@ -681,7 +682,7 @@ static int br_multicast_add_group(struct net_bridge *br,
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
                if (p->port == port)
-                       goto out;
+                       goto found;
                if ((unsigned long)p->port < (unsigned long)port)
                        break;
        }
@@ -692,6 +693,8 @@ static int br_multicast_add_group(struct net_bridge *br,
        rcu_assign_pointer(*pp, p);
        br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
 
+found:
+       mod_timer(&p->timer, now + br->multicast_membership_interval);
 out:
        err = 0;
 
@@ -944,7 +947,8 @@ void br_multicast_disable_port(struct net_bridge_port *port)
 
 static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
                                         struct net_bridge_port *port,
-                                        struct sk_buff *skb)
+                                        struct sk_buff *skb,
+                                        u16 vid)
 {
        struct igmpv3_report *ih;
        struct igmpv3_grec *grec;
@@ -954,12 +958,10 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
        int type;
        int err = 0;
        __be32 group;
-       u16 vid = 0;
 
        if (!pskb_may_pull(skb, sizeof(*ih)))
                return -EINVAL;
 
-       br_vlan_get_tag(skb, &vid);
        ih = igmpv3_report_hdr(skb);
        num = ntohs(ih->ngrec);
        len = sizeof(*ih);
@@ -1002,7 +1004,8 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_ip6_multicast_mld2_report(struct net_bridge *br,
                                        struct net_bridge_port *port,
-                                       struct sk_buff *skb)
+                                       struct sk_buff *skb,
+                                       u16 vid)
 {
        struct icmp6hdr *icmp6h;
        struct mld2_grec *grec;
@@ -1010,12 +1013,10 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
        int len;
        int num;
        int err = 0;
-       u16 vid = 0;
 
        if (!pskb_may_pull(skb, sizeof(*icmp6h)))
                return -EINVAL;
 
-       br_vlan_get_tag(skb, &vid);
        icmp6h = icmp6_hdr(skb);
        num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
        len = sizeof(*icmp6h);
@@ -1138,7 +1139,8 @@ static void br_multicast_query_received(struct net_bridge *br,
 
 static int br_ip4_multicast_query(struct net_bridge *br,
                                  struct net_bridge_port *port,
-                                 struct sk_buff *skb)
+                                 struct sk_buff *skb,
+                                 u16 vid)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct igmphdr *ih = igmp_hdr(skb);
@@ -1150,7 +1152,6 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        unsigned long now = jiffies;
        __be32 group;
        int err = 0;
-       u16 vid = 0;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
@@ -1186,14 +1187,10 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        if (!group)
                goto out;
 
-       br_vlan_get_tag(skb, &vid);
        mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
                goto out;
 
-       mod_timer(&mp->timer, now + br->multicast_membership_interval);
-       mp->timer_armed = true;
-
        max_delay *= br->multicast_last_member_count;
 
        if (mp->mglist &&
@@ -1219,7 +1216,8 @@ out:
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_ip6_multicast_query(struct net_bridge *br,
                                  struct net_bridge_port *port,
-                                 struct sk_buff *skb)
+                                 struct sk_buff *skb,
+                                 u16 vid)
 {
        const struct ipv6hdr *ip6h = ipv6_hdr(skb);
        struct mld_msg *mld;
@@ -1231,7 +1229,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        unsigned long now = jiffies;
        const struct in6_addr *group = NULL;
        int err = 0;
-       u16 vid = 0;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
@@ -1265,14 +1262,10 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        if (!group)
                goto out;
 
-       br_vlan_get_tag(skb, &vid);
        mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
                goto out;
 
-       mod_timer(&mp->timer, now + br->multicast_membership_interval);
-       mp->timer_armed = true;
-
        max_delay *= br->multicast_last_member_count;
        if (mp->mglist &&
            (timer_pending(&mp->timer) ?
@@ -1358,7 +1351,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
                        call_rcu_bh(&p->rcu, br_multicast_free_pg);
                        br_mdb_notify(br->dev, port, group, RTM_DELMDB);
 
-                       if (!mp->ports && !mp->mglist && mp->timer_armed &&
+                       if (!mp->ports && !mp->mglist &&
                            netif_running(br->dev))
                                mod_timer(&mp->timer, jiffies);
                }
@@ -1370,12 +1363,30 @@ static void br_multicast_leave_group(struct net_bridge *br,
                     br->multicast_last_member_interval;
 
        if (!port) {
-               if (mp->mglist && mp->timer_armed &&
+               if (mp->mglist &&
                    (timer_pending(&mp->timer) ?
                     time_after(mp->timer.expires, time) :
                     try_to_del_timer_sync(&mp->timer) >= 0)) {
                        mod_timer(&mp->timer, time);
                }
+
+               goto out;
+       }
+
+       for (p = mlock_dereference(mp->ports, br);
+            p != NULL;
+            p = mlock_dereference(p->next, br)) {
+               if (p->port != port)
+                       continue;
+
+               if (!hlist_unhashed(&p->mglist) &&
+                   (timer_pending(&p->timer) ?
+                    time_after(p->timer.expires, time) :
+                    try_to_del_timer_sync(&p->timer) >= 0)) {
+                       mod_timer(&p->timer, time);
+               }
+
+               break;
        }
 out:
        spin_unlock(&br->multicast_lock);
@@ -1424,7 +1435,8 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
 
 static int br_multicast_ipv4_rcv(struct net_bridge *br,
                                 struct net_bridge_port *port,
-                                struct sk_buff *skb)
+                                struct sk_buff *skb,
+                                u16 vid)
 {
        struct sk_buff *skb2 = skb;
        const struct iphdr *iph;
@@ -1432,7 +1444,6 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
        unsigned int len;
        unsigned int offset;
        int err;
-       u16 vid = 0;
 
        /* We treat OOM as packet loss for now. */
        if (!pskb_may_pull(skb, sizeof(*iph)))
@@ -1493,7 +1504,6 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
 
        err = 0;
 
-       br_vlan_get_tag(skb2, &vid);
        BR_INPUT_SKB_CB(skb)->igmp = 1;
        ih = igmp_hdr(skb2);
 
@@ -1504,10 +1514,10 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
                err = br_ip4_multicast_add_group(br, port, ih->group, vid);
                break;
        case IGMPV3_HOST_MEMBERSHIP_REPORT:
-               err = br_ip4_multicast_igmp3_report(br, port, skb2);
+               err = br_ip4_multicast_igmp3_report(br, port, skb2, vid);
                break;
        case IGMP_HOST_MEMBERSHIP_QUERY:
-               err = br_ip4_multicast_query(br, port, skb2);
+               err = br_ip4_multicast_query(br, port, skb2, vid);
                break;
        case IGMP_HOST_LEAVE_MESSAGE:
                br_ip4_multicast_leave_group(br, port, ih->group, vid);
@@ -1525,7 +1535,8 @@ err_out:
 #if IS_ENABLED(CONFIG_IPV6)
 static int br_multicast_ipv6_rcv(struct net_bridge *br,
                                 struct net_bridge_port *port,
-                                struct sk_buff *skb)
+                                struct sk_buff *skb,
+                                u16 vid)
 {
        struct sk_buff *skb2;
        const struct ipv6hdr *ip6h;
@@ -1535,7 +1546,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
        unsigned int len;
        int offset;
        int err;
-       u16 vid = 0;
 
        if (!pskb_may_pull(skb, sizeof(*ip6h)))
                return -EINVAL;
@@ -1625,7 +1635,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
 
        err = 0;
 
-       br_vlan_get_tag(skb, &vid);
        BR_INPUT_SKB_CB(skb)->igmp = 1;
 
        switch (icmp6_type) {
@@ -1642,10 +1651,10 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
                break;
            }
        case ICMPV6_MLD2_REPORT:
-               err = br_ip6_multicast_mld2_report(br, port, skb2);
+               err = br_ip6_multicast_mld2_report(br, port, skb2, vid);
                break;
        case ICMPV6_MGM_QUERY:
-               err = br_ip6_multicast_query(br, port, skb2);
+               err = br_ip6_multicast_query(br, port, skb2, vid);
                break;
        case ICMPV6_MGM_REDUCTION:
            {
@@ -1666,7 +1675,7 @@ out:
 #endif
 
 int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
-                    struct sk_buff *skb)
+                    struct sk_buff *skb, u16 vid)
 {
        BR_INPUT_SKB_CB(skb)->igmp = 0;
        BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
@@ -1676,10 +1685,10 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
 
        switch (skb->protocol) {
        case htons(ETH_P_IP):
-               return br_multicast_ipv4_rcv(br, port, skb);
+               return br_multicast_ipv4_rcv(br, port, skb, vid);
 #if IS_ENABLED(CONFIG_IPV6)
        case htons(ETH_P_IPV6):
-               return br_multicast_ipv6_rcv(br, port, skb);
+               return br_multicast_ipv6_rcv(br, port, skb, vid);
 #endif
        }
 
@@ -1798,7 +1807,6 @@ void br_multicast_stop(struct net_bridge *br)
                hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
                                          hlist[ver]) {
                        del_timer(&mp->timer);
-                       mp->timer_armed = false;
                        call_rcu_bh(&mp->rcu, br_multicast_free_group);
                }
        }
index f877362..80cad2c 100644 (file)
@@ -559,6 +559,8 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb)
        else if (skb->protocol == htons(ETH_P_PPP_SES))
                nf_bridge->mask |= BRNF_PPPoE;
 
+       /* Must drop socket now because of tproxy. */
+       skb_orphan(skb);
        return skb->dev;
 }
 
@@ -619,7 +621,7 @@ bad:
 
 /* Replicate the checks that IPv6 does on packet reception and pass the packet
  * to ip6tables, which doesn't support NAT, so things are fairly simple. */
-static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
+static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
                                           struct sk_buff *skb,
                                           const struct net_device *in,
                                           const struct net_device *out,
@@ -669,7 +671,8 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
  * receiving device) to make netfilter happy, the REDIRECT
  * target in particular.  Save the original destination IP
  * address to be able to detect DNAT afterwards. */
-static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
+static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
+                                     struct sk_buff *skb,
                                      const struct net_device *in,
                                      const struct net_device *out,
                                      int (*okfn)(struct sk_buff *))
@@ -691,7 +694,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
                        return NF_ACCEPT;
 
                nf_bridge_pull_encap_header_rcsum(skb);
-               return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
+               return br_nf_pre_routing_ipv6(ops, skb, in, out, okfn);
        }
 
        if (!brnf_call_iptables && !br->nf_call_iptables)
@@ -727,7 +730,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
  * took place when the packet entered the bridge), but we
  * register an IPv4 PRE_ROUTING 'sabotage' hook that will
  * prevent this from happening. */
-static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb,
+static unsigned int br_nf_local_in(const struct nf_hook_ops *ops,
+                                  struct sk_buff *skb,
                                   const struct net_device *in,
                                   const struct net_device *out,
                                   int (*okfn)(struct sk_buff *))
@@ -765,7 +769,8 @@ static int br_nf_forward_finish(struct sk_buff *skb)
  * but we are still able to filter on the 'real' indev/outdev
  * because of the physdev module. For ARP, indev and outdev are the
  * bridge ports. */
-static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
+static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
+                                    struct sk_buff *skb,
                                     const struct net_device *in,
                                     const struct net_device *out,
                                     int (*okfn)(struct sk_buff *))
@@ -818,7 +823,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
        return NF_STOLEN;
 }
 
-static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
+static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
+                                     struct sk_buff *skb,
                                      const struct net_device *in,
                                      const struct net_device *out,
                                      int (*okfn)(struct sk_buff *))
@@ -878,7 +884,8 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 #endif
 
 /* PF_BRIDGE/POST_ROUTING ********************************************/
-static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
+static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
+                                      struct sk_buff *skb,
                                       const struct net_device *in,
                                       const struct net_device *out,
                                       int (*okfn)(struct sk_buff *))
@@ -923,7 +930,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
 /* IP/SABOTAGE *****************************************************/
 /* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
  * for the second time. */
-static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb,
+static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops,
+                                  struct sk_buff *skb,
                                   const struct net_device *in,
                                   const struct net_device *out,
                                   int (*okfn)(struct sk_buff *))
index e74ddc1..f75d92e 100644 (file)
@@ -243,7 +243,7 @@ static int br_afspec(struct net_bridge *br,
 
                vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
 
-               if (vinfo->vid >= VLAN_N_VID)
+               if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
                        return -EINVAL;
 
                switch (cmd) {
index efb57d9..229d820 100644 (file)
@@ -126,7 +126,6 @@ struct net_bridge_mdb_entry
        struct timer_list               timer;
        struct br_ip                    addr;
        bool                            mglist;
-       bool                            timer_armed;
 };
 
 struct net_bridge_mdb_htable
@@ -344,10 +343,9 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
 }
 
 /* br_device.c */
-extern void br_dev_setup(struct net_device *dev);
-extern void br_dev_delete(struct net_device *dev, struct list_head *list);
-extern netdev_tx_t br_dev_xmit(struct sk_buff *skb,
-                              struct net_device *dev);
+void br_dev_setup(struct net_device *dev);
+void br_dev_delete(struct net_device *dev, struct list_head *list);
+netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
                                       struct sk_buff *skb)
@@ -358,8 +356,8 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
                netpoll_send_skb(np, skb);
 }
 
-extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp);
-extern void br_netpoll_disable(struct net_bridge_port *p);
+int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp);
+void br_netpoll_disable(struct net_bridge_port *p);
 #else
 static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
                                       struct sk_buff *skb)
@@ -377,116 +375,99 @@ static inline void br_netpoll_disable(struct net_bridge_port *p)
 #endif
 
 /* br_fdb.c */
-extern int br_fdb_init(void);
-extern void br_fdb_fini(void);
-extern void br_fdb_flush(struct net_bridge *br);
-extern void br_fdb_changeaddr(struct net_bridge_port *p,
-                             const unsigned char *newaddr);
-extern void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr);
-extern void br_fdb_cleanup(unsigned long arg);
-extern void br_fdb_delete_by_port(struct net_bridge *br,
-                                 const struct net_bridge_port *p, int do_all);
-extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
-                                                const unsigned char *addr,
-                                                __u16 vid);
-extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
-extern int br_fdb_fillbuf(struct net_bridge *br, void *buf,
-                         unsigned long count, unsigned long off);
-extern int br_fdb_insert(struct net_bridge *br,
-                        struct net_bridge_port *source,
-                        const unsigned char *addr,
-                        u16 vid);
-extern void br_fdb_update(struct net_bridge *br,
-                         struct net_bridge_port *source,
-                         const unsigned char *addr,
-                         u16 vid);
-extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid);
-
-extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
-                        struct net_device *dev,
-                        const unsigned char *addr);
-extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[],
-                     struct net_device *dev,
-                     const unsigned char *addr,
-                     u16 nlh_flags);
-extern int br_fdb_dump(struct sk_buff *skb,
-                      struct netlink_callback *cb,
-                      struct net_device *dev,
-                      int idx);
+int br_fdb_init(void);
+void br_fdb_fini(void);
+void br_fdb_flush(struct net_bridge *br);
+void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr);
+void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr);
+void br_fdb_cleanup(unsigned long arg);
+void br_fdb_delete_by_port(struct net_bridge *br,
+                          const struct net_bridge_port *p, int do_all);
+struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
+                                         const unsigned char *addr, __u16 vid);
+int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
+int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count,
+                  unsigned long off);
+int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
+                 const unsigned char *addr, u16 vid);
+void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
+                  const unsigned char *addr, u16 vid);
+int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid);
+
+int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
+                 struct net_device *dev, const unsigned char *addr);
+int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
+              const unsigned char *addr, u16 nlh_flags);
+int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+               struct net_device *dev, int idx);
 
 /* br_forward.c */
-extern void br_deliver(const struct net_bridge_port *to,
-               struct sk_buff *skb);
-extern int br_dev_queue_push_xmit(struct sk_buff *skb);
-extern void br_forward(const struct net_bridge_port *to,
+void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
+int br_dev_queue_push_xmit(struct sk_buff *skb);
+void br_forward(const struct net_bridge_port *to,
                struct sk_buff *skb, struct sk_buff *skb0);
-extern int br_forward_finish(struct sk_buff *skb);
-extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb,
-                            bool unicast);
-extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
-                            struct sk_buff *skb2, bool unicast);
+int br_forward_finish(struct sk_buff *skb);
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast);
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
+                     struct sk_buff *skb2, bool unicast);
 
 /* br_if.c */
-extern void br_port_carrier_check(struct net_bridge_port *p);
-extern int br_add_bridge(struct net *net, const char *name);
-extern int br_del_bridge(struct net *net, const char *name);
-extern void br_net_exit(struct net *net);
-extern int br_add_if(struct net_bridge *br,
-             struct net_device *dev);
-extern int br_del_if(struct net_bridge *br,
-             struct net_device *dev);
-extern int br_min_mtu(const struct net_bridge *br);
-extern netdev_features_t br_features_recompute(struct net_bridge *br,
-       netdev_features_t features);
+void br_port_carrier_check(struct net_bridge_port *p);
+int br_add_bridge(struct net *net, const char *name);
+int br_del_bridge(struct net *net, const char *name);
+void br_net_exit(struct net *net);
+int br_add_if(struct net_bridge *br, struct net_device *dev);
+int br_del_if(struct net_bridge *br, struct net_device *dev);
+int br_min_mtu(const struct net_bridge *br);
+netdev_features_t br_features_recompute(struct net_bridge *br,
+                                       netdev_features_t features);
 
 /* br_input.c */
-extern int br_handle_frame_finish(struct sk_buff *skb);
-extern rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
+int br_handle_frame_finish(struct sk_buff *skb);
+rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
 
 /* br_ioctl.c */
-extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg);
+int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd,
+                            void __user *arg);
 
 /* br_multicast.c */
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 extern unsigned int br_mdb_rehash_seq;
-extern int br_multicast_rcv(struct net_bridge *br,
-                           struct net_bridge_port *port,
-                           struct sk_buff *skb);
-extern struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
-                                              struct sk_buff *skb, u16 vid);
-extern void br_multicast_add_port(struct net_bridge_port *port);
-extern void br_multicast_del_port(struct net_bridge_port *port);
-extern void br_multicast_enable_port(struct net_bridge_port *port);
-extern void br_multicast_disable_port(struct net_bridge_port *port);
-extern void br_multicast_init(struct net_bridge *br);
-extern void br_multicast_open(struct net_bridge *br);
-extern void br_multicast_stop(struct net_bridge *br);
-extern void br_multicast_deliver(struct net_bridge_mdb_entry *mdst,
-                                struct sk_buff *skb);
-extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst,
-                                struct sk_buff *skb, struct sk_buff *skb2);
-extern int br_multicast_set_router(struct net_bridge *br, unsigned long val);
-extern int br_multicast_set_port_router(struct net_bridge_port *p,
-                                       unsigned long val);
-extern int br_multicast_toggle(struct net_bridge *br, unsigned long val);
-extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val);
-extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val);
-extern struct net_bridge_mdb_entry *br_mdb_ip_get(
-                               struct net_bridge_mdb_htable *mdb,
-                               struct br_ip *dst);
-extern struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
-                               struct net_bridge_port *port, struct br_ip *group);
-extern void br_multicast_free_pg(struct rcu_head *head);
-extern struct net_bridge_port_group *br_multicast_new_port_group(
-                               struct net_bridge_port *port,
-                               struct br_ip *group,
-                               struct net_bridge_port_group __rcu *next,
-                               unsigned char state);
-extern void br_mdb_init(void);
-extern void br_mdb_uninit(void);
-extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
-                         struct br_ip *group, int type);
+int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
+                    struct sk_buff *skb, u16 vid);
+struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
+                                       struct sk_buff *skb, u16 vid);
+void br_multicast_add_port(struct net_bridge_port *port);
+void br_multicast_del_port(struct net_bridge_port *port);
+void br_multicast_enable_port(struct net_bridge_port *port);
+void br_multicast_disable_port(struct net_bridge_port *port);
+void br_multicast_init(struct net_bridge *br);
+void br_multicast_open(struct net_bridge *br);
+void br_multicast_stop(struct net_bridge *br);
+void br_multicast_deliver(struct net_bridge_mdb_entry *mdst,
+                         struct sk_buff *skb);
+void br_multicast_forward(struct net_bridge_mdb_entry *mdst,
+                         struct sk_buff *skb, struct sk_buff *skb2);
+int br_multicast_set_router(struct net_bridge *br, unsigned long val);
+int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val);
+int br_multicast_toggle(struct net_bridge *br, unsigned long val);
+int br_multicast_set_querier(struct net_bridge *br, unsigned long val);
+int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val);
+struct net_bridge_mdb_entry *
+br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst);
+struct net_bridge_mdb_entry *
+br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port,
+                      struct br_ip *group);
+void br_multicast_free_pg(struct rcu_head *head);
+struct net_bridge_port_group *
+br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
+                           struct net_bridge_port_group __rcu *next,
+                           unsigned char state);
+void br_mdb_init(void);
+void br_mdb_uninit(void);
+void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
+                  struct br_ip *group, int type);
 
 #define mlock_dereference(X, br) \
        rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
@@ -523,7 +504,8 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,
 #else
 static inline int br_multicast_rcv(struct net_bridge *br,
                                   struct net_bridge_port *port,
-                                  struct sk_buff *skb)
+                                  struct sk_buff *skb,
+                                  u16 vid)
 {
        return 0;
 }
@@ -591,22 +573,21 @@ static inline void br_mdb_uninit(void)
 
 /* br_vlan.c */
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
-extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
-                              struct sk_buff *skb, u16 *vid);
-extern bool br_allowed_egress(struct net_bridge *br,
-                             const struct net_port_vlans *v,
-                             const struct sk_buff *skb);
-extern struct sk_buff *br_handle_vlan(struct net_bridge *br,
-                                     const struct net_port_vlans *v,
-                                     struct sk_buff *skb);
-extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
-extern int br_vlan_delete(struct net_bridge *br, u16 vid);
-extern void br_vlan_flush(struct net_bridge *br);
-extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
-extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
-extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
-extern void nbp_vlan_flush(struct net_bridge_port *port);
-extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);
+bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
+                       struct sk_buff *skb, u16 *vid);
+bool br_allowed_egress(struct net_bridge *br, const struct net_port_vlans *v,
+                      const struct sk_buff *skb);
+struct sk_buff *br_handle_vlan(struct net_bridge *br,
+                              const struct net_port_vlans *v,
+                              struct sk_buff *skb);
+int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
+int br_vlan_delete(struct net_bridge *br, u16 vid);
+void br_vlan_flush(struct net_bridge *br);
+int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
+int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
+int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
+void nbp_vlan_flush(struct net_bridge_port *port);
+bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);
 
 static inline struct net_port_vlans *br_get_vlan_info(
                                                const struct net_bridge *br)
@@ -643,9 +624,7 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
         * vid wasn't set
         */
        smp_rmb();
-       return (v->pvid & VLAN_TAG_PRESENT) ?
-                       (v->pvid & ~VLAN_TAG_PRESENT) :
-                       VLAN_N_VID;
+       return v->pvid ?: VLAN_N_VID;
 }
 
 #else
@@ -727,9 +706,9 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 
 /* br_netfilter.c */
 #ifdef CONFIG_BRIDGE_NETFILTER
-extern int br_netfilter_init(void);
-extern void br_netfilter_fini(void);
-extern void br_netfilter_rtable_init(struct net_bridge *);
+int br_netfilter_init(void);
+void br_netfilter_fini(void);
+void br_netfilter_rtable_init(struct net_bridge *);
 #else
 #define br_netfilter_init()    (0)
 #define br_netfilter_fini()    do { } while(0)
@@ -737,43 +716,39 @@ extern void br_netfilter_rtable_init(struct net_bridge *);
 #endif
 
 /* br_stp.c */
-extern void br_log_state(const struct net_bridge_port *p);
-extern struct net_bridge_port *br_get_port(struct net_bridge *br,
-                                          u16 port_no);
-extern void br_init_port(struct net_bridge_port *p);
-extern void br_become_designated_port(struct net_bridge_port *p);
+void br_log_state(const struct net_bridge_port *p);
+struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no);
+void br_init_port(struct net_bridge_port *p);
+void br_become_designated_port(struct net_bridge_port *p);
 
-extern void __br_set_forward_delay(struct net_bridge *br, unsigned long t);
-extern int br_set_forward_delay(struct net_bridge *br, unsigned long x);
-extern int br_set_hello_time(struct net_bridge *br, unsigned long x);
-extern int br_set_max_age(struct net_bridge *br, unsigned long x);
+void __br_set_forward_delay(struct net_bridge *br, unsigned long t);
+int br_set_forward_delay(struct net_bridge *br, unsigned long x);
+int br_set_hello_time(struct net_bridge *br, unsigned long x);
+int br_set_max_age(struct net_bridge *br, unsigned long x);
 
 
 /* br_stp_if.c */
-extern void br_stp_enable_bridge(struct net_bridge *br);
-extern void br_stp_disable_bridge(struct net_bridge *br);
-extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
-extern void br_stp_enable_port(struct net_bridge_port *p);
-extern void br_stp_disable_port(struct net_bridge_port *p);
-extern bool 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 int br_stp_set_port_priority(struct net_bridge_port *p,
-                                   unsigned long newprio);
-extern int br_stp_set_path_cost(struct net_bridge_port *p,
-                               unsigned long path_cost);
-extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
+void br_stp_enable_bridge(struct net_bridge *br);
+void br_stp_disable_bridge(struct net_bridge *br);
+void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
+void br_stp_enable_port(struct net_bridge_port *p);
+void br_stp_disable_port(struct net_bridge_port *p);
+bool br_stp_recalculate_bridge_id(struct net_bridge *br);
+void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a);
+void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio);
+int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio);
+int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost);
+ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
 
 /* br_stp_bpdu.c */
 struct stp_proto;
-extern void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
-                      struct net_device *dev);
+void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
+               struct net_device *dev);
 
 /* br_stp_timer.c */
-extern void br_stp_timer_init(struct net_bridge *br);
-extern void br_stp_port_timer_init(struct net_bridge_port *p);
-extern unsigned long br_timer_value(const struct timer_list *timer);
+void br_stp_timer_init(struct net_bridge *br);
+void br_stp_port_timer_init(struct net_bridge_port *p);
+unsigned long br_timer_value(const struct timer_list *timer);
 
 /* br.c */
 #if IS_ENABLED(CONFIG_ATM_LANE)
@@ -782,23 +757,23 @@ extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr)
 
 /* br_netlink.c */
 extern struct rtnl_link_ops br_link_ops;
-extern int br_netlink_init(void);
-extern void br_netlink_fini(void);
-extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
-extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
-extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg);
-extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-                     struct net_device *dev, u32 filter_mask);
+int br_netlink_init(void);
+void br_netlink_fini(void);
+void br_ifinfo_notify(int event, struct net_bridge_port *port);
+int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
+int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg);
+int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev,
+              u32 filter_mask);
 
 #ifdef CONFIG_SYSFS
 /* br_sysfs_if.c */
 extern const struct sysfs_ops brport_sysfs_ops;
-extern int br_sysfs_addif(struct net_bridge_port *p);
-extern int br_sysfs_renameif(struct net_bridge_port *p);
+int br_sysfs_addif(struct net_bridge_port *p);
+int br_sysfs_renameif(struct net_bridge_port *p);
 
 /* br_sysfs_br.c */
-extern int br_sysfs_addbr(struct net_device *dev);
-extern void br_sysfs_delbr(struct net_device *dev);
+int br_sysfs_addbr(struct net_device *dev);
+void br_sysfs_delbr(struct net_device *dev);
 
 #else
 
index 0c0fe36..2fe910c 100644 (file)
@@ -51,19 +51,19 @@ static inline int br_is_designated_port(const struct net_bridge_port *p)
 
 
 /* br_stp.c */
-extern void br_become_root_bridge(struct net_bridge *br);
-extern void br_config_bpdu_generation(struct net_bridge *);
-extern void br_configuration_update(struct net_bridge *);
-extern void br_port_state_selection(struct net_bridge *);
-extern void br_received_config_bpdu(struct net_bridge_port *p,
-                                   const struct br_config_bpdu *bpdu);
-extern void br_received_tcn_bpdu(struct net_bridge_port *p);
-extern void br_transmit_config(struct net_bridge_port *p);
-extern void br_transmit_tcn(struct net_bridge *br);
-extern void br_topology_change_detection(struct net_bridge *br);
+void br_become_root_bridge(struct net_bridge *br);
+void br_config_bpdu_generation(struct net_bridge *);
+void br_configuration_update(struct net_bridge *);
+void br_port_state_selection(struct net_bridge *);
+void br_received_config_bpdu(struct net_bridge_port *p,
+                            const struct br_config_bpdu *bpdu);
+void br_received_tcn_bpdu(struct net_bridge_port *p);
+void br_transmit_config(struct net_bridge_port *p);
+void br_transmit_tcn(struct net_bridge *br);
+void br_topology_change_detection(struct net_bridge *br);
 
 /* br_stp_bpdu.c */
-extern void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *);
-extern void br_send_tcn_bpdu(struct net_bridge_port *);
+void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *);
+void br_send_tcn_bpdu(struct net_bridge_port *);
 
 #endif
index 108084a..656a6f3 100644 (file)
@@ -134,7 +134,7 @@ static void br_stp_start(struct net_bridge *br)
 
        if (br->bridge_forward_delay < BR_MIN_FORWARD_DELAY)
                __br_set_forward_delay(br, BR_MIN_FORWARD_DELAY);
-       else if (br->bridge_forward_delay < BR_MAX_FORWARD_DELAY)
+       else if (br->bridge_forward_delay > BR_MAX_FORWARD_DELAY)
                __br_set_forward_delay(br, BR_MAX_FORWARD_DELAY);
 
        if (r == 0) {
index 9a9ffe7..53f0990 100644 (file)
@@ -45,37 +45,34 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                return 0;
        }
 
-       if (vid) {
-               if (v->port_idx) {
-                       p = v->parent.port;
-                       br = p->br;
-                       dev = p->dev;
-               } else {
-                       br = v->parent.br;
-                       dev = br->dev;
-               }
-               ops = dev->netdev_ops;
-
-               if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
-                       /* Add VLAN to the device filter if it is supported.
-                        * Stricly speaking, this is not necessary now, since
-                        * devices are made promiscuous by the bridge, but if
-                        * that ever changes this code will allow tagged
-                        * traffic to enter the bridge.
-                        */
-                       err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q),
-                                                      vid);
-                       if (err)
-                               return err;
-               }
-
-               err = br_fdb_insert(br, p, dev->dev_addr, vid);
-               if (err) {
-                       br_err(br, "failed insert local address into bridge "
-                              "forwarding table\n");
-                       goto out_filt;
-               }
+       if (v->port_idx) {
+               p = v->parent.port;
+               br = p->br;
+               dev = p->dev;
+       } else {
+               br = v->parent.br;
+               dev = br->dev;
+       }
+       ops = dev->netdev_ops;
+
+       if (p && (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
+               /* Add VLAN to the device filter if it is supported.
+                * Stricly speaking, this is not necessary now, since
+                * devices are made promiscuous by the bridge, but if
+                * that ever changes this code will allow tagged
+                * traffic to enter the bridge.
+                */
+               err = ops->ndo_vlan_rx_add_vid(dev, htons(ETH_P_8021Q),
+                                              vid);
+               if (err)
+                       return err;
+       }
 
+       err = br_fdb_insert(br, p, dev->dev_addr, vid);
+       if (err) {
+               br_err(br, "failed insert local address into bridge "
+                      "forwarding table\n");
+               goto out_filt;
        }
 
        set_bit(vid, v->vlan_bitmap);
@@ -98,7 +95,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
        __vlan_delete_pvid(v, vid);
        clear_bit(vid, v->untagged_bitmap);
 
-       if (v->port_idx && vid) {
+       if (v->port_idx) {
                struct net_device *dev = v->parent.port->dev;
                const struct net_device_ops *ops = dev->netdev_ops;
 
@@ -192,6 +189,8 @@ out:
 bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                        struct sk_buff *skb, u16 *vid)
 {
+       int err;
+
        /* If VLAN filtering is disabled on the bridge, all packets are
         * permitted.
         */
@@ -204,20 +203,32 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        if (!v)
                return false;
 
-       if (br_vlan_get_tag(skb, vid)) {
+       err = br_vlan_get_tag(skb, vid);
+       if (!*vid) {
                u16 pvid = br_get_pvid(v);
 
-               /* Frame did not have a tag.  See if pvid is set
-                * on this port.  That tells us which vlan untagged
-                * traffic belongs to.
+               /* Frame had a tag with VID 0 or did not have a tag.
+                * See if pvid is set on this port.  That tells us which
+                * vlan untagged or priority-tagged traffic belongs to.
                 */
                if (pvid == VLAN_N_VID)
                        return false;
 
-               /* PVID is set on this port.  Any untagged ingress
-                * frame is considered to belong to this vlan.
+               /* PVID is set on this port.  Any untagged or priority-tagged
+                * ingress frame is considered to belong to this vlan.
                 */
-               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
+               *vid = pvid;
+               if (likely(err))
+                       /* Untagged Frame. */
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
+               else
+                       /* Priority-tagged Frame.
+                        * At this point, We know that skb->vlan_tci had
+                        * VLAN_TAG_PRESENT bit and its VID field was 0x000.
+                        * We update only VID field and preserve PCP field.
+                        */
+                       skb->vlan_tci |= pvid;
+
                return true;
        }
 
@@ -248,7 +259,9 @@ bool br_allowed_egress(struct net_bridge *br,
        return false;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags)
 {
        struct net_port_vlans *pv = NULL;
@@ -278,7 +291,9 @@ out:
        return err;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int br_vlan_delete(struct net_bridge *br, u16 vid)
 {
        struct net_port_vlans *pv;
@@ -289,14 +304,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
        if (!pv)
                return -EINVAL;
 
-       if (vid) {
-               /* If the VID !=0 remove fdb for this vid. VID 0 is special
-                * in that it's the default and is always there in the fdb.
-                */
-               spin_lock_bh(&br->hash_lock);
-               fdb_delete_by_addr(br, br->dev->dev_addr, vid);
-               spin_unlock_bh(&br->hash_lock);
-       }
+       spin_lock_bh(&br->hash_lock);
+       fdb_delete_by_addr(br, br->dev->dev_addr, vid);
+       spin_unlock_bh(&br->hash_lock);
 
        __vlan_del(pv, vid);
        return 0;
@@ -329,7 +339,9 @@ unlock:
        return 0;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
        struct net_port_vlans *pv = NULL;
@@ -363,7 +375,9 @@ clean_up:
        return err;
 }
 
-/* Must be protected by RTNL */
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 {
        struct net_port_vlans *pv;
@@ -374,14 +388,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
        if (!pv)
                return -EINVAL;
 
-       if (vid) {
-               /* If the VID !=0 remove fdb for this vid. VID 0 is special
-                * in that it's the default and is always there in the fdb.
-                */
-               spin_lock_bh(&port->br->hash_lock);
-               fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
-               spin_unlock_bh(&port->br->hash_lock);
-       }
+       spin_lock_bh(&port->br->hash_lock);
+       fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
+       spin_unlock_bh(&port->br->hash_lock);
 
        return __vlan_del(pv, vid);
 }
index a9aff9c..5ca74a0 100644 (file)
@@ -1,6 +1,10 @@
 #
 # Bridge netfilter configuration
 #
+#
+config NF_TABLES_BRIDGE
+       depends on NF_TABLES
+       tristate "Ethernet Bridge nf_tables support"
 
 menuconfig BRIDGE_NF_EBTABLES
        tristate "Ethernet Bridge tables (ebtables) support"
index 0718699..ea7629f 100644 (file)
@@ -2,6 +2,8 @@
 # Makefile for the netfilter modules for Link Layer filtering on a bridge.
 #
 
+obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
+
 obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
 
 # tables
index 8b84c58..3fb3c84 100644 (file)
@@ -28,7 +28,7 @@ static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
        uint32_t cmp[2] = { 0, 0 };
        int key = ((const unsigned char *)mac)[5];
 
-       memcpy(((char *) cmp) + 2, mac, 6);
+       memcpy(((char *) cmp) + 2, mac, ETH_ALEN);
        start = wh->table[key];
        limit = wh->table[key + 1];
        if (ip) {
index 5180938..7c470c3 100644 (file)
@@ -181,6 +181,7 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
        ub->qlen++;
 
        pm = nlmsg_data(nlh);
+       memset(pm, 0, sizeof(*pm));
 
        /* Fill in the ulog data */
        pm->version = EBT_ULOG_VERSION;
@@ -193,8 +194,6 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
        pm->hook = hooknr;
        if (uloginfo->prefix != NULL)
                strcpy(pm->prefix, uloginfo->prefix);
-       else
-               *(pm->prefix) = '\0';
 
        if (in) {
                strcpy(pm->physindev, in->name);
@@ -204,16 +203,14 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
                        strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);
                else
                        strcpy(pm->indev, in->name);
-       } else
-               pm->indev[0] = pm->physindev[0] = '\0';
+       }
 
        if (out) {
                /* If out exists, then out is a bridge port */
                strcpy(pm->physoutdev, out->name);
                /* rcu_read_lock()ed by nf_hook_slow */
                strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name);
-       } else
-               pm->outdev[0] = pm->physoutdev[0] = '\0';
+       }
 
        if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
                BUG();
index 94b2b70..bb2da7b 100644 (file)
@@ -60,17 +60,21 @@ static const struct ebt_table frame_filter =
 };
 
 static unsigned int
-ebt_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
-   const struct net_device *out, int (*okfn)(struct sk_buff *))
+ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+           const struct net_device *in, const struct net_device *out,
+           int (*okfn)(struct sk_buff *))
 {
-       return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_filter);
+       return ebt_do_table(ops->hooknum, skb, in, out,
+                           dev_net(in)->xt.frame_filter);
 }
 
 static unsigned int
-ebt_out_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
-   const struct net_device *out, int (*okfn)(struct sk_buff *))
+ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
+            const struct net_device *in, const struct net_device *out,
+            int (*okfn)(struct sk_buff *))
 {
-       return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_filter);
+       return ebt_do_table(ops->hooknum, skb, in, out,
+                           dev_net(out)->xt.frame_filter);
 }
 
 static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
index 322555a..bd238f1 100644 (file)
@@ -60,17 +60,21 @@ static struct ebt_table frame_nat =
 };
 
 static unsigned int
-ebt_nat_in(unsigned int hook, struct sk_buff *skb, const struct net_device *in
-   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
+          const struct net_device *in, const struct net_device *out,
+          int (*okfn)(struct sk_buff *))
 {
-       return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_nat);
+       return ebt_do_table(ops->hooknum, skb, in, out,
+                           dev_net(in)->xt.frame_nat);
 }
 
 static unsigned int
-ebt_nat_out(unsigned int hook, struct sk_buff *skb, const struct net_device *in
-   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
+           const struct net_device *in, const struct net_device *out,
+           int (*okfn)(struct sk_buff *))
 {
-       return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_nat);
+       return ebt_do_table(ops->hooknum, skb, in, out,
+                           dev_net(out)->xt.frame_nat);
 }
 
 static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c
new file mode 100644 (file)
index 0000000..cf54b22
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netfilter_bridge.h>
+#include <net/netfilter/nf_tables.h>
+
+static struct nft_af_info nft_af_bridge __read_mostly = {
+       .family         = NFPROTO_BRIDGE,
+       .nhooks         = NF_BR_NUMHOOKS,
+       .owner          = THIS_MODULE,
+};
+
+static int nf_tables_bridge_init_net(struct net *net)
+{
+       net->nft.bridge = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
+       if (net->nft.bridge == NULL)
+               return -ENOMEM;
+
+       memcpy(net->nft.bridge, &nft_af_bridge, sizeof(nft_af_bridge));
+
+       if (nft_register_afinfo(net, net->nft.bridge) < 0)
+               goto err;
+
+       return 0;
+err:
+       kfree(net->nft.bridge);
+       return -ENOMEM;
+}
+
+static void nf_tables_bridge_exit_net(struct net *net)
+{
+       nft_unregister_afinfo(net->nft.bridge);
+       kfree(net->nft.bridge);
+}
+
+static struct pernet_operations nf_tables_bridge_net_ops = {
+       .init   = nf_tables_bridge_init_net,
+       .exit   = nf_tables_bridge_exit_net,
+};
+
+static unsigned int
+nft_do_chain_bridge(const struct nf_hook_ops *ops,
+                   struct sk_buff *skb,
+                   const struct net_device *in,
+                   const struct net_device *out,
+                   int (*okfn)(struct sk_buff *))
+{
+       struct nft_pktinfo pkt;
+
+       nft_set_pktinfo(&pkt, ops, skb, in, out);
+
+       return nft_do_chain_pktinfo(&pkt, ops);
+}
+
+static struct nf_chain_type filter_bridge = {
+       .family         = NFPROTO_BRIDGE,
+       .name           = "filter",
+       .type           = NFT_CHAIN_T_DEFAULT,
+       .hook_mask      = (1 << NF_BR_LOCAL_IN) |
+                         (1 << NF_BR_FORWARD) |
+                         (1 << NF_BR_LOCAL_OUT),
+       .fn             = {
+               [NF_BR_LOCAL_IN]        = nft_do_chain_bridge,
+               [NF_BR_FORWARD]         = nft_do_chain_bridge,
+               [NF_BR_LOCAL_OUT]       = nft_do_chain_bridge,
+       },
+};
+
+static int __init nf_tables_bridge_init(void)
+{
+       int ret;
+
+       nft_register_chain_type(&filter_bridge);
+       ret = register_pernet_subsys(&nf_tables_bridge_net_ops);
+       if (ret < 0)
+               nft_unregister_chain_type(&filter_bridge);
+
+       return ret;
+}
+
+static void __exit nf_tables_bridge_exit(void)
+{
+       unregister_pernet_subsys(&nf_tables_bridge_net_ops);
+       nft_unregister_chain_type(&filter_bridge);
+}
+
+module_init(nf_tables_bridge_init);
+module_exit(nf_tables_bridge_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_FAMILY(AF_BRIDGE);
index 6493351..1be0b52 100644 (file)
@@ -203,20 +203,10 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)
                        PKT_ERROR(pkt, "cow failed\n");
                        return -EPROTO;
                }
-               /*
-                * Is the SKB non-linear after skb_cow_data()? If so, we are
-                * going to add data to the last SKB, so we need to adjust
-                * lengths of the top SKB.
-                */
-               if (lastskb != skb) {
-                       pr_warn("Packet is non-linear\n");
-                       skb->len += len;
-                       skb->data_len += len;
-               }
        }
 
        /* All set to put the last SKB and optionally write data there. */
-       to = skb_put(lastskb, len);
+       to = pskb_put(skb, lastskb, len);
        if (likely(data))
                memcpy(to, data, len);
        return 0;
index 1dccb4c..6de58b4 100644 (file)
@@ -108,9 +108,9 @@ struct s_pstats {
 extern struct dev_rcv_lists can_rx_alldev_list;
 
 /* function prototypes for the CAN networklayer procfs (proc.c) */
-extern void can_init_proc(void);
-extern void can_remove_proc(void);
-extern void can_stat_update(unsigned long data);
+void can_init_proc(void);
+void can_remove_proc(void);
+void can_stat_update(unsigned long data);
 
 /* structures and variables from af_can.c needed in proc.c for reading */
 extern struct timer_list can_stattimer;    /* timer for statistics update */
index ed7d088..059a3ce 100644 (file)
@@ -23,7 +23,7 @@ struct ceph_auth_none_info {
        struct ceph_none_authorizer au;   /* we only need one; it's static */
 };
 
-extern int ceph_auth_none_init(struct ceph_auth_client *ac);
+int ceph_auth_none_init(struct ceph_auth_client *ac);
 
 #endif
 
index c5a058d..65ee720 100644 (file)
@@ -45,7 +45,7 @@ struct ceph_x_info {
        struct ceph_x_authorizer auth_authorizer;
 };
 
-extern int ceph_x_init(struct ceph_auth_client *ac);
+int ceph_x_init(struct ceph_auth_client *ac);
 
 #endif
 
index 3572dc5..d149822 100644 (file)
@@ -20,34 +20,32 @@ static inline void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
                kfree(key->key);
 }
 
-extern int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
-                                const struct ceph_crypto_key *src);
-extern int ceph_crypto_key_encode(struct ceph_crypto_key *key,
-                                 void **p, void *end);
-extern int ceph_crypto_key_decode(struct ceph_crypto_key *key,
-                                 void **p, void *end);
-extern int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in);
+int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
+                         const struct ceph_crypto_key *src);
+int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end);
+int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end);
+int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in);
 
 /* crypto.c */
-extern int ceph_decrypt(struct ceph_crypto_key *secret,
-                       void *dst, size_t *dst_len,
-                       const void *src, size_t src_len);
-extern int ceph_encrypt(struct ceph_crypto_key *secret,
-                       void *dst, size_t *dst_len,
-                       const void *src, size_t src_len);
-extern int ceph_decrypt2(struct ceph_crypto_key *secret,
-                       void *dst1, size_t *dst1_len,
-                       void *dst2, size_t *dst2_len,
-                       const void *src, size_t src_len);
-extern int ceph_encrypt2(struct ceph_crypto_key *secret,
-                        void *dst, size_t *dst_len,
-                        const void *src1, size_t src1_len,
-                        const void *src2, size_t src2_len);
-extern int ceph_crypto_init(void);
-extern void ceph_crypto_shutdown(void);
+int ceph_decrypt(struct ceph_crypto_key *secret,
+                void *dst, size_t *dst_len,
+                const void *src, size_t src_len);
+int ceph_encrypt(struct ceph_crypto_key *secret,
+                void *dst, size_t *dst_len,
+                const void *src, size_t src_len);
+int ceph_decrypt2(struct ceph_crypto_key *secret,
+                 void *dst1, size_t *dst1_len,
+                 void *dst2, size_t *dst2_len,
+                 const void *src, size_t src_len);
+int ceph_encrypt2(struct ceph_crypto_key *secret,
+                 void *dst, size_t *dst_len,
+                 const void *src1, size_t src1_len,
+                 const void *src2, size_t src2_len);
+int ceph_crypto_init(void);
+void ceph_crypto_shutdown(void);
 
 /* armor.c */
-extern int ceph_armor(char *dst, const char *src, const char *end);
-extern int ceph_unarmor(char *dst, const char *src, const char *end);
+int ceph_armor(char *dst, const char *src, const char *end);
+int ceph_unarmor(char *dst, const char *src, const char *end);
 
 #endif
index f0a1ba6..8903258 100644 (file)
@@ -71,6 +71,8 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
            __get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
            __get_user(kmsg->msg_flags, &umsg->msg_flags))
                return -EFAULT;
+       if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+               return -EINVAL;
        kmsg->msg_name = compat_ptr(tmp1);
        kmsg->msg_iov = compat_ptr(tmp2);
        kmsg->msg_control = compat_ptr(tmp3);
index af814e7..a16ed7b 100644 (file)
@@ -577,7 +577,7 @@ EXPORT_SYMBOL(skb_copy_datagram_from_iovec);
 /**
  *     zerocopy_sg_from_iovec - Build a zerocopy datagram from an iovec
  *     @skb: buffer to copy
- *     @from: io vector to copy to
+ *     @from: io vector to copy from
  *     @offset: offset in the io vector to start copying from
  *     @count: amount of vectors to copy to buffer from
  *
index 5c713f2..8ffc52e 100644 (file)
@@ -1203,7 +1203,7 @@ void netdev_state_change(struct net_device *dev)
 {
        if (dev->flags & IFF_UP) {
                call_netdevice_notifiers(NETDEV_CHANGE, dev);
-               rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
+               rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL);
        }
 }
 EXPORT_SYMBOL(netdev_state_change);
@@ -1293,7 +1293,7 @@ int dev_open(struct net_device *dev)
        if (ret < 0)
                return ret;
 
-       rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
+       rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL);
        call_netdevice_notifiers(NETDEV_UP, dev);
 
        return ret;
@@ -1307,7 +1307,7 @@ static int __dev_close_many(struct list_head *head)
        ASSERT_RTNL();
        might_sleep();
 
-       list_for_each_entry(dev, head, unreg_list) {
+       list_for_each_entry(dev, head, close_list) {
                call_netdevice_notifiers(NETDEV_GOING_DOWN, dev);
 
                clear_bit(__LINK_STATE_START, &dev->state);
@@ -1323,7 +1323,7 @@ static int __dev_close_many(struct list_head *head)
 
        dev_deactivate_many(head);
 
-       list_for_each_entry(dev, head, unreg_list) {
+       list_for_each_entry(dev, head, close_list) {
                const struct net_device_ops *ops = dev->netdev_ops;
 
                /*
@@ -1351,7 +1351,7 @@ static int __dev_close(struct net_device *dev)
        /* Temporarily disable netpoll until the interface is down */
        netpoll_rx_disable(dev);
 
-       list_add(&dev->unreg_list, &single);
+       list_add(&dev->close_list, &single);
        retval = __dev_close_many(&single);
        list_del(&single);
 
@@ -1362,21 +1362,20 @@ static int __dev_close(struct net_device *dev)
 static int dev_close_many(struct list_head *head)
 {
        struct net_device *dev, *tmp;
-       LIST_HEAD(tmp_list);
 
-       list_for_each_entry_safe(dev, tmp, head, unreg_list)
+       /* Remove the devices that don't need to be closed */
+       list_for_each_entry_safe(dev, tmp, head, close_list)
                if (!(dev->flags & IFF_UP))
-                       list_move(&dev->unreg_list, &tmp_list);
+                       list_del_init(&dev->close_list);
 
        __dev_close_many(head);
 
-       list_for_each_entry(dev, head, unreg_list) {
-               rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
+       list_for_each_entry_safe(dev, tmp, head, close_list) {
+               rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL);
                call_netdevice_notifiers(NETDEV_DOWN, dev);
+               list_del_init(&dev->close_list);
        }
 
-       /* rollback_registered_many needs the complete original list */
-       list_splice(&tmp_list, head);
        return 0;
 }
 
@@ -1397,7 +1396,7 @@ int dev_close(struct net_device *dev)
                /* Block netpoll rx while the interface is going down */
                netpoll_rx_disable(dev);
 
-               list_add(&dev->unreg_list, &single);
+               list_add(&dev->close_list, &single);
                dev_close_many(&single);
                list_del(&single);
 
@@ -1917,7 +1916,8 @@ static struct xps_map *expand_xps_map(struct xps_map *map,
        return new_map;
 }
 
-int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask, u16 index)
+int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask,
+                       u16 index)
 {
        struct xps_dev_maps *dev_maps, *new_dev_maps = NULL;
        struct xps_map *map, *new_map;
@@ -2377,6 +2377,8 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
        }
 
        SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
+       SKB_GSO_CB(skb)->encap_level = 0;
+
        skb_reset_mac_header(skb);
        skb_reset_mac_len(skb);
 
@@ -2536,7 +2538,7 @@ static inline int skb_needs_linearize(struct sk_buff *skb,
 }
 
 int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
-                       struct netdev_queue *txq)
+                       struct netdev_queue *txq, void *accel_priv)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        int rc = NETDEV_TX_OK;
@@ -2602,9 +2604,13 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                        dev_queue_xmit_nit(skb, dev);
 
                skb_len = skb->len;
-               rc = ops->ndo_start_xmit(skb, dev);
+               if (accel_priv)
+                       rc = ops->ndo_dfwd_start_xmit(skb, dev, accel_priv);
+               else
+                       rc = ops->ndo_start_xmit(skb, dev);
+
                trace_net_dev_xmit(skb, rc, dev, skb_len);
-               if (rc == NETDEV_TX_OK)
+               if (rc == NETDEV_TX_OK && txq)
                        txq_trans_update(txq);
                return rc;
        }
@@ -2620,7 +2626,10 @@ gso:
                        dev_queue_xmit_nit(nskb, dev);
 
                skb_len = nskb->len;
-               rc = ops->ndo_start_xmit(nskb, dev);
+               if (accel_priv)
+                       rc = ops->ndo_dfwd_start_xmit(nskb, dev, accel_priv);
+               else
+                       rc = ops->ndo_start_xmit(nskb, dev);
                trace_net_dev_xmit(nskb, rc, dev, skb_len);
                if (unlikely(rc != NETDEV_TX_OK)) {
                        if (rc & ~NETDEV_TX_MASK)
@@ -2645,6 +2654,7 @@ out_kfree_skb:
 out:
        return rc;
 }
+EXPORT_SYMBOL_GPL(dev_hard_start_xmit);
 
 static void qdisc_pkt_len_init(struct sk_buff *skb)
 {
@@ -2852,7 +2862,7 @@ int dev_queue_xmit(struct sk_buff *skb)
 
                        if (!netif_xmit_stopped(txq)) {
                                __this_cpu_inc(xmit_recursion);
-                               rc = dev_hard_start_xmit(skb, dev, txq);
+                               rc = dev_hard_start_xmit(skb, dev, txq, NULL);
                                __this_cpu_dec(xmit_recursion);
                                if (dev_xmit_complete(rc)) {
                                        HARD_TX_UNLOCK(dev, txq);
@@ -4373,42 +4383,40 @@ struct netdev_adjacent {
        /* upper master flag, there can only be one master device per list */
        bool master;
 
-       /* indicates that this dev is our first-level lower/upper device */
-       bool neighbour;
-
        /* counter for the number of times this device was added to us */
        u16 ref_nr;
 
+       /* private field for the users */
+       void *private;
+
        struct list_head list;
        struct rcu_head rcu;
 };
 
-static struct netdev_adjacent *__netdev_find_adj(struct net_device *dev,
-                                                struct net_device *adj_dev,
-                                                bool upper)
+static struct netdev_adjacent *__netdev_find_adj_rcu(struct net_device *dev,
+                                                    struct net_device *adj_dev,
+                                                    struct list_head *adj_list)
 {
        struct netdev_adjacent *adj;
-       struct list_head *dev_list;
 
-       dev_list = upper ? &dev->upper_dev_list : &dev->lower_dev_list;
-
-       list_for_each_entry(adj, dev_list, list) {
+       list_for_each_entry_rcu(adj, adj_list, list) {
                if (adj->dev == adj_dev)
                        return adj;
        }
        return NULL;
 }
 
-static inline struct netdev_adjacent *__netdev_find_upper(struct net_device *dev,
-                                                         struct net_device *udev)
+static struct netdev_adjacent *__netdev_find_adj(struct net_device *dev,
+                                                struct net_device *adj_dev,
+                                                struct list_head *adj_list)
 {
-       return __netdev_find_adj(dev, udev, true);
-}
+       struct netdev_adjacent *adj;
 
-static inline struct netdev_adjacent *__netdev_find_lower(struct net_device *dev,
-                                                         struct net_device *ldev)
-{
-       return __netdev_find_adj(dev, ldev, false);
+       list_for_each_entry(adj, adj_list, list) {
+               if (adj->dev == adj_dev)
+                       return adj;
+       }
+       return NULL;
 }
 
 /**
@@ -4425,7 +4433,7 @@ bool netdev_has_upper_dev(struct net_device *dev,
 {
        ASSERT_RTNL();
 
-       return __netdev_find_upper(dev, upper_dev);
+       return __netdev_find_adj(dev, upper_dev, &dev->all_adj_list.upper);
 }
 EXPORT_SYMBOL(netdev_has_upper_dev);
 
@@ -4440,7 +4448,7 @@ bool netdev_has_any_upper_dev(struct net_device *dev)
 {
        ASSERT_RTNL();
 
-       return !list_empty(&dev->upper_dev_list);
+       return !list_empty(&dev->all_adj_list.upper);
 }
 EXPORT_SYMBOL(netdev_has_any_upper_dev);
 
@@ -4457,10 +4465,10 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev)
 
        ASSERT_RTNL();
 
-       if (list_empty(&dev->upper_dev_list))
+       if (list_empty(&dev->adj_list.upper))
                return NULL;
 
-       upper = list_first_entry(&dev->upper_dev_list,
+       upper = list_first_entry(&dev->adj_list.upper,
                                 struct netdev_adjacent, list);
        if (likely(upper->master))
                return upper->dev;
@@ -4468,15 +4476,26 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev)
 }
 EXPORT_SYMBOL(netdev_master_upper_dev_get);
 
-/* netdev_upper_get_next_dev_rcu - Get the next dev from upper list
+void *netdev_adjacent_get_private(struct list_head *adj_list)
+{
+       struct netdev_adjacent *adj;
+
+       adj = list_entry(adj_list, struct netdev_adjacent, list);
+
+       return adj->private;
+}
+EXPORT_SYMBOL(netdev_adjacent_get_private);
+
+/**
+ * netdev_all_upper_get_next_dev_rcu - Get the next dev from upper list
  * @dev: device
  * @iter: list_head ** of the current position
  *
  * Gets the next device from the dev's upper list, starting from iter
  * position. The caller must hold RCU read lock.
  */
-struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
-                                                struct list_head **iter)
+struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev,
+                                                    struct list_head **iter)
 {
        struct netdev_adjacent *upper;
 
@@ -4484,14 +4503,71 @@ struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
 
        upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list);
 
-       if (&upper->list == &dev->upper_dev_list)
+       if (&upper->list == &dev->all_adj_list.upper)
                return NULL;
 
        *iter = &upper->list;
 
        return upper->dev;
 }
-EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu);
+EXPORT_SYMBOL(netdev_all_upper_get_next_dev_rcu);
+
+/**
+ * netdev_lower_get_next_private - Get the next ->private from the
+ *                                lower neighbour list
+ * @dev: device
+ * @iter: list_head ** of the current position
+ *
+ * Gets the next netdev_adjacent->private from the dev's lower neighbour
+ * list, starting from iter position. The caller must hold either hold the
+ * RTNL lock or its own locking that guarantees that the neighbour lower
+ * list will remain unchainged.
+ */
+void *netdev_lower_get_next_private(struct net_device *dev,
+                                   struct list_head **iter)
+{
+       struct netdev_adjacent *lower;
+
+       lower = list_entry(*iter, struct netdev_adjacent, list);
+
+       if (&lower->list == &dev->adj_list.lower)
+               return NULL;
+
+       if (iter)
+               *iter = lower->list.next;
+
+       return lower->private;
+}
+EXPORT_SYMBOL(netdev_lower_get_next_private);
+
+/**
+ * netdev_lower_get_next_private_rcu - Get the next ->private from the
+ *                                    lower neighbour list, RCU
+ *                                    variant
+ * @dev: device
+ * @iter: list_head ** of the current position
+ *
+ * Gets the next netdev_adjacent->private from the dev's lower neighbour
+ * list, starting from iter position. The caller must hold RCU read lock.
+ */
+void *netdev_lower_get_next_private_rcu(struct net_device *dev,
+                                       struct list_head **iter)
+{
+       struct netdev_adjacent *lower;
+
+       WARN_ON_ONCE(!rcu_read_lock_held());
+
+       lower = list_entry_rcu((*iter)->next, struct netdev_adjacent, list);
+
+       if (&lower->list == &dev->adj_list.lower)
+               return NULL;
+
+       if (iter)
+               *iter = &lower->list;
+
+       return lower->private;
+}
+EXPORT_SYMBOL(netdev_lower_get_next_private_rcu);
 
 /**
  * netdev_master_upper_dev_get_rcu - Get master upper device
@@ -4504,7 +4580,7 @@ struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev)
 {
        struct netdev_adjacent *upper;
 
-       upper = list_first_or_null_rcu(&dev->upper_dev_list,
+       upper = list_first_or_null_rcu(&dev->adj_list.upper,
                                       struct netdev_adjacent, list);
        if (upper && likely(upper->master))
                return upper->dev;
@@ -4514,15 +4590,16 @@ EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu);
 
 static int __netdev_adjacent_dev_insert(struct net_device *dev,
                                        struct net_device *adj_dev,
-                                       bool neighbour, bool master,
-                                       bool upper)
+                                       struct list_head *dev_list,
+                                       void *private, bool master)
 {
        struct netdev_adjacent *adj;
+       char linkname[IFNAMSIZ+7];
+       int ret;
 
-       adj = __netdev_find_adj(dev, adj_dev, upper);
+       adj = __netdev_find_adj(dev, adj_dev, dev_list);
 
        if (adj) {
-               BUG_ON(neighbour);
                adj->ref_nr++;
                return 0;
        }
@@ -4533,124 +4610,179 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,
 
        adj->dev = adj_dev;
        adj->master = master;
-       adj->neighbour = neighbour;
        adj->ref_nr = 1;
-
+       adj->private = private;
        dev_hold(adj_dev);
-       pr_debug("dev_hold for %s, because of %s link added from %s to %s\n",
-                adj_dev->name, upper ? "upper" : "lower", dev->name,
-                adj_dev->name);
 
-       if (!upper) {
-               list_add_tail_rcu(&adj->list, &dev->lower_dev_list);
-               return 0;
+       pr_debug("dev_hold for %s, because of link added from %s to %s\n",
+                adj_dev->name, dev->name, adj_dev->name);
+
+       if (dev_list == &dev->adj_list.lower) {
+               sprintf(linkname, "lower_%s", adj_dev->name);
+               ret = sysfs_create_link(&(dev->dev.kobj),
+                                       &(adj_dev->dev.kobj), linkname);
+               if (ret)
+                       goto free_adj;
+       } else if (dev_list == &dev->adj_list.upper) {
+               sprintf(linkname, "upper_%s", adj_dev->name);
+               ret = sysfs_create_link(&(dev->dev.kobj),
+                                       &(adj_dev->dev.kobj), linkname);
+               if (ret)
+                       goto free_adj;
        }
 
-       /* Ensure that master upper link is always the first item in list. */
-       if (master)
-               list_add_rcu(&adj->list, &dev->upper_dev_list);
-       else
-               list_add_tail_rcu(&adj->list, &dev->upper_dev_list);
+       /* Ensure that master link is always the first item in list. */
+       if (master) {
+               ret = sysfs_create_link(&(dev->dev.kobj),
+                                       &(adj_dev->dev.kobj), "master");
+               if (ret)
+                       goto remove_symlinks;
+
+               list_add_rcu(&adj->list, dev_list);
+       } else {
+               list_add_tail_rcu(&adj->list, dev_list);
+       }
 
        return 0;
-}
 
-static inline int __netdev_upper_dev_insert(struct net_device *dev,
-                                           struct net_device *udev,
-                                           bool master, bool neighbour)
-{
-       return __netdev_adjacent_dev_insert(dev, udev, neighbour, master,
-                                           true);
-}
+remove_symlinks:
+       if (dev_list == &dev->adj_list.lower) {
+               sprintf(linkname, "lower_%s", adj_dev->name);
+               sysfs_remove_link(&(dev->dev.kobj), linkname);
+       } else if (dev_list == &dev->adj_list.upper) {
+               sprintf(linkname, "upper_%s", adj_dev->name);
+               sysfs_remove_link(&(dev->dev.kobj), linkname);
+       }
 
-static inline int __netdev_lower_dev_insert(struct net_device *dev,
-                                           struct net_device *ldev,
-                                           bool neighbour)
-{
-       return __netdev_adjacent_dev_insert(dev, ldev, neighbour, false,
-                                           false);
+free_adj:
+       kfree(adj);
+       dev_put(adj_dev);
+
+       return ret;
 }
 
 void __netdev_adjacent_dev_remove(struct net_device *dev,
-                                 struct net_device *adj_dev, bool upper)
+                                 struct net_device *adj_dev,
+                                 struct list_head *dev_list)
 {
        struct netdev_adjacent *adj;
+       char linkname[IFNAMSIZ+7];
 
-       if (upper)
-               adj = __netdev_find_upper(dev, adj_dev);
-       else
-               adj = __netdev_find_lower(dev, adj_dev);
+       adj = __netdev_find_adj(dev, adj_dev, dev_list);
 
-       if (!adj)
+       if (!adj) {
+               pr_err("tried to remove device %s from %s\n",
+                      dev->name, adj_dev->name);
                BUG();
+       }
 
        if (adj->ref_nr > 1) {
+               pr_debug("%s to %s ref_nr-- = %d\n", dev->name, adj_dev->name,
+                        adj->ref_nr-1);
                adj->ref_nr--;
                return;
        }
 
+       if (adj->master)
+               sysfs_remove_link(&(dev->dev.kobj), "master");
+
+       if (dev_list == &dev->adj_list.lower) {
+               sprintf(linkname, "lower_%s", adj_dev->name);
+               sysfs_remove_link(&(dev->dev.kobj), linkname);
+       } else if (dev_list == &dev->adj_list.upper) {
+               sprintf(linkname, "upper_%s", adj_dev->name);
+               sysfs_remove_link(&(dev->dev.kobj), linkname);
+       }
+
        list_del_rcu(&adj->list);
-       pr_debug("dev_put for %s, because of %s link removed from %s to %s\n",
-                adj_dev->name, upper ? "upper" : "lower", dev->name,
-                adj_dev->name);
+       pr_debug("dev_put for %s, because link removed from %s to %s\n",
+                adj_dev->name, dev->name, adj_dev->name);
        dev_put(adj_dev);
        kfree_rcu(adj, rcu);
 }
 
-static inline void __netdev_upper_dev_remove(struct net_device *dev,
-                                            struct net_device *udev)
-{
-       return __netdev_adjacent_dev_remove(dev, udev, true);
-}
-
-static inline void __netdev_lower_dev_remove(struct net_device *dev,
-                                            struct net_device *ldev)
-{
-       return __netdev_adjacent_dev_remove(dev, ldev, false);
-}
-
-int __netdev_adjacent_dev_insert_link(struct net_device *dev,
-                                     struct net_device *upper_dev,
-                                     bool master, bool neighbour)
+int __netdev_adjacent_dev_link_lists(struct net_device *dev,
+                                    struct net_device *upper_dev,
+                                    struct list_head *up_list,
+                                    struct list_head *down_list,
+                                    void *private, bool master)
 {
        int ret;
 
-       ret = __netdev_upper_dev_insert(dev, upper_dev, master, neighbour);
+       ret = __netdev_adjacent_dev_insert(dev, upper_dev, up_list, private,
+                                          master);
        if (ret)
                return ret;
 
-       ret = __netdev_lower_dev_insert(upper_dev, dev, neighbour);
+       ret = __netdev_adjacent_dev_insert(upper_dev, dev, down_list, private,
+                                          false);
        if (ret) {
-               __netdev_upper_dev_remove(dev, upper_dev);
+               __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
                return ret;
        }
 
        return 0;
 }
 
-static inline int __netdev_adjacent_dev_link(struct net_device *dev,
-                                            struct net_device *udev)
+int __netdev_adjacent_dev_link(struct net_device *dev,
+                              struct net_device *upper_dev)
 {
-       return __netdev_adjacent_dev_insert_link(dev, udev, false, false);
+       return __netdev_adjacent_dev_link_lists(dev, upper_dev,
+                                               &dev->all_adj_list.upper,
+                                               &upper_dev->all_adj_list.lower,
+                                               NULL, false);
 }
 
-static inline int __netdev_adjacent_dev_link_neighbour(struct net_device *dev,
-                                                      struct net_device *udev,
-                                                      bool master)
+void __netdev_adjacent_dev_unlink_lists(struct net_device *dev,
+                                       struct net_device *upper_dev,
+                                       struct list_head *up_list,
+                                       struct list_head *down_list)
 {
-       return __netdev_adjacent_dev_insert_link(dev, udev, master, true);
+       __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
+       __netdev_adjacent_dev_remove(upper_dev, dev, down_list);
 }
 
 void __netdev_adjacent_dev_unlink(struct net_device *dev,
                                  struct net_device *upper_dev)
 {
-       __netdev_upper_dev_remove(dev, upper_dev);
-       __netdev_lower_dev_remove(upper_dev, dev);
+       __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
+                                          &dev->all_adj_list.upper,
+                                          &upper_dev->all_adj_list.lower);
 }
 
+int __netdev_adjacent_dev_link_neighbour(struct net_device *dev,
+                                        struct net_device *upper_dev,
+                                        void *private, bool master)
+{
+       int ret = __netdev_adjacent_dev_link(dev, upper_dev);
+
+       if (ret)
+               return ret;
+
+       ret = __netdev_adjacent_dev_link_lists(dev, upper_dev,
+                                              &dev->adj_list.upper,
+                                              &upper_dev->adj_list.lower,
+                                              private, master);
+       if (ret) {
+               __netdev_adjacent_dev_unlink(dev, upper_dev);
+               return ret;
+       }
+
+       return 0;
+}
+
+void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
+                                           struct net_device *upper_dev)
+{
+       __netdev_adjacent_dev_unlink(dev, upper_dev);
+       __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
+                                          &dev->adj_list.upper,
+                                          &upper_dev->adj_list.lower);
+}
 
 static int __netdev_upper_dev_link(struct net_device *dev,
-                                  struct net_device *upper_dev, bool master)
+                                  struct net_device *upper_dev, bool master,
+                                  void *private)
 {
        struct netdev_adjacent *i, *j, *to_i, *to_j;
        int ret = 0;
@@ -4661,26 +4793,29 @@ static int __netdev_upper_dev_link(struct net_device *dev,
                return -EBUSY;
 
        /* To prevent loops, check if dev is not upper device to upper_dev. */
-       if (__netdev_find_upper(upper_dev, dev))
+       if (__netdev_find_adj(upper_dev, dev, &upper_dev->all_adj_list.upper))
                return -EBUSY;
 
-       if (__netdev_find_upper(dev, upper_dev))
+       if (__netdev_find_adj(dev, upper_dev, &dev->all_adj_list.upper))
                return -EEXIST;
 
        if (master && netdev_master_upper_dev_get(dev))
                return -EBUSY;
 
-       ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, master);
+       ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private,
+                                                  master);
        if (ret)
                return ret;
 
        /* Now that we linked these devs, make all the upper_dev's
-        * upper_dev_list visible to every dev's lower_dev_list and vice
+        * all_adj_list.upper visible to every dev's all_adj_list.lower an
         * versa, and don't forget the devices itself. All of these
         * links are non-neighbours.
         */
-       list_for_each_entry(i, &dev->lower_dev_list, list) {
-               list_for_each_entry(j, &upper_dev->upper_dev_list, list) {
+       list_for_each_entry(i, &dev->all_adj_list.lower, list) {
+               list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
+                       pr_debug("Interlinking %s with %s, non-neighbour\n",
+                                i->dev->name, j->dev->name);
                        ret = __netdev_adjacent_dev_link(i->dev, j->dev);
                        if (ret)
                                goto rollback_mesh;
@@ -4688,14 +4823,18 @@ static int __netdev_upper_dev_link(struct net_device *dev,
        }
 
        /* add dev to every upper_dev's upper device */
-       list_for_each_entry(i, &upper_dev->upper_dev_list, list) {
+       list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
+               pr_debug("linking %s's upper device %s with %s\n",
+                        upper_dev->name, i->dev->name, dev->name);
                ret = __netdev_adjacent_dev_link(dev, i->dev);
                if (ret)
                        goto rollback_upper_mesh;
        }
 
        /* add upper_dev to every dev's lower device */
-       list_for_each_entry(i, &dev->lower_dev_list, list) {
+       list_for_each_entry(i, &dev->all_adj_list.lower, list) {
+               pr_debug("linking %s's lower device %s with %s\n", dev->name,
+                        i->dev->name, upper_dev->name);
                ret = __netdev_adjacent_dev_link(i->dev, upper_dev);
                if (ret)
                        goto rollback_lower_mesh;
@@ -4706,7 +4845,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
 
 rollback_lower_mesh:
        to_i = i;
-       list_for_each_entry(i, &dev->lower_dev_list, list) {
+       list_for_each_entry(i, &dev->all_adj_list.lower, list) {
                if (i == to_i)
                        break;
                __netdev_adjacent_dev_unlink(i->dev, upper_dev);
@@ -4716,7 +4855,7 @@ rollback_lower_mesh:
 
 rollback_upper_mesh:
        to_i = i;
-       list_for_each_entry(i, &upper_dev->upper_dev_list, list) {
+       list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
                if (i == to_i)
                        break;
                __netdev_adjacent_dev_unlink(dev, i->dev);
@@ -4727,8 +4866,8 @@ rollback_upper_mesh:
 rollback_mesh:
        to_i = i;
        to_j = j;
-       list_for_each_entry(i, &dev->lower_dev_list, list) {
-               list_for_each_entry(j, &upper_dev->upper_dev_list, list) {
+       list_for_each_entry(i, &dev->all_adj_list.lower, list) {
+               list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
                        if (i == to_i && j == to_j)
                                break;
                        __netdev_adjacent_dev_unlink(i->dev, j->dev);
@@ -4737,7 +4876,7 @@ rollback_mesh:
                        break;
        }
 
-       __netdev_adjacent_dev_unlink(dev, upper_dev);
+       __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev);
 
        return ret;
 }
@@ -4755,7 +4894,7 @@ rollback_mesh:
 int netdev_upper_dev_link(struct net_device *dev,
                          struct net_device *upper_dev)
 {
-       return __netdev_upper_dev_link(dev, upper_dev, false);
+       return __netdev_upper_dev_link(dev, upper_dev, false, NULL);
 }
 EXPORT_SYMBOL(netdev_upper_dev_link);
 
@@ -4773,10 +4912,18 @@ EXPORT_SYMBOL(netdev_upper_dev_link);
 int netdev_master_upper_dev_link(struct net_device *dev,
                                 struct net_device *upper_dev)
 {
-       return __netdev_upper_dev_link(dev, upper_dev, true);
+       return __netdev_upper_dev_link(dev, upper_dev, true, NULL);
 }
 EXPORT_SYMBOL(netdev_master_upper_dev_link);
 
+int netdev_master_upper_dev_link_private(struct net_device *dev,
+                                        struct net_device *upper_dev,
+                                        void *private)
+{
+       return __netdev_upper_dev_link(dev, upper_dev, true, private);
+}
+EXPORT_SYMBOL(netdev_master_upper_dev_link_private);
+
 /**
  * netdev_upper_dev_unlink - Removes a link to upper device
  * @dev: device
@@ -4791,29 +4938,59 @@ void netdev_upper_dev_unlink(struct net_device *dev,
        struct netdev_adjacent *i, *j;
        ASSERT_RTNL();
 
-       __netdev_adjacent_dev_unlink(dev, upper_dev);
+       __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev);
 
        /* Here is the tricky part. We must remove all dev's lower
         * devices from all upper_dev's upper devices and vice
         * versa, to maintain the graph relationship.
         */
-       list_for_each_entry(i, &dev->lower_dev_list, list)
-               list_for_each_entry(j, &upper_dev->upper_dev_list, list)
+       list_for_each_entry(i, &dev->all_adj_list.lower, list)
+               list_for_each_entry(j, &upper_dev->all_adj_list.upper, list)
                        __netdev_adjacent_dev_unlink(i->dev, j->dev);
 
        /* remove also the devices itself from lower/upper device
         * list
         */
-       list_for_each_entry(i, &dev->lower_dev_list, list)
+       list_for_each_entry(i, &dev->all_adj_list.lower, list)
                __netdev_adjacent_dev_unlink(i->dev, upper_dev);
 
-       list_for_each_entry(i, &upper_dev->upper_dev_list, list)
+       list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
                __netdev_adjacent_dev_unlink(dev, i->dev);
 
        call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
 }
 EXPORT_SYMBOL(netdev_upper_dev_unlink);
 
+void *netdev_lower_dev_get_private_rcu(struct net_device *dev,
+                                      struct net_device *lower_dev)
+{
+       struct netdev_adjacent *lower;
+
+       if (!lower_dev)
+               return NULL;
+       lower = __netdev_find_adj_rcu(dev, lower_dev, &dev->adj_list.lower);
+       if (!lower)
+               return NULL;
+
+       return lower->private;
+}
+EXPORT_SYMBOL(netdev_lower_dev_get_private_rcu);
+
+void *netdev_lower_dev_get_private(struct net_device *dev,
+                                  struct net_device *lower_dev)
+{
+       struct netdev_adjacent *lower;
+
+       if (!lower_dev)
+               return NULL;
+       lower = __netdev_find_adj(dev, lower_dev, &dev->adj_list.lower);
+       if (!lower)
+               return NULL;
+
+       return lower->private;
+}
+EXPORT_SYMBOL(netdev_lower_dev_get_private);
+
 static void dev_change_rx_flags(struct net_device *dev, int flags)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
@@ -4822,7 +4999,7 @@ static void dev_change_rx_flags(struct net_device *dev, int flags)
                ops->ndo_change_rx_flags(dev, flags);
 }
 
-static int __dev_set_promiscuity(struct net_device *dev, int inc)
+static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
 {
        unsigned int old_flags = dev->flags;
        kuid_t uid;
@@ -4865,6 +5042,8 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
 
                dev_change_rx_flags(dev, IFF_PROMISC);
        }
+       if (notify)
+               __dev_notify_flags(dev, old_flags, IFF_PROMISC);
        return 0;
 }
 
@@ -4884,7 +5063,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc)
        unsigned int old_flags = dev->flags;
        int err;
 
-       err = __dev_set_promiscuity(dev, inc);
+       err = __dev_set_promiscuity(dev, inc, true);
        if (err < 0)
                return err;
        if (dev->flags != old_flags)
@@ -4893,22 +5072,9 @@ int dev_set_promiscuity(struct net_device *dev, int inc)
 }
 EXPORT_SYMBOL(dev_set_promiscuity);
 
-/**
- *     dev_set_allmulti        - update allmulti count on a device
- *     @dev: device
- *     @inc: modifier
- *
- *     Add or remove reception of all multicast frames to a device. While the
- *     count in the device remains above zero the interface remains listening
- *     to all interfaces. Once it hits zero the device reverts back to normal
- *     filtering operation. A negative @inc value is used to drop the counter
- *     when releasing a resource needing all multicasts.
- *     Return 0 if successful or a negative errno code on error.
- */
-
-int dev_set_allmulti(struct net_device *dev, int inc)
+static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify)
 {
-       unsigned int old_flags = dev->flags;
+       unsigned int old_flags = dev->flags, old_gflags = dev->gflags;
 
        ASSERT_RTNL();
 
@@ -4931,9 +5097,30 @@ int dev_set_allmulti(struct net_device *dev, int inc)
        if (dev->flags ^ old_flags) {
                dev_change_rx_flags(dev, IFF_ALLMULTI);
                dev_set_rx_mode(dev);
+               if (notify)
+                       __dev_notify_flags(dev, old_flags,
+                                          dev->gflags ^ old_gflags);
        }
        return 0;
 }
+
+/**
+ *     dev_set_allmulti        - update allmulti count on a device
+ *     @dev: device
+ *     @inc: modifier
+ *
+ *     Add or remove reception of all multicast frames to a device. While the
+ *     count in the device remains above zero the interface remains listening
+ *     to all interfaces. Once it hits zero the device reverts back to normal
+ *     filtering operation. A negative @inc value is used to drop the counter
+ *     when releasing a resource needing all multicasts.
+ *     Return 0 if successful or a negative errno code on error.
+ */
+
+int dev_set_allmulti(struct net_device *dev, int inc)
+{
+       return __dev_set_allmulti(dev, inc, true);
+}
 EXPORT_SYMBOL(dev_set_allmulti);
 
 /*
@@ -4958,10 +5145,10 @@ void __dev_set_rx_mode(struct net_device *dev)
                 * therefore calling __dev_set_promiscuity here is safe.
                 */
                if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
-                       __dev_set_promiscuity(dev, 1);
+                       __dev_set_promiscuity(dev, 1, false);
                        dev->uc_promisc = true;
                } else if (netdev_uc_empty(dev) && dev->uc_promisc) {
-                       __dev_set_promiscuity(dev, -1);
+                       __dev_set_promiscuity(dev, -1, false);
                        dev->uc_promisc = false;
                }
        }
@@ -5050,9 +5237,13 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
 
        if ((flags ^ dev->gflags) & IFF_PROMISC) {
                int inc = (flags & IFF_PROMISC) ? 1 : -1;
+               unsigned int old_flags = dev->flags;
 
                dev->gflags ^= IFF_PROMISC;
-               dev_set_promiscuity(dev, inc);
+
+               if (__dev_set_promiscuity(dev, inc, false) >= 0)
+                       if (dev->flags != old_flags)
+                               dev_set_rx_mode(dev);
        }
 
        /* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
@@ -5063,16 +5254,20 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
                int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
 
                dev->gflags ^= IFF_ALLMULTI;
-               dev_set_allmulti(dev, inc);
+               __dev_set_allmulti(dev, inc, false);
        }
 
        return ret;
 }
 
-void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
+void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
+                       unsigned int gchanges)
 {
        unsigned int changes = dev->flags ^ old_flags;
 
+       if (gchanges)
+               rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC);
+
        if (changes & IFF_UP) {
                if (dev->flags & IFF_UP)
                        call_netdevice_notifiers(NETDEV_UP, dev);
@@ -5101,17 +5296,14 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
 int dev_change_flags(struct net_device *dev, unsigned int flags)
 {
        int ret;
-       unsigned int changes, old_flags = dev->flags;
+       unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags;
 
        ret = __dev_change_flags(dev, flags);
        if (ret < 0)
                return ret;
 
-       changes = old_flags ^ dev->flags;
-       if (changes)
-               rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
-
-       __dev_notify_flags(dev, old_flags);
+       changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags);
+       __dev_notify_flags(dev, old_flags, changes);
        return ret;
 }
 EXPORT_SYMBOL(dev_change_flags);
@@ -5247,15 +5439,18 @@ static int dev_new_index(struct net *net)
 
 /* Delayed registration/unregisteration */
 static LIST_HEAD(net_todo_list);
+static DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq);
 
 static void net_set_todo(struct net_device *dev)
 {
        list_add_tail(&dev->todo_list, &net_todo_list);
+       dev_net(dev)->dev_unreg_count++;
 }
 
 static void rollback_registered_many(struct list_head *head)
 {
        struct net_device *dev, *tmp;
+       LIST_HEAD(close_head);
 
        BUG_ON(dev_boot_phase);
        ASSERT_RTNL();
@@ -5278,7 +5473,9 @@ static void rollback_registered_many(struct list_head *head)
        }
 
        /* If device is running, close it first. */
-       dev_close_many(head);
+       list_for_each_entry(dev, head, unreg_list)
+               list_add_tail(&dev->close_list, &close_head);
+       dev_close_many(&close_head);
 
        list_for_each_entry(dev, head, unreg_list) {
                /* And unlink it from device chain. */
@@ -5301,7 +5498,7 @@ static void rollback_registered_many(struct list_head *head)
 
                if (!dev->rtnl_link_ops ||
                    dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
-                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
+                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
 
                /*
                 *      Flush the unicast and multicast chains
@@ -5700,7 +5897,7 @@ int register_netdevice(struct net_device *dev)
         */
        if (!dev->rtnl_link_ops ||
            dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
-               rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
+               rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL);
 
 out:
        return ret;
@@ -5918,6 +6115,12 @@ void netdev_run_todo(void)
                if (dev->destructor)
                        dev->destructor(dev);
 
+               /* Report a network device has been unregistered */
+               rtnl_lock();
+               dev_net(dev)->dev_unreg_count--;
+               __rtnl_unlock();
+               wake_up(&netdev_unregistering_wq);
+
                /* Free network device */
                kobject_put(&dev->dev.kobj);
        }
@@ -6001,6 +6204,16 @@ void netdev_set_default_ethtool_ops(struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops);
 
+void netdev_freemem(struct net_device *dev)
+{
+       char *addr = (char *)dev - dev->padded;
+
+       if (is_vmalloc_addr(addr))
+               vfree(addr);
+       else
+               kfree(addr);
+}
+
 /**
  *     alloc_netdev_mqs - allocate network device
  *     @sizeof_priv:   size of private data to allocate space for
@@ -6044,7 +6257,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        /* ensure 32-byte alignment of whole construct */
        alloc_size += NETDEV_ALIGN - 1;
 
-       p = kzalloc(alloc_size, GFP_KERNEL);
+       p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+       if (!p)
+               p = vzalloc(alloc_size);
        if (!p)
                return NULL;
 
@@ -6053,7 +6268,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
 
        dev->pcpu_refcnt = alloc_percpu(int);
        if (!dev->pcpu_refcnt)
-               goto free_p;
+               goto free_dev;
 
        if (dev_addr_init(dev))
                goto free_pcpu;
@@ -6068,9 +6283,12 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
 
        INIT_LIST_HEAD(&dev->napi_list);
        INIT_LIST_HEAD(&dev->unreg_list);
+       INIT_LIST_HEAD(&dev->close_list);
        INIT_LIST_HEAD(&dev->link_watch_list);
-       INIT_LIST_HEAD(&dev->upper_dev_list);
-       INIT_LIST_HEAD(&dev->lower_dev_list);
+       INIT_LIST_HEAD(&dev->adj_list.upper);
+       INIT_LIST_HEAD(&dev->adj_list.lower);
+       INIT_LIST_HEAD(&dev->all_adj_list.upper);
+       INIT_LIST_HEAD(&dev->all_adj_list.lower);
        dev->priv_flags = IFF_XMIT_DST_RELEASE;
        setup(dev);
 
@@ -6103,8 +6321,8 @@ free_pcpu:
        kfree(dev->_rx);
 #endif
 
-free_p:
-       kfree(p);
+free_dev:
+       netdev_freemem(dev);
        return NULL;
 }
 EXPORT_SYMBOL(alloc_netdev_mqs);
@@ -6141,7 +6359,7 @@ void free_netdev(struct net_device *dev)
 
        /*  Compatibility with error handling in drivers */
        if (dev->reg_state == NETREG_UNINITIALIZED) {
-               kfree((char *)dev - dev->padded);
+               netdev_freemem(dev);
                return;
        }
 
@@ -6303,7 +6521,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
        call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
        rcu_barrier();
        call_netdevice_notifiers(NETDEV_UNREGISTER_FINAL, dev);
-       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
+       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
 
        /*
         *      Flush the unicast and multicast chains
@@ -6342,7 +6560,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
         *      Prevent userspace races by waiting until the network
         *      device is fully setup before sending notifications.
         */
-       rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
+       rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL);
 
        synchronize_net();
        err = 0;
@@ -6603,6 +6821,34 @@ static void __net_exit default_device_exit(struct net *net)
        rtnl_unlock();
 }
 
+static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
+{
+       /* Return with the rtnl_lock held when there are no network
+        * devices unregistering in any network namespace in net_list.
+        */
+       struct net *net;
+       bool unregistering;
+       DEFINE_WAIT(wait);
+
+       for (;;) {
+               prepare_to_wait(&netdev_unregistering_wq, &wait,
+                               TASK_UNINTERRUPTIBLE);
+               unregistering = false;
+               rtnl_lock();
+               list_for_each_entry(net, net_list, exit_list) {
+                       if (net->dev_unreg_count > 0) {
+                               unregistering = true;
+                               break;
+                       }
+               }
+               if (!unregistering)
+                       break;
+               __rtnl_unlock();
+               schedule();
+       }
+       finish_wait(&netdev_unregistering_wq, &wait);
+}
+
 static void __net_exit default_device_exit_batch(struct list_head *net_list)
 {
        /* At exit all network devices most be removed from a network
@@ -6614,7 +6860,18 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
        struct net *net;
        LIST_HEAD(dev_kill_list);
 
-       rtnl_lock();
+       /* To prevent network device cleanup code from dereferencing
+        * loopback devices or network devices that have been freed
+        * wait here for all pending unregistrations to complete,
+        * before unregistring the loopback device and allowing the
+        * network namespace be freed.
+        *
+        * The netdev todo list containing all network devices
+        * unregistrations that happen in default_device_exit_batch
+        * will run in the rtnl_unlock() at the end of
+        * default_device_exit_batch.
+        */
+       rtnl_lock_unregistering(net_list);
        list_for_each_entry(net, net_list, exit_list) {
                for_each_netdev_reverse(net, dev) {
                        if (dev->rtnl_link_ops)
index 6cda4e2..ec40a84 100644 (file)
@@ -752,7 +752,7 @@ int dev_mc_del_global(struct net_device *dev, const unsigned char *addr)
 EXPORT_SYMBOL(dev_mc_del_global);
 
 /**
- *     dev_mc_sync - Synchronize device's unicast list to another device
+ *     dev_mc_sync - Synchronize device's multicast list to another device
  *     @to: destination device
  *     @from: source device
  *
@@ -780,7 +780,7 @@ int dev_mc_sync(struct net_device *to, struct net_device *from)
 EXPORT_SYMBOL(dev_mc_sync);
 
 /**
- *     dev_mc_sync_multiple - Synchronize device's unicast list to another
+ *     dev_mc_sync_multiple - Synchronize device's multicast list to another
  *     device, but allow for multiple calls to sync to multiple devices.
  *     @to: destination device
  *     @from: source device
index 78e9d92..30071de 100644 (file)
@@ -81,6 +81,8 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
        [NETIF_F_TSO6_BIT] =             "tx-tcp6-segmentation",
        [NETIF_F_FSO_BIT] =              "tx-fcoe-segmentation",
        [NETIF_F_GSO_GRE_BIT] =          "tx-gre-segmentation",
+       [NETIF_F_GSO_IPIP_BIT] =         "tx-ipip-segmentation",
+       [NETIF_F_GSO_SIT_BIT] =          "tx-sit-segmentation",
        [NETIF_F_GSO_UDP_TUNNEL_BIT] =   "tx-udp_tnl-segmentation",
        [NETIF_F_GSO_MPLS_BIT] =         "tx-mpls-segmentation",
 
@@ -94,6 +96,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
        [NETIF_F_LOOPBACK_BIT] =         "loopback",
        [NETIF_F_RXFCS_BIT] =            "rx-fcs",
        [NETIF_F_RXALL_BIT] =            "rx-all",
+       [NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload",
 };
 
 static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
index 2e65413..f409e0b 100644 (file)
@@ -460,7 +460,8 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh)
                if (frh->action && (frh->action != rule->action))
                        continue;
 
-               if (frh->table && (frh_get_table(frh, tb) != rule->table))
+               if (frh_get_table(frh, tb) &&
+                   (frh_get_table(frh, tb) != rule->table))
                        continue;
 
                if (tb[FRA_PRIORITY] &&
index 6438f29..01b7808 100644 (file)
@@ -644,7 +644,6 @@ void sk_filter_release_rcu(struct rcu_head *rcu)
        struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
 
        bpf_jit_free(fp);
-       kfree(fp);
 }
 EXPORT_SYMBOL(sk_filter_release_rcu);
 
@@ -683,7 +682,7 @@ int sk_unattached_filter_create(struct sk_filter **pfp,
        if (fprog->filter == NULL)
                return -EINVAL;
 
-       fp = kmalloc(fsize + sizeof(*fp), GFP_KERNEL);
+       fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL);
        if (!fp)
                return -ENOMEM;
        memcpy(fp->insns, fprog->filter, fsize);
@@ -723,6 +722,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 {
        struct sk_filter *fp, *old_fp;
        unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
+       unsigned int sk_fsize = sk_filter_size(fprog->len);
        int err;
 
        if (sock_flag(sk, SOCK_FILTER_LOCKED))
@@ -732,11 +732,11 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
        if (fprog->filter == NULL)
                return -EINVAL;
 
-       fp = sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL);
+       fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
        if (!fp)
                return -ENOMEM;
        if (copy_from_user(fp->insns, fprog->filter, fsize)) {
-               sock_kfree_s(sk, fp, fsize+sizeof(*fp));
+               sock_kfree_s(sk, fp, sk_fsize);
                return -EFAULT;
        }
 
index 1929af8..d6ef173 100644 (file)
@@ -25,9 +25,35 @@ static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *i
        memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
 }
 
+/**
+ * skb_flow_get_ports - extract the upper layer ports and return them
+ * @skb: buffer to extract the ports from
+ * @thoff: transport header offset
+ * @ip_proto: protocol for which to get port offset
+ *
+ * The function will try to retrieve the ports at offset thoff + poff where poff
+ * is the protocol port offset returned from proto_ports_offset
+ */
+__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto)
+{
+       int poff = proto_ports_offset(ip_proto);
+
+       if (poff >= 0) {
+               __be32 *ports, _ports;
+
+               ports = skb_header_pointer(skb, thoff + poff,
+                                          sizeof(_ports), &_ports);
+               if (ports)
+                       return *ports;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(skb_flow_get_ports);
+
 bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow)
 {
-       int poff, nhoff = skb_network_offset(skb);
+       int nhoff = skb_network_offset(skb);
        u8 ip_proto;
        __be16 proto = skb->protocol;
 
@@ -40,15 +66,15 @@ again:
                struct iphdr _iph;
 ip:
                iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
-               if (!iph)
+               if (!iph || iph->ihl < 5)
                        return false;
+               nhoff += iph->ihl * 4;
 
+               ip_proto = iph->protocol;
                if (ip_is_fragment(iph))
                        ip_proto = 0;
-               else
-                       ip_proto = iph->protocol;
+
                iph_to_flow_copy_addrs(flow, iph);
-               nhoff += iph->ihl * 4;
                break;
        }
        case __constant_htons(ETH_P_IPV6): {
@@ -150,16 +176,7 @@ ipv6:
        }
 
        flow->ip_proto = ip_proto;
-       poff = proto_ports_offset(ip_proto);
-       if (poff >= 0) {
-               __be32 *ports, _ports;
-
-               nhoff += poff;
-               ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports);
-               if (ports)
-                       flow->ports = *ports;
-       }
-
+       flow->ports = skb_flow_get_ports(skb, nhoff, ip_proto);
        flow->thoff = (u16) nhoff;
 
        return true;
@@ -167,6 +184,22 @@ ipv6:
 EXPORT_SYMBOL(skb_flow_dissect);
 
 static u32 hashrnd __read_mostly;
+static __always_inline void __flow_hash_secret_init(void)
+{
+       net_get_random_once(&hashrnd, sizeof(hashrnd));
+}
+
+static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c)
+{
+       __flow_hash_secret_init();
+       return jhash_3words(a, b, c, hashrnd);
+}
+
+static __always_inline u32 __flow_hash_1word(u32 a)
+{
+       __flow_hash_secret_init();
+       return jhash_1word(a, hashrnd);
+}
 
 /*
  * __skb_get_rxhash: calculate a flow hash based on src/dst addresses
@@ -193,9 +226,9 @@ void __skb_get_rxhash(struct sk_buff *skb)
                swap(keys.port16[0], keys.port16[1]);
        }
 
-       hash = jhash_3words((__force u32)keys.dst,
-                           (__force u32)keys.src,
-                           (__force u32)keys.ports, hashrnd);
+       hash = __flow_hash_3words((__force u32)keys.dst,
+                                 (__force u32)keys.src,
+                                 (__force u32)keys.ports);
        if (!hash)
                hash = 1;
 
@@ -231,7 +264,7 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb,
                hash = skb->sk->sk_hash;
        else
                hash = (__force u16) skb->protocol;
-       hash = jhash_1word(hash, hashrnd);
+       hash = __flow_hash_1word(hash);
 
        return (u16) (((u64) hash * qcount) >> 32) + qoffset;
 }
@@ -323,7 +356,7 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
                                else
                                        hash = (__force u16) skb->protocol ^
                                            skb->rxhash;
-                               hash = jhash_1word(hash, hashrnd);
+                               hash = __flow_hash_1word(hash);
                                queue_index = map->queues[
                                    ((u64)hash * map->len) >> 32];
                        }
@@ -378,11 +411,3 @@ struct netdev_queue *netdev_pick_tx(struct net_device *dev,
        skb_set_queue_mapping(skb, queue_index);
        return netdev_get_tx_queue(dev, queue_index);
 }
-
-static int __init initialize_hashrnd(void)
-{
-       get_random_bytes(&hashrnd, sizeof(hashrnd));
-       return 0;
-}
-
-late_initcall_sync(initialize_hashrnd);
index b77eeec..4cdb7c4 100644 (file)
@@ -100,7 +100,7 @@ int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
 EXPORT_SYMBOL(memcpy_toiovecend);
 
 /*
- *     Copy iovec from kernel. Returns -EFAULT on error.
+ *     Copy iovec to kernel. Returns -EFAULT on error.
  */
 
 int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
index 6072610..ca15f32 100644 (file)
@@ -867,7 +867,7 @@ static void neigh_invalidate(struct neighbour *neigh)
 static void neigh_probe(struct neighbour *neigh)
        __releases(neigh->lock)
 {
-       struct sk_buff *skb = skb_peek(&neigh->arp_queue);
+       struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue);
        /* keep skb alive even if arp_queue overflows */
        if (skb)
                skb = skb_copy(skb, GFP_ATOMIC);
index d954b56..d03f2c9 100644 (file)
@@ -1263,7 +1263,7 @@ static void netdev_release(struct device *d)
        BUG_ON(dev->reg_state != NETREG_RELEASED);
 
        kfree(dev->ifalias);
-       kfree((char *)dev - dev->padded);
+       netdev_freemem(dev);
 }
 
 static const void *net_namespace(struct device *d)
index fc75c9e..8f97199 100644 (file)
@@ -636,8 +636,9 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo
 
                        netpoll_send_skb(np, send_skb);
 
-                       /* If there are several rx_hooks for the same address,
-                          we're fine by sending a single reply */
+                       /* If there are several rx_skb_hooks for the same
+                        * address we're fine by sending a single reply
+                        */
                        break;
                }
                spin_unlock_irqrestore(&npinfo->rx_lock, flags);
@@ -719,8 +720,9 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo
 
                        netpoll_send_skb(np, send_skb);
 
-                       /* If there are several rx_hooks for the same address,
-                          we're fine by sending a single reply */
+                       /* If there are several rx_skb_hooks for the same
+                        * address, we're fine by sending a single reply
+                        */
                        break;
                }
                spin_unlock_irqrestore(&npinfo->rx_lock, flags);
@@ -756,11 +758,12 @@ static bool pkt_is_ns(struct sk_buff *skb)
 
 int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
 {
-       int proto, len, ulen;
-       int hits = 0;
+       int proto, len, ulen, data_len;
+       int hits = 0, offset;
        const struct iphdr *iph;
        struct udphdr *uh;
        struct netpoll *np, *tmp;
+       uint16_t source;
 
        if (list_empty(&npinfo->rx_np))
                goto out;
@@ -820,7 +823,10 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
 
                len -= iph->ihl*4;
                uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);
+               offset = (unsigned char *)(uh + 1) - skb->data;
                ulen = ntohs(uh->len);
+               data_len = skb->len - offset;
+               source = ntohs(uh->source);
 
                if (ulen != len)
                        goto out;
@@ -834,9 +840,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
                        if (np->local_port && np->local_port != ntohs(uh->dest))
                                continue;
 
-                       np->rx_hook(np, ntohs(uh->source),
-                                      (char *)(uh+1),
-                                      ulen - sizeof(struct udphdr));
+                       np->rx_skb_hook(np, source, skb, offset, data_len);
                        hits++;
                }
        } else {
@@ -859,7 +863,10 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
                if (!pskb_may_pull(skb, sizeof(struct udphdr)))
                        goto out;
                uh = udp_hdr(skb);
+               offset = (unsigned char *)(uh + 1) - skb->data;
                ulen = ntohs(uh->len);
+               data_len = skb->len - offset;
+               source = ntohs(uh->source);
                if (ulen != skb->len)
                        goto out;
                if (udp6_csum_init(skb, uh, IPPROTO_UDP))
@@ -872,9 +879,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
                        if (np->local_port && np->local_port != ntohs(uh->dest))
                                continue;
 
-                       np->rx_hook(np, ntohs(uh->source),
-                                      (char *)(uh+1),
-                                      ulen - sizeof(struct udphdr));
+                       np->rx_skb_hook(np, source, skb, offset, data_len);
                        hits++;
                }
 #endif
@@ -1062,7 +1067,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
 
        npinfo->netpoll = np;
 
-       if (np->rx_hook) {
+       if (np->rx_skb_hook) {
                spin_lock_irqsave(&npinfo->rx_lock, flags);
                npinfo->rx_flags |= NETPOLL_RX_ENABLED;
                list_add_tail(&np->rx, &npinfo->rx_np);
index d9cd627..9b7cf6c 100644 (file)
@@ -222,11 +222,10 @@ static void net_prio_attach(struct cgroup_subsys_state *css,
                            struct cgroup_taskset *tset)
 {
        struct task_struct *p;
-       void *v;
+       void *v = (void *)(unsigned long)css->cgroup->id;
 
        cgroup_taskset_for_each(p, css, tset) {
                task_lock(p);
-               v = (void *)(unsigned long)task_netprioidx(p);
                iterate_fd(p->files, 0, update_netprio, v);
                task_unlock(p);
        }
index 2a0e21d..cf67144 100644 (file)
@@ -1647,9 +1647,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
        }
 
        dev->rtnl_link_state = RTNL_LINK_INITIALIZED;
-       rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
 
-       __dev_notify_flags(dev, old_flags);
+       __dev_notify_flags(dev, old_flags, ~0U);
        return 0;
 }
 EXPORT_SYMBOL(rtnl_configure_link);
@@ -1985,14 +1984,15 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change)
+void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change,
+                 gfp_t flags)
 {
        struct net *net = dev_net(dev);
        struct sk_buff *skb;
        int err = -ENOBUFS;
        size_t if_info_size;
 
-       skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL);
+       skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), flags);
        if (skb == NULL)
                goto errout;
 
@@ -2003,7 +2003,7 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change)
                kfree_skb(skb);
                goto errout;
        }
-       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags);
        return;
 errout:
        if (err < 0)
@@ -2717,7 +2717,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi
        case NETDEV_JOIN:
                break;
        default:
-               rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
+               rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL);
                break;
        }
        return NOTIFY_DONE;
index 6a2f13c..897da56 100644 (file)
@@ -7,15 +7,20 @@
 #include <linux/hrtimer.h>
 #include <linux/ktime.h>
 #include <linux/string.h>
+#include <linux/net.h>
 
 #include <net/secure_seq.h>
 
-static u32 net_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
+#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
+#define NET_SECRET_SIZE (MD5_MESSAGE_BYTES / 4)
 
-void net_secret_init(void)
+static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned;
+
+static __always_inline void net_secret_init(void)
 {
-       get_random_bytes(net_secret, sizeof(net_secret));
+       net_get_random_once(net_secret, sizeof(net_secret));
 }
+#endif
 
 #ifdef CONFIG_INET
 static u32 seq_scale(u32 seq)
@@ -42,6 +47,7 @@ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr,
        u32 hash[MD5_DIGEST_WORDS];
        u32 i;
 
+       net_secret_init();
        memcpy(hash, saddr, 16);
        for (i = 0; i < 4; i++)
                secret[i] = net_secret[i] + (__force u32)daddr[i];
@@ -63,6 +69,7 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
        u32 hash[MD5_DIGEST_WORDS];
        u32 i;
 
+       net_secret_init();
        memcpy(hash, saddr, 16);
        for (i = 0; i < 4; i++)
                secret[i] = net_secret[i] + (__force u32) daddr[i];
@@ -82,6 +89,7 @@ __u32 secure_ip_id(__be32 daddr)
 {
        u32 hash[MD5_DIGEST_WORDS];
 
+       net_secret_init();
        hash[0] = (__force __u32) daddr;
        hash[1] = net_secret[13];
        hash[2] = net_secret[14];
@@ -96,6 +104,7 @@ __u32 secure_ipv6_id(const __be32 daddr[4])
 {
        __u32 hash[4];
 
+       net_secret_init();
        memcpy(hash, daddr, 16);
        md5_transform(hash, net_secret);
 
@@ -107,6 +116,7 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
 {
        u32 hash[MD5_DIGEST_WORDS];
 
+       net_secret_init();
        hash[0] = (__force u32)saddr;
        hash[1] = (__force u32)daddr;
        hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
@@ -121,6 +131,7 @@ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
 {
        u32 hash[MD5_DIGEST_WORDS];
 
+       net_secret_init();
        hash[0] = (__force u32)saddr;
        hash[1] = (__force u32)daddr;
        hash[2] = (__force u32)dport ^ net_secret[14];
@@ -140,6 +151,7 @@ u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
        u32 hash[MD5_DIGEST_WORDS];
        u64 seq;
 
+       net_secret_init();
        hash[0] = (__force u32)saddr;
        hash[1] = (__force u32)daddr;
        hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
@@ -164,6 +176,7 @@ u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
        u64 seq;
        u32 i;
 
+       net_secret_init();
        memcpy(hash, saddr, 16);
        for (i = 0; i < 4; i++)
                secret[i] = net_secret[i] + daddr[i];
index d81cff1..8cec1e6 100644 (file)
@@ -476,6 +476,18 @@ void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
 }
 EXPORT_SYMBOL(skb_add_rx_frag);
 
+void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size,
+                         unsigned int truesize)
+{
+       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+       skb_frag_size_add(frag, size);
+       skb->len += size;
+       skb->data_len += size;
+       skb->truesize += truesize;
+}
+EXPORT_SYMBOL(skb_coalesce_rx_frag);
+
 static void skb_drop_list(struct sk_buff **listp)
 {
        kfree_skb_list(*listp);
@@ -580,9 +592,6 @@ static void skb_release_head_state(struct sk_buff *skb)
 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
        nf_conntrack_put(skb->nfct);
 #endif
-#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
-       nf_conntrack_put_reasm(skb->nfct_reasm);
-#endif
 #ifdef CONFIG_BRIDGE_NETFILTER
        nf_bridge_put(skb->nf_bridge);
 #endif
@@ -903,6 +912,9 @@ EXPORT_SYMBOL(skb_clone);
 
 static void skb_headers_offset_update(struct sk_buff *skb, int off)
 {
+       /* Only adjust this if it actually is csum_start rather than csum */
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               skb->csum_start += off;
        /* {transport,network,mac}_header and tail are relative to skb->head */
        skb->transport_header += off;
        skb->network_header   += off;
@@ -1036,8 +1048,8 @@ EXPORT_SYMBOL(__pskb_copy);
  *     @ntail: room to add at tail
  *     @gfp_mask: allocation priority
  *
- *     Expands (or creates identical copy, if &nhead and &ntail are zero)
- *     header of skb. &sk_buff itself is not changed. &sk_buff MUST have
+ *     Expands (or creates identical copy, if @nhead and @ntail are zero)
+ *     header of @skb. &sk_buff itself is not changed. &sk_buff MUST have
  *     reference count of 1. Returns zero in the case of success or error,
  *     if expansion failed. In the last case, &sk_buff is not changed.
  *
@@ -1109,9 +1121,6 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 #endif
        skb->tail             += off;
        skb_headers_offset_update(skb, nhead);
-       /* Only adjust this if it actually is csum_start rather than csum */
-       if (skb->ip_summed == CHECKSUM_PARTIAL)
-               skb->csum_start += nhead;
        skb->cloned   = 0;
        skb->hdr_len  = 0;
        skb->nohdr    = 0;
@@ -1176,7 +1185,6 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
                                        NUMA_NO_NODE);
        int oldheadroom = skb_headroom(skb);
        int head_copy_len, head_copy_off;
-       int off;
 
        if (!n)
                return NULL;
@@ -1200,11 +1208,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
 
        copy_skb_header(n, skb);
 
-       off                  = newheadroom - oldheadroom;
-       if (n->ip_summed == CHECKSUM_PARTIAL)
-               n->csum_start += off;
-
-       skb_headers_offset_update(n, off);
+       skb_headers_offset_update(n, newheadroom - oldheadroom);
 
        return n;
 }
@@ -1256,6 +1260,29 @@ free_skb:
 }
 EXPORT_SYMBOL(skb_pad);
 
+/**
+ *     pskb_put - add data to the tail of a potentially fragmented buffer
+ *     @skb: start of the buffer to use
+ *     @tail: tail fragment of the buffer to use
+ *     @len: amount of data to add
+ *
+ *     This function extends the used data area of the potentially
+ *     fragmented buffer. @tail must be the last fragment of @skb -- or
+ *     @skb itself. If this would exceed the total buffer size the kernel
+ *     will panic. A pointer to the first byte of the extra data is
+ *     returned.
+ */
+
+unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
+{
+       if (tail != skb) {
+               skb->data_len += len;
+               skb->len += len;
+       }
+       return skb_put(tail, len);
+}
+EXPORT_SYMBOL_GPL(pskb_put);
+
 /**
  *     skb_put - add data to a buffer
  *     @skb: buffer to use
@@ -1933,9 +1960,8 @@ fault:
 EXPORT_SYMBOL(skb_store_bits);
 
 /* Checksum skb data. */
-
-__wsum skb_checksum(const struct sk_buff *skb, int offset,
-                         int len, __wsum csum)
+__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len,
+                     __wsum csum, const struct skb_checksum_ops *ops)
 {
        int start = skb_headlen(skb);
        int i, copy = start - offset;
@@ -1946,7 +1972,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
        if (copy > 0) {
                if (copy > len)
                        copy = len;
-               csum = csum_partial(skb->data + offset, copy, csum);
+               csum = ops->update(skb->data + offset, copy, csum);
                if ((len -= copy) == 0)
                        return csum;
                offset += copy;
@@ -1967,10 +1993,10 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
                        if (copy > len)
                                copy = len;
                        vaddr = kmap_atomic(skb_frag_page(frag));
-                       csum2 = csum_partial(vaddr + frag->page_offset +
-                                            offset - start, copy, 0);
+                       csum2 = ops->update(vaddr + frag->page_offset +
+                                           offset - start, copy, 0);
                        kunmap_atomic(vaddr);
-                       csum = csum_block_add(csum, csum2, pos);
+                       csum = ops->combine(csum, csum2, pos, copy);
                        if (!(len -= copy))
                                return csum;
                        offset += copy;
@@ -1989,9 +2015,9 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
                        __wsum csum2;
                        if (copy > len)
                                copy = len;
-                       csum2 = skb_checksum(frag_iter, offset - start,
-                                            copy, 0);
-                       csum = csum_block_add(csum, csum2, pos);
+                       csum2 = __skb_checksum(frag_iter, offset - start,
+                                              copy, 0, ops);
+                       csum = ops->combine(csum, csum2, pos, copy);
                        if ((len -= copy) == 0)
                                return csum;
                        offset += copy;
@@ -2003,6 +2029,18 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
 
        return csum;
 }
+EXPORT_SYMBOL(__skb_checksum);
+
+__wsum skb_checksum(const struct sk_buff *skb, int offset,
+                   int len, __wsum csum)
+{
+       const struct skb_checksum_ops ops = {
+               .update  = csum_partial_ext,
+               .combine = csum_block_add_ext,
+       };
+
+       return __skb_checksum(skb, offset, len, csum, &ops);
+}
 EXPORT_SYMBOL(skb_checksum);
 
 /* Both of above in one bottle. */
@@ -2522,14 +2560,14 @@ EXPORT_SYMBOL(skb_prepare_seq_read);
  * @data: destination pointer for data to be returned
  * @st: state variable
  *
- * Reads a block of skb data at &consumed relative to the
+ * Reads a block of skb data at @consumed relative to the
  * lower offset specified to skb_prepare_seq_read(). Assigns
- * the head of the data block to &data and returns the length
+ * the head of the data block to @data and returns the length
  * of the block or 0 if the end of the skb data or the upper
  * offset has been reached.
  *
  * The caller is not required to consume all of the data
- * returned, i.e. &consumed is typically set to the number
+ * returned, i.e. @consumed is typically set to the number
  * of bytes already consumed and the next call to
  * skb_seq_read() will return the remaining part of the block.
  *
@@ -2837,14 +2875,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
                __copy_skb_header(nskb, skb);
                nskb->mac_len = skb->mac_len;
 
-               /* nskb and skb might have different headroom */
-               if (nskb->ip_summed == CHECKSUM_PARTIAL)
-                       nskb->csum_start += skb_headroom(nskb) - headroom;
-
-               skb_reset_mac_header(nskb);
-               skb_set_network_header(nskb, skb->mac_len);
-               nskb->transport_header = (nskb->network_header +
-                                         skb_network_header_len(skb));
+               skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom);
 
                skb_copy_from_linear_data_offset(skb, -tnl_hlen,
                                                 nskb->data - tnl_hlen,
@@ -2936,32 +2967,30 @@ EXPORT_SYMBOL_GPL(skb_segment);
 
 int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
 {
-       struct sk_buff *p = *head;
-       struct sk_buff *nskb;
-       struct skb_shared_info *skbinfo = skb_shinfo(skb);
-       struct skb_shared_info *pinfo = skb_shinfo(p);
-       unsigned int headroom;
-       unsigned int len = skb_gro_len(skb);
+       struct skb_shared_info *pinfo, *skbinfo = skb_shinfo(skb);
        unsigned int offset = skb_gro_offset(skb);
        unsigned int headlen = skb_headlen(skb);
+       struct sk_buff *nskb, *lp, *p = *head;
+       unsigned int len = skb_gro_len(skb);
        unsigned int delta_truesize;
+       unsigned int headroom;
 
-       if (p->len + len >= 65536)
+       if (unlikely(p->len + len >= 65536))
                return -E2BIG;
 
-       if (pinfo->frag_list)
-               goto merge;
-       else if (headlen <= offset) {
+       lp = NAPI_GRO_CB(p)->last ?: p;
+       pinfo = skb_shinfo(lp);
+
+       if (headlen <= offset) {
                skb_frag_t *frag;
                skb_frag_t *frag2;
                int i = skbinfo->nr_frags;
                int nr_frags = pinfo->nr_frags + i;
 
-               offset -= headlen;
-
                if (nr_frags > MAX_SKB_FRAGS)
-                       return -E2BIG;
+                       goto merge;
 
+               offset -= headlen;
                pinfo->nr_frags = nr_frags;
                skbinfo->nr_frags = 0;
 
@@ -2992,7 +3021,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
                unsigned int first_offset;
 
                if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS)
-                       return -E2BIG;
+                       goto merge;
 
                first_offset = skb->data -
                               (unsigned char *)page_address(page) +
@@ -3010,7 +3039,10 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
                delta_truesize = skb->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff));
                NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD;
                goto done;
-       } else if (skb_gro_len(p) != pinfo->gso_size)
+       }
+       if (pinfo->frag_list)
+               goto merge;
+       if (skb_gro_len(p) != pinfo->gso_size)
                return -E2BIG;
 
        headroom = skb_headroom(p);
@@ -3062,16 +3094,24 @@ merge:
 
        __skb_pull(skb, offset);
 
-       NAPI_GRO_CB(p)->last->next = skb;
+       if (!NAPI_GRO_CB(p)->last)
+               skb_shinfo(p)->frag_list = skb;
+       else
+               NAPI_GRO_CB(p)->last->next = skb;
        NAPI_GRO_CB(p)->last = skb;
        skb_header_release(skb);
+       lp = p;
 
 done:
        NAPI_GRO_CB(p)->count++;
        p->data_len += len;
        p->truesize += delta_truesize;
        p->len += len;
-
+       if (lp != p) {
+               lp->data_len += len;
+               lp->truesize += delta_truesize;
+               lp->len += len;
+       }
        NAPI_GRO_CB(skb)->same_flow = 1;
        return 0;
 }
index 5b6beba..ab20ed9 100644 (file)
@@ -475,12 +475,6 @@ discard_and_relse:
 }
 EXPORT_SYMBOL(sk_receive_skb);
 
-void sk_reset_txq(struct sock *sk)
-{
-       sk_tx_queue_clear(sk);
-}
-EXPORT_SYMBOL(sk_reset_txq);
-
 struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
 {
        struct dst_entry *dst = __sk_dst_get(sk);
@@ -914,6 +908,13 @@ set_rcvbuf:
                }
                break;
 #endif
+
+       case SO_MAX_PACING_RATE:
+               sk->sk_max_pacing_rate = val;
+               sk->sk_pacing_rate = min(sk->sk_pacing_rate,
+                                        sk->sk_max_pacing_rate);
+               break;
+
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -1177,6 +1178,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 #endif
 
+       case SO_MAX_PACING_RATE:
+               v.val = sk->sk_max_pacing_rate;
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -1836,7 +1841,17 @@ EXPORT_SYMBOL(sock_alloc_send_skb);
 /* On 32bit arches, an skb frag is limited to 2^15 */
 #define SKB_FRAG_PAGE_ORDER    get_order(32768)
 
-bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag)
+/**
+ * skb_page_frag_refill - check that a page_frag contains enough room
+ * @sz: minimum size of the fragment we want to get
+ * @pfrag: pointer to page_frag
+ * @prio: priority for memory allocation
+ *
+ * Note: While this allocator tries to use high order pages, there is
+ * no guarantee that allocations succeed. Therefore, @sz MUST be
+ * less or equal than PAGE_SIZE.
+ */
+bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t prio)
 {
        int order;
 
@@ -1845,16 +1860,16 @@ bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag)
                        pfrag->offset = 0;
                        return true;
                }
-               if (pfrag->offset < pfrag->size)
+               if (pfrag->offset + sz <= pfrag->size)
                        return true;
                put_page(pfrag->page);
        }
 
        /* We restrict high order allocations to users that can afford to wait */
-       order = (sk->sk_allocation & __GFP_WAIT) ? SKB_FRAG_PAGE_ORDER : 0;
+       order = (prio & __GFP_WAIT) ? SKB_FRAG_PAGE_ORDER : 0;
 
        do {
-               gfp_t gfp = sk->sk_allocation;
+               gfp_t gfp = prio;
 
                if (order)
                        gfp |= __GFP_COMP | __GFP_NOWARN;
@@ -1866,6 +1881,15 @@ bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag)
                }
        } while (--order >= 0);
 
+       return false;
+}
+EXPORT_SYMBOL(skb_page_frag_refill);
+
+bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag)
+{
+       if (likely(skb_page_frag_refill(32U, pfrag, sk->sk_allocation)))
+               return true;
+
        sk_enter_memory_pressure(sk);
        sk_stream_moderate_sndbuf(sk);
        return false;
@@ -2319,6 +2343,8 @@ void sock_init_data(struct socket *sock, struct sock *sk)
        sk->sk_ll_usec          =       sysctl_net_busy_read;
 #endif
 
+       sk->sk_max_pacing_rate = ~0U;
+       sk->sk_pacing_rate = ~0U;
        /*
         * Before updating sk_refcnt, we must commit prior changes to memory
         * (Documentation/RCU/rculist_nulls.txt for details)
index aa88e23..2f737bf 100644 (file)
@@ -338,3 +338,52 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb,
                                  csum_unfold(*sum)));
 }
 EXPORT_SYMBOL(inet_proto_csum_replace16);
+
+struct __net_random_once_work {
+       struct work_struct work;
+       struct static_key *key;
+};
+
+static void __net_random_once_deferred(struct work_struct *w)
+{
+       struct __net_random_once_work *work =
+               container_of(w, struct __net_random_once_work, work);
+       if (!static_key_enabled(work->key))
+               static_key_slow_inc(work->key);
+       kfree(work);
+}
+
+static void __net_random_once_disable_jump(struct static_key *key)
+{
+       struct __net_random_once_work *w;
+
+       w = kmalloc(sizeof(*w), GFP_ATOMIC);
+       if (!w)
+               return;
+
+       INIT_WORK(&w->work, __net_random_once_deferred);
+       w->key = key;
+       schedule_work(&w->work);
+}
+
+bool __net_get_random_once(void *buf, int nbytes, bool *done,
+                          struct static_key *done_key)
+{
+       static DEFINE_SPINLOCK(lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+       if (*done) {
+               spin_unlock_irqrestore(&lock, flags);
+               return false;
+       }
+
+       get_random_bytes(buf, nbytes);
+       *done = true;
+       spin_unlock_irqrestore(&lock, flags);
+
+       __net_random_once_disable_jump(done_key);
+
+       return true;
+}
+EXPORT_SYMBOL(__net_get_random_once);
index a269aa7..3284bfa 100644 (file)
@@ -101,16 +101,16 @@ struct dccp_ackvec_record {
        u8               avr_ack_nonce:1;
 };
 
-extern int dccp_ackvec_init(void);
-extern void dccp_ackvec_exit(void);
+int dccp_ackvec_init(void);
+void dccp_ackvec_exit(void);
 
-extern struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority);
-extern void dccp_ackvec_free(struct dccp_ackvec *av);
+struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority);
+void dccp_ackvec_free(struct dccp_ackvec *av);
 
-extern void dccp_ackvec_input(struct dccp_ackvec *av, struct sk_buff *skb);
-extern int  dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8 sum);
-extern void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno);
-extern u16  dccp_ackvec_buflen(const struct dccp_ackvec *av);
+void dccp_ackvec_input(struct dccp_ackvec *av, struct sk_buff *skb);
+int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8 sum);
+void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno);
+u16 dccp_ackvec_buflen(const struct dccp_ackvec *av);
 
 static inline bool dccp_ackvec_is_empty(const struct dccp_ackvec *av)
 {
@@ -133,7 +133,6 @@ struct dccp_ackvec_parsed {
        struct list_head node;
 };
 
-extern int dccp_ackvec_parsed_add(struct list_head *head,
-                                 u8 *vec, u8 len, u8 nonce);
-extern void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks);
+int dccp_ackvec_parsed_add(struct list_head *head, u8 *vec, u8 len, u8 nonce);
+void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks);
 #endif /* _ACKVEC_H */
index fb85d37..6eb837a 100644 (file)
@@ -93,8 +93,8 @@ extern struct ccid_operations ccid2_ops;
 extern struct ccid_operations ccid3_ops;
 #endif
 
-extern int  ccid_initialize_builtins(void);
-extern void ccid_cleanup_builtins(void);
+int ccid_initialize_builtins(void);
+void ccid_cleanup_builtins(void);
 
 struct ccid {
        struct ccid_operations *ccid_ops;
@@ -106,12 +106,12 @@ static inline void *ccid_priv(const struct ccid *ccid)
        return (void *)ccid->ccid_priv;
 }
 
-extern bool ccid_support_check(u8 const *ccid_array, u8 array_len);
-extern int  ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len);
-extern int  ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
-                                         char __user *, int __user *);
+bool ccid_support_check(u8 const *ccid_array, u8 array_len);
+int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len);
+int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
+                                 char __user *, int __user *);
 
-extern struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx);
+struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx);
 
 static inline int ccid_get_current_rx_ccid(struct dccp_sock *dp)
 {
@@ -131,8 +131,8 @@ static inline int ccid_get_current_tx_ccid(struct dccp_sock *dp)
        return ccid->ccid_ops->ccid_id;
 }
 
-extern void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk);
-extern void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk);
+void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk);
+void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk);
 
 /*
  * Congestion control of queued data packets via CCID decision.
index d1d2f53..57f631a 100644 (file)
@@ -65,9 +65,9 @@ static inline u8 tfrc_lh_length(struct tfrc_loss_hist *lh)
 
 struct tfrc_rx_hist;
 
-extern int  tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *,
-                                u32 (*first_li)(struct sock *), struct sock *);
-extern u8   tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *);
-extern void tfrc_lh_cleanup(struct tfrc_loss_hist *lh);
+int tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *,
+                        u32 (*first_li)(struct sock *), struct sock *);
+u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *);
+void tfrc_lh_cleanup(struct tfrc_loss_hist *lh);
 
 #endif /* _DCCP_LI_HIST_ */
index 7ee4a9d..ee362b0 100644 (file)
@@ -60,8 +60,8 @@ static inline struct tfrc_tx_hist_entry *
        return head;
 }
 
-extern int  tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno);
-extern void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp);
+int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno);
+void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp);
 
 /* Subtraction a-b modulo-16, respects circular wrap-around */
 #define SUB16(a, b) (((a) + 16 - (b)) & 0xF)
@@ -139,20 +139,17 @@ static inline bool tfrc_rx_hist_loss_pending(const struct tfrc_rx_hist *h)
        return h->loss_count > 0;
 }
 
-extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h,
-                                   const struct sk_buff *skb, const u64 ndp);
+void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, const struct sk_buff *skb,
+                            const u64 ndp);
 
-extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb);
+int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb);
 
 struct tfrc_loss_hist;
-extern int  tfrc_rx_handle_loss(struct tfrc_rx_hist *h,
-                               struct tfrc_loss_hist *lh,
-                               struct sk_buff *skb, const u64 ndp,
-                               u32 (*first_li)(struct sock *sk),
-                               struct sock *sk);
-extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h,
-                                  const struct sk_buff *skb);
-extern int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h);
-extern void tfrc_rx_hist_purge(struct tfrc_rx_hist *h);
+int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, struct tfrc_loss_hist *lh,
+                       struct sk_buff *skb, const u64 ndp,
+                       u32 (*first_li)(struct sock *sk), struct sock *sk);
+u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb);
+int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h);
+void tfrc_rx_hist_purge(struct tfrc_rx_hist *h);
 
 #endif /* _DCCP_PKT_HIST_ */
index ed698c4..40ee7d6 100644 (file)
@@ -55,21 +55,21 @@ static inline u32 tfrc_ewma(const u32 avg, const u32 newval, const u8 weight)
        return avg ? (weight * avg + (10 - weight) * newval) / 10 : newval;
 }
 
-extern u32  tfrc_calc_x(u16 s, u32 R, u32 p);
-extern u32  tfrc_calc_x_reverse_lookup(u32 fvalue);
-extern u32  tfrc_invert_loss_event_rate(u32 loss_event_rate);
+u32 tfrc_calc_x(u16 s, u32 R, u32 p);
+u32 tfrc_calc_x_reverse_lookup(u32 fvalue);
+u32 tfrc_invert_loss_event_rate(u32 loss_event_rate);
 
-extern int  tfrc_tx_packet_history_init(void);
-extern void tfrc_tx_packet_history_exit(void);
-extern int  tfrc_rx_packet_history_init(void);
-extern void tfrc_rx_packet_history_exit(void);
+int tfrc_tx_packet_history_init(void);
+void tfrc_tx_packet_history_exit(void);
+int tfrc_rx_packet_history_init(void);
+void tfrc_rx_packet_history_exit(void);
 
-extern int  tfrc_li_init(void);
-extern void tfrc_li_exit(void);
+int tfrc_li_init(void);
+void tfrc_li_exit(void);
 
 #ifdef CONFIG_IP_DCCP_TFRC_LIB
-extern int  tfrc_lib_init(void);
-extern void tfrc_lib_exit(void);
+int tfrc_lib_init(void);
+void tfrc_lib_exit(void);
 #else
 #define tfrc_lib_init() (0)
 #define tfrc_lib_exit()
index 708e75b..3094878 100644 (file)
@@ -53,7 +53,7 @@ extern struct inet_hashinfo dccp_hashinfo;
 
 extern struct percpu_counter dccp_orphan_count;
 
-extern void dccp_time_wait(struct sock *sk, int state, int timeo);
+void dccp_time_wait(struct sock *sk, int state, int timeo);
 
 /*
  *  Set safe upper bounds for header and option length. Since Data Offset is 8
@@ -224,114 +224,108 @@ static inline void dccp_csum_outgoing(struct sk_buff *skb)
        skb->csum = skb_checksum(skb, 0, (cov > skb->len)? skb->len : cov, 0);
 }
 
-extern void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb);
+void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb);
 
-extern int  dccp_retransmit_skb(struct sock *sk);
+int dccp_retransmit_skb(struct sock *sk);
 
-extern void dccp_send_ack(struct sock *sk);
-extern void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
-                               struct request_sock *rsk);
+void dccp_send_ack(struct sock *sk);
+void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+                        struct request_sock *rsk);
 
-extern void dccp_send_sync(struct sock *sk, const u64 seq,
-                          const enum dccp_pkt_type pkt_type);
+void dccp_send_sync(struct sock *sk, const u64 seq,
+                   const enum dccp_pkt_type pkt_type);
 
 /*
  * TX Packet Dequeueing Interface
  */
-extern void            dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb);
-extern bool            dccp_qpolicy_full(struct sock *sk);
-extern void            dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb);
-extern struct sk_buff  *dccp_qpolicy_top(struct sock *sk);
-extern struct sk_buff  *dccp_qpolicy_pop(struct sock *sk);
-extern bool            dccp_qpolicy_param_ok(struct sock *sk, __be32 param);
+void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb);
+bool dccp_qpolicy_full(struct sock *sk);
+void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb);
+struct sk_buff *dccp_qpolicy_top(struct sock *sk);
+struct sk_buff *dccp_qpolicy_pop(struct sock *sk);
+bool dccp_qpolicy_param_ok(struct sock *sk, __be32 param);
 
 /*
  * TX Packet Output and TX Timers
  */
-extern void   dccp_write_xmit(struct sock *sk);
-extern void   dccp_write_space(struct sock *sk);
-extern void   dccp_flush_write_queue(struct sock *sk, long *time_budget);
+void dccp_write_xmit(struct sock *sk);
+void dccp_write_space(struct sock *sk);
+void dccp_flush_write_queue(struct sock *sk, long *time_budget);
 
-extern void dccp_init_xmit_timers(struct sock *sk);
+void dccp_init_xmit_timers(struct sock *sk);
 static inline void dccp_clear_xmit_timers(struct sock *sk)
 {
        inet_csk_clear_xmit_timers(sk);
 }
 
-extern unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu);
+unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu);
 
-extern const char *dccp_packet_name(const int type);
+const char *dccp_packet_name(const int type);
 
-extern void dccp_set_state(struct sock *sk, const int state);
-extern void dccp_done(struct sock *sk);
+void dccp_set_state(struct sock *sk, const int state);
+void dccp_done(struct sock *sk);
 
-extern int  dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp,
-                           struct sk_buff const *skb);
+int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp,
+                   struct sk_buff const *skb);
 
-extern int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
+int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
 
-extern struct sock *dccp_create_openreq_child(struct sock *sk,
-                                             const struct request_sock *req,
-                                             const struct sk_buff *skb);
+struct sock *dccp_create_openreq_child(struct sock *sk,
+                                      const struct request_sock *req,
+                                      const struct sk_buff *skb);
 
-extern int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
+int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
 
-extern struct sock *dccp_v4_request_recv_sock(struct sock *sk,
-                                             struct sk_buff *skb,
-                                             struct request_sock *req,
-                                             struct dst_entry *dst);
-extern struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
-                                  struct request_sock *req,
-                                  struct request_sock **prev);
+struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
+                                      struct request_sock *req,
+                                      struct dst_entry *dst);
+struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
+                           struct request_sock *req,
+                           struct request_sock **prev);
 
-extern int dccp_child_process(struct sock *parent, struct sock *child,
-                             struct sk_buff *skb);
-extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
-                                 struct dccp_hdr *dh, unsigned int len);
-extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
-                               const struct dccp_hdr *dh, const unsigned int len);
+int dccp_child_process(struct sock *parent, struct sock *child,
+                      struct sk_buff *skb);
+int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
+                          struct dccp_hdr *dh, unsigned int len);
+int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
+                        const struct dccp_hdr *dh, const unsigned int len);
 
-extern int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized);
-extern void dccp_destroy_sock(struct sock *sk);
+int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized);
+void dccp_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,
-                                           struct request_sock *req);
+void dccp_close(struct sock *sk, long timeout);
+struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
+                                  struct request_sock *req);
 
-extern int        dccp_connect(struct sock *sk);
-extern int        dccp_disconnect(struct sock *sk, int flags);
-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,
-                                  char __user *optval, unsigned int optlen);
+int dccp_connect(struct sock *sk);
+int dccp_disconnect(struct sock *sk, int flags);
+int dccp_getsockopt(struct sock *sk, int level, int optname,
+                   char __user *optval, int __user *optlen);
+int dccp_setsockopt(struct sock *sk, int level, int optname,
+                   char __user *optval, unsigned int optlen);
 #ifdef CONFIG_COMPAT
-extern int        compat_dccp_getsockopt(struct sock *sk,
-                               int level, int optname,
-                               char __user *optval, int __user *optlen);
-extern int        compat_dccp_setsockopt(struct sock *sk,
-                               int level, int optname,
-                               char __user *optval, unsigned int optlen);
+int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
+                          char __user *optval, int __user *optlen);
+int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
+                          char __user *optval, unsigned int optlen);
 #endif
-extern int        dccp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern int        dccp_sendmsg(struct kiocb *iocb, struct sock *sk,
-                               struct msghdr *msg, size_t size);
-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 int        dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
-                                  int addr_len);
-
-extern struct sk_buff *dccp_ctl_make_reset(struct sock *sk,
-                                          struct sk_buff *skb);
-extern int        dccp_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);
-extern u32        dccp_sample_rtt(struct sock *sk, long delta);
+int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                size_t size);
+int dccp_recvmsg(struct kiocb *iocb, struct sock *sk,
+                struct msghdr *msg, size_t len, int nonblock, int flags,
+                int *addr_len);
+void dccp_shutdown(struct sock *sk, int how);
+int inet_dccp_listen(struct socket *sock, int backlog);
+unsigned int dccp_poll(struct file *file, struct socket *sock,
+                      poll_table *wait);
+int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+
+struct sk_buff *dccp_ctl_make_reset(struct sock *sk, struct sk_buff *skb);
+int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code);
+void dccp_send_close(struct sock *sk, const int active);
+int dccp_invalid_packet(struct sk_buff *skb);
+u32 dccp_sample_rtt(struct sock *sk, long delta);
 
 static inline int dccp_bad_service_code(const struct sock *sk,
                                        const __be32 service)
@@ -475,25 +469,25 @@ static inline int dccp_ack_pending(const struct sock *sk)
        return dccp_ackvec_pending(sk) || inet_csk_ack_scheduled(sk);
 }
 
-extern int  dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val);
-extern int  dccp_feat_finalise_settings(struct dccp_sock *dp);
-extern int  dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq);
-extern int  dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*,
-                                 struct sk_buff *skb);
-extern int  dccp_feat_activate_values(struct sock *sk, struct list_head *fn);
-extern void dccp_feat_list_purge(struct list_head *fn_list);
-
-extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb);
-extern int dccp_insert_options_rsk(struct dccp_request_sock*, struct sk_buff*);
-extern int dccp_insert_option_elapsed_time(struct sk_buff *skb, u32 elapsed);
-extern u32 dccp_timestamp(void);
-extern void dccp_timestamping_init(void);
-extern int dccp_insert_option(struct sk_buff *skb, unsigned char option,
-                             const void *value, unsigned char len);
+int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val);
+int dccp_feat_finalise_settings(struct dccp_sock *dp);
+int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq);
+int dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*,
+                         struct sk_buff *skb);
+int dccp_feat_activate_values(struct sock *sk, struct list_head *fn);
+void dccp_feat_list_purge(struct list_head *fn_list);
+
+int dccp_insert_options(struct sock *sk, struct sk_buff *skb);
+int dccp_insert_options_rsk(struct dccp_request_sock *, struct sk_buff *);
+int dccp_insert_option_elapsed_time(struct sk_buff *skb, u32 elapsed);
+u32 dccp_timestamp(void);
+void dccp_timestamping_init(void);
+int dccp_insert_option(struct sk_buff *skb, unsigned char option,
+                      const void *value, unsigned char len);
 
 #ifdef CONFIG_SYSCTL
-extern int dccp_sysctl_init(void);
-extern void dccp_sysctl_exit(void);
+int dccp_sysctl_init(void);
+void dccp_sysctl_exit(void);
 #else
 static inline int dccp_sysctl_init(void)
 {
index 90b957d..0e75ceb 100644 (file)
@@ -107,13 +107,13 @@ extern unsigned long sysctl_dccp_sequence_window;
 extern int          sysctl_dccp_rx_ccid;
 extern int          sysctl_dccp_tx_ccid;
 
-extern int  dccp_feat_init(struct sock *sk);
-extern void dccp_feat_initialise_sysctls(void);
-extern int  dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
-                                 u8 const *list, u8 len);
-extern int  dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
-                                   u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
-extern int  dccp_feat_clone_list(struct list_head const *, struct list_head *);
+int dccp_feat_init(struct sock *sk);
+void dccp_feat_initialise_sysctls(void);
+int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
+                         u8 const *list, u8 len);
+int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
+                           u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
+int dccp_feat_clone_list(struct list_head const *, struct list_head *);
 
 /*
  * Encoding variable-length options and their maximum length.
@@ -127,11 +127,11 @@ extern int  dccp_feat_clone_list(struct list_head const *, struct list_head *);
  */
 #define DCCP_OPTVAL_MAXLEN     6
 
-extern void dccp_encode_value_var(const u64 value, u8 *to, const u8 len);
-extern u64  dccp_decode_value_var(const u8 *bf, const u8 len);
-extern u64  dccp_feat_nn_get(struct sock *sk, u8 feat);
+void dccp_encode_value_var(const u64 value, u8 *to, const u8 len);
+u64 dccp_decode_value_var(const u8 *bf, const u8 len);
+u64 dccp_feat_nn_get(struct sock *sk, u8 feat);
 
-extern int  dccp_insert_option_mandatory(struct sk_buff *skb);
-extern int  dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat,
-                              u8 *val, u8 len, bool repeat_first);
+int dccp_insert_option_mandatory(struct sk_buff *skb);
+int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat, u8 *val, u8 len,
+                      bool repeat_first);
 #endif /* _DCCP_FEAT_H */
index ebc54fe..d9f65fc 100644 (file)
@@ -174,6 +174,7 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk,
        mtu = dst_mtu(dst);
 
        if (inet->pmtudisc != IP_PMTUDISC_DONT &&
+           ip_sk_accept_pmtu(sk) &&
            inet_csk(sk)->icsk_pmtu_cookie > mtu) {
                dccp_sync_mss(sk, mtu);
 
@@ -409,9 +410,9 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        newinet            = inet_sk(newsk);
        ireq               = inet_rsk(req);
-       newinet->inet_daddr     = ireq->rmt_addr;
-       newinet->inet_rcv_saddr = ireq->loc_addr;
-       newinet->inet_saddr     = ireq->loc_addr;
+       newinet->inet_daddr     = ireq->ir_rmt_addr;
+       newinet->inet_rcv_saddr = ireq->ir_loc_addr;
+       newinet->inet_saddr     = ireq->ir_loc_addr;
        newinet->inet_opt       = ireq->opt;
        ireq->opt          = NULL;
        newinet->mc_index  = inet_iif(skb);
@@ -516,10 +517,10 @@ static int dccp_v4_send_response(struct sock *sk, struct request_sock *req)
                const struct inet_request_sock *ireq = inet_rsk(req);
                struct dccp_hdr *dh = dccp_hdr(skb);
 
-               dh->dccph_checksum = dccp_v4_csum_finish(skb, ireq->loc_addr,
-                                                             ireq->rmt_addr);
-               err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
-                                           ireq->rmt_addr,
+               dh->dccph_checksum = dccp_v4_csum_finish(skb, ireq->ir_loc_addr,
+                                                             ireq->ir_rmt_addr);
+               err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
+                                           ireq->ir_rmt_addr,
                                            ireq->opt);
                err = net_xmit_eval(err);
        }
@@ -641,8 +642,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                goto drop_and_free;
 
        ireq = inet_rsk(req);
-       ireq->loc_addr = ip_hdr(skb)->daddr;
-       ireq->rmt_addr = ip_hdr(skb)->saddr;
+       ireq->ir_loc_addr = ip_hdr(skb)->daddr;
+       ireq->ir_rmt_addr = ip_hdr(skb)->saddr;
 
        /*
         * Step 3: Process LISTEN state
index 6cf9f77..4ac71ff 100644 (file)
@@ -67,7 +67,7 @@ static inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb)
        struct dccp_hdr *dh = dccp_hdr(skb);
 
        dccp_csum_outgoing(skb);
-       dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &np->daddr);
+       dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &sk->sk_v6_daddr);
 }
 
 static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb)
@@ -216,7 +216,7 @@ out:
 
 static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
 {
-       struct inet6_request_sock *ireq6 = inet6_rsk(req);
+       struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sk_buff *skb;
        struct in6_addr *final_p, final;
@@ -226,12 +226,12 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
 
        memset(&fl6, 0, sizeof(fl6));
        fl6.flowi6_proto = IPPROTO_DCCP;
-       fl6.daddr = ireq6->rmt_addr;
-       fl6.saddr = ireq6->loc_addr;
+       fl6.daddr = ireq->ir_v6_rmt_addr;
+       fl6.saddr = ireq->ir_v6_loc_addr;
        fl6.flowlabel = 0;
-       fl6.flowi6_oif = ireq6->iif;
-       fl6.fl6_dport = inet_rsk(req)->rmt_port;
-       fl6.fl6_sport = inet_rsk(req)->loc_port;
+       fl6.flowi6_oif = ireq->ir_iif;
+       fl6.fl6_dport = ireq->ir_rmt_port;
+       fl6.fl6_sport = htons(ireq->ir_num);
        security_req_classify_flow(req, flowi6_to_flowi(&fl6));
 
 
@@ -249,9 +249,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
                struct dccp_hdr *dh = dccp_hdr(skb);
 
                dh->dccph_checksum = dccp_v6_csum_finish(skb,
-                                                        &ireq6->loc_addr,
-                                                        &ireq6->rmt_addr);
-               fl6.daddr = ireq6->rmt_addr;
+                                                        &ireq->ir_v6_loc_addr,
+                                                        &ireq->ir_v6_rmt_addr);
+               fl6.daddr = ireq->ir_v6_rmt_addr;
                err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
                err = net_xmit_eval(err);
        }
@@ -264,8 +264,7 @@ done:
 static void dccp_v6_reqsk_destructor(struct request_sock *req)
 {
        dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
-       if (inet6_rsk(req)->pktopts != NULL)
-               kfree_skb(inet6_rsk(req)->pktopts);
+       kfree_skb(inet_rsk(req)->pktopts);
 }
 
 static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
@@ -359,7 +358,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct request_sock *req;
        struct dccp_request_sock *dreq;
-       struct inet6_request_sock *ireq6;
+       struct inet_request_sock *ireq;
        struct ipv6_pinfo *np = inet6_sk(sk);
        const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
        struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
@@ -398,22 +397,22 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_free;
 
-       ireq6 = inet6_rsk(req);
-       ireq6->rmt_addr = ipv6_hdr(skb)->saddr;
-       ireq6->loc_addr = ipv6_hdr(skb)->daddr;
+       ireq = inet_rsk(req);
+       ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
+       ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
 
        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;
+               ireq->pktopts = skb;
        }
-       ireq6->iif = sk->sk_bound_dev_if;
+       ireq->ir_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);
+           ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
+               ireq->ir_iif = inet6_iif(skb);
 
        /*
         * Step 3: Process LISTEN state
@@ -446,7 +445,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                                              struct request_sock *req,
                                              struct dst_entry *dst)
 {
-       struct inet6_request_sock *ireq6 = inet6_rsk(req);
+       struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
        struct inet_sock *newinet;
        struct dccp6_sock *newdp6;
@@ -467,11 +466,11 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
 
                memcpy(newnp, np, sizeof(struct ipv6_pinfo));
 
-               ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr);
+               ipv6_addr_set_v4mapped(newinet->inet_daddr, &newsk->sk_v6_daddr);
 
                ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr);
 
-               newnp->rcv_saddr = newnp->saddr;
+               newsk->sk_v6_rcv_saddr = newnp->saddr;
 
                inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped;
                newsk->sk_backlog_rcv = dccp_v4_do_rcv;
@@ -505,12 +504,12 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
 
                memset(&fl6, 0, sizeof(fl6));
                fl6.flowi6_proto = IPPROTO_DCCP;
-               fl6.daddr = ireq6->rmt_addr;
+               fl6.daddr = ireq->ir_v6_rmt_addr;
                final_p = fl6_update_dst(&fl6, np->opt, &final);
-               fl6.saddr = ireq6->loc_addr;
+               fl6.saddr = ireq->ir_v6_loc_addr;
                fl6.flowi6_oif = sk->sk_bound_dev_if;
-               fl6.fl6_dport = inet_rsk(req)->rmt_port;
-               fl6.fl6_sport = inet_rsk(req)->loc_port;
+               fl6.fl6_dport = ireq->ir_rmt_port;
+               fl6.fl6_sport = htons(ireq->ir_num);
                security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
 
                dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false);
@@ -538,10 +537,10 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
 
        memcpy(newnp, np, sizeof(struct ipv6_pinfo));
 
-       newnp->daddr = ireq6->rmt_addr;
-       newnp->saddr = ireq6->loc_addr;
-       newnp->rcv_saddr = ireq6->loc_addr;
-       newsk->sk_bound_dev_if = ireq6->iif;
+       newsk->sk_v6_daddr      = ireq->ir_v6_rmt_addr;
+       newnp->saddr            = ireq->ir_v6_loc_addr;
+       newsk->sk_v6_rcv_saddr  = ireq->ir_v6_loc_addr;
+       newsk->sk_bound_dev_if  = ireq->ir_iif;
 
        /* Now IPv6 options...
 
@@ -554,10 +553,10 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
 
        /* Clone pktoptions received with SYN */
        newnp->pktoptions = NULL;
-       if (ireq6->pktopts != NULL) {
-               newnp->pktoptions = skb_clone(ireq6->pktopts, GFP_ATOMIC);
-               consume_skb(ireq6->pktopts);
-               ireq6->pktopts = NULL;
+       if (ireq->pktopts != NULL) {
+               newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC);
+               consume_skb(ireq->pktopts);
+               ireq->pktopts = NULL;
                if (newnp->pktoptions)
                        skb_set_owner_r(newnp->pktoptions, newsk);
        }
@@ -885,7 +884,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                        return -EINVAL;
        }
 
-       np->daddr = usin->sin6_addr;
+       sk->sk_v6_daddr = usin->sin6_addr;
        np->flow_label = fl6.flowlabel;
 
        /*
@@ -915,16 +914,16 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                        goto failure;
                }
                ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
-               ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &np->rcv_saddr);
+               ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &sk->sk_v6_rcv_saddr);
 
                return err;
        }
 
-       if (!ipv6_addr_any(&np->rcv_saddr))
-               saddr = &np->rcv_saddr;
+       if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr))
+               saddr = &sk->sk_v6_rcv_saddr;
 
        fl6.flowi6_proto = IPPROTO_DCCP;
-       fl6.daddr = np->daddr;
+       fl6.daddr = sk->sk_v6_daddr;
        fl6.saddr = saddr ? *saddr : np->saddr;
        fl6.flowi6_oif = sk->sk_bound_dev_if;
        fl6.fl6_dport = usin->sin6_port;
@@ -941,7 +940,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        if (saddr == NULL) {
                saddr = &fl6.saddr;
-               np->rcv_saddr = *saddr;
+               sk->sk_v6_rcv_saddr = *saddr;
        }
 
        /* set the source address */
@@ -963,7 +962,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                goto late_failure;
 
        dp->dccps_iss = secure_dccpv6_sequence_number(np->saddr.s6_addr32,
-                                                     np->daddr.s6_addr32,
+                                                     sk->sk_v6_daddr.s6_addr32,
                                                      inet->inet_sport,
                                                      inet->inet_dport);
        err = dccp_connect(sk);
index 6eef81f..af259e1 100644 (file)
@@ -25,12 +25,10 @@ struct dccp6_sock {
 
 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 */
index 662071b..9e2f78b 100644 (file)
@@ -56,12 +56,9 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
 #if IS_ENABLED(CONFIG_IPV6)
                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);
-                       tw6->tw_v6_daddr = np->daddr;
-                       tw6->tw_v6_rcv_saddr = np->rcv_saddr;
+                       tw->tw_v6_daddr = sk->sk_v6_daddr;
+                       tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
                        tw->tw_ipv6only = np->ipv6only;
                }
 #endif
@@ -269,10 +266,10 @@ int dccp_reqsk_init(struct request_sock *req,
 {
        struct dccp_request_sock *dreq = dccp_rsk(req);
 
-       inet_rsk(req)->rmt_port   = dccp_hdr(skb)->dccph_sport;
-       inet_rsk(req)->loc_port   = dccp_hdr(skb)->dccph_dport;
-       inet_rsk(req)->acked      = 0;
-       dreq->dreq_timestamp_echo = 0;
+       inet_rsk(req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport;
+       inet_rsk(req)->ir_num      = ntohs(dccp_hdr(skb)->dccph_dport);
+       inet_rsk(req)->acked       = 0;
+       dreq->dreq_timestamp_echo  = 0;
 
        /* inherit feature negotiation options from listening socket */
        return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg);
index d17fc90..8876078 100644 (file)
@@ -424,8 +424,8 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
        /* Build and checksum header */
        dh = dccp_zeroed_hdr(skb, dccp_header_size);
 
-       dh->dccph_sport = inet_rsk(req)->loc_port;
-       dh->dccph_dport = inet_rsk(req)->rmt_port;
+       dh->dccph_sport = htons(inet_rsk(req)->ir_num);
+       dh->dccph_dport = inet_rsk(req)->ir_rmt_port;
        dh->dccph_doff  = (dccp_header_size +
                           DCCP_SKB_CB(skb)->dccpd_opt_len) / 4;
        dh->dccph_type  = DCCP_PKT_RESPONSE;
index ba64750..eb892b4 100644 (file)
@@ -1158,10 +1158,8 @@ static int __init dccp_init(void)
                goto out_free_bind_bucket_cachep;
        }
 
-       for (i = 0; i <= dccp_hashinfo.ehash_mask; i++) {
+       for (i = 0; i <= dccp_hashinfo.ehash_mask; i++)
                INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i);
-               INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].twchain, i);
-       }
 
        if (inet_ehash_locks_alloc(&dccp_hashinfo))
                        goto out_free_dccp_ehash;
index 2a7efe3..e83015c 100644 (file)
@@ -87,7 +87,7 @@ static void dnrmg_send_peer(struct sk_buff *skb)
 }
 
 
-static unsigned int dnrmg_hook(unsigned int hook,
+static unsigned int dnrmg_hook(const struct nf_hook_ops *ops,
                        struct sk_buff *skb,
                        const struct net_device *in,
                        const struct net_device *out,
index be1f64d..8f032ba 100644 (file)
@@ -58,7 +58,7 @@
 #include <net/ipv6.h>
 #include <net/ip.h>
 #include <net/dsa.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 
 __setup("ether=", netdev_boot_setup);
 
@@ -133,7 +133,7 @@ int eth_rebuild_header(struct sk_buff *skb)
                return arp_find(eth->h_dest, skb);
 #endif
        default:
-               printk(KERN_DEBUG
+               netdev_dbg(dev,
                       "%s: unable to resolve type %X addresses.\n",
                       dev->name, ntohs(eth->h_proto));
 
@@ -169,20 +169,9 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
                else
                        skb->pkt_type = PACKET_MULTICAST;
        }
-
-       /*
-        *      This ALLMULTI check should be redundant by 1.4
-        *      so don't forget to remove it.
-        *
-        *      Seems, you forgot to remove it. All silly devices
-        *      seems to set IFF_PROMISC.
-        */
-
-       else if (1 /*dev->flags&IFF_PROMISC */ ) {
-               if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
-                                                     dev->dev_addr)))
-                       skb->pkt_type = PACKET_OTHERHOST;
-       }
+       else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+                                                  dev->dev_addr)))
+               skb->pkt_type = PACKET_OTHERHOST;
 
        /*
         * Some variants of DSA tagging don't have an ethertype field
@@ -190,12 +179,13 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
         * variants has been configured on the receiving interface,
         * and if so, set skb->protocol without looking at the packet.
         */
-       if (netdev_uses_dsa_tags(dev))
+       if (unlikely(netdev_uses_dsa_tags(dev)))
                return htons(ETH_P_DSA);
-       if (netdev_uses_trailer_tags(dev))
+
+       if (unlikely(netdev_uses_trailer_tags(dev)))
                return htons(ETH_P_TRAILER);
 
-       if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
+       if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN))
                return eth->h_proto;
 
        /*
@@ -204,7 +194,7 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
         *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
         *      won't work for fault tolerant netware but does for the rest.
         */
-       if (skb->len >= 2 && *(unsigned short *)(skb->data) == 0xFFFF)
+       if (unlikely(skb->len >= 2 && *(unsigned short *)(skb->data) == 0xFFFF))
                return htons(ETH_P_802_3);
 
        /*
diff --git a/net/hsr/Kconfig b/net/hsr/Kconfig
new file mode 100644 (file)
index 0000000..0d3d709
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# IEC 62439-3 High-availability Seamless Redundancy
+#
+
+config HSR
+       tristate "High-availability Seamless Redundancy (HSR)"
+       ---help---
+         If you say Y here, then your Linux box will be able to act as a
+         DANH ("Doubly attached node implementing HSR"). For this to work,
+         your Linux box needs (at least) two physical Ethernet interfaces,
+         and it must be connected as a node in a ring network together with
+         other HSR capable nodes.
+
+         All Ethernet frames sent over the hsr device will be sent in both
+         directions on the ring (over both slave ports), giving a redundant,
+         instant fail-over network. Each HSR node in the ring acts like a
+         bridge for HSR frames, but filters frames that have been forwarded
+         earlier.
+
+         This code is a "best effort" to comply with the HSR standard as
+         described in IEC 62439-3:2010 (HSRv0), but no compliancy tests have
+         been made.
+
+         You need to perform any and all necessary tests yourself before
+         relying on this code in a safety critical system!
+
+         If unsure, say N.
diff --git a/net/hsr/Makefile b/net/hsr/Makefile
new file mode 100644 (file)
index 0000000..b68359f
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for HSR
+#
+
+obj-$(CONFIG_HSR)      += hsr.o
+
+hsr-y                  := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
new file mode 100644 (file)
index 0000000..cac505f
--- /dev/null
@@ -0,0 +1,596 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * This file contains device methods for creating, using and destroying
+ * virtual HSR devices.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/pkt_sched.h>
+#include "hsr_device.h"
+#include "hsr_framereg.h"
+#include "hsr_main.h"
+
+
+static bool is_admin_up(struct net_device *dev)
+{
+       return dev && (dev->flags & IFF_UP);
+}
+
+static bool is_slave_up(struct net_device *dev)
+{
+       return dev && is_admin_up(dev) && netif_oper_up(dev);
+}
+
+static void __hsr_set_operstate(struct net_device *dev, int transition)
+{
+       write_lock_bh(&dev_base_lock);
+       if (dev->operstate != transition) {
+               dev->operstate = transition;
+               write_unlock_bh(&dev_base_lock);
+               netdev_state_change(dev);
+       } else {
+               write_unlock_bh(&dev_base_lock);
+       }
+}
+
+void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
+                      struct net_device *slave2)
+{
+       if (!is_admin_up(hsr_dev)) {
+               __hsr_set_operstate(hsr_dev, IF_OPER_DOWN);
+               return;
+       }
+
+       if (is_slave_up(slave1) || is_slave_up(slave2))
+               __hsr_set_operstate(hsr_dev, IF_OPER_UP);
+       else
+               __hsr_set_operstate(hsr_dev, IF_OPER_LOWERLAYERDOWN);
+}
+
+void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
+                    struct net_device *slave2)
+{
+       if (is_slave_up(slave1) || is_slave_up(slave2))
+               netif_carrier_on(hsr_dev);
+       else
+               netif_carrier_off(hsr_dev);
+}
+
+
+void hsr_check_announce(struct net_device *hsr_dev, int old_operstate)
+{
+       struct hsr_priv *hsr_priv;
+
+       hsr_priv = netdev_priv(hsr_dev);
+
+       if ((hsr_dev->operstate == IF_OPER_UP) && (old_operstate != IF_OPER_UP)) {
+               /* Went up */
+               hsr_priv->announce_count = 0;
+               hsr_priv->announce_timer.expires = jiffies +
+                               msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
+               add_timer(&hsr_priv->announce_timer);
+       }
+
+       if ((hsr_dev->operstate != IF_OPER_UP) && (old_operstate == IF_OPER_UP))
+               /* Went down */
+               del_timer(&hsr_priv->announce_timer);
+}
+
+
+int hsr_get_max_mtu(struct hsr_priv *hsr_priv)
+{
+       int mtu_max;
+
+       if (hsr_priv->slave[0] && hsr_priv->slave[1])
+               mtu_max = min(hsr_priv->slave[0]->mtu, hsr_priv->slave[1]->mtu);
+       else if (hsr_priv->slave[0])
+               mtu_max = hsr_priv->slave[0]->mtu;
+       else if (hsr_priv->slave[1])
+               mtu_max = hsr_priv->slave[1]->mtu;
+       else
+               mtu_max = HSR_TAGLEN;
+
+       return mtu_max - HSR_TAGLEN;
+}
+
+static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct hsr_priv *hsr_priv;
+
+       hsr_priv = netdev_priv(dev);
+
+       if (new_mtu > hsr_get_max_mtu(hsr_priv)) {
+               netdev_info(hsr_priv->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n",
+                           HSR_TAGLEN);
+               return -EINVAL;
+       }
+
+       dev->mtu = new_mtu;
+
+       return 0;
+}
+
+static int hsr_dev_open(struct net_device *dev)
+{
+       struct hsr_priv *hsr_priv;
+       int i;
+       char *slave_name;
+
+       hsr_priv = netdev_priv(dev);
+
+       for (i = 0; i < HSR_MAX_SLAVE; i++) {
+               if (hsr_priv->slave[i])
+                       slave_name = hsr_priv->slave[i]->name;
+               else
+                       slave_name = "null";
+
+               if (!is_slave_up(hsr_priv->slave[i]))
+                       netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n",
+                                   'A' + i, slave_name);
+       }
+
+       return 0;
+}
+
+static int hsr_dev_close(struct net_device *dev)
+{
+       /* Nothing to do here. We could try to restore the state of the slaves
+        * to what they were before being changed by the hsr master dev's state,
+        * but they might have been changed manually in the mean time too, so
+        * taking them up or down here might be confusing and is probably not a
+        * good idea.
+        */
+       return 0;
+}
+
+
+static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr_priv)
+{
+       unsigned long irqflags;
+
+       /* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values
+        * between 0001-1001 ("ring identifier", for regular HSR frames),
+        * or 1111 ("HSR management", supervision frames). Unfortunately, the
+        * spec writers forgot to explain what a "ring identifier" is, or
+        * how it is used. So we just set this to 0001 for regular frames,
+        * and 1111 for supervision frames.
+        */
+       set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1);
+
+       /* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet
+        * frame is the content of the frame located between the Length/Type
+        * field and the Frame Check Sequence."
+        *
+        * IEC 62439-3, p 48, specifies the "original LPDU" to include the
+        * original "LT" field (what "LT" means is not explained anywhere as
+        * far as I can see - perhaps "Length/Type"?). So LSDU_size might
+        * equal original length + 2.
+        *   Also, the fact that this field is not used anywhere (might be used
+        * by a RedBox connecting HSR and PRP nets?) means I cannot test its
+        * correctness. Instead of guessing, I set this to 0 here, to make any
+        * problems immediately apparent. Anyone using this driver with PRP/HSR
+        * RedBoxes might need to fix this...
+        */
+       set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0);
+
+       spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
+       hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr);
+       hsr_priv->sequence_nr++;
+       spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
+
+       hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
+
+       hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP);
+}
+
+static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr_priv,
+                     enum hsr_dev_idx dev_idx)
+{
+       struct hsr_ethhdr *hsr_ethhdr;
+
+       hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
+
+       skb->dev = hsr_priv->slave[dev_idx];
+
+       hsr_addr_subst_dest(hsr_priv, &hsr_ethhdr->ethhdr, dev_idx);
+
+       /* Address substitution (IEC62439-3 pp 26, 50): replace mac
+        * address of outgoing frame with that of the outgoing slave's.
+        */
+       memcpy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr, ETH_ALEN);
+
+       return dev_queue_xmit(skb);
+}
+
+
+static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hsr_priv *hsr_priv;
+       struct hsr_ethhdr *hsr_ethhdr;
+       struct sk_buff *skb2;
+       int res1, res2;
+
+       hsr_priv = netdev_priv(dev);
+       hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
+
+       if ((skb->protocol != htons(ETH_P_PRP)) ||
+           (hsr_ethhdr->ethhdr.h_proto != htons(ETH_P_PRP))) {
+               hsr_fill_tag(hsr_ethhdr, hsr_priv);
+               skb->protocol = htons(ETH_P_PRP);
+       }
+
+       skb2 = pskb_copy(skb, GFP_ATOMIC);
+
+       res1 = NET_XMIT_DROP;
+       if (likely(hsr_priv->slave[HSR_DEV_SLAVE_A]))
+               res1 = slave_xmit(skb, hsr_priv, HSR_DEV_SLAVE_A);
+
+       res2 = NET_XMIT_DROP;
+       if (likely(skb2 && hsr_priv->slave[HSR_DEV_SLAVE_B]))
+               res2 = slave_xmit(skb2, hsr_priv, HSR_DEV_SLAVE_B);
+
+       if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
+                  res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
+               hsr_priv->dev->stats.tx_packets++;
+               hsr_priv->dev->stats.tx_bytes += skb->len;
+       } else {
+               hsr_priv->dev->stats.tx_dropped++;
+       }
+
+       return NETDEV_TX_OK;
+}
+
+
+static int hsr_header_create(struct sk_buff *skb, struct net_device *dev,
+                            unsigned short type, const void *daddr,
+                            const void *saddr, unsigned int len)
+{
+       int res;
+
+       /* Make room for the HSR tag now. We will fill it in later (in
+        * hsr_dev_xmit)
+        */
+       if (skb_headroom(skb) < HSR_TAGLEN + ETH_HLEN)
+               return -ENOBUFS;
+       skb_push(skb, HSR_TAGLEN);
+
+       /* To allow VLAN/HSR combos we should probably use
+        * res = dev_hard_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN);
+        * here instead. It would require other changes too, though - e.g.
+        * separate headers for each slave etc...
+        */
+       res = eth_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN);
+       if (res <= 0)
+               return res;
+       skb_reset_mac_header(skb);
+
+       return res + HSR_TAGLEN;
+}
+
+
+static const struct header_ops hsr_header_ops = {
+       .create  = hsr_header_create,
+       .parse   = eth_header_parse,
+};
+
+
+/* HSR:2010 supervision frames should be padded so that the whole frame,
+ * including headers and FCS, is 64 bytes (without VLAN).
+ */
+static int hsr_pad(int size)
+{
+       const int min_size = ETH_ZLEN - HSR_TAGLEN - ETH_HLEN;
+
+       if (size >= min_size)
+               return size;
+       return min_size;
+}
+
+static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
+{
+       struct hsr_priv *hsr_priv;
+       struct sk_buff *skb;
+       int hlen, tlen;
+       struct hsr_sup_tag *hsr_stag;
+       struct hsr_sup_payload *hsr_sp;
+       unsigned long irqflags;
+
+       hlen = LL_RESERVED_SPACE(hsr_dev);
+       tlen = hsr_dev->needed_tailroom;
+       skb = alloc_skb(hsr_pad(sizeof(struct hsr_sup_payload)) + hlen + tlen,
+                       GFP_ATOMIC);
+
+       if (skb == NULL)
+               return;
+
+       hsr_priv = netdev_priv(hsr_dev);
+
+       skb_reserve(skb, hlen);
+
+       skb->dev = hsr_dev;
+       skb->protocol = htons(ETH_P_PRP);
+       skb->priority = TC_PRIO_CONTROL;
+
+       if (dev_hard_header(skb, skb->dev, ETH_P_PRP,
+                           hsr_priv->sup_multicast_addr,
+                           skb->dev->dev_addr, skb->len) < 0)
+               goto out;
+
+       skb_pull(skb, sizeof(struct ethhdr));
+       hsr_stag = (typeof(hsr_stag)) skb->data;
+
+       set_hsr_stag_path(hsr_stag, 0xf);
+       set_hsr_stag_HSR_Ver(hsr_stag, 0);
+
+       spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
+       hsr_stag->sequence_nr = htons(hsr_priv->sequence_nr);
+       hsr_priv->sequence_nr++;
+       spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
+
+       hsr_stag->HSR_TLV_Type = type;
+       hsr_stag->HSR_TLV_Length = 12;
+
+       skb_push(skb, sizeof(struct ethhdr));
+
+       /* Payload: MacAddressA */
+       hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp));
+       memcpy(hsr_sp->MacAddressA, hsr_dev->dev_addr, ETH_ALEN);
+
+       dev_queue_xmit(skb);
+       return;
+
+out:
+       kfree_skb(skb);
+}
+
+
+/* Announce (supervision frame) timer function
+ */
+static void hsr_announce(unsigned long data)
+{
+       struct hsr_priv *hsr_priv;
+
+       hsr_priv = (struct hsr_priv *) data;
+
+       if (hsr_priv->announce_count < 3) {
+               send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_ANNOUNCE);
+               hsr_priv->announce_count++;
+       } else {
+               send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_LIFE_CHECK);
+       }
+
+       if (hsr_priv->announce_count < 3)
+               hsr_priv->announce_timer.expires = jiffies +
+                               msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
+       else
+               hsr_priv->announce_timer.expires = jiffies +
+                               msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
+
+       if (is_admin_up(hsr_priv->dev))
+               add_timer(&hsr_priv->announce_timer);
+}
+
+
+static void restore_slaves(struct net_device *hsr_dev)
+{
+       struct hsr_priv *hsr_priv;
+       int i;
+       int res;
+
+       hsr_priv = netdev_priv(hsr_dev);
+
+       rtnl_lock();
+
+       /* Restore promiscuity */
+       for (i = 0; i < HSR_MAX_SLAVE; i++) {
+               if (!hsr_priv->slave[i])
+                       continue;
+               res = dev_set_promiscuity(hsr_priv->slave[i], -1);
+               if (res)
+                       netdev_info(hsr_dev,
+                                   "Cannot restore slave promiscuity (%s, %d)\n",
+                                   hsr_priv->slave[i]->name, res);
+       }
+
+       rtnl_unlock();
+}
+
+static void reclaim_hsr_dev(struct rcu_head *rh)
+{
+       struct hsr_priv *hsr_priv;
+
+       hsr_priv = container_of(rh, struct hsr_priv, rcu_head);
+       free_netdev(hsr_priv->dev);
+}
+
+
+/* According to comments in the declaration of struct net_device, this function
+ * is "Called from unregister, can be used to call free_netdev". Ok then...
+ */
+static void hsr_dev_destroy(struct net_device *hsr_dev)
+{
+       struct hsr_priv *hsr_priv;
+
+       hsr_priv = netdev_priv(hsr_dev);
+
+       del_timer(&hsr_priv->announce_timer);
+       unregister_hsr_master(hsr_priv);    /* calls list_del_rcu on hsr_priv */
+       restore_slaves(hsr_dev);
+       call_rcu(&hsr_priv->rcu_head, reclaim_hsr_dev);   /* reclaim hsr_priv */
+}
+
+static const struct net_device_ops hsr_device_ops = {
+       .ndo_change_mtu = hsr_dev_change_mtu,
+       .ndo_open = hsr_dev_open,
+       .ndo_stop = hsr_dev_close,
+       .ndo_start_xmit = hsr_dev_xmit,
+};
+
+
+void hsr_dev_setup(struct net_device *dev)
+{
+       random_ether_addr(dev->dev_addr);
+
+       ether_setup(dev);
+       dev->header_ops          = &hsr_header_ops;
+       dev->netdev_ops          = &hsr_device_ops;
+       dev->tx_queue_len        = 0;
+
+       dev->destructor = hsr_dev_destroy;
+}
+
+
+/* Return true if dev is a HSR master; return false otherwise.
+ */
+bool is_hsr_master(struct net_device *dev)
+{
+       return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit);
+}
+
+static int check_slave_ok(struct net_device *dev)
+{
+       /* Don't allow HSR on non-ethernet like devices */
+       if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) ||
+           (dev->addr_len != ETH_ALEN)) {
+               netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n");
+               return -EINVAL;
+       }
+
+       /* Don't allow enslaving hsr devices */
+       if (is_hsr_master(dev)) {
+               netdev_info(dev, "Cannot create trees of HSR devices.\n");
+               return -EINVAL;
+       }
+
+       if (is_hsr_slave(dev)) {
+               netdev_info(dev, "This device is already a HSR slave.\n");
+               return -EINVAL;
+       }
+
+       if (dev->priv_flags & IFF_802_1Q_VLAN) {
+               netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n");
+               return -EINVAL;
+       }
+
+       /* HSR over bonded devices has not been tested, but I'm not sure it
+        * won't work...
+        */
+
+       return 0;
+}
+
+
+/* Default multicast address for HSR Supervision frames */
+static const unsigned char def_multicast_addr[ETH_ALEN] = {
+       0x01, 0x15, 0x4e, 0x00, 0x01, 0x00
+};
+
+int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
+                    unsigned char multicast_spec)
+{
+       struct hsr_priv *hsr_priv;
+       int i;
+       int res;
+
+       hsr_priv = netdev_priv(hsr_dev);
+       hsr_priv->dev = hsr_dev;
+       INIT_LIST_HEAD(&hsr_priv->node_db);
+       INIT_LIST_HEAD(&hsr_priv->self_node_db);
+       for (i = 0; i < HSR_MAX_SLAVE; i++)
+               hsr_priv->slave[i] = slave[i];
+
+       spin_lock_init(&hsr_priv->seqnr_lock);
+       /* Overflow soon to find bugs easier: */
+       hsr_priv->sequence_nr = USHRT_MAX - 1024;
+
+       init_timer(&hsr_priv->announce_timer);
+       hsr_priv->announce_timer.function = hsr_announce;
+       hsr_priv->announce_timer.data = (unsigned long) hsr_priv;
+
+       memcpy(hsr_priv->sup_multicast_addr, def_multicast_addr, ETH_ALEN);
+       hsr_priv->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;
+
+/* FIXME: should I modify the value of these?
+ *
+ * - hsr_dev->flags - i.e.
+ *                     IFF_MASTER/SLAVE?
+ * - hsr_dev->priv_flags - i.e.
+ *                     IFF_EBRIDGE?
+ *                     IFF_TX_SKB_SHARING?
+ *                     IFF_HSR_MASTER/SLAVE?
+ */
+
+       for (i = 0; i < HSR_MAX_SLAVE; i++) {
+               res = check_slave_ok(slave[i]);
+               if (res)
+                       return res;
+       }
+
+       hsr_dev->features = slave[0]->features & slave[1]->features;
+       /* Prevent recursive tx locking */
+       hsr_dev->features |= NETIF_F_LLTX;
+       /* VLAN on top of HSR needs testing and probably some work on
+        * hsr_header_create() etc.
+        */
+       hsr_dev->features |= NETIF_F_VLAN_CHALLENGED;
+
+       /* Set hsr_dev's MAC address to that of mac_slave1 */
+       memcpy(hsr_dev->dev_addr, hsr_priv->slave[0]->dev_addr, ETH_ALEN);
+
+       /* Set required header length */
+       for (i = 0; i < HSR_MAX_SLAVE; i++) {
+               if (slave[i]->hard_header_len + HSR_TAGLEN >
+                                               hsr_dev->hard_header_len)
+                       hsr_dev->hard_header_len =
+                                       slave[i]->hard_header_len + HSR_TAGLEN;
+       }
+
+       /* MTU */
+       for (i = 0; i < HSR_MAX_SLAVE; i++)
+               if (slave[i]->mtu - HSR_TAGLEN < hsr_dev->mtu)
+                       hsr_dev->mtu = slave[i]->mtu - HSR_TAGLEN;
+
+       /* Make sure the 1st call to netif_carrier_on() gets through */
+       netif_carrier_off(hsr_dev);
+
+       /* Promiscuity */
+       for (i = 0; i < HSR_MAX_SLAVE; i++) {
+               res = dev_set_promiscuity(slave[i], 1);
+               if (res) {
+                       netdev_info(hsr_dev, "Cannot set slave promiscuity (%s, %d)\n",
+                                   slave[i]->name, res);
+                       goto fail;
+               }
+       }
+
+       /* Make sure we recognize frames from ourselves in hsr_rcv() */
+       res = hsr_create_self_node(&hsr_priv->self_node_db,
+                                       hsr_dev->dev_addr,
+                                       hsr_priv->slave[1]->dev_addr);
+       if (res < 0)
+               goto fail;
+
+       res = register_netdevice(hsr_dev);
+       if (res)
+               goto fail;
+
+       register_hsr_master(hsr_priv);
+
+       return 0;
+
+fail:
+       restore_slaves(hsr_dev);
+       return res;
+}
diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h
new file mode 100644 (file)
index 0000000..2c7148e
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __HSR_DEVICE_H
+#define __HSR_DEVICE_H
+
+#include <linux/netdevice.h>
+#include "hsr_main.h"
+
+void hsr_dev_setup(struct net_device *dev);
+int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
+                    unsigned char multicast_spec);
+void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
+                      struct net_device *slave2);
+void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
+                    struct net_device *slave2);
+void hsr_check_announce(struct net_device *hsr_dev, int old_operstate);
+bool is_hsr_master(struct net_device *dev);
+int hsr_get_max_mtu(struct hsr_priv *hsr_priv);
+
+#endif /* __HSR_DEVICE_H */
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
new file mode 100644 (file)
index 0000000..003f5bb
--- /dev/null
@@ -0,0 +1,503 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * The HSR spec says never to forward the same frame twice on the same
+ * interface. A frame is identified by its source MAC address and its HSR
+ * sequence number. This code keeps track of senders and their sequence numbers
+ * to allow filtering of duplicate frames, and to detect HSR ring errors.
+ */
+
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/rculist.h>
+#include "hsr_main.h"
+#include "hsr_framereg.h"
+#include "hsr_netlink.h"
+
+
+struct node_entry {
+       struct list_head mac_list;
+       unsigned char   MacAddressA[ETH_ALEN];
+       unsigned char   MacAddressB[ETH_ALEN];
+       enum hsr_dev_idx   AddrB_if;    /* The local slave through which AddrB
+                                        * frames are received from this node
+                                        */
+       unsigned long   time_in[HSR_MAX_SLAVE];
+       bool            time_in_stale[HSR_MAX_SLAVE];
+       u16             seq_out[HSR_MAX_DEV];
+       struct rcu_head rcu_head;
+};
+
+/*     TODO: use hash lists for mac addresses (linux/jhash.h)?    */
+
+
+
+/* Search for mac entry. Caller must hold rcu read lock.
+ */
+static struct node_entry *find_node_by_AddrA(struct list_head *node_db,
+                                            const unsigned char addr[ETH_ALEN])
+{
+       struct node_entry *node;
+
+       list_for_each_entry_rcu(node, node_db, mac_list) {
+               if (ether_addr_equal(node->MacAddressA, addr))
+                       return node;
+       }
+
+       return NULL;
+}
+
+
+/* Search for mac entry. Caller must hold rcu read lock.
+ */
+static struct node_entry *find_node_by_AddrB(struct list_head *node_db,
+                                            const unsigned char addr[ETH_ALEN])
+{
+       struct node_entry *node;
+
+       list_for_each_entry_rcu(node, node_db, mac_list) {
+               if (ether_addr_equal(node->MacAddressB, addr))
+                       return node;
+       }
+
+       return NULL;
+}
+
+
+/* Search for mac entry. Caller must hold rcu read lock.
+ */
+struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb)
+{
+       struct node_entry *node;
+       struct ethhdr *ethhdr;
+
+       if (!skb_mac_header_was_set(skb))
+               return NULL;
+
+       ethhdr = (struct ethhdr *) skb_mac_header(skb);
+
+       list_for_each_entry_rcu(node, node_db, mac_list) {
+               if (ether_addr_equal(node->MacAddressA, ethhdr->h_source))
+                       return node;
+               if (ether_addr_equal(node->MacAddressB, ethhdr->h_source))
+                       return node;
+       }
+
+       return NULL;
+}
+
+
+/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
+ * frames from self that's been looped over the HSR ring.
+ */
+int hsr_create_self_node(struct list_head *self_node_db,
+                        unsigned char addr_a[ETH_ALEN],
+                        unsigned char addr_b[ETH_ALEN])
+{
+       struct node_entry *node, *oldnode;
+
+       node = kmalloc(sizeof(*node), GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+
+       memcpy(node->MacAddressA, addr_a, ETH_ALEN);
+       memcpy(node->MacAddressB, addr_b, ETH_ALEN);
+
+       rcu_read_lock();
+       oldnode = list_first_or_null_rcu(self_node_db,
+                                               struct node_entry, mac_list);
+       if (oldnode) {
+               list_replace_rcu(&oldnode->mac_list, &node->mac_list);
+               rcu_read_unlock();
+               synchronize_rcu();
+               kfree(oldnode);
+       } else {
+               rcu_read_unlock();
+               list_add_tail_rcu(&node->mac_list, self_node_db);
+       }
+
+       return 0;
+}
+
+static void node_entry_reclaim(struct rcu_head *rh)
+{
+       kfree(container_of(rh, struct node_entry, rcu_head));
+}
+
+
+/* Add/merge node to the database of nodes. 'skb' must contain an HSR
+ * supervision frame.
+ * - If the supervision header's MacAddressA field is not yet in the database,
+ * this frame is from an hitherto unknown node - add it to the database.
+ * - If the sender's MAC address is not the same as its MacAddressA address,
+ * the node is using PICS_SUBS (address substitution). Record the sender's
+ * address as the node's MacAddressB.
+ *
+ * This function needs to work even if the sender node has changed one of its
+ * slaves' MAC addresses. In this case, there are four different cases described
+ * by (Addr-changed, received-from) pairs as follows. Note that changing the
+ * SlaveA address is equal to changing the node's own address:
+ *
+ * - (AddrB, SlaveB): The new AddrB will be recorded by PICS_SUBS code since
+ *                   node == NULL.
+ * - (AddrB, SlaveA): Will work as usual (the AddrB change won't be detected
+ *                   from this frame).
+ *
+ * - (AddrA, SlaveB): The old node will be found. We need to detect this and
+ *                   remove the node.
+ * - (AddrA, SlaveA): A new node will be registered (non-PICS_SUBS at first).
+ *                   The old one will be pruned after HSR_NODE_FORGET_TIME.
+ *
+ * We also need to detect if the sender's SlaveA and SlaveB cables have been
+ * swapped.
+ */
+struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
+                                 struct node_entry *node,
+                                 struct sk_buff *skb,
+                                 enum hsr_dev_idx dev_idx)
+{
+       struct hsr_sup_payload *hsr_sp;
+       struct hsr_ethhdr_sp *hsr_ethsup;
+       int i;
+       unsigned long now;
+
+       hsr_ethsup = (struct hsr_ethhdr_sp *) skb_mac_header(skb);
+       hsr_sp = (struct hsr_sup_payload *) skb->data;
+
+       if (node && !ether_addr_equal(node->MacAddressA, hsr_sp->MacAddressA)) {
+               /* Node has changed its AddrA, frame was received from SlaveB */
+               list_del_rcu(&node->mac_list);
+               call_rcu(&node->rcu_head, node_entry_reclaim);
+               node = NULL;
+       }
+
+       if (node && (dev_idx == node->AddrB_if) &&
+           !ether_addr_equal(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) {
+               /* Cables have been swapped */
+               list_del_rcu(&node->mac_list);
+               call_rcu(&node->rcu_head, node_entry_reclaim);
+               node = NULL;
+       }
+
+       if (node && (dev_idx != node->AddrB_if) &&
+           (node->AddrB_if != HSR_DEV_NONE) &&
+           !ether_addr_equal(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) {
+               /* Cables have been swapped */
+               list_del_rcu(&node->mac_list);
+               call_rcu(&node->rcu_head, node_entry_reclaim);
+               node = NULL;
+       }
+
+       if (node)
+               return node;
+
+       node = find_node_by_AddrA(&hsr_priv->node_db, hsr_sp->MacAddressA);
+       if (node) {
+               /* Node is known, but frame was received from an unknown
+                * address. Node is PICS_SUBS capable; merge its AddrB.
+                */
+               memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN);
+               node->AddrB_if = dev_idx;
+               return node;
+       }
+
+       node = kzalloc(sizeof(*node), GFP_ATOMIC);
+       if (!node)
+               return NULL;
+
+       memcpy(node->MacAddressA, hsr_sp->MacAddressA, ETH_ALEN);
+       memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN);
+       if (!ether_addr_equal(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source))
+               node->AddrB_if = dev_idx;
+       else
+               node->AddrB_if = HSR_DEV_NONE;
+
+       /* We are only interested in time diffs here, so use current jiffies
+        * as initialization. (0 could trigger an spurious ring error warning).
+        */
+       now = jiffies;
+       for (i = 0; i < HSR_MAX_SLAVE; i++)
+               node->time_in[i] = now;
+       for (i = 0; i < HSR_MAX_DEV; i++)
+               node->seq_out[i] = ntohs(hsr_ethsup->hsr_sup.sequence_nr) - 1;
+
+       list_add_tail_rcu(&node->mac_list, &hsr_priv->node_db);
+
+       return node;
+}
+
+
+/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
+ *
+ * If the frame was sent by a node's B interface, replace the sender
+ * address with that node's "official" address (MacAddressA) so that upper
+ * layers recognize where it came from.
+ */
+void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb)
+{
+       struct ethhdr *ethhdr;
+       struct node_entry *node;
+
+       if (!skb_mac_header_was_set(skb)) {
+               WARN_ONCE(1, "%s: Mac header not set\n", __func__);
+               return;
+       }
+       ethhdr = (struct ethhdr *) skb_mac_header(skb);
+
+       rcu_read_lock();
+       node = find_node_by_AddrB(&hsr_priv->node_db, ethhdr->h_source);
+       if (node)
+               memcpy(ethhdr->h_source, node->MacAddressA, ETH_ALEN);
+       rcu_read_unlock();
+}
+
+
+/* 'skb' is a frame meant for another host.
+ * 'hsr_dev_idx' is the HSR index of the outgoing device
+ *
+ * Substitute the target (dest) MAC address if necessary, so the it matches the
+ * recipient interface MAC address, regardless of whether that is the
+ * recipient's A or B interface.
+ * This is needed to keep the packets flowing through switches that learn on
+ * which "side" the different interfaces are.
+ */
+void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
+                        enum hsr_dev_idx dev_idx)
+{
+       struct node_entry *node;
+
+       rcu_read_lock();
+       node = find_node_by_AddrA(&hsr_priv->node_db, ethhdr->h_dest);
+       if (node && (node->AddrB_if == dev_idx))
+               memcpy(ethhdr->h_dest, node->MacAddressB, ETH_ALEN);
+       rcu_read_unlock();
+}
+
+
+/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
+ * false otherwise.
+ */
+static bool seq_nr_after(u16 a, u16 b)
+{
+       /* Remove inconsistency where
+        * seq_nr_after(a, b) == seq_nr_before(a, b) */
+       if ((int) b - a == 32768)
+               return false;
+
+       return (((s16) (b - a)) < 0);
+}
+#define seq_nr_before(a, b)            seq_nr_after((b), (a))
+#define seq_nr_after_or_eq(a, b)       (!seq_nr_before((a), (b)))
+#define seq_nr_before_or_eq(a, b)      (!seq_nr_after((a), (b)))
+
+
+void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx)
+{
+       if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
+               WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx);
+               return;
+       }
+       node->time_in[dev_idx] = jiffies;
+       node->time_in_stale[dev_idx] = false;
+}
+
+
+/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
+ * ethhdr->h_source address and skb->mac_header set.
+ *
+ * Return:
+ *      1 if frame can be shown to have been sent recently on this interface,
+ *      0 otherwise, or
+ *      negative error code on error
+ */
+int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
+                          struct sk_buff *skb)
+{
+       struct hsr_ethhdr *hsr_ethhdr;
+       u16 sequence_nr;
+
+       if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
+               WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx);
+               return -EINVAL;
+       }
+       if (!skb_mac_header_was_set(skb)) {
+               WARN_ONCE(1, "%s: Mac header not set\n", __func__);
+               return -EINVAL;
+       }
+       hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
+
+       sequence_nr = ntohs(hsr_ethhdr->hsr_tag.sequence_nr);
+       if (seq_nr_before_or_eq(sequence_nr, node->seq_out[dev_idx]))
+               return 1;
+
+       node->seq_out[dev_idx] = sequence_nr;
+       return 0;
+}
+
+
+
+static bool is_late(struct node_entry *node, enum hsr_dev_idx dev_idx)
+{
+       enum hsr_dev_idx other;
+
+       if (node->time_in_stale[dev_idx])
+               return true;
+
+       if (dev_idx == HSR_DEV_SLAVE_A)
+               other = HSR_DEV_SLAVE_B;
+       else
+               other = HSR_DEV_SLAVE_A;
+
+       if (node->time_in_stale[other])
+               return false;
+
+       if (time_after(node->time_in[other], node->time_in[dev_idx] +
+                      msecs_to_jiffies(MAX_SLAVE_DIFF)))
+               return true;
+
+       return false;
+}
+
+
+/* Remove stale sequence_nr records. Called by timer every
+ * HSR_LIFE_CHECK_INTERVAL (two seconds or so).
+ */
+void hsr_prune_nodes(struct hsr_priv *hsr_priv)
+{
+       struct node_entry *node;
+       unsigned long timestamp;
+       unsigned long time_a, time_b;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(node, &hsr_priv->node_db, mac_list) {
+               /* Shorthand */
+               time_a = node->time_in[HSR_DEV_SLAVE_A];
+               time_b = node->time_in[HSR_DEV_SLAVE_B];
+
+               /* Check for timestamps old enough to risk wrap-around */
+               if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET/2))
+                       node->time_in_stale[HSR_DEV_SLAVE_A] = true;
+               if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET/2))
+                       node->time_in_stale[HSR_DEV_SLAVE_B] = true;
+
+               /* Get age of newest frame from node.
+                * At least one time_in is OK here; nodes get pruned long
+                * before both time_ins can get stale
+                */
+               timestamp = time_a;
+               if (node->time_in_stale[HSR_DEV_SLAVE_A] ||
+                   (!node->time_in_stale[HSR_DEV_SLAVE_B] &&
+                   time_after(time_b, time_a)))
+                       timestamp = time_b;
+
+               /* Warn of ring error only as long as we get frames at all */
+               if (time_is_after_jiffies(timestamp +
+                                       msecs_to_jiffies(1.5*MAX_SLAVE_DIFF))) {
+
+                       if (is_late(node, HSR_DEV_SLAVE_A))
+                               hsr_nl_ringerror(hsr_priv, node->MacAddressA,
+                                                HSR_DEV_SLAVE_A);
+                       else if (is_late(node, HSR_DEV_SLAVE_B))
+                               hsr_nl_ringerror(hsr_priv, node->MacAddressA,
+                                                HSR_DEV_SLAVE_B);
+               }
+
+               /* Prune old entries */
+               if (time_is_before_jiffies(timestamp +
+                                       msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
+                       hsr_nl_nodedown(hsr_priv, node->MacAddressA);
+                       list_del_rcu(&node->mac_list);
+                       /* Note that we need to free this entry later: */
+                       call_rcu(&node->rcu_head, node_entry_reclaim);
+               }
+       }
+       rcu_read_unlock();
+}
+
+
+void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
+                       unsigned char addr[ETH_ALEN])
+{
+       struct node_entry *node;
+
+       if (!_pos) {
+               node = list_first_or_null_rcu(&hsr_priv->node_db,
+                                               struct node_entry, mac_list);
+               if (node)
+                       memcpy(addr, node->MacAddressA, ETH_ALEN);
+               return node;
+       }
+
+       node = _pos;
+       list_for_each_entry_continue_rcu(node, &hsr_priv->node_db, mac_list) {
+               memcpy(addr, node->MacAddressA, ETH_ALEN);
+               return node;
+       }
+
+       return NULL;
+}
+
+
+int hsr_get_node_data(struct hsr_priv *hsr_priv,
+                     const unsigned char *addr,
+                     unsigned char addr_b[ETH_ALEN],
+                     unsigned int *addr_b_ifindex,
+                     int *if1_age,
+                     u16 *if1_seq,
+                     int *if2_age,
+                     u16 *if2_seq)
+{
+       struct node_entry *node;
+       unsigned long tdiff;
+
+
+       rcu_read_lock();
+       node = find_node_by_AddrA(&hsr_priv->node_db, addr);
+       if (!node) {
+               rcu_read_unlock();
+               return -ENOENT; /* No such entry */
+       }
+
+       memcpy(addr_b, node->MacAddressB, ETH_ALEN);
+
+       tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_A];
+       if (node->time_in_stale[HSR_DEV_SLAVE_A])
+               *if1_age = INT_MAX;
+#if HZ <= MSEC_PER_SEC
+       else if (tdiff > msecs_to_jiffies(INT_MAX))
+               *if1_age = INT_MAX;
+#endif
+       else
+               *if1_age = jiffies_to_msecs(tdiff);
+
+       tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_B];
+       if (node->time_in_stale[HSR_DEV_SLAVE_B])
+               *if2_age = INT_MAX;
+#if HZ <= MSEC_PER_SEC
+       else if (tdiff > msecs_to_jiffies(INT_MAX))
+               *if2_age = INT_MAX;
+#endif
+       else
+               *if2_age = jiffies_to_msecs(tdiff);
+
+       /* Present sequence numbers as if they were incoming on interface */
+       *if1_seq = node->seq_out[HSR_DEV_SLAVE_B];
+       *if2_seq = node->seq_out[HSR_DEV_SLAVE_A];
+
+       if ((node->AddrB_if != HSR_DEV_NONE) && hsr_priv->slave[node->AddrB_if])
+               *addr_b_ifindex = hsr_priv->slave[node->AddrB_if]->ifindex;
+       else
+               *addr_b_ifindex = -1;
+
+       rcu_read_unlock();
+
+       return 0;
+}
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
new file mode 100644 (file)
index 0000000..e6c4022
--- /dev/null
@@ -0,0 +1,53 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef _HSR_FRAMEREG_H
+#define _HSR_FRAMEREG_H
+
+#include "hsr_main.h"
+
+struct node_entry;
+
+struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb);
+
+struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
+                                 struct node_entry *node,
+                                 struct sk_buff *skb,
+                                 enum hsr_dev_idx dev_idx);
+
+void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb);
+void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
+                        enum hsr_dev_idx dev_idx);
+
+void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx);
+
+int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
+                          struct sk_buff *skb);
+
+void hsr_prune_nodes(struct hsr_priv *hsr_priv);
+
+int hsr_create_self_node(struct list_head *self_node_db,
+                        unsigned char addr_a[ETH_ALEN],
+                        unsigned char addr_b[ETH_ALEN]);
+
+void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
+                       unsigned char addr[ETH_ALEN]);
+
+int hsr_get_node_data(struct hsr_priv *hsr_priv,
+                     const unsigned char *addr,
+                     unsigned char addr_b[ETH_ALEN],
+                     unsigned int *addr_b_ifindex,
+                     int *if1_age,
+                     u16 *if1_seq,
+                     int *if2_age,
+                     u16 *if2_seq);
+
+#endif /* _HSR_FRAMEREG_H */
diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c
new file mode 100644 (file)
index 0000000..af68dd8
--- /dev/null
@@ -0,0 +1,469 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * In addition to routines for registering and unregistering HSR support, this
+ * file also contains the receive routine that handles all incoming frames with
+ * Ethertype (protocol) ETH_P_PRP (HSRv0), and network device event handling.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/rculist.h>
+#include <linux/timer.h>
+#include <linux/etherdevice.h>
+#include "hsr_main.h"
+#include "hsr_device.h"
+#include "hsr_netlink.h"
+#include "hsr_framereg.h"
+
+
+/* List of all registered virtual HSR devices */
+static LIST_HEAD(hsr_list);
+
+void register_hsr_master(struct hsr_priv *hsr_priv)
+{
+       list_add_tail_rcu(&hsr_priv->hsr_list, &hsr_list);
+}
+
+void unregister_hsr_master(struct hsr_priv *hsr_priv)
+{
+       struct hsr_priv *hsr_priv_it;
+
+       list_for_each_entry(hsr_priv_it, &hsr_list, hsr_list)
+               if (hsr_priv_it == hsr_priv) {
+                       list_del_rcu(&hsr_priv_it->hsr_list);
+                       return;
+               }
+}
+
+bool is_hsr_slave(struct net_device *dev)
+{
+       struct hsr_priv *hsr_priv_it;
+
+       list_for_each_entry_rcu(hsr_priv_it, &hsr_list, hsr_list) {
+               if (dev == hsr_priv_it->slave[0])
+                       return true;
+               if (dev == hsr_priv_it->slave[1])
+                       return true;
+       }
+
+       return false;
+}
+
+
+/* If dev is a HSR slave device, return the virtual master device. Return NULL
+ * otherwise.
+ */
+static struct hsr_priv *get_hsr_master(struct net_device *dev)
+{
+       struct hsr_priv *hsr_priv;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
+               if ((dev == hsr_priv->slave[0]) ||
+                   (dev == hsr_priv->slave[1])) {
+                       rcu_read_unlock();
+                       return hsr_priv;
+               }
+
+       rcu_read_unlock();
+       return NULL;
+}
+
+
+/* If dev is a HSR slave device, return the other slave device. Return NULL
+ * otherwise.
+ */
+static struct net_device *get_other_slave(struct hsr_priv *hsr_priv,
+                                         struct net_device *dev)
+{
+       if (dev == hsr_priv->slave[0])
+               return hsr_priv->slave[1];
+       if (dev == hsr_priv->slave[1])
+               return hsr_priv->slave[0];
+
+       return NULL;
+}
+
+
+static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
+                            void *ptr)
+{
+       struct net_device *slave, *other_slave;
+       struct hsr_priv *hsr_priv;
+       int old_operstate;
+       int mtu_max;
+       int res;
+       struct net_device *dev;
+
+       dev = netdev_notifier_info_to_dev(ptr);
+
+       hsr_priv = get_hsr_master(dev);
+       if (hsr_priv) {
+               /* dev is a slave device */
+               slave = dev;
+               other_slave = get_other_slave(hsr_priv, slave);
+       } else {
+               if (!is_hsr_master(dev))
+                       return NOTIFY_DONE;
+               hsr_priv = netdev_priv(dev);
+               slave = hsr_priv->slave[0];
+               other_slave = hsr_priv->slave[1];
+       }
+
+       switch (event) {
+       case NETDEV_UP:         /* Administrative state DOWN */
+       case NETDEV_DOWN:       /* Administrative state UP */
+       case NETDEV_CHANGE:     /* Link (carrier) state changes */
+               old_operstate = hsr_priv->dev->operstate;
+               hsr_set_carrier(hsr_priv->dev, slave, other_slave);
+               /* netif_stacked_transfer_operstate() cannot be used here since
+                * it doesn't set IF_OPER_LOWERLAYERDOWN (?)
+                */
+               hsr_set_operstate(hsr_priv->dev, slave, other_slave);
+               hsr_check_announce(hsr_priv->dev, old_operstate);
+               break;
+       case NETDEV_CHANGEADDR:
+
+               /* This should not happen since there's no ndo_set_mac_address()
+                * for HSR devices - i.e. not supported.
+                */
+               if (dev == hsr_priv->dev)
+                       break;
+
+               if (dev == hsr_priv->slave[0])
+                       memcpy(hsr_priv->dev->dev_addr,
+                              hsr_priv->slave[0]->dev_addr, ETH_ALEN);
+
+               /* Make sure we recognize frames from ourselves in hsr_rcv() */
+               res = hsr_create_self_node(&hsr_priv->self_node_db,
+                                          hsr_priv->dev->dev_addr,
+                                          hsr_priv->slave[1] ?
+                                               hsr_priv->slave[1]->dev_addr :
+                                               hsr_priv->dev->dev_addr);
+               if (res)
+                       netdev_warn(hsr_priv->dev,
+                                   "Could not update HSR node address.\n");
+
+               if (dev == hsr_priv->slave[0])
+                       call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr_priv->dev);
+               break;
+       case NETDEV_CHANGEMTU:
+               if (dev == hsr_priv->dev)
+                       break; /* Handled in ndo_change_mtu() */
+               mtu_max = hsr_get_max_mtu(hsr_priv);
+               if (hsr_priv->dev->mtu > mtu_max)
+                       dev_set_mtu(hsr_priv->dev, mtu_max);
+               break;
+       case NETDEV_UNREGISTER:
+               if (dev == hsr_priv->slave[0])
+                       hsr_priv->slave[0] = NULL;
+               if (dev == hsr_priv->slave[1])
+                       hsr_priv->slave[1] = NULL;
+
+               /* There should really be a way to set a new slave device... */
+
+               break;
+       case NETDEV_PRE_TYPE_CHANGE:
+               /* HSR works only on Ethernet devices. Refuse slave to change
+                * its type.
+                */
+               return NOTIFY_BAD;
+       }
+
+       return NOTIFY_DONE;
+}
+
+
+static struct timer_list prune_timer;
+
+static void prune_nodes_all(unsigned long data)
+{
+       struct hsr_priv *hsr_priv;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
+               hsr_prune_nodes(hsr_priv);
+       rcu_read_unlock();
+
+       prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
+       add_timer(&prune_timer);
+}
+
+
+static struct sk_buff *hsr_pull_tag(struct sk_buff *skb)
+{
+       struct hsr_tag *hsr_tag;
+       struct sk_buff *skb2;
+
+       skb2 = skb_share_check(skb, GFP_ATOMIC);
+       if (unlikely(!skb2))
+               goto err_free;
+       skb = skb2;
+
+       if (unlikely(!pskb_may_pull(skb, HSR_TAGLEN)))
+               goto err_free;
+
+       hsr_tag = (struct hsr_tag *) skb->data;
+       skb->protocol = hsr_tag->encap_proto;
+       skb_pull(skb, HSR_TAGLEN);
+
+       return skb;
+
+err_free:
+       kfree_skb(skb);
+       return NULL;
+}
+
+
+/* The uses I can see for these HSR supervision frames are:
+ * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type =
+ *    22") to reset any sequence_nr counters belonging to that node. Useful if
+ *    the other node's counter has been reset for some reason.
+ *    --
+ *    Or not - resetting the counter and bridging the frame would create a
+ *    loop, unfortunately.
+ *
+ * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck
+ *    frame is received from a particular node, we know something is wrong.
+ *    We just register these (as with normal frames) and throw them away.
+ *
+ * 3) Allow different MAC addresses for the two slave interfaces, using the
+ *    MacAddressA field.
+ */
+static bool is_supervision_frame(struct hsr_priv *hsr_priv, struct sk_buff *skb)
+{
+       struct hsr_sup_tag *hsr_stag;
+
+       if (!ether_addr_equal(eth_hdr(skb)->h_dest,
+                             hsr_priv->sup_multicast_addr))
+               return false;
+
+       hsr_stag = (struct hsr_sup_tag *) skb->data;
+       if (get_hsr_stag_path(hsr_stag) != 0x0f)
+               return false;
+       if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
+           (hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
+               return false;
+       if (hsr_stag->HSR_TLV_Length != 12)
+               return false;
+
+       return true;
+}
+
+
+/* Implementation somewhat according to IEC-62439-3, p. 43
+ */
+static int hsr_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct hsr_priv *hsr_priv;
+       struct net_device *other_slave;
+       struct node_entry *node;
+       bool deliver_to_self;
+       struct sk_buff *skb_deliver;
+       enum hsr_dev_idx dev_in_idx, dev_other_idx;
+       bool dup_out;
+       int ret;
+
+       hsr_priv = get_hsr_master(dev);
+
+       if (!hsr_priv) {
+               /* Non-HSR-slave device 'dev' is connected to a HSR network */
+               kfree_skb(skb);
+               dev->stats.rx_errors++;
+               return NET_RX_SUCCESS;
+       }
+
+       if (dev == hsr_priv->slave[0]) {
+               dev_in_idx = HSR_DEV_SLAVE_A;
+               dev_other_idx = HSR_DEV_SLAVE_B;
+       } else {
+               dev_in_idx = HSR_DEV_SLAVE_B;
+               dev_other_idx = HSR_DEV_SLAVE_A;
+       }
+
+       node = hsr_find_node(&hsr_priv->self_node_db, skb);
+       if (node) {
+               /* Always kill frames sent by ourselves */
+               kfree_skb(skb);
+               return NET_RX_SUCCESS;
+       }
+
+       /* Is this frame a candidate for local reception? */
+       deliver_to_self = false;
+       if ((skb->pkt_type == PACKET_HOST) ||
+           (skb->pkt_type == PACKET_MULTICAST) ||
+           (skb->pkt_type == PACKET_BROADCAST))
+               deliver_to_self = true;
+       else if (ether_addr_equal(eth_hdr(skb)->h_dest,
+                                    hsr_priv->dev->dev_addr)) {
+               skb->pkt_type = PACKET_HOST;
+               deliver_to_self = true;
+       }
+
+
+       rcu_read_lock(); /* node_db */
+       node = hsr_find_node(&hsr_priv->node_db, skb);
+
+       if (is_supervision_frame(hsr_priv, skb)) {
+               skb_pull(skb, sizeof(struct hsr_sup_tag));
+               node = hsr_merge_node(hsr_priv, node, skb, dev_in_idx);
+               if (!node) {
+                       rcu_read_unlock(); /* node_db */
+                       kfree_skb(skb);
+                       hsr_priv->dev->stats.rx_dropped++;
+                       return NET_RX_DROP;
+               }
+               skb_push(skb, sizeof(struct hsr_sup_tag));
+               deliver_to_self = false;
+       }
+
+       if (!node) {
+               /* Source node unknown; this might be a HSR frame from
+                * another net (different multicast address). Ignore it.
+                */
+               rcu_read_unlock(); /* node_db */
+               kfree_skb(skb);
+               return NET_RX_SUCCESS;
+       }
+
+       /* Register ALL incoming frames as outgoing through the other interface.
+        * This allows us to register frames as incoming only if they are valid
+        * for the receiving interface, without using a specific counter for
+        * incoming frames.
+        */
+       dup_out = hsr_register_frame_out(node, dev_other_idx, skb);
+       if (!dup_out)
+               hsr_register_frame_in(node, dev_in_idx);
+
+       /* Forward this frame? */
+       if (!dup_out && (skb->pkt_type != PACKET_HOST))
+               other_slave = get_other_slave(hsr_priv, dev);
+       else
+               other_slave = NULL;
+
+       if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb))
+               deliver_to_self = false;
+
+       rcu_read_unlock(); /* node_db */
+
+       if (!deliver_to_self && !other_slave) {
+               kfree_skb(skb);
+               /* Circulated frame; silently remove it. */
+               return NET_RX_SUCCESS;
+       }
+
+       skb_deliver = skb;
+       if (deliver_to_self && other_slave) {
+               /* skb_clone() is not enough since we will strip the hsr tag
+                * and do address substitution below
+                */
+               skb_deliver = pskb_copy(skb, GFP_ATOMIC);
+               if (!skb_deliver) {
+                       deliver_to_self = false;
+                       hsr_priv->dev->stats.rx_dropped++;
+               }
+       }
+
+       if (deliver_to_self) {
+               bool multicast_frame;
+
+               skb_deliver = hsr_pull_tag(skb_deliver);
+               if (!skb_deliver) {
+                       hsr_priv->dev->stats.rx_dropped++;
+                       goto forward;
+               }
+#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+               /* Move everything in the header that is after the HSR tag,
+                * to work around alignment problems caused by the 6-byte HSR
+                * tag. In practice, this removes/overwrites the HSR tag in
+                * the header and restores a "standard" packet.
+                */
+               memmove(skb_deliver->data - HSR_TAGLEN, skb_deliver->data,
+                       skb_headlen(skb_deliver));
+
+               /* Adjust skb members so they correspond with the move above.
+                * This cannot possibly underflow skb->data since hsr_pull_tag()
+                * above succeeded.
+                * At this point in the protocol stack, the transport and
+                * network headers have not been set yet, and we haven't touched
+                * the mac header nor the head. So we only need to adjust data
+                * and tail:
+                */
+               skb_deliver->data -= HSR_TAGLEN;
+               skb_deliver->tail -= HSR_TAGLEN;
+#endif
+               skb_deliver->dev = hsr_priv->dev;
+               hsr_addr_subst_source(hsr_priv, skb_deliver);
+               multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST);
+               ret = netif_rx(skb_deliver);
+               if (ret == NET_RX_DROP) {
+                       hsr_priv->dev->stats.rx_dropped++;
+               } else {
+                       hsr_priv->dev->stats.rx_packets++;
+                       hsr_priv->dev->stats.rx_bytes += skb->len;
+                       if (multicast_frame)
+                               hsr_priv->dev->stats.multicast++;
+               }
+       }
+
+forward:
+       if (other_slave) {
+               skb_push(skb, ETH_HLEN);
+               skb->dev = other_slave;
+               dev_queue_xmit(skb);
+       }
+
+       return NET_RX_SUCCESS;
+}
+
+
+static struct packet_type hsr_pt __read_mostly = {
+       .type = htons(ETH_P_PRP),
+       .func = hsr_rcv,
+};
+
+static struct notifier_block hsr_nb = {
+       .notifier_call = hsr_netdev_notify,     /* Slave event notifications */
+};
+
+
+static int __init hsr_init(void)
+{
+       int res;
+
+       BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN);
+
+       dev_add_pack(&hsr_pt);
+
+       init_timer(&prune_timer);
+       prune_timer.function = prune_nodes_all;
+       prune_timer.data = 0;
+       prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
+       add_timer(&prune_timer);
+
+       register_netdevice_notifier(&hsr_nb);
+
+       res = hsr_netlink_init();
+
+       return res;
+}
+
+static void __exit hsr_exit(void)
+{
+       unregister_netdevice_notifier(&hsr_nb);
+       del_timer(&prune_timer);
+       hsr_netlink_exit();
+       dev_remove_pack(&hsr_pt);
+}
+
+module_init(hsr_init);
+module_exit(hsr_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h
new file mode 100644 (file)
index 0000000..56fe060
--- /dev/null
@@ -0,0 +1,166 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef _HSR_PRIVATE_H
+#define _HSR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/list.h>
+
+
+/* Time constants as specified in the HSR specification (IEC-62439-3 2010)
+ * Table 8.
+ * All values in milliseconds.
+ */
+#define HSR_LIFE_CHECK_INTERVAL                 2000 /* ms */
+#define HSR_NODE_FORGET_TIME           60000 /* ms */
+#define HSR_ANNOUNCE_INTERVAL            100 /* ms */
+
+
+/* By how much may slave1 and slave2 timestamps of latest received frame from
+ * each node differ before we notify of communication problem?
+ */
+#define MAX_SLAVE_DIFF                  3000 /* ms */
+
+
+/* How often shall we check for broken ring and remove node entries older than
+ * HSR_NODE_FORGET_TIME?
+ */
+#define PRUNE_PERIOD                    3000 /* ms */
+
+
+#define HSR_TLV_ANNOUNCE                  22
+#define HSR_TLV_LIFE_CHECK                23
+
+
+/* HSR Tag.
+ * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
+ * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest,
+ * h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr,
+ * encapsulated protocol } instead.
+ */
+#define HSR_TAGLEN     6
+
+/* Field names below as defined in the IEC:2010 standard for HSR. */
+struct hsr_tag {
+       __be16          path_and_LSDU_size;
+       __be16          sequence_nr;
+       __be16          encap_proto;
+} __packed;
+
+
+/* The helper functions below assumes that 'path' occupies the 4 most
+ * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
+ * equivalently, the 4 most significant bits of HSR tag byte 14).
+ *
+ * This is unclear in the IEC specification; its definition of MAC addresses
+ * indicates the spec is written with the least significant bit first (to the
+ * left). This, however, would mean that the LSDU field would be split in two
+ * with the path field in-between, which seems strange. I'm guessing the MAC
+ * address definition is in error.
+ */
+static inline u16 get_hsr_tag_path(struct hsr_tag *ht)
+{
+       return ntohs(ht->path_and_LSDU_size) >> 12;
+}
+
+static inline u16 get_hsr_tag_LSDU_size(struct hsr_tag *ht)
+{
+       return ntohs(ht->path_and_LSDU_size) & 0x0FFF;
+}
+
+static inline void set_hsr_tag_path(struct hsr_tag *ht, u16 path)
+{
+       ht->path_and_LSDU_size = htons(
+                       (ntohs(ht->path_and_LSDU_size) & 0x0FFF) | (path << 12));
+}
+
+static inline void set_hsr_tag_LSDU_size(struct hsr_tag *ht, u16 LSDU_size)
+{
+       ht->path_and_LSDU_size = htons(
+                       (ntohs(ht->path_and_LSDU_size) & 0xF000) |
+                       (LSDU_size & 0x0FFF));
+}
+
+struct hsr_ethhdr {
+       struct ethhdr   ethhdr;
+       struct hsr_tag  hsr_tag;
+} __packed;
+
+
+/* HSR Supervision Frame data types.
+ * Field names as defined in the IEC:2010 standard for HSR.
+ */
+struct hsr_sup_tag {
+       __be16          path_and_HSR_Ver;
+       __be16          sequence_nr;
+       __u8            HSR_TLV_Type;
+       __u8            HSR_TLV_Length;
+} __packed;
+
+struct hsr_sup_payload {
+       unsigned char   MacAddressA[ETH_ALEN];
+} __packed;
+
+static inline u16 get_hsr_stag_path(struct hsr_sup_tag *hst)
+{
+       return get_hsr_tag_path((struct hsr_tag *) hst);
+}
+
+static inline u16 get_hsr_stag_HSR_ver(struct hsr_sup_tag *hst)
+{
+       return get_hsr_tag_LSDU_size((struct hsr_tag *) hst);
+}
+
+static inline void set_hsr_stag_path(struct hsr_sup_tag *hst, u16 path)
+{
+       set_hsr_tag_path((struct hsr_tag *) hst, path);
+}
+
+static inline void set_hsr_stag_HSR_Ver(struct hsr_sup_tag *hst, u16 HSR_Ver)
+{
+       set_hsr_tag_LSDU_size((struct hsr_tag *) hst, HSR_Ver);
+}
+
+struct hsr_ethhdr_sp {
+       struct ethhdr           ethhdr;
+       struct hsr_sup_tag      hsr_sup;
+} __packed;
+
+
+enum hsr_dev_idx {
+       HSR_DEV_NONE = -1,
+       HSR_DEV_SLAVE_A = 0,
+       HSR_DEV_SLAVE_B,
+       HSR_DEV_MASTER,
+};
+#define HSR_MAX_SLAVE  (HSR_DEV_SLAVE_B + 1)
+#define HSR_MAX_DEV    (HSR_DEV_MASTER + 1)
+
+struct hsr_priv {
+       struct list_head        hsr_list;       /* List of hsr devices */
+       struct rcu_head         rcu_head;
+       struct net_device       *dev;
+       struct net_device       *slave[HSR_MAX_SLAVE];
+       struct list_head        node_db;        /* Other HSR nodes */
+       struct list_head        self_node_db;   /* MACs of slaves */
+       struct timer_list       announce_timer; /* Supervision frame dispatch */
+       int announce_count;
+       u16 sequence_nr;
+       spinlock_t seqnr_lock;                  /* locking for sequence_nr */
+       unsigned char           sup_multicast_addr[ETH_ALEN];
+};
+
+void register_hsr_master(struct hsr_priv *hsr_priv);
+void unregister_hsr_master(struct hsr_priv *hsr_priv);
+bool is_hsr_slave(struct net_device *dev);
+
+#endif /*  _HSR_PRIVATE_H */
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
new file mode 100644 (file)
index 0000000..4e66bf6
--- /dev/null
@@ -0,0 +1,457 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ *
+ * Routines for handling Netlink messages for HSR.
+ */
+
+#include "hsr_netlink.h"
+#include <linux/kernel.h>
+#include <net/rtnetlink.h>
+#include <net/genetlink.h>
+#include "hsr_main.h"
+#include "hsr_device.h"
+#include "hsr_framereg.h"
+
+static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = {
+       [IFLA_HSR_SLAVE1]               = { .type = NLA_U32 },
+       [IFLA_HSR_SLAVE2]               = { .type = NLA_U32 },
+       [IFLA_HSR_MULTICAST_SPEC]       = { .type = NLA_U8 },
+};
+
+
+/* Here, it seems a netdevice has already been allocated for us, and the
+ * hsr_dev_setup routine has been executed. Nice!
+ */
+static int hsr_newlink(struct net *src_net, struct net_device *dev,
+                      struct nlattr *tb[], struct nlattr *data[])
+{
+       struct net_device *link[2];
+       unsigned char multicast_spec;
+
+       if (!data[IFLA_HSR_SLAVE1]) {
+               netdev_info(dev, "IFLA_HSR_SLAVE1 missing!\n");
+               return -EINVAL;
+       }
+       link[0] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE1]));
+       if (!data[IFLA_HSR_SLAVE2]) {
+               netdev_info(dev, "IFLA_HSR_SLAVE2 missing!\n");
+               return -EINVAL;
+       }
+       link[1] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE2]));
+
+       if (!link[0] || !link[1])
+               return -ENODEV;
+       if (link[0] == link[1])
+               return -EINVAL;
+
+       if (!data[IFLA_HSR_MULTICAST_SPEC])
+               multicast_spec = 0;
+       else
+               multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]);
+
+       return hsr_dev_finalize(dev, link, multicast_spec);
+}
+
+static struct rtnl_link_ops hsr_link_ops __read_mostly = {
+       .kind           = "hsr",
+       .maxtype        = IFLA_HSR_MAX,
+       .policy         = hsr_policy,
+       .priv_size      = sizeof(struct hsr_priv),
+       .setup          = hsr_dev_setup,
+       .newlink        = hsr_newlink,
+};
+
+
+
+/* attribute policy */
+/* NLA_BINARY missing in libnl; use NLA_UNSPEC in userspace instead. */
+static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = {
+       [HSR_A_NODE_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN },
+       [HSR_A_NODE_ADDR_B] = { .type = NLA_BINARY, .len = ETH_ALEN },
+       [HSR_A_IFINDEX] = { .type = NLA_U32 },
+       [HSR_A_IF1_AGE] = { .type = NLA_U32 },
+       [HSR_A_IF2_AGE] = { .type = NLA_U32 },
+       [HSR_A_IF1_SEQ] = { .type = NLA_U16 },
+       [HSR_A_IF2_SEQ] = { .type = NLA_U16 },
+};
+
+static struct genl_family hsr_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = 0,
+       .name = "HSR",
+       .version = 1,
+       .maxattr = HSR_A_MAX,
+};
+
+static struct genl_multicast_group hsr_network_genl_mcgrp = {
+       .name = "hsr-network",
+};
+
+
+
+/* This is called if for some node with MAC address addr, we only get frames
+ * over one of the slave interfaces. This would indicate an open network ring
+ * (i.e. a link has failed somewhere).
+ */
+void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+                     enum hsr_dev_idx dev_idx)
+{
+       struct sk_buff *skb;
+       void *msg_head;
+       int res;
+       int ifindex;
+
+       skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       if (!skb)
+               goto fail;
+
+       msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_RING_ERROR);
+       if (!msg_head)
+               goto nla_put_failure;
+
+       res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+       if (res < 0)
+               goto nla_put_failure;
+
+       if (hsr_priv->slave[dev_idx])
+               ifindex = hsr_priv->slave[dev_idx]->ifindex;
+       else
+               ifindex = -1;
+       res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex);
+       if (res < 0)
+               goto nla_put_failure;
+
+       genlmsg_end(skb, msg_head);
+       genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+
+       return;
+
+nla_put_failure:
+       kfree_skb(skb);
+
+fail:
+       netdev_warn(hsr_priv->dev, "Could not send HSR ring error message\n");
+}
+
+/* This is called when we haven't heard from the node with MAC address addr for
+ * some time (just before the node is removed from the node table/list).
+ */
+void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN])
+{
+       struct sk_buff *skb;
+       void *msg_head;
+       int res;
+
+       skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       if (!skb)
+               goto fail;
+
+       msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN);
+       if (!msg_head)
+               goto nla_put_failure;
+
+
+       res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+       if (res < 0)
+               goto nla_put_failure;
+
+       genlmsg_end(skb, msg_head);
+       genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC);
+
+       return;
+
+nla_put_failure:
+       kfree_skb(skb);
+
+fail:
+       netdev_warn(hsr_priv->dev, "Could not send HSR node down\n");
+}
+
+
+/* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table
+ * about the status of a specific node in the network, defined by its MAC
+ * address.
+ *
+ * Input: hsr ifindex, node mac address
+ * Output: hsr ifindex, node mac address (copied from request),
+ *        age of latest frame from node over slave 1, slave 2 [ms]
+ */
+static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
+{
+       /* For receiving */
+       struct nlattr *na;
+       struct net_device *hsr_dev;
+
+       /* For sending */
+       struct sk_buff *skb_out;
+       void *msg_head;
+       struct hsr_priv *hsr_priv;
+       unsigned char hsr_node_addr_b[ETH_ALEN];
+       int hsr_node_if1_age;
+       u16 hsr_node_if1_seq;
+       int hsr_node_if2_age;
+       u16 hsr_node_if2_seq;
+       int addr_b_ifindex;
+       int res;
+
+       if (!info)
+               goto invalid;
+
+       na = info->attrs[HSR_A_IFINDEX];
+       if (!na)
+               goto invalid;
+       na = info->attrs[HSR_A_NODE_ADDR];
+       if (!na)
+               goto invalid;
+
+       hsr_dev = __dev_get_by_index(genl_info_net(info),
+                                       nla_get_u32(info->attrs[HSR_A_IFINDEX]));
+       if (!hsr_dev)
+               goto invalid;
+       if (!is_hsr_master(hsr_dev))
+               goto invalid;
+
+
+       /* Send reply */
+
+       skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb_out) {
+               res = -ENOMEM;
+               goto fail;
+       }
+
+       msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
+                               info->snd_seq, &hsr_genl_family, 0,
+                               HSR_C_SET_NODE_STATUS);
+       if (!msg_head) {
+               res = -ENOMEM;
+               goto nla_put_failure;
+       }
+
+       res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
+       if (res < 0)
+               goto nla_put_failure;
+
+       hsr_priv = netdev_priv(hsr_dev);
+       res = hsr_get_node_data(hsr_priv,
+                       (unsigned char *) nla_data(info->attrs[HSR_A_NODE_ADDR]),
+                       hsr_node_addr_b,
+                       &addr_b_ifindex,
+                       &hsr_node_if1_age,
+                       &hsr_node_if1_seq,
+                       &hsr_node_if2_age,
+                       &hsr_node_if2_seq);
+       if (res < 0)
+               goto fail;
+
+       res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN,
+                                       nla_data(info->attrs[HSR_A_NODE_ADDR]));
+       if (res < 0)
+               goto nla_put_failure;
+
+       if (addr_b_ifindex > -1) {
+               res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN,
+                                                               hsr_node_addr_b);
+               if (res < 0)
+                       goto nla_put_failure;
+
+               res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, addr_b_ifindex);
+               if (res < 0)
+                       goto nla_put_failure;
+       }
+
+       res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age);
+       if (res < 0)
+               goto nla_put_failure;
+       res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq);
+       if (res < 0)
+               goto nla_put_failure;
+       if (hsr_priv->slave[0])
+               res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX,
+                                               hsr_priv->slave[0]->ifindex);
+       if (res < 0)
+               goto nla_put_failure;
+
+       res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age);
+       if (res < 0)
+               goto nla_put_failure;
+       res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq);
+       if (res < 0)
+               goto nla_put_failure;
+       if (hsr_priv->slave[1])
+               res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX,
+                                               hsr_priv->slave[1]->ifindex);
+
+       genlmsg_end(skb_out, msg_head);
+       genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
+
+       return 0;
+
+invalid:
+       netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
+       return 0;
+
+nla_put_failure:
+       kfree_skb(skb_out);
+       /* Fall through */
+
+fail:
+       return res;
+}
+
+static struct genl_ops hsr_ops_get_node_status = {
+       .cmd = HSR_C_GET_NODE_STATUS,
+       .flags = 0,
+       .policy = hsr_genl_policy,
+       .doit = hsr_get_node_status,
+       .dumpit = NULL,
+};
+
+
+/* Get a list of MacAddressA of all nodes known to this node (other than self).
+ */
+static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
+{
+       /* For receiving */
+       struct nlattr *na;
+       struct net_device *hsr_dev;
+
+       /* For sending */
+       struct sk_buff *skb_out;
+       void *msg_head;
+       struct hsr_priv *hsr_priv;
+       void *pos;
+       unsigned char addr[ETH_ALEN];
+       int res;
+
+       if (!info)
+               goto invalid;
+
+       na = info->attrs[HSR_A_IFINDEX];
+       if (!na)
+               goto invalid;
+
+       hsr_dev = __dev_get_by_index(genl_info_net(info),
+                                    nla_get_u32(info->attrs[HSR_A_IFINDEX]));
+       if (!hsr_dev)
+               goto invalid;
+       if (!is_hsr_master(hsr_dev))
+               goto invalid;
+
+
+       /* Send reply */
+
+       skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb_out) {
+               res = -ENOMEM;
+               goto fail;
+       }
+
+       msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid,
+                               info->snd_seq, &hsr_genl_family, 0,
+                               HSR_C_SET_NODE_LIST);
+       if (!msg_head) {
+               res = -ENOMEM;
+               goto nla_put_failure;
+       }
+
+       res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex);
+       if (res < 0)
+               goto nla_put_failure;
+
+       hsr_priv = netdev_priv(hsr_dev);
+
+       rcu_read_lock();
+       pos = hsr_get_next_node(hsr_priv, NULL, addr);
+       while (pos) {
+               res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr);
+               if (res < 0) {
+                       rcu_read_unlock();
+                       goto nla_put_failure;
+               }
+               pos = hsr_get_next_node(hsr_priv, pos, addr);
+       }
+       rcu_read_unlock();
+
+       genlmsg_end(skb_out, msg_head);
+       genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
+
+       return 0;
+
+invalid:
+       netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
+       return 0;
+
+nla_put_failure:
+       kfree_skb(skb_out);
+       /* Fall through */
+
+fail:
+       return res;
+}
+
+
+static struct genl_ops hsr_ops_get_node_list = {
+       .cmd = HSR_C_GET_NODE_LIST,
+       .flags = 0,
+       .policy = hsr_genl_policy,
+       .doit = hsr_get_node_list,
+       .dumpit = NULL,
+};
+
+int __init hsr_netlink_init(void)
+{
+       int rc;
+
+       rc = rtnl_link_register(&hsr_link_ops);
+       if (rc)
+               goto fail_rtnl_link_register;
+
+       rc = genl_register_family(&hsr_genl_family);
+       if (rc)
+               goto fail_genl_register_family;
+
+       rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_status);
+       if (rc)
+               goto fail_genl_register_ops;
+
+       rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_list);
+       if (rc)
+               goto fail_genl_register_ops_node_list;
+
+       rc = genl_register_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
+       if (rc)
+               goto fail_genl_register_mc_group;
+
+       return 0;
+
+fail_genl_register_mc_group:
+       genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_list);
+fail_genl_register_ops_node_list:
+       genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status);
+fail_genl_register_ops:
+       genl_unregister_family(&hsr_genl_family);
+fail_genl_register_family:
+       rtnl_link_unregister(&hsr_link_ops);
+fail_rtnl_link_register:
+
+       return rc;
+}
+
+void __exit hsr_netlink_exit(void)
+{
+       genl_unregister_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp);
+       genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status);
+       genl_unregister_family(&hsr_genl_family);
+
+       rtnl_link_unregister(&hsr_link_ops);
+}
+
+MODULE_ALIAS_RTNL_LINK("hsr");
diff --git a/net/hsr/hsr_netlink.h b/net/hsr/hsr_netlink.h
new file mode 100644 (file)
index 0000000..d4579dc
--- /dev/null
@@ -0,0 +1,30 @@
+/* Copyright 2011-2013 Autronica Fire and Security AS
+ *
+ * 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.
+ *
+ * Author(s):
+ *     2011-2013 Arvid Brodin, arvid.brodin@xdin.com
+ */
+
+#ifndef __HSR_NETLINK_H
+#define __HSR_NETLINK_H
+
+#include <linux/if_ether.h>
+#include <linux/module.h>
+#include <uapi/linux/hsr_netlink.h>
+
+struct hsr_priv;
+
+int __init hsr_netlink_init(void);
+void __exit hsr_netlink_exit(void);
+
+void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
+                     int dev_idx);
+void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN]);
+void hsr_nl_framedrop(int dropcount, int dev_idx);
+void hsr_nl_linkdown(int dev_idx);
+
+#endif /* __HSR_NETLINK_H */
index c85e71e..426b5df 100644 (file)
@@ -440,7 +440,6 @@ lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
                default:
                        pr_debug("ERROR: unknown UDP format\n");
                        goto err;
-                       break;
                }
 
                pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
@@ -655,7 +654,9 @@ static int lowpan_header_create(struct sk_buff *skb,
        head[1] = iphc1;
 
        skb_pull(skb, sizeof(struct ipv6hdr));
+       skb_reset_transport_header(skb);
        memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+       skb_reset_network_header(skb);
 
        lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data,
                                skb->len);
@@ -738,7 +739,6 @@ static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr)
                return -ENOMEM;
 
        skb_push(new, sizeof(struct ipv6hdr));
-       skb_reset_network_header(new);
        skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
 
        new->protocol = htons(ETH_P_IPV6);
@@ -785,7 +785,6 @@ lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
                goto skb_err;
 
        frame->skb->priority = skb->priority;
-       frame->skb->dev = skb->dev;
 
        /* reserve headroom for uncompressed ipv6 header */
        skb_reserve(frame->skb, sizeof(struct ipv6hdr));
@@ -1061,7 +1060,6 @@ lowpan_process_data(struct sk_buff *skb)
                skb = new;
 
                skb_push(skb, sizeof(struct udphdr));
-               skb_reset_transport_header(skb);
                skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
 
                lowpan_raw_dump_table(__func__, "raw UDP header dump",
@@ -1104,50 +1102,40 @@ static int lowpan_set_address(struct net_device *dev, void *p)
        return 0;
 }
 
-static int lowpan_get_mac_header_length(struct sk_buff *skb)
-{
-       /*
-        * Currently long addressing mode is supported only, so the overall
-        * header size is 21:
-        * FC SeqNum DPAN DA  SA  Sec
-        * 2  +  1  +  2 + 8 + 8 + 0  = 21
-        */
-       return 21;
-}
-
 static int
 lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
                        int mlen, int plen, int offset, int type)
 {
        struct sk_buff *frag;
-       int hlen, ret;
+       int hlen;
 
        hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
                        LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
 
        lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
 
-       frag = dev_alloc_skb(hlen + mlen + plen + IEEE802154_MFR_SIZE);
+       frag = netdev_alloc_skb(skb->dev,
+                               hlen + mlen + plen + IEEE802154_MFR_SIZE);
        if (!frag)
                return -ENOMEM;
 
        frag->priority = skb->priority;
-       frag->dev = skb->dev;
 
        /* copy header, MFR and payload */
-       memcpy(skb_put(frag, mlen), skb->data, mlen);
-       memcpy(skb_put(frag, hlen), head, hlen);
+       skb_put(frag, mlen);
+       skb_copy_to_linear_data(frag, skb_mac_header(skb), mlen);
 
-       if (plen)
-               skb_copy_from_linear_data_offset(skb, offset + mlen,
-                                       skb_put(frag, plen), plen);
+       skb_put(frag, hlen);
+       skb_copy_to_linear_data_offset(frag, mlen, head, hlen);
+
+       skb_put(frag, plen);
+       skb_copy_to_linear_data_offset(frag, mlen + hlen,
+                                      skb_network_header(skb) + offset, plen);
 
        lowpan_raw_dump_table(__func__, " raw fragment dump", frag->data,
                                                                frag->len);
 
-       ret = dev_queue_xmit(frag);
-
-       return ret;
+       return dev_queue_xmit(frag);
 }
 
 static int
@@ -1156,7 +1144,7 @@ lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
        int  err, header_length, payload_length, tag, offset = 0;
        u8 head[5];
 
-       header_length = lowpan_get_mac_header_length(skb);
+       header_length = skb->mac_len;
        payload_length = skb->len - header_length;
        tag = lowpan_dev_info(dev)->fragment_tag++;
 
@@ -1181,7 +1169,7 @@ lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
        head[0] &= ~LOWPAN_DISPATCH_FRAG1;
        head[0] |= LOWPAN_DISPATCH_FRAGN;
 
-       while ((payload_length - offset > 0) && (err >= 0)) {
+       while (payload_length - offset > 0) {
                int len = LOWPAN_FRAG_SIZE;
 
                head[4] = offset / 8;
@@ -1327,8 +1315,6 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
 
                /* Pull off the 1-byte of 6lowpan header. */
                skb_pull(local_skb, 1);
-               skb_reset_network_header(local_skb);
-               skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
 
                lowpan_give_skb_to_devices(local_skb);
 
@@ -1372,6 +1358,10 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
        real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
        if (!real_dev)
                return -ENODEV;
+       if (real_dev->type != ARPHRD_IEEE802154) {
+               dev_put(real_dev);
+               return -EINVAL;
+       }
 
        lowpan_dev_info(dev)->real_dev = real_dev;
        lowpan_dev_info(dev)->fragment_tag = 0;
@@ -1386,6 +1376,9 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
 
        entry->ldev = dev;
 
+       /* Set the lowpan harware address to the wpan hardware address. */
+       memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
+
        mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
        INIT_LIST_HEAD(&entry->list);
        list_add_tail(&entry->list, &lowpan_devices);
index 7a1874b..68af9aa 100644 (file)
@@ -245,31 +245,6 @@ out:
 }
 EXPORT_SYMBOL(inet_listen);
 
-u32 inet_ehash_secret __read_mostly;
-EXPORT_SYMBOL(inet_ehash_secret);
-
-u32 ipv6_hash_secret __read_mostly;
-EXPORT_SYMBOL(ipv6_hash_secret);
-
-/*
- * inet_ehash_secret must be set exactly once, and to a non nul value
- * ipv6_hash_secret must be set exactly once.
- */
-void build_ehash_secret(void)
-{
-       u32 rnd;
-
-       do {
-               get_random_bytes(&rnd, sizeof(rnd));
-       } while (rnd == 0);
-
-       if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0) {
-               get_random_bytes(&ipv6_hash_secret, sizeof(ipv6_hash_secret));
-               net_secret_init();
-       }
-}
-EXPORT_SYMBOL(build_ehash_secret);
-
 /*
  *     Create an inet socket.
  */
@@ -286,10 +261,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
        int try_loading_module = 0;
        int err;
 
-       if (unlikely(!inet_ehash_secret))
-               if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
-                       build_ehash_secret();
-
        sock->state = SS_UNCONNECTED;
 
        /* Look for the requested type/protocol pair. */
@@ -1256,36 +1227,36 @@ static int inet_gso_send_check(struct sk_buff *skb)
        if (ihl < sizeof(*iph))
                goto out;
 
+       proto = iph->protocol;
+
+       /* Warning: after this point, iph might be no longer valid */
        if (unlikely(!pskb_may_pull(skb, ihl)))
                goto out;
-
        __skb_pull(skb, ihl);
+
        skb_reset_transport_header(skb);
-       iph = ip_hdr(skb);
-       proto = iph->protocol;
        err = -EPROTONOSUPPORT;
 
-       rcu_read_lock();
        ops = rcu_dereference(inet_offloads[proto]);
        if (likely(ops && ops->callbacks.gso_send_check))
                err = ops->callbacks.gso_send_check(skb);
-       rcu_read_unlock();
 
 out:
        return err;
 }
 
 static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
-       netdev_features_t features)
+                                       netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        const struct net_offload *ops;
+       unsigned int offset = 0;
+       bool udpfrag, encap;
        struct iphdr *iph;
        int proto;
+       int nhoff;
        int ihl;
        int id;
-       unsigned int offset = 0;
-       bool tunnel;
 
        if (unlikely(skb_shinfo(skb)->gso_type &
                     ~(SKB_GSO_TCPV4 |
@@ -1293,12 +1264,16 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_IPIP |
+                      SKB_GSO_SIT |
                       SKB_GSO_TCPV6 |
                       SKB_GSO_UDP_TUNNEL |
                       SKB_GSO_MPLS |
                       0)))
                goto out;
 
+       skb_reset_network_header(skb);
+       nhoff = skb_network_header(skb) - skb_mac_header(skb);
        if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
                goto out;
 
@@ -1307,42 +1282,50 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
        if (ihl < sizeof(*iph))
                goto out;
 
+       id = ntohs(iph->id);
+       proto = iph->protocol;
+
+       /* Warning: after this point, iph might be no longer valid */
        if (unlikely(!pskb_may_pull(skb, ihl)))
                goto out;
+       __skb_pull(skb, ihl);
 
-       tunnel = !!skb->encapsulation;
+       encap = SKB_GSO_CB(skb)->encap_level > 0;
+       if (encap)
+               features = skb->dev->hw_enc_features & netif_skb_features(skb);
+       SKB_GSO_CB(skb)->encap_level += ihl;
 
-       __skb_pull(skb, ihl);
        skb_reset_transport_header(skb);
-       iph = ip_hdr(skb);
-       id = ntohs(iph->id);
-       proto = iph->protocol;
+
        segs = ERR_PTR(-EPROTONOSUPPORT);
 
-       rcu_read_lock();
+       /* Note : following gso_segment() might change skb->encapsulation */
+       udpfrag = !skb->encapsulation && proto == IPPROTO_UDP;
+
        ops = rcu_dereference(inet_offloads[proto]);
        if (likely(ops && ops->callbacks.gso_segment))
                segs = ops->callbacks.gso_segment(skb, features);
-       rcu_read_unlock();
 
        if (IS_ERR_OR_NULL(segs))
                goto out;
 
        skb = segs;
        do {
-               iph = ip_hdr(skb);
-               if (!tunnel && proto == IPPROTO_UDP) {
+               iph = (struct iphdr *)(skb_mac_header(skb) + nhoff);
+               if (udpfrag) {
                        iph->id = htons(id);
                        iph->frag_off = htons(offset >> 3);
                        if (skb->next != NULL)
                                iph->frag_off |= htons(IP_MF);
-                       offset += (skb->len - skb->mac_len - iph->ihl * 4);
-               } else  {
+                       offset += skb->len - nhoff - ihl;
+               } else {
                        iph->id = htons(id++);
                }
-               iph->tot_len = htons(skb->len - skb->mac_len);
-               iph->check = 0;
-               iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
+               iph->tot_len = htons(skb->len - nhoff);
+               ip_send_check(iph);
+               if (encap)
+                       skb_reset_inner_headers(skb);
+               skb->network_header = (u8 *)iph - skb->head;
        } while ((skb = skb->next));
 
 out:
@@ -1548,6 +1531,7 @@ static const struct net_protocol tcp_protocol = {
 };
 
 static const struct net_protocol udp_protocol = {
+       .early_demux =  udp_v4_early_demux,
        .handler =      udp_rcv,
        .err_handler =  udp_err,
        .no_policy =    1,
@@ -1648,6 +1632,13 @@ static struct packet_offload ip_packet_offload __read_mostly = {
        },
 };
 
+static const struct net_offload ipip_offload = {
+       .callbacks = {
+               .gso_send_check = inet_gso_send_check,
+               .gso_segment    = inet_gso_segment,
+       },
+};
+
 static int __init ipv4_offload_init(void)
 {
        /*
@@ -1659,6 +1650,7 @@ static int __init ipv4_offload_init(void)
                pr_crit("%s: Cannot add TCP protocol offload\n", __func__);
 
        dev_add_offload(&ip_packet_offload);
+       inet_add_offload(&ipip_offload, IPPROTO_IPIP);
        return 0;
 }
 
@@ -1707,8 +1699,6 @@ static int __init inet_init(void)
        ip_static_sysctl_init();
 #endif
 
-       tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;
-
        /*
         *      Add all the base protocols.
         */
index 109ee89..7785b28 100644 (file)
@@ -121,7 +121,6 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
        struct aead_givcrypt_request *req;
        struct scatterlist *sg;
        struct scatterlist *asg;
-       struct esp_data *esp;
        struct sk_buff *trailer;
        void *tmp;
        u8 *iv;
@@ -139,8 +138,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
 
        /* skb is pure payload to encrypt */
 
-       esp = x->data;
-       aead = esp->aead;
+       aead = x->data;
        alen = crypto_aead_authsize(aead);
 
        tfclen = 0;
@@ -154,8 +152,6 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
        }
        blksize = ALIGN(crypto_aead_blocksize(aead), 4);
        clen = ALIGN(skb->len + 2 + tfclen, blksize);
-       if (esp->padlen)
-               clen = ALIGN(clen, esp->padlen);
        plen = clen - skb->len - tfclen;
 
        err = skb_cow_data(skb, tfclen + plen + alen, &trailer);
@@ -280,8 +276,7 @@ static int esp_input_done2(struct sk_buff *skb, int err)
 {
        const struct iphdr *iph;
        struct xfrm_state *x = xfrm_input_state(skb);
-       struct esp_data *esp = x->data;
-       struct crypto_aead *aead = esp->aead;
+       struct crypto_aead *aead = x->data;
        int alen = crypto_aead_authsize(aead);
        int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
        int elen = skb->len - hlen;
@@ -376,8 +371,7 @@ static void esp_input_done(struct crypto_async_request *base, int err)
 static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct ip_esp_hdr *esph;
-       struct esp_data *esp = x->data;
-       struct crypto_aead *aead = esp->aead;
+       struct crypto_aead *aead = x->data;
        struct aead_request *req;
        struct sk_buff *trailer;
        int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
@@ -459,9 +453,8 @@ out:
 
 static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
 {
-       struct esp_data *esp = x->data;
-       u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
-       u32 align = max_t(u32, blksize, esp->padlen);
+       struct crypto_aead *aead = x->data;
+       u32 blksize = ALIGN(crypto_aead_blocksize(aead), 4);
        unsigned int net_adj;
 
        switch (x->props.mode) {
@@ -476,8 +469,8 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
                BUG();
        }
 
-       return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) -
-                net_adj) & ~(align - 1)) + net_adj - 2;
+       return ((mtu - x->props.header_len - crypto_aead_authsize(aead) -
+                net_adj) & ~(blksize - 1)) + net_adj - 2;
 }
 
 static void esp4_err(struct sk_buff *skb, u32 info)
@@ -511,18 +504,16 @@ static void esp4_err(struct sk_buff *skb, u32 info)
 
 static void esp_destroy(struct xfrm_state *x)
 {
-       struct esp_data *esp = x->data;
+       struct crypto_aead *aead = x->data;
 
-       if (!esp)
+       if (!aead)
                return;
 
-       crypto_free_aead(esp->aead);
-       kfree(esp);
+       crypto_free_aead(aead);
 }
 
 static int esp_init_aead(struct xfrm_state *x)
 {
-       struct esp_data *esp = x->data;
        struct crypto_aead *aead;
        int err;
 
@@ -531,7 +522,7 @@ static int esp_init_aead(struct xfrm_state *x)
        if (IS_ERR(aead))
                goto error;
 
-       esp->aead = aead;
+       x->data = aead;
 
        err = crypto_aead_setkey(aead, x->aead->alg_key,
                                 (x->aead->alg_key_len + 7) / 8);
@@ -548,7 +539,6 @@ error:
 
 static int esp_init_authenc(struct xfrm_state *x)
 {
-       struct esp_data *esp = x->data;
        struct crypto_aead *aead;
        struct crypto_authenc_key_param *param;
        struct rtattr *rta;
@@ -583,7 +573,7 @@ static int esp_init_authenc(struct xfrm_state *x)
        if (IS_ERR(aead))
                goto error;
 
-       esp->aead = aead;
+       x->data = aead;
 
        keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) +
                 (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param));
@@ -638,16 +628,11 @@ error:
 
 static int esp_init_state(struct xfrm_state *x)
 {
-       struct esp_data *esp;
        struct crypto_aead *aead;
        u32 align;
        int err;
 
-       esp = kzalloc(sizeof(*esp), GFP_KERNEL);
-       if (esp == NULL)
-               return -ENOMEM;
-
-       x->data = esp;
+       x->data = NULL;
 
        if (x->aead)
                err = esp_init_aead(x);
@@ -657,9 +642,7 @@ static int esp_init_state(struct xfrm_state *x)
        if (err)
                goto error;
 
-       aead = esp->aead;
-
-       esp->padlen = 0;
+       aead = x->data;
 
        x->props.header_len = sizeof(struct ip_esp_hdr) +
                              crypto_aead_ivsize(aead);
@@ -683,9 +666,7 @@ static int esp_init_state(struct xfrm_state *x)
        }
 
        align = ALIGN(crypto_aead_blocksize(aead), 4);
-       if (esp->padlen)
-               align = max_t(u32, align, esp->padlen);
-       x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead);
+       x->props.trailer_len = align + 1 + crypto_aead_authsize(aead);
 
 error:
        return err;
index b3f627a..d846304 100644 (file)
@@ -933,7 +933,6 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
                local_bh_disable();
 
                frn->tb_id = tb->tb_id;
-               rcu_read_lock();
                frn->err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF);
 
                if (!frn->err) {
@@ -942,7 +941,6 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
                        frn->type = res.type;
                        frn->scope = res.scope;
                }
-               rcu_read_unlock();
                local_bh_enable();
        }
 }
index af0f14a..388d113 100644 (file)
@@ -24,21 +24,17 @@ static inline void fib_alias_accessed(struct fib_alias *fa)
 }
 
 /* Exported by fib_semantics.c */
-extern void fib_release_info(struct fib_info *);
-extern struct fib_info *fib_create_info(struct fib_config *cfg);
-extern int fib_nh_match(struct fib_config *cfg, struct fib_info *fi);
-extern int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
-                        u32 tb_id, u8 type, __be32 dst,
-                        int dst_len, u8 tos, struct fib_info *fi,
-                        unsigned int);
-extern void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
-                     int dst_len, u32 tb_id, struct nl_info *info,
-                     unsigned int nlm_flags);
-extern struct fib_alias *fib_find_alias(struct list_head *fah,
-                                       u8 tos, u32 prio);
-extern int fib_detect_death(struct fib_info *fi, int order,
-                           struct fib_info **last_resort,
-                           int *last_idx, int dflt);
+void fib_release_info(struct fib_info *);
+struct fib_info *fib_create_info(struct fib_config *cfg);
+int fib_nh_match(struct fib_config *cfg, struct fib_info *fi);
+int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, u32 tb_id,
+                 u8 type, __be32 dst, int dst_len, u8 tos, struct fib_info *fi,
+                 unsigned int);
+void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, int dst_len,
+              u32 tb_id, const struct nl_info *info, unsigned int nlm_flags);
+struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio);
+int fib_detect_death(struct fib_info *fi, int order,
+                    struct fib_info **last_resort, int *last_idx, int dflt);
 
 static inline void fib_result_assign(struct fib_result *res,
                                     struct fib_info *fi)
index d5dbca5..e63f47a 100644 (file)
@@ -380,7 +380,7 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi)
 }
 
 void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
-              int dst_len, u32 tb_id, struct nl_info *info,
+              int dst_len, u32 tb_id, const struct nl_info *info,
               unsigned int nlm_flags)
 {
        struct sk_buff *skb;
index 3df6d3e..ec9a9ef 100644 (file)
@@ -762,12 +762,9 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn)
 
                if (IS_LEAF(node) || ((struct tnode *) node)->pos >
                   tn->pos + tn->bits - 1) {
-                       if (tkey_extract_bits(node->key,
-                                             oldtnode->pos + oldtnode->bits,
-                                             1) == 0)
-                               put_child(tn, 2*i, node);
-                       else
-                               put_child(tn, 2*i+1, node);
+                       put_child(tn,
+                               tkey_extract_bits(node->key, oldtnode->pos, oldtnode->bits + 1),
+                               node);
                        continue;
                }
 
@@ -1120,12 +1117,8 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
                 *  first tnode need some special handling
                 */
 
-               if (tp)
-                       pos = tp->pos+tp->bits;
-               else
-                       pos = 0;
-
                if (n) {
+                       pos = tp ? tp->pos+tp->bits : 0;
                        newpos = tkey_mismatch(key, pos, n->key);
                        tn = tnode_new(n->key, newpos, 1);
                } else {
index 736c9fc..5893e99 100644 (file)
@@ -93,35 +93,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
 }
 EXPORT_SYMBOL_GPL(gre_build_header);
 
-struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
-{
-       int err;
-
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
-
-       if (skb_is_gso(skb)) {
-               err = skb_unclone(skb, GFP_ATOMIC);
-               if (unlikely(err))
-                       goto error;
-               skb_shinfo(skb)->gso_type |= SKB_GSO_GRE;
-               return skb;
-       } else if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) {
-               err = skb_checksum_help(skb);
-               if (unlikely(err))
-                       goto error;
-       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       return skb;
-error:
-       kfree_skb(skb);
-       return ERR_PTR(err);
-}
-EXPORT_SYMBOL_GPL(gre_handle_offloads);
-
 static __sum16 check_checksum(struct sk_buff *skb)
 {
        __sum16 csum = 0;
index 55e6bfb..e5d4361 100644 (file)
@@ -39,7 +39,8 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                  SKB_GSO_UDP |
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
-                                 SKB_GSO_GRE)))
+                                 SKB_GSO_GRE |
+                                 SKB_GSO_IPIP)))
                goto out;
 
        if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
index 5f7d11a..5c0e8bc 100644 (file)
@@ -353,6 +353,9 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        saddr = fib_compute_spec_dst(skb);
        ipc.opt = NULL;
        ipc.tx_flags = 0;
+       ipc.ttl = 0;
+       ipc.tos = -1;
+
        if (icmp_param->replyopts.opt.opt.optlen) {
                ipc.opt = &icmp_param->replyopts.opt;
                if (ipc.opt->opt.srr)
@@ -608,6 +611,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        ipc.addr = iph->saddr;
        ipc.opt = &icmp_param->replyopts.opt;
        ipc.tx_flags = 0;
+       ipc.ttl = 0;
+       ipc.tos = -1;
 
        rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos,
                               type, code, icmp_param);
index dace87f..7defdc9 100644 (file)
@@ -736,7 +736,7 @@ static void igmp_gq_timer_expire(unsigned long data)
 
        in_dev->mr_gq_running = 0;
        igmpv3_send_report(in_dev, NULL);
-       __in_dev_put(in_dev);
+       in_dev_put(in_dev);
 }
 
 static void igmp_ifc_timer_expire(unsigned long data)
@@ -749,7 +749,7 @@ static void igmp_ifc_timer_expire(unsigned long data)
                igmp_ifc_start_timer(in_dev,
                                     unsolicited_report_interval(in_dev));
        }
-       __in_dev_put(in_dev);
+       in_dev_put(in_dev);
 }
 
 static void igmp_ifc_event(struct in_device *in_dev)
index 6acb541..fc0e649 100644 (file)
@@ -29,27 +29,19 @@ const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n";
 EXPORT_SYMBOL(inet_csk_timer_bug_msg);
 #endif
 
-/*
- * This struct holds the first and last local port number.
- */
-struct local_ports sysctl_local_ports __read_mostly = {
-       .lock = __SEQLOCK_UNLOCKED(sysctl_local_ports.lock),
-       .range = { 32768, 61000 },
-};
-
 unsigned long *sysctl_local_reserved_ports;
 EXPORT_SYMBOL(sysctl_local_reserved_ports);
 
-void inet_get_local_port_range(int *low, int *high)
+void inet_get_local_port_range(struct net *net, int *low, int *high)
 {
        unsigned int seq;
 
        do {
-               seq = read_seqbegin(&sysctl_local_ports.lock);
+               seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock);
 
-               *low = sysctl_local_ports.range[0];
-               *high = sysctl_local_ports.range[1];
-       } while (read_seqretry(&sysctl_local_ports.lock, seq));
+               *low = net->ipv4.sysctl_local_ports.range[0];
+               *high = net->ipv4.sysctl_local_ports.range[1];
+       } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq));
 }
 EXPORT_SYMBOL(inet_get_local_port_range);
 
@@ -79,17 +71,16 @@ int inet_csk_bind_conflict(const struct sock *sk,
                            (!reuseport || !sk2->sk_reuseport ||
                            (sk2->sk_state != TCP_TIME_WAIT &&
                             !uid_eq(uid, sock_i_uid(sk2))))) {
-                               const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);
-                               if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) ||
-                                   sk2_rcv_saddr == sk_rcv_saddr(sk))
+
+                               if (!sk2->sk_rcv_saddr || !sk->sk_rcv_saddr ||
+                                   sk2->sk_rcv_saddr == sk->sk_rcv_saddr)
                                        break;
                        }
                        if (!relax && reuse && sk2->sk_reuse &&
                            sk2->sk_state != TCP_LISTEN) {
-                               const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);
 
-                               if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) ||
-                                   sk2_rcv_saddr == sk_rcv_saddr(sk))
+                               if (!sk2->sk_rcv_saddr || !sk->sk_rcv_saddr ||
+                                   sk2->sk_rcv_saddr == sk->sk_rcv_saddr)
                                        break;
                        }
                }
@@ -116,7 +107,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
                int remaining, rover, low, high;
 
 again:
-               inet_get_local_port_range(&low, &high);
+               inet_get_local_port_range(net, &low, &high);
                remaining = (high - low) + 1;
                smallest_rover = rover = net_random() % remaining + low;
 
@@ -421,8 +412,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
                           sk->sk_protocol,
                           flags,
-                          (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr,
-                          ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport);
+                          (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
+                          ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport);
        security_req_classify_flow(req, flowi4_to_flowi(fl4));
        rt = ip_route_output_flow(net, fl4, sk);
        if (IS_ERR(rt))
@@ -457,8 +448,8 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk,
        flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
                           sk->sk_protocol, inet_sk_flowi_flags(sk),
-                          (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr,
-                          ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport);
+                          (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
+                          ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport);
        security_req_classify_flow(req, flowi4_to_flowi(fl4));
        rt = ip_route_output_flow(net, fl4, sk);
        if (IS_ERR(rt))
@@ -504,9 +495,9 @@ struct request_sock *inet_csk_search_req(const struct sock *sk,
             prev = &req->dl_next) {
                const struct inet_request_sock *ireq = inet_rsk(req);
 
-               if (ireq->rmt_port == rport &&
-                   ireq->rmt_addr == raddr &&
-                   ireq->loc_addr == laddr &&
+               if (ireq->ir_rmt_port == rport &&
+                   ireq->ir_rmt_addr == raddr &&
+                   ireq->ir_loc_addr == laddr &&
                    AF_INET_FAMILY(req->rsk_ops->family)) {
                        WARN_ON(req->sk);
                        *prevp = prev;
@@ -523,7 +514,8 @@ void inet_csk_reqsk_queue_hash_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 = inet_synq_hash(inet_rsk(req)->rmt_addr, inet_rsk(req)->rmt_port,
+       const u32 h = inet_synq_hash(inet_rsk(req)->ir_rmt_addr,
+                                    inet_rsk(req)->ir_rmt_port,
                                     lopt->hash_rnd, lopt->nr_table_entries);
 
        reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);
@@ -683,9 +675,9 @@ struct sock *inet_csk_clone_lock(const struct sock *sk,
                newsk->sk_state = TCP_SYN_RECV;
                newicsk->icsk_bind_hash = NULL;
 
-               inet_sk(newsk)->inet_dport = inet_rsk(req)->rmt_port;
-               inet_sk(newsk)->inet_num = ntohs(inet_rsk(req)->loc_port);
-               inet_sk(newsk)->inet_sport = inet_rsk(req)->loc_port;
+               inet_sk(newsk)->inet_dport = inet_rsk(req)->ir_rmt_port;
+               inet_sk(newsk)->inet_num = inet_rsk(req)->ir_num;
+               inet_sk(newsk)->inet_sport = htons(inet_rsk(req)->ir_num);
                newsk->sk_write_space = sk_stream_write_space;
 
                newicsk->icsk_retransmits = 0;
index 5f64875..56a964a 100644 (file)
@@ -121,13 +121,13 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
 
 #if IS_ENABLED(CONFIG_IPV6)
        if (r->idiag_family == AF_INET6) {
-               const struct ipv6_pinfo *np = inet6_sk(sk);
 
-               *(struct in6_addr *)r->id.idiag_src = np->rcv_saddr;
-               *(struct in6_addr *)r->id.idiag_dst = np->daddr;
+               *(struct in6_addr *)r->id.idiag_src = sk->sk_v6_rcv_saddr;
+               *(struct in6_addr *)r->id.idiag_dst = sk->sk_v6_daddr;
 
                if (ext & (1 << (INET_DIAG_TCLASS - 1)))
-                       if (nla_put_u8(skb, INET_DIAG_TCLASS, np->tclass) < 0)
+                       if (nla_put_u8(skb, INET_DIAG_TCLASS,
+                                      inet6_sk(sk)->tclass) < 0)
                                goto errout;
        }
 #endif
@@ -222,7 +222,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
                               u32 portid, u32 seq, u16 nlmsg_flags,
                               const struct nlmsghdr *unlh)
 {
-       long tmo;
+       s32 tmo;
        struct inet_diag_msg *r;
        struct nlmsghdr *nlh;
 
@@ -234,7 +234,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
        r = nlmsg_data(nlh);
        BUG_ON(tw->tw_state != TCP_TIME_WAIT);
 
-       tmo = tw->tw_ttd - jiffies;
+       tmo = tw->tw_ttd - inet_tw_time_stamp();
        if (tmo < 0)
                tmo = 0;
 
@@ -248,18 +248,15 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
        r->id.idiag_dst[0]    = tw->tw_daddr;
        r->idiag_state        = tw->tw_substate;
        r->idiag_timer        = 3;
-       r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
+       r->idiag_expires      = jiffies_to_msecs(tmo);
        r->idiag_rqueue       = 0;
        r->idiag_wqueue       = 0;
        r->idiag_uid          = 0;
        r->idiag_inode        = 0;
 #if IS_ENABLED(CONFIG_IPV6)
        if (tw->tw_family == AF_INET6) {
-               const struct inet6_timewait_sock *tw6 =
-                                               inet6_twsk((struct sock *)tw);
-
-               *(struct in6_addr *)r->id.idiag_src = tw6->tw_v6_rcv_saddr;
-               *(struct in6_addr *)r->id.idiag_dst = tw6->tw_v6_daddr;
+               *(struct in6_addr *)r->id.idiag_src = tw->tw_v6_rcv_saddr;
+               *(struct in6_addr *)r->id.idiag_dst = tw->tw_v6_daddr;
        }
 #endif
 
@@ -273,10 +270,11 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
                        const struct nlmsghdr *unlh)
 {
        if (sk->sk_state == TCP_TIME_WAIT)
-               return inet_twsk_diag_fill((struct inet_timewait_sock *)sk,
-                                          skb, r, portid, seq, nlmsg_flags,
-                                          unlh);
-       return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, nlmsg_flags, unlh);
+               return inet_twsk_diag_fill(inet_twsk(sk), skb, r, portid, seq,
+                                          nlmsg_flags, unlh);
+
+       return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq,
+                                 nlmsg_flags, unlh);
 }
 
 int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb,
@@ -338,12 +336,9 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s
                err = 0;
 
 out:
-       if (sk) {
-               if (sk->sk_state == TCP_TIME_WAIT)
-                       inet_twsk_put((struct inet_timewait_sock *)sk);
-               else
-                       sock_put(sk);
-       }
+       if (sk)
+               sock_gen_put(sk);
+
 out_nosk:
        return err;
 }
@@ -489,10 +484,9 @@ int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
        entry.family = sk->sk_family;
 #if IS_ENABLED(CONFIG_IPV6)
        if (entry.family == AF_INET6) {
-               struct ipv6_pinfo *np = inet6_sk(sk);
 
-               entry.saddr = np->rcv_saddr.s6_addr32;
-               entry.daddr = np->daddr.s6_addr32;
+               entry.saddr = sk->sk_v6_rcv_saddr.s6_addr32;
+               entry.daddr = sk->sk_v6_daddr.s6_addr32;
        } else
 #endif
        {
@@ -635,22 +629,22 @@ static int inet_csk_diag_dump(struct sock *sk,
                                  cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
 }
 
-static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
+static int inet_twsk_diag_dump(struct sock *sk,
                               struct sk_buff *skb,
                               struct netlink_callback *cb,
                               struct inet_diag_req_v2 *r,
                               const struct nlattr *bc)
 {
+       struct inet_timewait_sock *tw = inet_twsk(sk);
+
        if (bc != NULL) {
                struct inet_diag_entry entry;
 
                entry.family = tw->tw_family;
 #if IS_ENABLED(CONFIG_IPV6)
                if (tw->tw_family == AF_INET6) {
-                       struct inet6_timewait_sock *tw6 =
-                                               inet6_twsk((struct sock *)tw);
-                       entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32;
-                       entry.daddr = tw6->tw_v6_daddr.s6_addr32;
+                       entry.saddr = tw->tw_v6_rcv_saddr.s6_addr32;
+                       entry.daddr = tw->tw_v6_daddr.s6_addr32;
                } else
 #endif
                {
@@ -682,12 +676,12 @@ static inline void inet_diag_req_addrs(const struct sock *sk,
 #if IS_ENABLED(CONFIG_IPV6)
        if (sk->sk_family == AF_INET6) {
                if (req->rsk_ops->family == AF_INET6) {
-                       entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32;
-                       entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32;
+                       entry->saddr = ireq->ir_v6_loc_addr.s6_addr32;
+                       entry->daddr = ireq->ir_v6_rmt_addr.s6_addr32;
                } else if (req->rsk_ops->family == AF_INET) {
-                       ipv6_addr_set_v4mapped(ireq->loc_addr,
+                       ipv6_addr_set_v4mapped(ireq->ir_loc_addr,
                                               &entry->saddr_storage);
-                       ipv6_addr_set_v4mapped(ireq->rmt_addr,
+                       ipv6_addr_set_v4mapped(ireq->ir_rmt_addr,
                                               &entry->daddr_storage);
                        entry->saddr = entry->saddr_storage.s6_addr32;
                        entry->daddr = entry->daddr_storage.s6_addr32;
@@ -695,8 +689,8 @@ static inline void inet_diag_req_addrs(const struct sock *sk,
        } else
 #endif
        {
-               entry->saddr = &ireq->loc_addr;
-               entry->daddr = &ireq->rmt_addr;
+               entry->saddr = &ireq->ir_loc_addr;
+               entry->daddr = &ireq->ir_rmt_addr;
        }
 }
 
@@ -731,9 +725,9 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
                tmo = 0;
 
        r->id.idiag_sport = inet->inet_sport;
-       r->id.idiag_dport = ireq->rmt_port;
-       r->id.idiag_src[0] = ireq->loc_addr;
-       r->id.idiag_dst[0] = ireq->rmt_addr;
+       r->id.idiag_dport = ireq->ir_rmt_port;
+       r->id.idiag_src[0] = ireq->ir_loc_addr;
+       r->id.idiag_dst[0] = ireq->ir_rmt_addr;
        r->idiag_expires = jiffies_to_msecs(tmo);
        r->idiag_rqueue = 0;
        r->idiag_wqueue = 0;
@@ -792,13 +786,13 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
 
                        if (reqnum < s_reqnum)
                                continue;
-                       if (r->id.idiag_dport != ireq->rmt_port &&
+                       if (r->id.idiag_dport != ireq->ir_rmt_port &&
                            r->id.idiag_dport)
                                continue;
 
                        if (bc) {
                                inet_diag_req_addrs(sk, req, &entry);
-                               entry.dport = ntohs(ireq->rmt_port);
+                               entry.dport = ntohs(ireq->ir_rmt_port);
 
                                if (!inet_diag_bc_run(bc, &entry))
                                        continue;
@@ -911,8 +905,7 @@ skip_listen_ht:
 
                num = 0;
 
-               if (hlist_nulls_empty(&head->chain) &&
-                       hlist_nulls_empty(&head->twchain))
+               if (hlist_nulls_empty(&head->chain))
                        continue;
 
                if (i > s_i)
@@ -920,7 +913,7 @@ skip_listen_ht:
 
                spin_lock_bh(lock);
                sk_nulls_for_each(sk, node, &head->chain) {
-                       struct inet_sock *inet = inet_sk(sk);
+                       int res;
 
                        if (!net_eq(sock_net(sk), net))
                                continue;
@@ -929,15 +922,19 @@ skip_listen_ht:
                        if (!(r->idiag_states & (1 << sk->sk_state)))
                                goto next_normal;
                        if (r->sdiag_family != AF_UNSPEC &&
-                                       sk->sk_family != r->sdiag_family)
+                           sk->sk_family != r->sdiag_family)
                                goto next_normal;
-                       if (r->id.idiag_sport != inet->inet_sport &&
+                       if (r->id.idiag_sport != htons(sk->sk_num) &&
                            r->id.idiag_sport)
                                goto next_normal;
-                       if (r->id.idiag_dport != inet->inet_dport &&
+                       if (r->id.idiag_dport != sk->sk_dport &&
                            r->id.idiag_dport)
                                goto next_normal;
-                       if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
+                       if (sk->sk_state == TCP_TIME_WAIT)
+                               res = inet_twsk_diag_dump(sk, skb, cb, r, bc);
+                       else
+                               res = inet_csk_diag_dump(sk, skb, cb, r, bc);
+                       if (res < 0) {
                                spin_unlock_bh(lock);
                                goto done;
                        }
@@ -945,33 +942,6 @@ next_normal:
                        ++num;
                }
 
-               if (r->idiag_states & TCPF_TIME_WAIT) {
-                       struct inet_timewait_sock *tw;
-
-                       inet_twsk_for_each(tw, node,
-                                   &head->twchain) {
-                               if (!net_eq(twsk_net(tw), net))
-                                       continue;
-
-                               if (num < s_num)
-                                       goto next_dying;
-                               if (r->sdiag_family != AF_UNSPEC &&
-                                               tw->tw_family != r->sdiag_family)
-                                       goto next_dying;
-                               if (r->id.idiag_sport != tw->tw_sport &&
-                                   r->id.idiag_sport)
-                                       goto next_dying;
-                               if (r->id.idiag_dport != tw->tw_dport &&
-                                   r->id.idiag_dport)
-                                       goto next_dying;
-                               if (inet_twsk_diag_dump(tw, skb, cb, r, bc) < 0) {
-                                       spin_unlock_bh(lock);
-                                       goto done;
-                               }
-next_dying:
-                               ++num;
-                       }
-               }
                spin_unlock_bh(lock);
        }
 
index c5313a9..bb075fc 100644 (file)
@@ -93,9 +93,6 @@ void inet_frags_init(struct inet_frags *f)
        }
        rwlock_init(&f->lock);
 
-       f->rnd = (u32) ((totalram_pages ^ (totalram_pages >> 7)) ^
-                                  (jiffies ^ (jiffies >> 6)));
-
        setup_timer(&f->secret_timer, inet_frag_secret_rebuild,
                        (unsigned long)f);
        f->secret_timer.expires = jiffies + f->secret_interval;
index 7bd8983..8b9cf27 100644 (file)
 #include <net/secure_seq.h>
 #include <net/ip.h>
 
+static unsigned int inet_ehashfn(struct net *net, const __be32 laddr,
+                                const __u16 lport, const __be32 faddr,
+                                const __be16 fport)
+{
+       static u32 inet_ehash_secret __read_mostly;
+
+       net_get_random_once(&inet_ehash_secret, sizeof(inet_ehash_secret));
+
+       return __inet_ehashfn(laddr, lport, faddr, fport,
+                             inet_ehash_secret + net_hash_mix(net));
+}
+
+
+static unsigned int inet_sk_ehashfn(const struct sock *sk)
+{
+       const struct inet_sock *inet = inet_sk(sk);
+       const __be32 laddr = inet->inet_rcv_saddr;
+       const __u16 lport = inet->inet_num;
+       const __be32 faddr = inet->inet_daddr;
+       const __be16 fport = inet->inet_dport;
+       struct net *net = sock_net(sk);
+
+       return inet_ehashfn(net, laddr, lport, faddr, fport);
+}
+
 /*
  * Allocate and initialize a new local port bind bucket.
  * The bindhash mutex for snum's hash chain must be held here.
@@ -230,6 +255,19 @@ begin:
 }
 EXPORT_SYMBOL_GPL(__inet_lookup_listener);
 
+/* All sockets share common refcount, but have different destructors */
+void sock_gen_put(struct sock *sk)
+{
+       if (!atomic_dec_and_test(&sk->sk_refcnt))
+               return;
+
+       if (sk->sk_state == TCP_TIME_WAIT)
+               inet_twsk_free(inet_twsk(sk));
+       else
+               sk_free(sk);
+}
+EXPORT_SYMBOL_GPL(sock_gen_put);
+
 struct sock *__inet_lookup_established(struct net *net,
                                  struct inet_hashinfo *hashinfo,
                                  const __be32 saddr, const __be16 sport,
@@ -255,13 +293,13 @@ begin:
                if (likely(INET_MATCH(sk, net, acookie,
                                      saddr, daddr, ports, dif))) {
                        if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
-                               goto begintw;
+                               goto out;
                        if (unlikely(!INET_MATCH(sk, net, acookie,
                                                 saddr, daddr, ports, dif))) {
-                               sock_put(sk);
+                               sock_gen_put(sk);
                                goto begin;
                        }
-                       goto out;
+                       goto found;
                }
        }
        /*
@@ -271,37 +309,9 @@ begin:
         */
        if (get_nulls_value(node) != slot)
                goto begin;
-
-begintw:
-       /* Must check for a TIME_WAIT'er before going to listener hash. */
-       sk_nulls_for_each_rcu(sk, node, &head->twchain) {
-               if (sk->sk_hash != hash)
-                       continue;
-               if (likely(INET_TW_MATCH(sk, net, acookie,
-                                        saddr, daddr, ports,
-                                        dif))) {
-                       if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
-                               sk = NULL;
-                               goto out;
-                       }
-                       if (unlikely(!INET_TW_MATCH(sk, net, acookie,
-                                                   saddr, daddr, ports,
-                                                   dif))) {
-                               sock_put(sk);
-                               goto begintw;
-                       }
-                       goto out;
-               }
-       }
-       /*
-        * if the nulls value we got at the end of this lookup is
-        * not the expected one, we must restart lookup.
-        * We probably met an item that was moved to another chain.
-        */
-       if (get_nulls_value(node) != slot)
-               goto begintw;
-       sk = NULL;
 out:
+       sk = NULL;
+found:
        rcu_read_unlock();
        return sk;
 }
@@ -326,39 +336,29 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
        spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
        struct sock *sk2;
        const struct hlist_nulls_node *node;
-       struct inet_timewait_sock *tw;
+       struct inet_timewait_sock *tw = NULL;
        int twrefcnt = 0;
 
        spin_lock(lock);
 
-       /* Check TIME-WAIT sockets first. */
-       sk_nulls_for_each(sk2, node, &head->twchain) {
-               if (sk2->sk_hash != hash)
-                       continue;
-
-               if (likely(INET_TW_MATCH(sk2, net, acookie,
-                                        saddr, daddr, ports, dif))) {
-                       tw = inet_twsk(sk2);
-                       if (twsk_unique(sk, sk2, twp))
-                               goto unique;
-                       else
-                               goto not_unique;
-               }
-       }
-       tw = NULL;
-
-       /* And established part... */
        sk_nulls_for_each(sk2, node, &head->chain) {
                if (sk2->sk_hash != hash)
                        continue;
+
                if (likely(INET_MATCH(sk2, net, acookie,
-                                     saddr, daddr, ports, dif)))
+                                        saddr, daddr, ports, dif))) {
+                       if (sk2->sk_state == TCP_TIME_WAIT) {
+                               tw = inet_twsk(sk2);
+                               if (twsk_unique(sk, sk2, twp))
+                                       break;
+                       }
                        goto not_unique;
+               }
        }
 
-unique:
        /* Must record num and sport now. Otherwise we will see
-        * in hash table socket with a funny identity. */
+        * in hash table socket with a funny identity.
+        */
        inet->inet_num = lport;
        inet->inet_sport = htons(lport);
        sk->sk_hash = hash;
@@ -494,7 +494,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
                u32 offset = hint + port_offset;
                struct inet_timewait_sock *tw = NULL;
 
-               inet_get_local_port_range(&low, &high);
+               inet_get_local_port_range(net, &low, &high);
                remaining = (high - low) + 1;
 
                local_bh_disable();
index 1f27c9f..6d592f8 100644 (file)
@@ -87,19 +87,11 @@ static void __inet_twsk_kill(struct inet_timewait_sock *tw,
        refcnt += inet_twsk_bind_unhash(tw, hashinfo);
        spin_unlock(&bhead->lock);
 
-#ifdef SOCK_REFCNT_DEBUG
-       if (atomic_read(&tw->tw_refcnt) != 1) {
-               pr_debug("%s timewait_sock %p refcnt=%d\n",
-                        tw->tw_prot->name, tw, atomic_read(&tw->tw_refcnt));
-       }
-#endif
-       while (refcnt) {
-               inet_twsk_put(tw);
-               refcnt--;
-       }
+       BUG_ON(refcnt >= atomic_read(&tw->tw_refcnt));
+       atomic_sub(refcnt, &tw->tw_refcnt);
 }
 
-static noinline void inet_twsk_free(struct inet_timewait_sock *tw)
+void inet_twsk_free(struct inet_timewait_sock *tw)
 {
        struct module *owner = tw->tw_prot->owner;
        twsk_destructor((struct sock *)tw);
@@ -118,6 +110,18 @@ void inet_twsk_put(struct inet_timewait_sock *tw)
 }
 EXPORT_SYMBOL_GPL(inet_twsk_put);
 
+static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
+                                  struct hlist_nulls_head *list)
+{
+       hlist_nulls_add_head_rcu(&tw->tw_node, list);
+}
+
+static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
+                                   struct hlist_head *list)
+{
+       hlist_add_head(&tw->tw_bind_node, list);
+}
+
 /*
  * Enter the time wait state. This is called with locally disabled BH.
  * Essentially we whip up a timewait bucket, copy the relevant info into it
@@ -146,26 +150,21 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
        spin_lock(lock);
 
        /*
-        * Step 2: Hash TW into TIMEWAIT chain.
-        * Should be done before removing sk from established chain
-        * because readers are lockless and search established first.
+        * Step 2: Hash TW into tcp ehash chain.
+        * Notes :
+        * - tw_refcnt is set to 3 because :
+        * - We have one reference from bhash chain.
+        * - We have one reference from ehash chain.
+        * We can use atomic_set() because prior spin_lock()/spin_unlock()
+        * committed into memory all tw fields.
         */
-       inet_twsk_add_node_rcu(tw, &ehead->twchain);
+       atomic_set(&tw->tw_refcnt, 1 + 1 + 1);
+       inet_twsk_add_node_rcu(tw, &ehead->chain);
 
-       /* Step 3: Remove SK from established hash. */
+       /* Step 3: Remove SK from hash chain */
        if (__sk_nulls_del_node_init_rcu(sk))
                sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
 
-       /*
-        * Notes :
-        * - We initially set tw_refcnt to 0 in inet_twsk_alloc()
-        * - We add one reference for the bhash link
-        * - We add one reference for the ehash link
-        * - We want this refcnt update done before allowing other
-        *   threads to find this tw in ehash chain.
-        */
-       atomic_add(1 + 1 + 1, &tw->tw_refcnt);
-
        spin_unlock(lock);
 }
 EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
@@ -387,11 +386,11 @@ void inet_twsk_schedule(struct inet_timewait_sock *tw,
                        if (slot >= INET_TWDR_TWKILL_SLOTS)
                                slot = INET_TWDR_TWKILL_SLOTS - 1;
                }
-               tw->tw_ttd = jiffies + timeo;
+               tw->tw_ttd = inet_tw_time_stamp() + timeo;
                slot = (twdr->slot + slot) & (INET_TWDR_TWKILL_SLOTS - 1);
                list = &twdr->cells[slot];
        } else {
-               tw->tw_ttd = jiffies + (slot << INET_TWDR_RECYCLE_TICK);
+               tw->tw_ttd = inet_tw_time_stamp() + (slot << INET_TWDR_RECYCLE_TICK);
 
                if (twdr->twcal_hand < 0) {
                        twdr->twcal_hand = 0;
@@ -490,7 +489,9 @@ void inet_twsk_purge(struct inet_hashinfo *hashinfo,
 restart_rcu:
                rcu_read_lock();
 restart:
-               sk_nulls_for_each_rcu(sk, node, &head->twchain) {
+               sk_nulls_for_each_rcu(sk, node, &head->chain) {
+                       if (sk->sk_state != TCP_TIME_WAIT)
+                               continue;
                        tw = inet_twsk(sk);
                        if ((tw->tw_family != family) ||
                                atomic_read(&twsk_net(tw)->count))
index b66910a..2481993 100644 (file)
@@ -106,6 +106,7 @@ struct ip4_create_arg {
 
 static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot)
 {
+       net_get_random_once(&ip4_frags.rnd, sizeof(ip4_frags.rnd));
        return jhash_3words((__force u32)id << 16 | prot,
                            (__force u32)saddr, (__force u32)daddr,
                            ip4_frags.rnd) & (INETFRAGS_HASHSZ - 1);
index a04d872..9124027 100644 (file)
@@ -772,15 +772,20 @@ static inline int ip_ufo_append_data(struct sock *sk,
                /* initialize protocol header pointer */
                skb->transport_header = skb->network_header + fragheaderlen;
 
-               skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
 
-               /* specify the length of each IP datagram fragment */
-               skb_shinfo(skb)->gso_size = maxfraglen - fragheaderlen;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+
                __skb_queue_tail(queue, skb);
+       } else if (skb_is_gso(skb)) {
+               goto append;
        }
 
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       /* specify the length of each IP datagram fragment */
+       skb_shinfo(skb)->gso_size = maxfraglen - fragheaderlen;
+       skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+
+append:
        return skb_append_datato_frags(sk, skb, getfrag, from,
                                       (length - transhdrlen));
 }
@@ -805,7 +810,7 @@ static int __ip_append_data(struct sock *sk,
        int copy;
        int err;
        int offset = 0;
-       unsigned int maxfraglen, fragheaderlen;
+       unsigned int maxfraglen, fragheaderlen, maxnonfragsize;
        int csummode = CHECKSUM_NONE;
        struct rtable *rt = (struct rtable *)cork->dst;
 
@@ -818,8 +823,10 @@ static int __ip_append_data(struct sock *sk,
 
        fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
+       maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ?
+                        mtu : 0xFFFF;
 
-       if (cork->length + length > 0xFFFF - fragheaderlen) {
+       if (cork->length + length > maxnonfragsize - fragheaderlen) {
                ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
                               mtu-exthdrlen);
                return -EMSGSIZE;
@@ -1030,7 +1037,6 @@ error:
 static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
                         struct ipcm_cookie *ipc, struct rtable **rtp)
 {
-       struct inet_sock *inet = inet_sk(sk);
        struct ip_options_rcu *opt;
        struct rtable *rt;
 
@@ -1056,10 +1062,13 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
         * We steal reference to this route, caller should not release it
         */
        *rtp = NULL;
-       cork->fragsize = inet->pmtudisc == IP_PMTUDISC_PROBE ?
-                        rt->dst.dev->mtu : dst_mtu(&rt->dst);
+       cork->fragsize = ip_sk_use_pmtu(sk) ?
+                        dst_mtu(&rt->dst) : rt->dst.dev->mtu;
        cork->dst = &rt->dst;
        cork->length = 0;
+       cork->ttl = ipc->ttl;
+       cork->tos = ipc->tos;
+       cork->priority = ipc->priority;
        cork->tx_flags = ipc->tx_flags;
 
        return 0;
@@ -1114,7 +1123,7 @@ ssize_t   ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
        int mtu;
        int len;
        int err;
-       unsigned int maxfraglen, fragheaderlen, fraggap;
+       unsigned int maxfraglen, fragheaderlen, fraggap, maxnonfragsize;
 
        if (inet->hdrincl)
                return -EPERM;
@@ -1138,8 +1147,10 @@ ssize_t  ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
 
        fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
+       maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ?
+                        mtu : 0xFFFF;
 
-       if (cork->length + size > 0xFFFF - fragheaderlen) {
+       if (cork->length + size > maxnonfragsize - fragheaderlen) {
                ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu);
                return -EMSGSIZE;
        }
@@ -1303,7 +1314,8 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        /* DF bit is set when we want to see DF on outgoing frames.
         * If local_df is set too, we still allow to fragment this frame
         * locally. */
-       if (inet->pmtudisc >= IP_PMTUDISC_DO ||
+       if (inet->pmtudisc == IP_PMTUDISC_DO ||
+           inet->pmtudisc == IP_PMTUDISC_PROBE ||
            (skb->len <= dst_mtu(&rt->dst) &&
             ip_dont_fragment(sk, &rt->dst)))
                df = htons(IP_DF);
@@ -1311,7 +1323,9 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        if (cork->flags & IPCORK_OPT)
                opt = cork->opt;
 
-       if (rt->rt_type == RTN_MULTICAST)
+       if (cork->ttl != 0)
+               ttl = cork->ttl;
+       else if (rt->rt_type == RTN_MULTICAST)
                ttl = inet->mc_ttl;
        else
                ttl = ip_select_ttl(inet, &rt->dst);
@@ -1319,7 +1333,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        iph = ip_hdr(skb);
        iph->version = 4;
        iph->ihl = 5;
-       iph->tos = inet->tos;
+       iph->tos = (cork->tos != -1) ? cork->tos : inet->tos;
        iph->frag_off = df;
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
@@ -1331,7 +1345,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
                ip_options_build(skb, opt, cork->addr, rt, 0);
        }
 
-       skb->priority = sk->sk_priority;
+       skb->priority = (cork->tos != -1) ? cork->priority: sk->sk_priority;
        skb->mark = sk->sk_mark;
        /*
         * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
@@ -1481,6 +1495,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
        ipc.addr = daddr;
        ipc.opt = NULL;
        ipc.tx_flags = 0;
+       ipc.ttl = 0;
+       ipc.tos = -1;
 
        if (replyopts.opt.opt.optlen) {
                ipc.opt = &replyopts.opt;
index d9c4f11..3f85826 100644 (file)
@@ -189,7 +189,7 @@ EXPORT_SYMBOL(ip_cmsg_recv);
 
 int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
 {
-       int err;
+       int err, val;
        struct cmsghdr *cmsg;
 
        for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
@@ -215,6 +215,24 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
                        ipc->addr = info->ipi_spec_dst.s_addr;
                        break;
                }
+               case IP_TTL:
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
+                               return -EINVAL;
+                       val = *(int *)CMSG_DATA(cmsg);
+                       if (val < 1 || val > 255)
+                               return -EINVAL;
+                       ipc->ttl = val;
+                       break;
+               case IP_TOS:
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
+                               return -EINVAL;
+                       val = *(int *)CMSG_DATA(cmsg);
+                       if (val < 0 || val > 255)
+                               return -EINVAL;
+                       ipc->tos = val;
+                       ipc->priority = rt_tos2priority(ipc->tos);
+                       break;
+
                default:
                        return -EINVAL;
                }
@@ -609,7 +627,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
                inet->nodefrag = val ? 1 : 0;
                break;
        case IP_MTU_DISCOVER:
-               if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE)
+               if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_INTERFACE)
                        goto e_inval;
                inet->pmtudisc = val;
                break;
@@ -1034,11 +1052,12 @@ e_inval:
  * destination in skb->cb[] before dst drop.
  * This way, receiver doesnt make cache line misses to read rtable.
  */
-void ipv4_pktinfo_prepare(struct sk_buff *skb)
+void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
 {
        struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
 
-       if (skb_rtable(skb)) {
+       if ((inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) &&
+           skb_rtable(skb)) {
                pktinfo->ipi_ifindex = inet_iif(skb);
                pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb);
        } else {
index ac9fabe..63a6d6d 100644 (file)
@@ -623,6 +623,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                        tunnel->err_count = 0;
        }
 
+       tos = ip_tunnel_ecn_encap(tos, inner_iph, skb);
        ttl = tnl_params->ttl;
        if (ttl == 0) {
                if (skb->protocol == htons(ETH_P_IP))
@@ -641,18 +642,17 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
 
        max_headroom = LL_RESERVED_SPACE(rt->dst.dev) + sizeof(struct iphdr)
                        + rt->dst.header_len;
-       if (max_headroom > dev->needed_headroom) {
+       if (max_headroom > dev->needed_headroom)
                dev->needed_headroom = max_headroom;
-               if (skb_cow_head(skb, dev->needed_headroom)) {
-                       dev->stats.tx_dropped++;
-                       dev_kfree_skb(skb);
-                       return;
-               }
+
+       if (skb_cow_head(skb, dev->needed_headroom)) {
+               dev->stats.tx_dropped++;
+               dev_kfree_skb(skb);
+               return;
        }
 
        err = iptunnel_xmit(rt, skb, fl4.saddr, fl4.daddr, protocol,
-                           ip_tunnel_ecn_encap(tos, inner_iph, skb), ttl, df,
-                           !net_eq(tunnel->net, dev_net(dev)));
+                           tos, ttl, df, !net_eq(tunnel->net, dev_net(dev)));
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
 
        return;
@@ -853,8 +853,10 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
        /* FB netdevice is special: we have one, and only one per netns.
         * Allowing to move it to another netns is clearly unsafe.
         */
-       if (!IS_ERR(itn->fb_tunnel_dev))
+       if (!IS_ERR(itn->fb_tunnel_dev)) {
                itn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
+               ip_tunnel_add(itn, netdev_priv(itn->fb_tunnel_dev));
+       }
        rtnl_unlock();
 
        return PTR_RET(itn->fb_tunnel_dev);
@@ -884,8 +886,6 @@ static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head,
                        if (!net_eq(dev_net(t->dev), net))
                                unregister_netdevice_queue(t->dev, head);
        }
-       if (itn->fb_tunnel_dev)
-               unregister_netdevice_queue(itn->fb_tunnel_dev, head);
 }
 
 void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops)
index d6c856b..42ffbc8 100644 (file)
@@ -61,7 +61,7 @@ int iptunnel_xmit(struct rtable *rt, struct sk_buff *skb,
        memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 
        /* Push down and install the IP header. */
-       __skb_push(skb, sizeof(struct iphdr));
+       skb_push(skb, sizeof(struct iphdr));
        skb_reset_network_header(skb);
 
        iph = ip_hdr(skb);
@@ -116,3 +116,36 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
        return 0;
 }
 EXPORT_SYMBOL_GPL(iptunnel_pull_header);
+
+struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
+                                        bool csum_help,
+                                        int gso_type_mask)
+{
+       int err;
+
+       if (likely(!skb->encapsulation)) {
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
+       }
+
+       if (skb_is_gso(skb)) {
+               err = skb_unclone(skb, GFP_ATOMIC);
+               if (unlikely(err))
+                       goto error;
+               skb_shinfo(skb)->gso_type |= gso_type_mask;
+               return skb;
+       }
+
+       if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
+               err = skb_checksum_help(skb);
+               if (unlikely(err))
+                       goto error;
+       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
+               skb->ip_summed = CHECKSUM_NONE;
+
+       return skb;
+error:
+       kfree_skb(skb);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(iptunnel_handle_offloads);
index e805e7b..5d9c845 100644 (file)
@@ -49,70 +49,6 @@ static struct rtnl_link_ops vti_link_ops __read_mostly;
 static int vti_net_id __read_mostly;
 static int vti_tunnel_init(struct net_device *dev);
 
-static int vti_err(struct sk_buff *skb, u32 info)
-{
-
-       /* All the routers (except for Linux) return only
-        * 8 bytes of packet payload. It means, that precise relaying of
-        * ICMP in the real Internet is absolutely infeasible.
-        */
-       struct net *net = dev_net(skb->dev);
-       struct ip_tunnel_net *itn = net_generic(net, vti_net_id);
-       struct iphdr *iph = (struct iphdr *)skb->data;
-       const int type = icmp_hdr(skb)->type;
-       const int code = icmp_hdr(skb)->code;
-       struct ip_tunnel *t;
-       int err;
-
-       switch (type) {
-       default:
-       case ICMP_PARAMETERPROB:
-               return 0;
-
-       case ICMP_DEST_UNREACH:
-               switch (code) {
-               case ICMP_SR_FAILED:
-               case ICMP_PORT_UNREACH:
-                       /* Impossible event. */
-                       return 0;
-               default:
-                       /* All others are translated to HOST_UNREACH. */
-                       break;
-               }
-               break;
-       case ICMP_TIME_EXCEEDED:
-               if (code != ICMP_EXC_TTL)
-                       return 0;
-               break;
-       }
-
-       err = -ENOENT;
-
-       t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
-                            iph->daddr, iph->saddr, 0);
-       if (t == NULL)
-               goto out;
-
-       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
-               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->parms.link, 0, IPPROTO_IPIP, 0);
-               err = 0;
-               goto out;
-       }
-
-       err = 0;
-       if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
-               goto out;
-
-       if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
-               t->err_count++;
-       else
-               t->err_count = 1;
-       t->err_time = jiffies;
-out:
-       return err;
-}
-
 /* We dont digest the packet therefore let the packet pass */
 static int vti_rcv(struct sk_buff *skb)
 {
@@ -125,8 +61,17 @@ static int vti_rcv(struct sk_buff *skb)
                                  iph->saddr, iph->daddr, 0);
        if (tunnel != NULL) {
                struct pcpu_tstats *tstats;
+               u32 oldmark = skb->mark;
+               int ret;
+
 
-               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+               /* temporarily mark the skb with the tunnel o_key, to
+                * only match policies with this mark.
+                */
+               skb->mark = be32_to_cpu(tunnel->parms.o_key);
+               ret = xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb);
+               skb->mark = oldmark;
+               if (!ret)
                        return -1;
 
                tstats = this_cpu_ptr(tunnel->dev->tstats);
@@ -135,7 +80,6 @@ static int vti_rcv(struct sk_buff *skb)
                tstats->rx_bytes += skb->len;
                u64_stats_update_end(&tstats->syncp);
 
-               skb->mark = 0;
                secpath_reset(skb);
                skb->dev = tunnel->dev;
                return 1;
@@ -167,7 +111,7 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 
        memset(&fl4, 0, sizeof(fl4));
        flowi4_init_output(&fl4, tunnel->parms.link,
-                          be32_to_cpu(tunnel->parms.i_key), RT_TOS(tos),
+                          be32_to_cpu(tunnel->parms.o_key), RT_TOS(tos),
                           RT_SCOPE_UNIVERSE,
                           IPPROTO_IPIP, 0,
                           dst, tiph->saddr, 0, 0);
@@ -296,9 +240,8 @@ static void __net_init vti_fb_tunnel_init(struct net_device *dev)
        iph->ihl                = 5;
 }
 
-static struct xfrm_tunnel vti_handler __read_mostly = {
+static struct xfrm_tunnel_notifier vti_handler __read_mostly = {
        .handler        =       vti_rcv,
-       .err_handler    =       vti_err,
        .priority       =       1,
 };
 
index 7f80fb4..fe3e9f7 100644 (file)
@@ -220,17 +220,17 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if (unlikely(skb->protocol != htons(ETH_P_IP)))
                goto tx_error;
 
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP);
+       if (IS_ERR(skb))
+               goto out;
 
        ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
        return NETDEV_TX_OK;
 
 tx_error:
-       dev->stats.tx_errors++;
        dev_kfree_skb(skb);
+out:
+       dev->stats.tx_errors++;
        return NETDEV_TX_OK;
 }
 
@@ -275,6 +275,7 @@ static const struct net_device_ops ipip_netdev_ops = {
 #define IPIP_FEATURES (NETIF_F_SG |            \
                       NETIF_F_FRAGLIST |       \
                       NETIF_F_HIGHDMA |        \
+                      NETIF_F_GSO_SOFTWARE |   \
                       NETIF_F_HW_CSUM)
 
 static void ipip_tunnel_setup(struct net_device *dev)
index 1657e39..40d5607 100644 (file)
@@ -36,6 +36,27 @@ config NF_CONNTRACK_PROC_COMPAT
 
          If unsure, say Y.
 
+config NF_TABLES_IPV4
+       depends on NF_TABLES
+       tristate "IPv4 nf_tables support"
+
+config NFT_REJECT_IPV4
+       depends on NF_TABLES_IPV4
+       tristate "nf_tables IPv4 reject support"
+
+config NFT_CHAIN_ROUTE_IPV4
+       depends on NF_TABLES_IPV4
+       tristate "IPv4 nf_tables route chain support"
+
+config NFT_CHAIN_NAT_IPV4
+       depends on NF_TABLES_IPV4
+       depends on NF_NAT_IPV4 && NFT_NAT
+       tristate "IPv4 nf_tables nat chain support"
+
+config NF_TABLES_ARP
+       depends on NF_TABLES
+       tristate "ARP nf_tables support"
+
 config IP_NF_IPTABLES
        tristate "IP tables support (required for filtering/masq/NAT)"
        default m if NETFILTER_ADVANCED=n
index 3622b24..19df72b 100644 (file)
@@ -27,6 +27,12 @@ obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o
 # NAT protocols (nf_nat)
 obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
 
+obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o
+obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
+obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
+obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
+obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o
+
 # generic IP tables 
 obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
 
index 85a4f21..59da7cd 100644 (file)
@@ -271,6 +271,11 @@ unsigned int arpt_do_table(struct sk_buff *skb,
        local_bh_disable();
        addend = xt_write_recseq_begin();
        private = table->private;
+       /*
+        * Ensure we load private-> members after we've fetched the base
+        * pointer.
+        */
+       smp_read_barrier_depends();
        table_base = private->entries[smp_processor_id()];
 
        e = get_entry(table_base, private->hook_entry[hook]);
index a865f6f..802ddec 100644 (file)
@@ -27,13 +27,14 @@ static const struct xt_table packet_filter = {
 
 /* The work comes in here from netfilter.c */
 static unsigned int
-arptable_filter_hook(unsigned int hook, struct sk_buff *skb,
+arptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
        const struct net *net = dev_net((in != NULL) ? in : out);
 
-       return arpt_do_table(skb, hook, in, out, net->ipv4.arptable_filter);
+       return arpt_do_table(skb, ops->hooknum, in, out,
+                            net->ipv4.arptable_filter);
 }
 
 static struct nf_hook_ops *arpfilter_ops __read_mostly;
index d23118d..718dfbd 100644 (file)
@@ -327,6 +327,11 @@ ipt_do_table(struct sk_buff *skb,
        addend = xt_write_recseq_begin();
        private = table->private;
        cpu        = smp_processor_id();
+       /*
+        * Ensure we load private-> members after we've fetched the base
+        * pointer.
+        */
+       smp_read_barrier_depends();
        table_base = private->entries[cpu];
        jumpstack  = (struct ipt_entry **)private->jumpstack[cpu];
        stackptr   = per_cpu_ptr(private->stackptr, cpu);
index 0b732ef..2510c02 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
 #include <net/netfilter/nf_conntrack.h>
 #include <net/net_namespace.h>
+#include <net/netns/generic.h>
 #include <net/checksum.h>
 #include <net/ip.h>
 
@@ -57,15 +58,21 @@ struct clusterip_config {
        struct rcu_head rcu;
 };
 
-static LIST_HEAD(clusterip_configs);
+#ifdef CONFIG_PROC_FS
+static const struct file_operations clusterip_proc_fops;
+#endif
 
-/* clusterip_lock protects the clusterip_configs list */
-static DEFINE_SPINLOCK(clusterip_lock);
+static int clusterip_net_id __read_mostly;
+
+struct clusterip_net {
+       struct list_head configs;
+       /* lock protects the configs list */
+       spinlock_t lock;
 
 #ifdef CONFIG_PROC_FS
-static const struct file_operations clusterip_proc_fops;
-static struct proc_dir_entry *clusterip_procdir;
+       struct proc_dir_entry *procdir;
 #endif
+};
 
 static inline void
 clusterip_config_get(struct clusterip_config *c)
@@ -92,10 +99,13 @@ clusterip_config_put(struct clusterip_config *c)
 static inline void
 clusterip_config_entry_put(struct clusterip_config *c)
 {
+       struct net *net = dev_net(c->dev);
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+
        local_bh_disable();
-       if (atomic_dec_and_lock(&c->entries, &clusterip_lock)) {
+       if (atomic_dec_and_lock(&c->entries, &cn->lock)) {
                list_del_rcu(&c->list);
-               spin_unlock(&clusterip_lock);
+               spin_unlock(&cn->lock);
                local_bh_enable();
 
                dev_mc_del(c->dev, c->clustermac);
@@ -113,11 +123,12 @@ clusterip_config_entry_put(struct clusterip_config *c)
 }
 
 static struct clusterip_config *
-__clusterip_config_find(__be32 clusterip)
+__clusterip_config_find(struct net *net, __be32 clusterip)
 {
        struct clusterip_config *c;
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
 
-       list_for_each_entry_rcu(c, &clusterip_configs, list) {
+       list_for_each_entry_rcu(c, &cn->configs, list) {
                if (c->clusterip == clusterip)
                        return c;
        }
@@ -126,12 +137,12 @@ __clusterip_config_find(__be32 clusterip)
 }
 
 static inline struct clusterip_config *
-clusterip_config_find_get(__be32 clusterip, int entry)
+clusterip_config_find_get(struct net *net, __be32 clusterip, int entry)
 {
        struct clusterip_config *c;
 
        rcu_read_lock_bh();
-       c = __clusterip_config_find(clusterip);
+       c = __clusterip_config_find(net, clusterip);
        if (c) {
                if (unlikely(!atomic_inc_not_zero(&c->refcount)))
                        c = NULL;
@@ -158,6 +169,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
                        struct net_device *dev)
 {
        struct clusterip_config *c;
+       struct clusterip_net *cn = net_generic(dev_net(dev), clusterip_net_id);
 
        c = kzalloc(sizeof(*c), GFP_ATOMIC);
        if (!c)
@@ -180,7 +192,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
                /* create proc dir entry */
                sprintf(buffer, "%pI4", &ip);
                c->pde = proc_create_data(buffer, S_IWUSR|S_IRUSR,
-                                         clusterip_procdir,
+                                         cn->procdir,
                                          &clusterip_proc_fops, c);
                if (!c->pde) {
                        kfree(c);
@@ -189,9 +201,9 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
        }
 #endif
 
-       spin_lock_bh(&clusterip_lock);
-       list_add_rcu(&c->list, &clusterip_configs);
-       spin_unlock_bh(&clusterip_lock);
+       spin_lock_bh(&cn->lock);
+       list_add_rcu(&c->list, &cn->configs);
+       spin_unlock_bh(&cn->lock);
 
        return c;
 }
@@ -370,7 +382,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
 
        /* FIXME: further sanity checks */
 
-       config = clusterip_config_find_get(e->ip.dst.s_addr, 1);
+       config = clusterip_config_find_get(par->net, e->ip.dst.s_addr, 1);
        if (!config) {
                if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
                        pr_info("no config found for %pI4, need 'new'\n",
@@ -384,7 +396,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
                                return -EINVAL;
                        }
 
-                       dev = dev_get_by_name(&init_net, e->ip.iniface);
+                       dev = dev_get_by_name(par->net, e->ip.iniface);
                        if (!dev) {
                                pr_info("no such interface %s\n",
                                        e->ip.iniface);
@@ -483,7 +495,7 @@ static void arp_print(struct arp_payload *payload)
 #endif
 
 static unsigned int
-arp_mangle(unsigned int hook,
+arp_mangle(const struct nf_hook_ops *ops,
           struct sk_buff *skb,
           const struct net_device *in,
           const struct net_device *out,
@@ -492,6 +504,7 @@ arp_mangle(unsigned int hook,
        struct arphdr *arp = arp_hdr(skb);
        struct arp_payload *payload;
        struct clusterip_config *c;
+       struct net *net = dev_net(in ? in : out);
 
        /* we don't care about non-ethernet and non-ipv4 ARP */
        if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
@@ -508,7 +521,7 @@ arp_mangle(unsigned int hook,
 
        /* if there is no clusterip configuration for the arp reply's
         * source ip, we don't want to mangle it */
-       c = clusterip_config_find_get(payload->src_ip, 0);
+       c = clusterip_config_find_get(net, payload->src_ip, 0);
        if (!c)
                return NF_ACCEPT;
 
@@ -698,48 +711,75 @@ static const struct file_operations clusterip_proc_fops = {
 
 #endif /* CONFIG_PROC_FS */
 
+static int clusterip_net_init(struct net *net)
+{
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+
+       INIT_LIST_HEAD(&cn->configs);
+
+       spin_lock_init(&cn->lock);
+
+#ifdef CONFIG_PROC_FS
+       cn->procdir = proc_mkdir("ipt_CLUSTERIP", net->proc_net);
+       if (!cn->procdir) {
+               pr_err("Unable to proc dir entry\n");
+               return -ENOMEM;
+       }
+#endif /* CONFIG_PROC_FS */
+
+       return 0;
+}
+
+static void clusterip_net_exit(struct net *net)
+{
+#ifdef CONFIG_PROC_FS
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+       proc_remove(cn->procdir);
+#endif
+}
+
+static struct pernet_operations clusterip_net_ops = {
+       .init = clusterip_net_init,
+       .exit = clusterip_net_exit,
+       .id   = &clusterip_net_id,
+       .size = sizeof(struct clusterip_net),
+};
+
 static int __init clusterip_tg_init(void)
 {
        int ret;
 
-       ret = xt_register_target(&clusterip_tg_reg);
+       ret = register_pernet_subsys(&clusterip_net_ops);
        if (ret < 0)
                return ret;
 
+       ret = xt_register_target(&clusterip_tg_reg);
+       if (ret < 0)
+               goto cleanup_subsys;
+
        ret = nf_register_hook(&cip_arp_ops);
        if (ret < 0)
                goto cleanup_target;
 
-#ifdef CONFIG_PROC_FS
-       clusterip_procdir = proc_mkdir("ipt_CLUSTERIP", init_net.proc_net);
-       if (!clusterip_procdir) {
-               pr_err("Unable to proc dir entry\n");
-               ret = -ENOMEM;
-               goto cleanup_hook;
-       }
-#endif /* CONFIG_PROC_FS */
-
        pr_info("ClusterIP Version %s loaded successfully\n",
                CLUSTERIP_VERSION);
+
        return 0;
 
-#ifdef CONFIG_PROC_FS
-cleanup_hook:
-       nf_unregister_hook(&cip_arp_ops);
-#endif /* CONFIG_PROC_FS */
 cleanup_target:
        xt_unregister_target(&clusterip_tg_reg);
+cleanup_subsys:
+       unregister_pernet_subsys(&clusterip_net_ops);
        return ret;
 }
 
 static void __exit clusterip_tg_exit(void)
 {
        pr_info("ClusterIP Version %s unloading\n", CLUSTERIP_VERSION);
-#ifdef CONFIG_PROC_FS
-       proc_remove(clusterip_procdir);
-#endif
+
        nf_unregister_hook(&cip_arp_ops);
        xt_unregister_target(&clusterip_tg_reg);
+       unregister_pernet_subsys(&clusterip_net_ops);
 
        /* Wait for completion of call_rcu_bh()'s (clusterip_config_rcu_free) */
        rcu_barrier_bh();
index 67e17dc..01cffea 100644 (file)
@@ -267,7 +267,8 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
        if (th == NULL)
                return NF_DROP;
 
-       synproxy_parse_options(skb, par->thoff, th, &opts);
+       if (!synproxy_parse_options(skb, par->thoff, th, &opts))
+               return NF_DROP;
 
        if (th->syn && !(th->ack || th->fin || th->rst)) {
                /* Initial SYN from client */
@@ -296,7 +297,7 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
        return XT_CONTINUE;
 }
 
-static unsigned int ipv4_synproxy_hook(unsigned int hooknum,
+static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops *ops,
                                       struct sk_buff *skb,
                                       const struct net_device *in,
                                       const struct net_device *out,
@@ -350,7 +351,8 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum,
 
                /* fall through */
        case TCP_CONNTRACK_SYN_SENT:
-               synproxy_parse_options(skb, thoff, th, &opts);
+               if (!synproxy_parse_options(skb, thoff, th, &opts))
+                       return NF_DROP;
 
                if (!th->syn && th->ack &&
                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
@@ -373,7 +375,9 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum,
                if (!th->syn || !th->ack)
                        break;
 
-               synproxy_parse_options(skb, thoff, th, &opts);
+               if (!synproxy_parse_options(skb, thoff, th, &opts))
+                       return NF_DROP;
+
                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
                        synproxy->tsoff = opts.tsval - synproxy->its;
 
index cbc2215..9cb993c 100644 (file)
@@ -220,6 +220,7 @@ static void ipt_ulog_packet(struct net *net,
        ub->qlen++;
 
        pm = nlmsg_data(nlh);
+       memset(pm, 0, sizeof(*pm));
 
        /* We might not have a timestamp, get one */
        if (skb->tstamp.tv64 == 0)
@@ -238,8 +239,6 @@ static void ipt_ulog_packet(struct net *net,
        }
        else if (loginfo->prefix[0] != '\0')
                strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix));
-       else
-               *(pm->prefix) = '\0';
 
        if (in && in->hard_header_len > 0 &&
            skb->mac_header != skb->network_header &&
@@ -251,13 +250,9 @@ static void ipt_ulog_packet(struct net *net,
 
        if (in)
                strncpy(pm->indev_name, in->name, sizeof(pm->indev_name));
-       else
-               pm->indev_name[0] = '\0';
 
        if (out)
                strncpy(pm->outdev_name, out->name, sizeof(pm->outdev_name));
-       else
-               pm->outdev_name[0] = '\0';
 
        /* copy_len <= skb->len, so can't fail. */
        if (skb_copy_bits(skb, 0, pm->payload, copy_len) < 0)
index 50af5b4..e08a74a 100644 (file)
@@ -33,20 +33,21 @@ static const struct xt_table packet_filter = {
 };
 
 static unsigned int
-iptable_filter_hook(unsigned int hook, struct sk_buff *skb,
+iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                    const struct net_device *in, const struct net_device *out,
                    int (*okfn)(struct sk_buff *))
 {
        const struct net *net;
 
-       if (hook == NF_INET_LOCAL_OUT &&
+       if (ops->hooknum == NF_INET_LOCAL_OUT &&
            (skb->len < sizeof(struct iphdr) ||
             ip_hdrlen(skb) < sizeof(struct iphdr)))
                /* root is playing with raw sockets. */
                return NF_ACCEPT;
 
        net = dev_net((in != NULL) ? in : out);
-       return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_filter);
+       return ipt_do_table(skb, ops->hooknum, in, out,
+                           net->ipv4.iptable_filter);
 }
 
 static struct nf_hook_ops *filter_ops __read_mostly;
index 0d8cd82..6a5079c 100644 (file)
@@ -79,19 +79,19 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-iptable_mangle_hook(unsigned int hook,
+iptable_mangle_hook(const struct nf_hook_ops *ops,
                     struct sk_buff *skb,
                     const struct net_device *in,
                     const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
-       if (hook == NF_INET_LOCAL_OUT)
+       if (ops->hooknum == NF_INET_LOCAL_OUT)
                return ipt_mangle_out(skb, out);
-       if (hook == NF_INET_POST_ROUTING)
-               return ipt_do_table(skb, hook, in, out,
+       if (ops->hooknum == NF_INET_POST_ROUTING)
+               return ipt_do_table(skb, ops->hooknum, in, out,
                                    dev_net(out)->ipv4.iptable_mangle);
        /* PREROUTING/INPUT/FORWARD: */
-       return ipt_do_table(skb, hook, in, out,
+       return ipt_do_table(skb, ops->hooknum, in, out,
                            dev_net(in)->ipv4.iptable_mangle);
 }
 
index 683bfaf..ee28861 100644 (file)
@@ -61,7 +61,7 @@ static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
 }
 
 static unsigned int
-nf_nat_ipv4_fn(unsigned int hooknum,
+nf_nat_ipv4_fn(const struct nf_hook_ops *ops,
               struct sk_buff *skb,
               const struct net_device *in,
               const struct net_device *out,
@@ -71,7 +71,7 @@ nf_nat_ipv4_fn(unsigned int hooknum,
        enum ip_conntrack_info ctinfo;
        struct nf_conn_nat *nat;
        /* maniptype == SRC for postrouting. */
-       enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
+       enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
 
        /* We never see fragments: conntrack defrags on pre-routing
         * and local-out, and nf_nat_out protects post-routing.
@@ -108,7 +108,7 @@ nf_nat_ipv4_fn(unsigned int hooknum,
        case IP_CT_RELATED_REPLY:
                if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
                        if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
-                                                          hooknum))
+                                                          ops->hooknum))
                                return NF_DROP;
                        else
                                return NF_ACCEPT;
@@ -121,14 +121,14 @@ nf_nat_ipv4_fn(unsigned int hooknum,
                if (!nf_nat_initialized(ct, maniptype)) {
                        unsigned int ret;
 
-                       ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
+                       ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct);
                        if (ret != NF_ACCEPT)
                                return ret;
                } else {
                        pr_debug("Already setup manip %s for ct %p\n",
                                 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
                                 ct);
-                       if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+                       if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
                                goto oif_changed;
                }
                break;
@@ -137,11 +137,11 @@ nf_nat_ipv4_fn(unsigned int hooknum,
                /* ESTABLISHED */
                NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
                             ctinfo == IP_CT_ESTABLISHED_REPLY);
-               if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+               if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
                        goto oif_changed;
        }
 
-       return nf_nat_packet(ct, ctinfo, hooknum, skb);
+       return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
 
 oif_changed:
        nf_ct_kill_acct(ct, ctinfo, skb);
@@ -149,7 +149,7 @@ oif_changed:
 }
 
 static unsigned int
-nf_nat_ipv4_in(unsigned int hooknum,
+nf_nat_ipv4_in(const struct nf_hook_ops *ops,
               struct sk_buff *skb,
               const struct net_device *in,
               const struct net_device *out,
@@ -158,7 +158,7 @@ nf_nat_ipv4_in(unsigned int hooknum,
        unsigned int ret;
        __be32 daddr = ip_hdr(skb)->daddr;
 
-       ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
+       ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            daddr != ip_hdr(skb)->daddr)
                skb_dst_drop(skb);
@@ -167,7 +167,7 @@ nf_nat_ipv4_in(unsigned int hooknum,
 }
 
 static unsigned int
-nf_nat_ipv4_out(unsigned int hooknum,
+nf_nat_ipv4_out(const struct nf_hook_ops *ops,
                struct sk_buff *skb,
                const struct net_device *in,
                const struct net_device *out,
@@ -185,7 +185,7 @@ nf_nat_ipv4_out(unsigned int hooknum,
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
+       ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn);
 #ifdef CONFIG_XFRM
        if (ret != NF_DROP && ret != NF_STOLEN &&
            !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
@@ -207,7 +207,7 @@ nf_nat_ipv4_out(unsigned int hooknum,
 }
 
 static unsigned int
-nf_nat_ipv4_local_fn(unsigned int hooknum,
+nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
                     struct sk_buff *skb,
                     const struct net_device *in,
                     const struct net_device *out,
@@ -223,7 +223,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
+       ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
index 1f82aea..b2f7e8f 100644 (file)
@@ -20,20 +20,20 @@ static const struct xt_table packet_raw = {
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-iptable_raw_hook(unsigned int hook, struct sk_buff *skb,
+iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                 const struct net_device *in, const struct net_device *out,
                 int (*okfn)(struct sk_buff *))
 {
        const struct net *net;
 
-       if (hook == NF_INET_LOCAL_OUT && 
+       if (ops->hooknum == NF_INET_LOCAL_OUT &&
            (skb->len < sizeof(struct iphdr) ||
             ip_hdrlen(skb) < sizeof(struct iphdr)))
                /* root is playing with raw sockets. */
                return NF_ACCEPT;
 
        net = dev_net((in != NULL) ? in : out);
-       return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_raw);
+       return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.iptable_raw);
 }
 
 static struct nf_hook_ops *rawtable_ops __read_mostly;
index f867a8d..c86647e 100644 (file)
@@ -37,21 +37,22 @@ static const struct xt_table security_table = {
 };
 
 static unsigned int
-iptable_security_hook(unsigned int hook, struct sk_buff *skb,
+iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                      const struct net_device *in,
                      const struct net_device *out,
                      int (*okfn)(struct sk_buff *))
 {
        const struct net *net;
 
-       if (hook == NF_INET_LOCAL_OUT &&
+       if (ops->hooknum == NF_INET_LOCAL_OUT &&
            (skb->len < sizeof(struct iphdr) ||
             ip_hdrlen(skb) < sizeof(struct iphdr)))
                /* Somebody is playing with raw sockets. */
                return NF_ACCEPT;
 
        net = dev_net((in != NULL) ? in : out);
-       return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_security);
+       return ipt_do_table(skb, ops->hooknum, in, out,
+                           net->ipv4.iptable_security);
 }
 
 static struct nf_hook_ops *sectbl_ops __read_mostly;
index 86f5b34..ecd8bec 100644 (file)
@@ -92,7 +92,7 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
        return NF_ACCEPT;
 }
 
-static unsigned int ipv4_helper(unsigned int hooknum,
+static unsigned int ipv4_helper(const struct nf_hook_ops *ops,
                                struct sk_buff *skb,
                                const struct net_device *in,
                                const struct net_device *out,
@@ -121,7 +121,7 @@ static unsigned int ipv4_helper(unsigned int hooknum,
                            ct, ctinfo);
 }
 
-static unsigned int ipv4_confirm(unsigned int hooknum,
+static unsigned int ipv4_confirm(const struct nf_hook_ops *ops,
                                 struct sk_buff *skb,
                                 const struct net_device *in,
                                 const struct net_device *out,
@@ -147,16 +147,16 @@ out:
        return nf_conntrack_confirm(skb);
 }
 
-static unsigned int ipv4_conntrack_in(unsigned int hooknum,
+static unsigned int ipv4_conntrack_in(const struct nf_hook_ops *ops,
                                      struct sk_buff *skb,
                                      const struct net_device *in,
                                      const struct net_device *out,
                                      int (*okfn)(struct sk_buff *))
 {
-       return nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb);
+       return nf_conntrack_in(dev_net(in), PF_INET, ops->hooknum, skb);
 }
 
-static unsigned int ipv4_conntrack_local(unsigned int hooknum,
+static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops,
                                         struct sk_buff *skb,
                                         const struct net_device *in,
                                         const struct net_device *out,
@@ -166,7 +166,7 @@ static unsigned int ipv4_conntrack_local(unsigned int hooknum,
        if (skb->len < sizeof(struct iphdr) ||
            ip_hdrlen(skb) < sizeof(struct iphdr))
                return NF_ACCEPT;
-       return nf_conntrack_in(dev_net(out), PF_INET, hooknum, skb);
+       return nf_conntrack_in(dev_net(out), PF_INET, ops->hooknum, skb);
 }
 
 /* Connection tracking may drop packets, but never alters them, so
index 7428155..12e13bd 100644 (file)
@@ -60,7 +60,7 @@ static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum,
                return IP_DEFRAG_CONNTRACK_OUT + zone;
 }
 
-static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
+static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops,
                                          struct sk_buff *skb,
                                          const struct net_device *in,
                                          const struct net_device *out,
@@ -83,7 +83,9 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
 #endif
        /* Gather fragments. */
        if (ip_is_fragment(ip_hdr(skb))) {
-               enum ip_defrag_users user = nf_ct_defrag_user(hooknum, skb);
+               enum ip_defrag_users user =
+                       nf_ct_defrag_user(ops->hooknum, skb);
+
                if (nf_ct_ipv4_gather_frags(skb, user))
                        return NF_STOLEN;
        }
diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c
new file mode 100644 (file)
index 0000000..3e67ef1
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2008-2010 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netfilter_arp.h>
+#include <net/netfilter/nf_tables.h>
+
+static struct nft_af_info nft_af_arp __read_mostly = {
+       .family         = NFPROTO_ARP,
+       .nhooks         = NF_ARP_NUMHOOKS,
+       .owner          = THIS_MODULE,
+};
+
+static int nf_tables_arp_init_net(struct net *net)
+{
+       net->nft.arp = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
+       if (net->nft.arp== NULL)
+               return -ENOMEM;
+
+       memcpy(net->nft.arp, &nft_af_arp, sizeof(nft_af_arp));
+
+       if (nft_register_afinfo(net, net->nft.arp) < 0)
+               goto err;
+
+       return 0;
+err:
+       kfree(net->nft.arp);
+       return -ENOMEM;
+}
+
+static void nf_tables_arp_exit_net(struct net *net)
+{
+       nft_unregister_afinfo(net->nft.arp);
+       kfree(net->nft.arp);
+}
+
+static struct pernet_operations nf_tables_arp_net_ops = {
+       .init   = nf_tables_arp_init_net,
+       .exit   = nf_tables_arp_exit_net,
+};
+
+static unsigned int
+nft_do_chain_arp(const struct nf_hook_ops *ops,
+                 struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 int (*okfn)(struct sk_buff *))
+{
+       struct nft_pktinfo pkt;
+
+       nft_set_pktinfo(&pkt, ops, skb, in, out);
+
+       return nft_do_chain_pktinfo(&pkt, ops);
+}
+
+static struct nf_chain_type filter_arp = {
+       .family         = NFPROTO_ARP,
+       .name           = "filter",
+       .type           = NFT_CHAIN_T_DEFAULT,
+       .hook_mask      = (1 << NF_ARP_IN) |
+                         (1 << NF_ARP_OUT) |
+                         (1 << NF_ARP_FORWARD),
+       .fn             = {
+               [NF_ARP_IN]             = nft_do_chain_arp,
+               [NF_ARP_OUT]            = nft_do_chain_arp,
+               [NF_ARP_FORWARD]        = nft_do_chain_arp,
+       },
+};
+
+static int __init nf_tables_arp_init(void)
+{
+       int ret;
+
+       nft_register_chain_type(&filter_arp);
+       ret = register_pernet_subsys(&nf_tables_arp_net_ops);
+       if (ret < 0)
+               nft_unregister_chain_type(&filter_arp);
+
+       return ret;
+}
+
+static void __exit nf_tables_arp_exit(void)
+{
+       unregister_pernet_subsys(&nf_tables_arp_net_ops);
+       nft_unregister_chain_type(&filter_arp);
+}
+
+module_init(nf_tables_arp_init);
+module_exit(nf_tables_arp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_FAMILY(3); /* NFPROTO_ARP */
diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c
new file mode 100644 (file)
index 0000000..0f4cbfe
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012-2013 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/net_namespace.h>
+#include <net/ip.h>
+#include <net/netfilter/nf_tables_ipv4.h>
+
+static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
+                                   struct sk_buff *skb,
+                                   const struct net_device *in,
+                                   const struct net_device *out,
+                                   int (*okfn)(struct sk_buff *))
+{
+       struct nft_pktinfo pkt;
+
+       if (unlikely(skb->len < sizeof(struct iphdr) ||
+                    ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) {
+               if (net_ratelimit())
+                       pr_info("nf_tables_ipv4: ignoring short SOCK_RAW "
+                               "packet\n");
+               return NF_ACCEPT;
+       }
+       nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+
+       return nft_do_chain_pktinfo(&pkt, ops);
+}
+
+static struct nft_af_info nft_af_ipv4 __read_mostly = {
+       .family         = NFPROTO_IPV4,
+       .nhooks         = NF_INET_NUMHOOKS,
+       .owner          = THIS_MODULE,
+       .hooks          = {
+               [NF_INET_LOCAL_OUT]     = nft_ipv4_output,
+       },
+};
+
+static int nf_tables_ipv4_init_net(struct net *net)
+{
+       net->nft.ipv4 = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
+       if (net->nft.ipv4 == NULL)
+               return -ENOMEM;
+
+       memcpy(net->nft.ipv4, &nft_af_ipv4, sizeof(nft_af_ipv4));
+
+       if (nft_register_afinfo(net, net->nft.ipv4) < 0)
+               goto err;
+
+       return 0;
+err:
+       kfree(net->nft.ipv4);
+       return -ENOMEM;
+}
+
+static void nf_tables_ipv4_exit_net(struct net *net)
+{
+       nft_unregister_afinfo(net->nft.ipv4);
+       kfree(net->nft.ipv4);
+}
+
+static struct pernet_operations nf_tables_ipv4_net_ops = {
+       .init   = nf_tables_ipv4_init_net,
+       .exit   = nf_tables_ipv4_exit_net,
+};
+
+static unsigned int
+nft_do_chain_ipv4(const struct nf_hook_ops *ops,
+                 struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 int (*okfn)(struct sk_buff *))
+{
+       struct nft_pktinfo pkt;
+
+       nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+
+       return nft_do_chain_pktinfo(&pkt, ops);
+}
+
+static struct nf_chain_type filter_ipv4 = {
+       .family         = NFPROTO_IPV4,
+       .name           = "filter",
+       .type           = NFT_CHAIN_T_DEFAULT,
+       .hook_mask      = (1 << NF_INET_LOCAL_IN) |
+                         (1 << NF_INET_LOCAL_OUT) |
+                         (1 << NF_INET_FORWARD) |
+                         (1 << NF_INET_PRE_ROUTING) |
+                         (1 << NF_INET_POST_ROUTING),
+       .fn             = {
+               [NF_INET_LOCAL_IN]      = nft_do_chain_ipv4,
+               [NF_INET_LOCAL_OUT]     = nft_ipv4_output,
+               [NF_INET_FORWARD]       = nft_do_chain_ipv4,
+               [NF_INET_PRE_ROUTING]   = nft_do_chain_ipv4,
+               [NF_INET_POST_ROUTING]  = nft_do_chain_ipv4,
+       },
+};
+
+static int __init nf_tables_ipv4_init(void)
+{
+       nft_register_chain_type(&filter_ipv4);
+       return register_pernet_subsys(&nf_tables_ipv4_net_ops);
+}
+
+static void __exit nf_tables_ipv4_exit(void)
+{
+       unregister_pernet_subsys(&nf_tables_ipv4_net_ops);
+       nft_unregister_chain_type(&filter_ipv4);
+}
+
+module_init(nf_tables_ipv4_init);
+module_exit(nf_tables_ipv4_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_FAMILY(AF_INET);
diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c
new file mode 100644 (file)
index 0000000..cf2c792
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
+ * Copyright (c) 2012 Intel 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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_ipv4.h>
+#include <net/netfilter/nf_nat_l3proto.h>
+#include <net/ip.h>
+
+/*
+ * NAT chains
+ */
+
+static unsigned int nf_nat_fn(const struct nf_hook_ops *ops,
+                             struct sk_buff *skb,
+                             const struct net_device *in,
+                             const struct net_device *out,
+                             int (*okfn)(struct sk_buff *))
+{
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+       struct nf_conn_nat *nat;
+       enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
+       struct nft_pktinfo pkt;
+       unsigned int ret;
+
+       if (ct == NULL || nf_ct_is_untracked(ct))
+               return NF_ACCEPT;
+
+       NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)));
+
+       nat = nfct_nat(ct);
+       if (nat == NULL) {
+               /* Conntrack module was loaded late, can't add extension. */
+               if (nf_ct_is_confirmed(ct))
+                       return NF_ACCEPT;
+               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
+               if (nat == NULL)
+                       return NF_ACCEPT;
+       }
+
+       switch (ctinfo) {
+       case IP_CT_RELATED:
+       case IP_CT_RELATED + IP_CT_IS_REPLY:
+               if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
+                       if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
+                                                          ops->hooknum))
+                               return NF_DROP;
+                       else
+                               return NF_ACCEPT;
+               }
+               /* Fall through */
+       case IP_CT_NEW:
+               if (nf_nat_initialized(ct, maniptype))
+                       break;
+
+               nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+
+               ret = nft_do_chain_pktinfo(&pkt, ops);
+               if (ret != NF_ACCEPT)
+                       return ret;
+               if (!nf_nat_initialized(ct, maniptype)) {
+                       ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
+                       if (ret != NF_ACCEPT)
+                               return ret;
+               }
+       default:
+               break;
+       }
+
+       return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
+}
+
+static unsigned int nf_nat_prerouting(const struct nf_hook_ops *ops,
+                                     struct sk_buff *skb,
+                                     const struct net_device *in,
+                                     const struct net_device *out,
+                                     int (*okfn)(struct sk_buff *))
+{
+       __be32 daddr = ip_hdr(skb)->daddr;
+       unsigned int ret;
+
+       ret = nf_nat_fn(ops, skb, in, out, okfn);
+       if (ret != NF_DROP && ret != NF_STOLEN &&
+           ip_hdr(skb)->daddr != daddr) {
+               skb_dst_drop(skb);
+       }
+       return ret;
+}
+
+static unsigned int nf_nat_postrouting(const struct nf_hook_ops *ops,
+                                      struct sk_buff *skb,
+                                      const struct net_device *in,
+                                      const struct net_device *out,
+                                      int (*okfn)(struct sk_buff *))
+{
+       enum ip_conntrack_info ctinfo __maybe_unused;
+       const struct nf_conn *ct __maybe_unused;
+       unsigned int ret;
+
+       ret = nf_nat_fn(ops, skb, in, out, okfn);
+#ifdef CONFIG_XFRM
+       if (ret != NF_DROP && ret != NF_STOLEN &&
+           (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
+               enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+
+               if (ct->tuplehash[dir].tuple.src.u3.ip !=
+                   ct->tuplehash[!dir].tuple.dst.u3.ip ||
+                   ct->tuplehash[dir].tuple.src.u.all !=
+                   ct->tuplehash[!dir].tuple.dst.u.all)
+                       return nf_xfrm_me_harder(skb, AF_INET) == 0 ?
+                                                               ret : NF_DROP;
+       }
+#endif
+       return ret;
+}
+
+static unsigned int nf_nat_output(const struct nf_hook_ops *ops,
+                                 struct sk_buff *skb,
+                                 const struct net_device *in,
+                                 const struct net_device *out,
+                                 int (*okfn)(struct sk_buff *))
+{
+       enum ip_conntrack_info ctinfo;
+       const struct nf_conn *ct;
+       unsigned int ret;
+
+       ret = nf_nat_fn(ops, skb, in, out, okfn);
+       if (ret != NF_DROP && ret != NF_STOLEN &&
+           (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
+               enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+
+               if (ct->tuplehash[dir].tuple.dst.u3.ip !=
+                   ct->tuplehash[!dir].tuple.src.u3.ip) {
+                       if (ip_route_me_harder(skb, RTN_UNSPEC))
+                               ret = NF_DROP;
+               }
+#ifdef CONFIG_XFRM
+               else if (ct->tuplehash[dir].tuple.dst.u.all !=
+                        ct->tuplehash[!dir].tuple.src.u.all)
+                       if (nf_xfrm_me_harder(skb, AF_INET))
+                               ret = NF_DROP;
+#endif
+       }
+       return ret;
+}
+
+static struct nf_chain_type nft_chain_nat_ipv4 = {
+       .family         = NFPROTO_IPV4,
+       .name           = "nat",
+       .type           = NFT_CHAIN_T_NAT,
+       .hook_mask      = (1 << NF_INET_PRE_ROUTING) |
+                         (1 << NF_INET_POST_ROUTING) |
+                         (1 << NF_INET_LOCAL_OUT) |
+                         (1 << NF_INET_LOCAL_IN),
+       .fn             = {
+               [NF_INET_PRE_ROUTING]   = nf_nat_prerouting,
+               [NF_INET_POST_ROUTING]  = nf_nat_postrouting,
+               [NF_INET_LOCAL_OUT]     = nf_nat_output,
+               [NF_INET_LOCAL_IN]      = nf_nat_fn,
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init nft_chain_nat_init(void)
+{
+       int err;
+
+       err = nft_register_chain_type(&nft_chain_nat_ipv4);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static void __exit nft_chain_nat_exit(void)
+{
+       nft_unregister_chain_type(&nft_chain_nat_ipv4);
+}
+
+module_init(nft_chain_nat_init);
+module_exit(nft_chain_nat_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat");
diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c
new file mode 100644 (file)
index 0000000..4e6bf9a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_ipv4.h>
+#include <net/route.h>
+#include <net/ip.h>
+
+static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
+                                       struct sk_buff *skb,
+                                       const struct net_device *in,
+                                       const struct net_device *out,
+                                       int (*okfn)(struct sk_buff *))
+{
+       unsigned int ret;
+       struct nft_pktinfo pkt;
+       u32 mark;
+       __be32 saddr, daddr;
+       u_int8_t tos;
+       const struct iphdr *iph;
+
+       /* root is playing with raw sockets. */
+       if (skb->len < sizeof(struct iphdr) ||
+           ip_hdrlen(skb) < sizeof(struct iphdr))
+               return NF_ACCEPT;
+
+       nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+
+       mark = skb->mark;
+       iph = ip_hdr(skb);
+       saddr = iph->saddr;
+       daddr = iph->daddr;
+       tos = iph->tos;
+
+       ret = nft_do_chain_pktinfo(&pkt, ops);
+       if (ret != NF_DROP && ret != NF_QUEUE) {
+               iph = ip_hdr(skb);
+
+               if (iph->saddr != saddr ||
+                   iph->daddr != daddr ||
+                   skb->mark != mark ||
+                   iph->tos != tos)
+                       if (ip_route_me_harder(skb, RTN_UNSPEC))
+                               ret = NF_DROP;
+       }
+       return ret;
+}
+
+static struct nf_chain_type nft_chain_route_ipv4 = {
+       .family         = NFPROTO_IPV4,
+       .name           = "route",
+       .type           = NFT_CHAIN_T_ROUTE,
+       .hook_mask      = (1 << NF_INET_LOCAL_OUT),
+       .fn             = {
+               [NF_INET_LOCAL_OUT]     = nf_route_table_hook,
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init nft_chain_route_init(void)
+{
+       return nft_register_chain_type(&nft_chain_route_ipv4);
+}
+
+static void __exit nft_chain_route_exit(void)
+{
+       nft_unregister_chain_type(&nft_chain_route_ipv4);
+}
+
+module_init(nft_chain_route_init);
+module_exit(nft_chain_route_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_CHAIN(AF_INET, "route");
diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c
new file mode 100644 (file)
index 0000000..fff5ba1
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/icmp.h>
+
+struct nft_reject {
+       enum nft_reject_types   type:8;
+       u8                      icmp_code;
+};
+
+static void nft_reject_eval(const struct nft_expr *expr,
+                             struct nft_data data[NFT_REG_MAX + 1],
+                             const struct nft_pktinfo *pkt)
+{
+       struct nft_reject *priv = nft_expr_priv(expr);
+
+       switch (priv->type) {
+       case NFT_REJECT_ICMP_UNREACH:
+               icmp_send(pkt->skb, ICMP_DEST_UNREACH, priv->icmp_code, 0);
+               break;
+       case NFT_REJECT_TCP_RST:
+               break;
+       }
+
+       data[NFT_REG_VERDICT].verdict = NF_DROP;
+}
+
+static const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = {
+       [NFTA_REJECT_TYPE]              = { .type = NLA_U32 },
+       [NFTA_REJECT_ICMP_CODE]         = { .type = NLA_U8 },
+};
+
+static int nft_reject_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
+{
+       struct nft_reject *priv = nft_expr_priv(expr);
+
+       if (tb[NFTA_REJECT_TYPE] == NULL)
+               return -EINVAL;
+
+       priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
+       switch (priv->type) {
+       case NFT_REJECT_ICMP_UNREACH:
+               if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
+                       return -EINVAL;
+               priv->icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
+       case NFT_REJECT_TCP_RST:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_reject *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_REJECT_TYPE, priv->type))
+               goto nla_put_failure;
+
+       switch (priv->type) {
+       case NFT_REJECT_ICMP_UNREACH:
+               if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
+                       goto nla_put_failure;
+               break;
+       }
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_reject_type;
+static const struct nft_expr_ops nft_reject_ops = {
+       .type           = &nft_reject_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
+       .eval           = nft_reject_eval,
+       .init           = nft_reject_init,
+       .dump           = nft_reject_dump,
+};
+
+static struct nft_expr_type nft_reject_type __read_mostly = {
+       .name           = "reject",
+       .ops            = &nft_reject_ops,
+       .policy         = nft_reject_policy,
+       .maxattr        = NFTA_REJECT_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_reject_module_init(void)
+{
+       return nft_register_expr(&nft_reject_type);
+}
+
+static void __exit nft_reject_module_exit(void)
+{
+       nft_unregister_expr(&nft_reject_type);
+}
+
+module_init(nft_reject_module_init);
+module_exit(nft_reject_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("reject");
index d7d9882..9afbdb1 100644 (file)
@@ -202,15 +202,14 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
 #if IS_ENABLED(CONFIG_IPV6)
                } else if (skb->protocol == htons(ETH_P_IPV6) &&
                           sk->sk_family == AF_INET6) {
-                       struct ipv6_pinfo *np = inet6_sk(sk);
 
                        pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk,
                                 (int) isk->inet_num,
-                                &inet6_sk(sk)->rcv_saddr,
+                                &sk->sk_v6_rcv_saddr,
                                 sk->sk_bound_dev_if);
 
-                       if (!ipv6_addr_any(&np->rcv_saddr) &&
-                           !ipv6_addr_equal(&np->rcv_saddr,
+                       if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr) &&
+                           !ipv6_addr_equal(&sk->sk_v6_rcv_saddr,
                                             &ipv6_hdr(skb)->daddr))
                                continue;
 #endif
@@ -237,11 +236,11 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low,
        unsigned int seq;
 
        do {
-               seq = read_seqbegin(&sysctl_local_ports.lock);
+               seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock);
 
                *low = data[0];
                *high = data[1];
-       } while (read_seqretry(&sysctl_local_ports.lock, seq));
+       } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq));
 }
 
 
@@ -362,7 +361,7 @@ static void ping_set_saddr(struct sock *sk, struct sockaddr *saddr)
        } else if (saddr->sa_family == AF_INET6) {
                struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr;
                struct ipv6_pinfo *np = inet6_sk(sk);
-               np->rcv_saddr = np->saddr = addr->sin6_addr;
+               sk->sk_v6_rcv_saddr = np->saddr = addr->sin6_addr;
 #endif
        }
 }
@@ -376,7 +375,7 @@ static void ping_clear_saddr(struct sock *sk, int dif)
 #if IS_ENABLED(CONFIG_IPV6)
        } else if (sk->sk_family == AF_INET6) {
                struct ipv6_pinfo *np = inet6_sk(sk);
-               memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr));
+               memset(&sk->sk_v6_rcv_saddr, 0, sizeof(sk->sk_v6_rcv_saddr));
                memset(&np->saddr, 0, sizeof(np->saddr));
 #endif
        }
@@ -416,10 +415,12 @@ int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                 (int)sk->sk_bound_dev_if);
 
        err = 0;
-       if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) ||
-           (sk->sk_family == AF_INET6 &&
-            !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)))
+       if (sk->sk_family == AF_INET && isk->inet_rcv_saddr)
                sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
+#if IS_ENABLED(CONFIG_IPV6)
+       if (sk->sk_family == AF_INET6 && !ipv6_addr_any(&sk->sk_v6_rcv_saddr))
+               sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
+#endif
 
        if (snum)
                sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
@@ -429,7 +430,7 @@ int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 
 #if IS_ENABLED(CONFIG_IPV6)
        if (sk->sk_family == AF_INET6)
-               memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr));
+               memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
 #endif
 
        sk_dst_reset(sk);
@@ -713,6 +714,8 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        ipc.opt = NULL;
        ipc.oif = sk->sk_bound_dev_if;
        ipc.tx_flags = 0;
+       ipc.ttl = 0;
+       ipc.tos = -1;
 
        sock_tx_timestamp(sk, &ipc.tx_flags);
 
@@ -744,7 +747,7 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                        return -EINVAL;
                faddr = ipc.opt->opt.faddr;
        }
-       tos = RT_TOS(inet->tos);
+       tos = get_rttos(&ipc, inet);
        if (sock_flag(sk, SOCK_LOCALROUTE) ||
            (msg->msg_flags & MSG_DONTROUTE) ||
            (ipc.opt && ipc.opt->opt.is_strictroute)) {
index bfec521..41e1d28 100644 (file)
@@ -218,8 +218,10 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
                ipv4_sk_update_pmtu(skb, sk, info);
-       else if (type == ICMP_REDIRECT)
+       else if (type == ICMP_REDIRECT) {
                ipv4_sk_redirect(skb, sk);
+               return;
+       }
 
        /* Report error on raw socket, if:
           1. User requested ip_recverr.
@@ -297,7 +299,7 @@ static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        /* Charge it to the socket. */
 
-       ipv4_pktinfo_prepare(skb);
+       ipv4_pktinfo_prepare(sk, skb);
        if (sock_queue_rcv_skb(sk, skb) < 0) {
                kfree_skb(skb);
                return NET_RX_DROP;
@@ -517,6 +519,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        ipc.addr = inet->inet_saddr;
        ipc.opt = NULL;
        ipc.tx_flags = 0;
+       ipc.ttl = 0;
+       ipc.tos = -1;
        ipc.oif = sk->sk_bound_dev_if;
 
        if (msg->msg_controllen) {
@@ -556,7 +560,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                        daddr = ipc.opt->opt.faddr;
                }
        }
-       tos = RT_CONN_FLAGS(sk);
+       tos = get_rtconn_flags(&ipc, sk);
        if (msg->msg_flags & MSG_DONTROUTE)
                tos |= RTO_ONLINK;
 
index 727f436..f428935 100644 (file)
@@ -295,7 +295,7 @@ static int rt_cpu_seq_show(struct seq_file *seq, void *v)
        seq_printf(seq,"%08x  %08x %08x %08x %08x %08x %08x %08x "
                   " %08x %08x %08x %08x %08x %08x %08x %08x %08x \n",
                   dst_entries_get_slow(&ipv4_dst_ops),
-                  st->in_hit,
+                  0, /* st->in_hit */
                   st->in_slow_tot,
                   st->in_slow_mc,
                   st->in_no_route,
@@ -303,16 +303,16 @@ static int rt_cpu_seq_show(struct seq_file *seq, void *v)
                   st->in_martian_dst,
                   st->in_martian_src,
 
-                  st->out_hit,
+                  0, /* st->out_hit */
                   st->out_slow_tot,
                   st->out_slow_mc,
 
-                  st->gc_total,
-                  st->gc_ignored,
-                  st->gc_goal_miss,
-                  st->gc_dst_overflow,
-                  st->in_hlist_search,
-                  st->out_hlist_search
+                  0, /* st->gc_total */
+                  0, /* st->gc_ignored */
+                  0, /* st->gc_goal_miss */
+                  0, /* st->gc_dst_overflow */
+                  0, /* st->in_hlist_search */
+                  0  /* st->out_hlist_search */
                );
        return 0;
 }
@@ -1036,6 +1036,10 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
        bool new = false;
 
        bh_lock_sock(sk);
+
+       if (!ip_sk_accept_pmtu(sk))
+               goto out;
+
        rt = (struct rtable *) __sk_dst_get(sk);
 
        if (sock_owned_by_user(sk) || !rt) {
@@ -2072,7 +2076,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
                                                              RT_SCOPE_LINK);
                        goto make_route;
                }
-               if (fl4->saddr) {
+               if (!fl4->saddr) {
                        if (ipv4_is_multicast(fl4->daddr))
                                fl4->saddr = inet_select_addr(dev_out, 0,
                                                              fl4->flowi4_scope);
index 14a15c4..b95331e 100644 (file)
 
 extern int sysctl_tcp_syncookies;
 
-__u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
-EXPORT_SYMBOL(syncookie_secret);
-
-static __init int init_syncookies(void)
-{
-       get_random_bytes(syncookie_secret, sizeof(syncookie_secret));
-       return 0;
-}
-__initcall(init_syncookies);
+static u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
 
 #define COOKIEBITS 24  /* Upper bits store count */
 #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
@@ -44,8 +36,11 @@ static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS],
 static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport,
                       u32 count, int c)
 {
-       __u32 *tmp = __get_cpu_var(ipv4_cookie_scratch);
+       __u32 *tmp;
+
+       net_get_random_once(syncookie_secret, sizeof(syncookie_secret));
 
+       tmp  = __get_cpu_var(ipv4_cookie_scratch);
        memcpy(tmp + 4, syncookie_secret[c], sizeof(syncookie_secret[c]));
        tmp[0] = (__force u32)saddr;
        tmp[1] = (__force u32)daddr;
@@ -89,8 +84,7 @@ __u32 cookie_init_timestamp(struct request_sock *req)
 
 
 static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
-                                  __be16 dport, __u32 sseq, __u32 count,
-                                  __u32 data)
+                                  __be16 dport, __u32 sseq, __u32 data)
 {
        /*
         * Compute the secure sequence number.
@@ -102,7 +96,7 @@ static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
         * As an extra hack, we add a small "data" value that encodes the
         * MSS into the second hash value.
         */
-
+       u32 count = tcp_cookie_time();
        return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
                sseq + (count << COOKIEBITS) +
                ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
@@ -114,22 +108,21 @@ static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
  * If the syncookie is bad, the data returned will be out of
  * range.  This must be checked by the caller.
  *
- * The count value used to generate the cookie must be within
- * "maxdiff" if the current (passed-in) "count".  The return value
- * is (__u32)-1 if this test fails.
+ * The count value used to generate the cookie must be less than
+ * MAX_SYNCOOKIE_AGE minutes in the past.
+ * The return value (__u32)-1 if this test fails.
  */
 static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,
-                                 __be16 sport, __be16 dport, __u32 sseq,
-                                 __u32 count, __u32 maxdiff)
+                                 __be16 sport, __be16 dport, __u32 sseq)
 {
-       __u32 diff;
+       u32 diff, count = tcp_cookie_time();
 
        /* Strip away the layers from the cookie */
        cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
 
        /* Cookie is now reduced to (count * 2^24) ^ (hash % 2^24) */
        diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS);
-       if (diff >= maxdiff)
+       if (diff >= MAX_SYNCOOKIE_AGE)
                return (__u32)-1;
 
        return (cookie -
@@ -138,22 +131,22 @@ static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,
 }
 
 /*
- * MSS Values are taken from the 2009 paper
- * 'Measuring TCP Maximum Segment Size' by S. Alcock and R. Nelson:
- *  - values 1440 to 1460 accounted for 80% of observed mss values
- *  - values outside the 536-1460 range are rare (<0.2%).
+ * MSS Values are chosen based on the 2011 paper
+ * 'An Analysis of TCP Maximum Segement Sizes' by S. Alcock and R. Nelson.
+ * Values ..
+ *  .. lower than 536 are rare (< 0.2%)
+ *  .. between 537 and 1299 account for less than < 1.5% of observed values
+ *  .. in the 1300-1349 range account for about 15 to 20% of observed mss values
+ *  .. exceeding 1460 are very rare (< 0.04%)
  *
- * Table must be sorted.
+ *  1460 is the single most frequently announced mss value (30 to 46% depending
+ *  on monitor location).  Table must be sorted.
  */
 static __u16 const msstab[] = {
-       64,
-       512,
        536,
-       1024,
-       1440,
+       1300,
+       1440,   /* 1440, 1452: PPPoE */
        1460,
-       4312,
-       8960,
 };
 
 /*
@@ -173,7 +166,7 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
 
        return secure_tcp_syn_cookie(iph->saddr, iph->daddr,
                                     th->source, th->dest, ntohl(th->seq),
-                                    jiffies / (HZ * 60), mssind);
+                                    mssind);
 }
 EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence);
 
@@ -188,13 +181,6 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
        return __cookie_v4_init_sequence(iph, th, mssp);
 }
 
-/*
- * This (misnamed) value is the age of syncookie which is permitted.
- * Its ideal value should be dependent on TCP_TIMEOUT_INIT and
- * sysctl_tcp_retries1. It's a rather complicated formula (exponential
- * backoff) to compute at runtime so it's currently hardcoded here.
- */
-#define COUNTER_TRIES 4
 /*
  * Check if a ack sequence number is a valid syncookie.
  * Return the decoded mss if it is, or 0 if not.
@@ -204,9 +190,7 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
 {
        __u32 seq = ntohl(th->seq) - 1;
        __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
-                                           th->source, th->dest, seq,
-                                           jiffies / (HZ * 60),
-                                           COUNTER_TRIES);
+                                           th->source, th->dest, seq);
 
        return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
 }
@@ -315,10 +299,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
        treq->rcv_isn           = ntohl(th->seq) - 1;
        treq->snt_isn           = cookie;
        req->mss                = mss;
-       ireq->loc_port          = th->dest;
-       ireq->rmt_port          = th->source;
-       ireq->loc_addr          = ip_hdr(skb)->daddr;
-       ireq->rmt_addr          = ip_hdr(skb)->saddr;
+       ireq->ir_num            = ntohs(th->dest);
+       ireq->ir_rmt_port       = th->source;
+       ireq->ir_loc_addr       = ip_hdr(skb)->daddr;
+       ireq->ir_rmt_addr       = ip_hdr(skb)->saddr;
        ireq->ecn_ok            = ecn_ok;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
@@ -358,8 +342,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
        flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
                           inet_sk_flowi_flags(sk),
-                          (opt && opt->srr) ? opt->faddr : ireq->rmt_addr,
-                          ireq->loc_addr, th->source, th->dest);
+                          (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
+                          ireq->ir_loc_addr, th->source, th->dest);
        security_req_classify_flow(req, flowi4_to_flowi(&fl4));
        rt = ip_route_output_key(sock_net(sk), &fl4);
        if (IS_ERR(rt)) {
index 540279f..3d69ec8 100644 (file)
@@ -43,12 +43,12 @@ static int ip_ping_group_range_min[] = { 0, 0 };
 static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
 
 /* Update system visible IP port range */
-static void set_local_port_range(int range[2])
+static void set_local_port_range(struct net *net, int range[2])
 {
-       write_seqlock(&sysctl_local_ports.lock);
-       sysctl_local_ports.range[0] = range[0];
-       sysctl_local_ports.range[1] = range[1];
-       write_sequnlock(&sysctl_local_ports.lock);
+       write_seqlock(&net->ipv4.sysctl_local_ports.lock);
+       net->ipv4.sysctl_local_ports.range[0] = range[0];
+       net->ipv4.sysctl_local_ports.range[1] = range[1];
+       write_sequnlock(&net->ipv4.sysctl_local_ports.lock);
 }
 
 /* Validate changes from /proc interface. */
@@ -56,6 +56,8 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
                                 void __user *buffer,
                                 size_t *lenp, loff_t *ppos)
 {
+       struct net *net =
+               container_of(table->data, struct net, ipv4.sysctl_local_ports.range);
        int ret;
        int range[2];
        struct ctl_table tmp = {
@@ -66,14 +68,15 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
                .extra2 = &ip_local_port_range_max,
        };
 
-       inet_get_local_port_range(range, range + 1);
+       inet_get_local_port_range(net, &range[0], &range[1]);
+
        ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
        if (write && ret == 0) {
                if (range[1] < range[0])
                        ret = -EINVAL;
                else
-                       set_local_port_range(range);
+                       set_local_port_range(net, range);
        }
 
        return ret;
@@ -83,23 +86,27 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
 static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
 {
        kgid_t *data = table->data;
+       struct net *net =
+               container_of(table->data, struct net, ipv4.sysctl_ping_group_range);
        unsigned int seq;
        do {
-               seq = read_seqbegin(&sysctl_local_ports.lock);
+               seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock);
 
                *low = data[0];
                *high = data[1];
-       } while (read_seqretry(&sysctl_local_ports.lock, seq));
+       } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq));
 }
 
 /* Update system visible IP port range */
 static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t high)
 {
        kgid_t *data = table->data;
-       write_seqlock(&sysctl_local_ports.lock);
+       struct net *net =
+               container_of(table->data, struct net, ipv4.sysctl_ping_group_range);
+       write_seqlock(&net->ipv4.sysctl_local_ports.lock);
        data[0] = low;
        data[1] = high;
-       write_sequnlock(&sysctl_local_ports.lock);
+       write_sequnlock(&net->ipv4.sysctl_local_ports.lock);
 }
 
 /* Validate changes from /proc interface. */
@@ -193,49 +200,6 @@ static int proc_allowed_congestion_control(struct ctl_table *ctl,
        return ret;
 }
 
-static int ipv4_tcp_mem(struct ctl_table *ctl, int write,
-                          void __user *buffer, size_t *lenp,
-                          loff_t *ppos)
-{
-       int ret;
-       unsigned long vec[3];
-       struct net *net = current->nsproxy->net_ns;
-#ifdef CONFIG_MEMCG_KMEM
-       struct mem_cgroup *memcg;
-#endif
-
-       struct ctl_table tmp = {
-               .data = &vec,
-               .maxlen = sizeof(vec),
-               .mode = ctl->mode,
-       };
-
-       if (!write) {
-               ctl->data = &net->ipv4.sysctl_tcp_mem;
-               return proc_doulongvec_minmax(ctl, write, buffer, lenp, ppos);
-       }
-
-       ret = proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
-       if (ret)
-               return ret;
-
-#ifdef CONFIG_MEMCG_KMEM
-       rcu_read_lock();
-       memcg = mem_cgroup_from_task(current);
-
-       tcp_prot_mem(memcg, vec[0], 0);
-       tcp_prot_mem(memcg, vec[1], 1);
-       tcp_prot_mem(memcg, vec[2], 2);
-       rcu_read_unlock();
-#endif
-
-       net->ipv4.sysctl_tcp_mem[0] = vec[0];
-       net->ipv4.sysctl_tcp_mem[1] = vec[1];
-       net->ipv4.sysctl_tcp_mem[2] = vec[2];
-
-       return 0;
-}
-
 static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write,
                                 void __user *buffer, size_t *lenp,
                                 loff_t *ppos)
@@ -267,6 +231,11 @@ static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write,
                        ret = -EINVAL;
                        goto bad_key;
                }
+               /* Generate a dummy secret but don't publish it. This
+                * is needed so we don't regenerate a new key on the
+                * first invocation of tcp_fastopen_cookie_gen
+                */
+               tcp_fastopen_init_key_once(false);
                tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH);
        }
 
@@ -474,13 +443,6 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
-       {
-               .procname       = "ip_local_port_range",
-               .data           = &sysctl_local_ports.range,
-               .maxlen         = sizeof(sysctl_local_ports.range),
-               .mode           = 0644,
-               .proc_handler   = ipv4_local_port_range,
-       },
        {
                .procname       = "ip_local_reserved_ports",
                .data           = NULL, /* initialized in sysctl_ipv4_init */
@@ -551,6 +513,13 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "tcp_mem",
+               .maxlen         = sizeof(sysctl_tcp_mem),
+               .data           = &sysctl_tcp_mem,
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
        {
                .procname       = "tcp_wmem",
                .data           = &sysctl_tcp_wmem,
@@ -731,13 +700,6 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_allowed_congestion_control,
        },
-       {
-               .procname       = "tcp_max_ssthresh",
-               .data           = &sysctl_tcp_max_ssthresh,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
        {
                .procname       = "tcp_thin_linear_timeouts",
                .data           = &sysctl_tcp_thin_linear_timeouts,
@@ -854,10 +816,11 @@ static struct ctl_table ipv4_net_table[] = {
                .proc_handler   = proc_dointvec
        },
        {
-               .procname       = "tcp_mem",
-               .maxlen         = sizeof(init_net.ipv4.sysctl_tcp_mem),
+               .procname       = "ip_local_port_range",
+               .maxlen         = sizeof(init_net.ipv4.sysctl_local_ports.range),
+               .data           = &init_net.ipv4.sysctl_local_ports.range,
                .mode           = 0644,
-               .proc_handler   = ipv4_tcp_mem,
+               .proc_handler   = ipv4_local_port_range,
        },
        { }
 };
@@ -868,30 +831,15 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
 
        table = ipv4_net_table;
        if (!net_eq(net, &init_net)) {
+               int i;
+
                table = kmemdup(table, sizeof(ipv4_net_table), GFP_KERNEL);
                if (table == NULL)
                        goto err_alloc;
 
-               table[0].data =
-                       &net->ipv4.sysctl_icmp_echo_ignore_all;
-               table[1].data =
-                       &net->ipv4.sysctl_icmp_echo_ignore_broadcasts;
-               table[2].data =
-                       &net->ipv4.sysctl_icmp_ignore_bogus_error_responses;
-               table[3].data =
-                       &net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr;
-               table[4].data =
-                       &net->ipv4.sysctl_icmp_ratelimit;
-               table[5].data =
-                       &net->ipv4.sysctl_icmp_ratemask;
-               table[6].data =
-                       &net->ipv4.sysctl_ping_group_range;
-               table[7].data =
-                       &net->ipv4.sysctl_tcp_ecn;
-
-               /* Don't export sysctls to unprivileged users */
-               if (net->user_ns != &init_user_ns)
-                       table[0].procname = NULL;
+               /* Update the variables to point into the current struct net */
+               for (i = 0; i < ARRAY_SIZE(ipv4_net_table) - 1; i++)
+                       table[i].data += (void *)net - (void *)&init_net;
        }
 
        /*
@@ -901,7 +849,12 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
        net->ipv4.sysctl_ping_group_range[0] = make_kgid(&init_user_ns, 1);
        net->ipv4.sysctl_ping_group_range[1] = make_kgid(&init_user_ns, 0);
 
-       tcp_init_mem(net);
+       /*
+        * Set defaults for local port range
+        */
+       seqlock_init(&net->ipv4.sysctl_local_ports.lock);
+       net->ipv4.sysctl_local_ports.range[0] =  32768;
+       net->ipv4.sysctl_local_ports.range[1] =  61000;
 
        net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table);
        if (net->ipv4.ipv4_hdr == NULL)
index 6e5617b..8e8529d 100644 (file)
@@ -288,9 +288,11 @@ int sysctl_tcp_min_tso_segs __read_mostly = 2;
 struct percpu_counter tcp_orphan_count;
 EXPORT_SYMBOL_GPL(tcp_orphan_count);
 
+long sysctl_tcp_mem[3] __read_mostly;
 int sysctl_tcp_wmem[3] __read_mostly;
 int sysctl_tcp_rmem[3] __read_mostly;
 
+EXPORT_SYMBOL(sysctl_tcp_mem);
 EXPORT_SYMBOL(sysctl_tcp_rmem);
 EXPORT_SYMBOL(sysctl_tcp_wmem);
 
@@ -3097,13 +3099,13 @@ static int __init set_thash_entries(char *str)
 }
 __setup("thash_entries=", set_thash_entries);
 
-void tcp_init_mem(struct net *net)
+static void tcp_init_mem(void)
 {
        unsigned long limit = nr_free_buffer_pages() / 8;
        limit = max(limit, 128UL);
-       net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3;
-       net->ipv4.sysctl_tcp_mem[1] = limit;
-       net->ipv4.sysctl_tcp_mem[2] = net->ipv4.sysctl_tcp_mem[0] * 2;
+       sysctl_tcp_mem[0] = limit / 4 * 3;
+       sysctl_tcp_mem[1] = limit;
+       sysctl_tcp_mem[2] = sysctl_tcp_mem[0] * 2;
 }
 
 void __init tcp_init(void)
@@ -3137,10 +3139,9 @@ void __init tcp_init(void)
                                        &tcp_hashinfo.ehash_mask,
                                        0,
                                        thash_entries ? 0 : 512 * 1024);
-       for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) {
+       for (i = 0; i <= tcp_hashinfo.ehash_mask; i++)
                INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i);
-               INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].twchain, i);
-       }
+
        if (inet_ehash_locks_alloc(&tcp_hashinfo))
                panic("TCP: failed to alloc ehash_locks");
        tcp_hashinfo.bhash =
@@ -3166,7 +3167,7 @@ void __init tcp_init(void)
        sysctl_tcp_max_orphans = cnt / 2;
        sysctl_max_syn_backlog = max(128, cnt / 256);
 
-       tcp_init_mem(&init_net);
+       tcp_init_mem();
        /* Set per-socket limits to no more than 1/128 the pressure threshold */
        limit = nr_free_buffer_pages() << (PAGE_SHIFT - 7);
        max_wshare = min(4UL*1024*1024, limit);
index f45e1c2..821846f 100644 (file)
@@ -140,7 +140,8 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
                ca->cnt = 1;
 }
 
-static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                             u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct bictcp *ca = inet_csk_ca(sk);
@@ -149,7 +150,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
        else {
                bictcp_update(ca, tp->snd_cwnd);
                tcp_cong_avoid_ai(tp, ca->cnt);
index 019c238..ad37bf1 100644 (file)
@@ -15,8 +15,6 @@
 #include <linux/gfp.h>
 #include <net/tcp.h>
 
-int sysctl_tcp_max_ssthresh = 0;
-
 static DEFINE_SPINLOCK(tcp_cong_list_lock);
 static LIST_HEAD(tcp_cong_list);
 
@@ -299,35 +297,24 @@ bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
 }
 EXPORT_SYMBOL_GPL(tcp_is_cwnd_limited);
 
-/*
- * Slow start is used when congestion window is less than slow start
- * threshold. This version implements the basic RFC2581 version
- * and optionally supports:
- *     RFC3742 Limited Slow Start        - growth limited to max_ssthresh
- *     RFC3465 Appropriate Byte Counting - growth limited by bytes acknowledged
+/* Slow start is used when congestion window is no greater than the slow start
+ * threshold. We base on RFC2581 and also handle stretch ACKs properly.
+ * We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but
+ * something better;) a packet is only considered (s)acked in its entirety to
+ * defend the ACK attacks described in the RFC. Slow start processes a stretch
+ * ACK of degree N as if N acks of degree 1 are received back to back except
+ * ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and
+ * returns the leftover acks to adjust cwnd in congestion avoidance mode.
  */
-void tcp_slow_start(struct tcp_sock *tp)
+int tcp_slow_start(struct tcp_sock *tp, u32 acked)
 {
-       int cnt; /* increase in packets */
-       unsigned int delta = 0;
-       u32 snd_cwnd = tp->snd_cwnd;
-
-       if (unlikely(!snd_cwnd)) {
-               pr_err_once("snd_cwnd is nul, please report this bug.\n");
-               snd_cwnd = 1U;
-       }
+       u32 cwnd = tp->snd_cwnd + acked;
 
-       if (sysctl_tcp_max_ssthresh > 0 && tp->snd_cwnd > sysctl_tcp_max_ssthresh)
-               cnt = sysctl_tcp_max_ssthresh >> 1;     /* limited slow start */
-       else
-               cnt = snd_cwnd;                         /* exponential increase */
-
-       tp->snd_cwnd_cnt += cnt;
-       while (tp->snd_cwnd_cnt >= snd_cwnd) {
-               tp->snd_cwnd_cnt -= snd_cwnd;
-               delta++;
-       }
-       tp->snd_cwnd = min(snd_cwnd + delta, tp->snd_cwnd_clamp);
+       if (cwnd > tp->snd_ssthresh)
+               cwnd = tp->snd_ssthresh + 1;
+       acked -= cwnd - tp->snd_cwnd;
+       tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);
+       return acked;
 }
 EXPORT_SYMBOL_GPL(tcp_slow_start);
 
@@ -351,7 +338,7 @@ EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai);
 /* This is Jacobson's slow start and congestion avoidance.
  * SIGCOMM '88, p. 328.
  */
-void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
@@ -360,7 +347,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
 
        /* In "safe" area, increase. */
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
        /* In dangerous area, increase slowly. */
        else
                tcp_cong_avoid_ai(tp, tp->snd_cwnd);
index b6ae92a..828e4c3 100644 (file)
@@ -304,7 +304,8 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
                ca->cnt = 1;
 }
 
-static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                             u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct bictcp *ca = inet_csk_ca(sk);
@@ -315,7 +316,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
        if (tp->snd_cwnd <= tp->snd_ssthresh) {
                if (hystart && after(ack, ca->end_seq))
                        bictcp_hystart_reset(sk);
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
        } else {
                bictcp_update(ca, tp->snd_cwnd);
                tcp_cong_avoid_ai(tp, ca->cnt);
index ab7bd35..f195d93 100644 (file)
@@ -8,12 +8,26 @@
 #include <net/inetpeer.h>
 #include <net/tcp.h>
 
-int sysctl_tcp_fastopen __read_mostly;
+int sysctl_tcp_fastopen __read_mostly = TFO_CLIENT_ENABLE;
 
 struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
 
 static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock);
 
+void tcp_fastopen_init_key_once(bool publish)
+{
+       static u8 key[TCP_FASTOPEN_KEY_LENGTH];
+
+       /* tcp_fastopen_reset_cipher publishes the new context
+        * atomically, so we allow this race happening here.
+        *
+        * All call sites of tcp_fastopen_cookie_gen also check
+        * for a valid cookie, so this is an acceptable risk.
+        */
+       if (net_get_random_once(key, sizeof(key)) && publish)
+               tcp_fastopen_reset_cipher(key, sizeof(key));
+}
+
 static void tcp_fastopen_ctx_free(struct rcu_head *head)
 {
        struct tcp_fastopen_context *ctx =
@@ -70,6 +84,8 @@ void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
        __be32 path[4] = { src, dst, 0, 0 };
        struct tcp_fastopen_context *ctx;
 
+       tcp_fastopen_init_key_once(true);
+
        rcu_read_lock();
        ctx = rcu_dereference(tcp_fastopen_ctx);
        if (ctx) {
@@ -78,14 +94,3 @@ void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
        }
        rcu_read_unlock();
 }
-
-static int __init tcp_fastopen_init(void)
-{
-       __u8 key[TCP_FASTOPEN_KEY_LENGTH];
-
-       get_random_bytes(key, sizeof(key));
-       tcp_fastopen_reset_cipher(key, sizeof(key));
-       return 0;
-}
-
-late_initcall(tcp_fastopen_init);
index 30f27f6..8ed9305 100644 (file)
@@ -109,7 +109,7 @@ static void hstcp_init(struct sock *sk)
        tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128);
 }
 
-static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 in_flight)
+static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hstcp *ca = inet_csk_ca(sk);
@@ -118,7 +118,7 @@ static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 in_flight)
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
        else {
                /* Update AIMD parameters.
                 *
index c1a8175..4a194ac 100644 (file)
@@ -227,7 +227,7 @@ static u32 htcp_recalc_ssthresh(struct sock *sk)
        return max((tp->snd_cwnd * ca->beta) >> 7, 2U);
 }
 
-static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct htcp *ca = inet_csk_ca(sk);
@@ -236,7 +236,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
        else {
                /* In dangerous area, increase slowly.
                 * In theory this is tp->snd_cwnd += alpha / tp->snd_cwnd
index 57bdd17..478fe82 100644 (file)
@@ -85,7 +85,8 @@ static inline u32 hybla_fraction(u32 odds)
  *     o Give cwnd a new value based on the model proposed
  *     o remember increments <1
  */
-static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                            u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hybla *ca = inet_csk_ca(sk);
@@ -102,7 +103,7 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                return;
 
        if (!ca->hybla_en) {
-               tcp_reno_cong_avoid(sk, ack, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
                return;
        }
 
index 834857f..8a52099 100644 (file)
@@ -256,7 +256,8 @@ static void tcp_illinois_state(struct sock *sk, u8 new_state)
 /*
  * Increase window in response to successful acknowledgment.
  */
-static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                                   u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct illinois *ca = inet_csk_ca(sk);
@@ -270,7 +271,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
 
        /* In slow start */
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
 
        else {
                u32 delta;
index 25a89ea..c53b7f3 100644 (file)
@@ -267,11 +267,31 @@ static bool TCP_ECN_rcv_ecn_echo(const struct tcp_sock *tp, const struct tcphdr
  * 1. Tuning sk->sk_sndbuf, when connection enters established state.
  */
 
-static void tcp_fixup_sndbuf(struct sock *sk)
+static void tcp_sndbuf_expand(struct sock *sk)
 {
-       int sndmem = SKB_TRUESIZE(tcp_sk(sk)->rx_opt.mss_clamp + MAX_TCP_HEADER);
+       const struct tcp_sock *tp = tcp_sk(sk);
+       int sndmem, per_mss;
+       u32 nr_segs;
+
+       /* Worst case is non GSO/TSO : each frame consumes one skb
+        * and skb->head is kmalloced using power of two area of memory
+        */
+       per_mss = max_t(u32, tp->rx_opt.mss_clamp, tp->mss_cache) +
+                 MAX_TCP_HEADER +
+                 SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+       per_mss = roundup_pow_of_two(per_mss) +
+                 SKB_DATA_ALIGN(sizeof(struct sk_buff));
+
+       nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd);
+       nr_segs = max_t(u32, nr_segs, tp->reordering + 1);
+
+       /* Fast Recovery (RFC 5681 3.2) :
+        * Cubic needs 1.7 factor, rounded to 2 to include
+        * extra cushion (application might react slowly to POLLOUT)
+        */
+       sndmem = 2 * nr_segs * per_mss;
 
-       sndmem *= TCP_INIT_CWND;
        if (sk->sk_sndbuf < sndmem)
                sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]);
 }
@@ -355,6 +375,12 @@ static void tcp_fixup_rcvbuf(struct sock *sk)
        rcvmem = 2 * SKB_TRUESIZE(mss + MAX_TCP_HEADER) *
                 tcp_default_init_rwnd(mss);
 
+       /* Dynamic Right Sizing (DRS) has 2 to 3 RTT latency
+        * Allow enough cushion so that sender is not limited by our window
+        */
+       if (sysctl_tcp_moderate_rcvbuf)
+               rcvmem <<= 2;
+
        if (sk->sk_rcvbuf < rcvmem)
                sk->sk_rcvbuf = min(rcvmem, sysctl_tcp_rmem[2]);
 }
@@ -370,9 +396,11 @@ void tcp_init_buffer_space(struct sock *sk)
        if (!(sk->sk_userlocks & SOCK_RCVBUF_LOCK))
                tcp_fixup_rcvbuf(sk);
        if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK))
-               tcp_fixup_sndbuf(sk);
+               tcp_sndbuf_expand(sk);
 
        tp->rcvq_space.space = tp->rcv_wnd;
+       tp->rcvq_space.time = tcp_time_stamp;
+       tp->rcvq_space.seq = tp->copied_seq;
 
        maxwin = tcp_full_space(sk);
 
@@ -512,48 +540,62 @@ void tcp_rcv_space_adjust(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        int time;
-       int space;
-
-       if (tp->rcvq_space.time == 0)
-               goto new_measure;
+       int copied;
 
        time = tcp_time_stamp - tp->rcvq_space.time;
        if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0)
                return;
 
-       space = 2 * (tp->copied_seq - tp->rcvq_space.seq);
+       /* Number of bytes copied to user in last RTT */
+       copied = tp->copied_seq - tp->rcvq_space.seq;
+       if (copied <= tp->rcvq_space.space)
+               goto new_measure;
 
-       space = max(tp->rcvq_space.space, space);
+       /* A bit of theory :
+        * copied = bytes received in previous RTT, our base window
+        * To cope with packet losses, we need a 2x factor
+        * To cope with slow start, and sender growing its cwin by 100 %
+        * every RTT, we need a 4x factor, because the ACK we are sending
+        * now is for the next RTT, not the current one :
+        * <prev RTT . ><current RTT .. ><next RTT .... >
+        */
 
-       if (tp->rcvq_space.space != space) {
-               int rcvmem;
+       if (sysctl_tcp_moderate_rcvbuf &&
+           !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
+               int rcvwin, rcvmem, rcvbuf;
 
-               tp->rcvq_space.space = space;
+               /* minimal window to cope with packet losses, assuming
+                * steady state. Add some cushion because of small variations.
+                */
+               rcvwin = (copied << 1) + 16 * tp->advmss;
 
-               if (sysctl_tcp_moderate_rcvbuf &&
-                   !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
-                       int new_clamp = space;
+               /* If rate increased by 25%,
+                *      assume slow start, rcvwin = 3 * copied
+                * If rate increased by 50%,
+                *      assume sender can use 2x growth, rcvwin = 4 * copied
+                */
+               if (copied >=
+                   tp->rcvq_space.space + (tp->rcvq_space.space >> 2)) {
+                       if (copied >=
+                           tp->rcvq_space.space + (tp->rcvq_space.space >> 1))
+                               rcvwin <<= 1;
+                       else
+                               rcvwin += (rcvwin >> 1);
+               }
 
-                       /* Receive space grows, normalize in order to
-                        * take into account packet headers and sk_buff
-                        * structure overhead.
-                        */
-                       space /= tp->advmss;
-                       if (!space)
-                               space = 1;
-                       rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER);
-                       while (tcp_win_from_space(rcvmem) < tp->advmss)
-                               rcvmem += 128;
-                       space *= rcvmem;
-                       space = min(space, sysctl_tcp_rmem[2]);
-                       if (space > sk->sk_rcvbuf) {
-                               sk->sk_rcvbuf = space;
-
-                               /* Make the window clamp follow along.  */
-                               tp->window_clamp = new_clamp;
-                       }
+               rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER);
+               while (tcp_win_from_space(rcvmem) < tp->advmss)
+                       rcvmem += 128;
+
+               rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]);
+               if (rcvbuf > sk->sk_rcvbuf) {
+                       sk->sk_rcvbuf = rcvbuf;
+
+                       /* Make the window clamp follow along.  */
+                       tp->window_clamp = rcvwin;
                }
        }
+       tp->rcvq_space.space = copied;
 
 new_measure:
        tp->rcvq_space.seq = tp->copied_seq;
@@ -713,7 +755,12 @@ static void tcp_update_pacing_rate(struct sock *sk)
        if (tp->srtt > 8 + 2)
                do_div(rate, tp->srtt);
 
-       sk->sk_pacing_rate = min_t(u64, rate, ~0U);
+       /* ACCESS_ONCE() is needed because sch_fq fetches sk_pacing_rate
+        * without any lock. We want to make sure compiler wont store
+        * intermediate values in this location.
+        */
+       ACCESS_ONCE(sk->sk_pacing_rate) = min_t(u64, rate,
+                                               sk->sk_max_pacing_rate);
 }
 
 /* Calculate rto without backoff.  This is the second half of Van Jacobson's
@@ -1284,7 +1331,10 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
                tp->lost_cnt_hint -= tcp_skb_pcount(prev);
        }
 
-       TCP_SKB_CB(skb)->tcp_flags |= TCP_SKB_CB(prev)->tcp_flags;
+       TCP_SKB_CB(prev)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+       if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
+               TCP_SKB_CB(prev)->end_seq++;
+
        if (skb == tcp_highest_sack(sk))
                tcp_advance_highest_sack(sk, skb);
 
@@ -2853,7 +2903,8 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
         * left edge of the send window.
         * See draft-ietf-tcplw-high-performance-00, section 3.3.
         */
-       if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
+       if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
+           flag & FLAG_ACKED)
                seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
 
        if (seq_rtt < 0)
@@ -2868,20 +2919,25 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
 }
 
 /* Compute time elapsed between (last) SYNACK and the ACK completing 3WHS. */
-static void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req)
+static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        s32 seq_rtt = -1;
 
-       if (tp->lsndtime && !tp->total_retrans)
-               seq_rtt = tcp_time_stamp - tp->lsndtime;
-       tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1);
+       if (synack_stamp && !tp->total_retrans)
+               seq_rtt = tcp_time_stamp - synack_stamp;
+
+       /* If the ACK acks both the SYNACK and the (Fast Open'd) data packets
+        * sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack()
+        */
+       if (!tp->srtt)
+               tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1);
 }
 
-static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
-       icsk->icsk_ca_ops->cong_avoid(sk, ack, in_flight);
+       icsk->icsk_ca_ops->cong_avoid(sk, ack, acked, in_flight);
        tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -2970,7 +3026,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
        const struct inet_connection_sock *icsk = inet_csk(sk);
        struct sk_buff *skb;
        u32 now = tcp_time_stamp;
-       int fully_acked = true;
+       bool fully_acked = true;
        int flag = 0;
        u32 pkts_acked = 0;
        u32 reord = tp->packets_out;
@@ -2978,6 +3034,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
        s32 seq_rtt = -1;
        s32 ca_seq_rtt = -1;
        ktime_t last_ackt = net_invalid_timestamp();
+       bool rtt_update;
 
        while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {
                struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
@@ -3054,14 +3111,13 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
        if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED))
                flag |= FLAG_SACK_RENEGING;
 
-       if (tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt) ||
-           (flag & FLAG_ACKED))
-               tcp_rearm_rto(sk);
+       rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt);
 
        if (flag & FLAG_ACKED) {
                const struct tcp_congestion_ops *ca_ops
                        = inet_csk(sk)->icsk_ca_ops;
 
+               tcp_rearm_rto(sk);
                if (unlikely(icsk->icsk_mtup.probe_size &&
                             !after(tp->mtu_probe.probe_seq_end, tp->snd_una))) {
                        tcp_mtup_probe_success(sk);
@@ -3100,6 +3156,13 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
 
                        ca_ops->pkts_acked(sk, pkts_acked, rtt_us);
                }
+       } else if (skb && rtt_update && sack_rtt >= 0 &&
+                  sack_rtt > (s32)(now - TCP_SKB_CB(skb)->when)) {
+               /* Do not re-arm RTO if the sack RTT is measured from data sent
+                * after when the head was last (re)transmitted. Otherwise the
+                * timeout may continue to extend in loss recovery.
+                */
+               tcp_rearm_rto(sk);
        }
 
 #if FASTRETRANS_DEBUG > 0
@@ -3288,7 +3351,7 @@ static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag)
                        tcp_init_cwnd_reduction(sk, true);
                        tcp_set_ca_state(sk, TCP_CA_CWR);
                        tcp_end_cwnd_reduction(sk);
-                       tcp_set_ca_state(sk, TCP_CA_Open);
+                       tcp_try_keep_open(sk);
                        NET_INC_STATS_BH(sock_net(sk),
                                         LINUX_MIB_TCPLOSSPROBERECOVERY);
                }
@@ -3391,7 +3454,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 
        /* Advance cwnd if state allows */
        if (tcp_may_raise_cwnd(sk, flag))
-               tcp_cong_avoid(sk, ack, prior_in_flight);
+               tcp_cong_avoid(sk, ack, acked, prior_in_flight);
 
        if (tcp_ack_is_dubious(sk, flag)) {
                is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
@@ -4701,15 +4764,7 @@ static void tcp_new_space(struct sock *sk)
        struct tcp_sock *tp = tcp_sk(sk);
 
        if (tcp_should_expand_sndbuf(sk)) {
-               int sndmem = SKB_TRUESIZE(max_t(u32,
-                                               tp->rx_opt.mss_clamp,
-                                               tp->mss_cache) +
-                                         MAX_TCP_HEADER);
-               int demanded = max_t(unsigned int, tp->snd_cwnd,
-                                    tp->reordering + 1);
-               sndmem *= 2 * demanded;
-               if (sndmem > sk->sk_sndbuf)
-                       sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]);
+               tcp_sndbuf_expand(sk);
                tp->snd_cwnd_stamp = tcp_time_stamp;
        }
 
@@ -5584,6 +5639,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
        struct request_sock *req;
        int queued = 0;
        bool acceptable;
+       u32 synack_stamp;
 
        tp->rx_opt.saw_tstamp = 0;
 
@@ -5666,16 +5722,18 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                 * so release it.
                 */
                if (req) {
+                       synack_stamp = tcp_rsk(req)->snt_synack;
                        tp->total_retrans = req->num_retrans;
                        reqsk_fastopen_remove(sk, req, false);
                } else {
+                       synack_stamp = tp->lsndtime;
                        /* Make sure socket is routed, for correct metrics. */
                        icsk->icsk_af_ops->rebuild_header(sk);
                        tcp_init_congestion_control(sk);
 
                        tcp_mtup_init(sk);
-                       tcp_init_buffer_space(sk);
                        tp->copied_seq = tp->rcv_nxt;
+                       tcp_init_buffer_space(sk);
                }
                smp_mb();
                tcp_set_state(sk, TCP_ESTABLISHED);
@@ -5691,7 +5749,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
                tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale;
                tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
-               tcp_synack_rtt_meas(sk, req);
+               tcp_synack_rtt_meas(sk, synack_stamp);
 
                if (tp->rx_opt.tstamp_ok)
                        tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
@@ -5709,6 +5767,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                } else
                        tcp_init_metrics(sk);
 
+               tcp_update_pacing_rate(sk);
+
                /* Prevent spurious tcp_cwnd_restart() on first data packet */
                tp->lsndtime = tcp_time_stamp;
 
index b14266b..14bba8a 100644 (file)
@@ -288,6 +288,7 @@ static void tcp_v4_mtu_reduced(struct sock *sk)
        mtu = dst_mtu(dst);
 
        if (inet->pmtudisc != IP_PMTUDISC_DONT &&
+           ip_sk_accept_pmtu(sk) &&
            inet_csk(sk)->icsk_pmtu_cookie > mtu) {
                tcp_sync_mss(sk, mtu);
 
@@ -835,11 +836,11 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        skb = tcp_make_synack(sk, dst, req, NULL);
 
        if (skb) {
-               __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr);
+               __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr);
 
                skb_set_queue_mapping(skb, queue_mapping);
-               err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
-                                           ireq->rmt_addr,
+               err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
+                                           ireq->ir_rmt_addr,
                                            ireq->opt);
                err = net_xmit_eval(err);
                if (!tcp_rsk(req)->snt_synack && !err)
@@ -972,7 +973,7 @@ static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
 {
        union tcp_md5_addr *addr;
 
-       addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr;
+       addr = (union tcp_md5_addr *)&inet_rsk(req)->ir_rmt_addr;
        return tcp_md5_do_lookup(sk, addr, AF_INET);
 }
 
@@ -1149,8 +1150,8 @@ int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
                saddr = inet_sk(sk)->inet_saddr;
                daddr = inet_sk(sk)->inet_daddr;
        } else if (req) {
-               saddr = inet_rsk(req)->loc_addr;
-               daddr = inet_rsk(req)->rmt_addr;
+               saddr = inet_rsk(req)->ir_loc_addr;
+               daddr = inet_rsk(req)->ir_rmt_addr;
        } else {
                const struct iphdr *iph = ip_hdr(skb);
                saddr = iph->saddr;
@@ -1366,8 +1367,8 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk,
                kfree_skb(skb_synack);
                return -1;
        }
-       err = ip_build_and_send_pkt(skb_synack, sk, ireq->loc_addr,
-                                   ireq->rmt_addr, ireq->opt);
+       err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr,
+                                   ireq->ir_rmt_addr, ireq->opt);
        err = net_xmit_eval(err);
        if (!err)
                tcp_rsk(req)->snt_synack = tcp_time_stamp;
@@ -1410,8 +1411,8 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk,
        inet_csk(child)->icsk_af_ops->rebuild_header(child);
        tcp_init_congestion_control(child);
        tcp_mtup_init(child);
-       tcp_init_buffer_space(child);
        tcp_init_metrics(child);
+       tcp_init_buffer_space(child);
 
        /* Queue the data carried in the SYN packet. We need to first
         * bump skb's refcnt because the caller will attempt to free it.
@@ -1502,8 +1503,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_openreq_init(req, &tmp_opt, skb);
 
        ireq = inet_rsk(req);
-       ireq->loc_addr = daddr;
-       ireq->rmt_addr = saddr;
+       ireq->ir_loc_addr = daddr;
+       ireq->ir_rmt_addr = saddr;
        ireq->no_srccheck = inet_sk(sk)->transparent;
        ireq->opt = tcp_v4_save_options(skb);
 
@@ -1578,15 +1579,15 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
            fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
 
        if (skb_synack) {
-               __tcp_v4_send_check(skb_synack, ireq->loc_addr, ireq->rmt_addr);
+               __tcp_v4_send_check(skb_synack, ireq->ir_loc_addr, ireq->ir_rmt_addr);
                skb_set_queue_mapping(skb_synack, skb_get_queue_mapping(skb));
        } else
                goto drop_and_free;
 
        if (likely(!do_fastopen)) {
                int err;
-               err = ip_build_and_send_pkt(skb_synack, sk, ireq->loc_addr,
-                    ireq->rmt_addr, ireq->opt);
+               err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr,
+                    ireq->ir_rmt_addr, ireq->opt);
                err = net_xmit_eval(err);
                if (err || want_cookie)
                        goto drop_and_free;
@@ -1644,9 +1645,9 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        newtp                 = tcp_sk(newsk);
        newinet               = inet_sk(newsk);
        ireq                  = inet_rsk(req);
-       newinet->inet_daddr   = ireq->rmt_addr;
-       newinet->inet_rcv_saddr = ireq->loc_addr;
-       newinet->inet_saddr           = ireq->loc_addr;
+       newinet->inet_daddr   = ireq->ir_rmt_addr;
+       newinet->inet_rcv_saddr = ireq->ir_loc_addr;
+       newinet->inet_saddr           = ireq->ir_loc_addr;
        inet_opt              = ireq->opt;
        rcu_assign_pointer(newinet->inet_opt, inet_opt);
        ireq->opt             = NULL;
@@ -2194,18 +2195,6 @@ EXPORT_SYMBOL(tcp_v4_destroy_sock);
 #ifdef CONFIG_PROC_FS
 /* Proc filesystem TCP sock list dumping. */
 
-static inline struct inet_timewait_sock *tw_head(struct hlist_nulls_head *head)
-{
-       return hlist_nulls_empty(head) ? NULL :
-               list_entry(head->first, struct inet_timewait_sock, tw_node);
-}
-
-static inline struct inet_timewait_sock *tw_next(struct inet_timewait_sock *tw)
-{
-       return !is_a_nulls(tw->tw_node.next) ?
-               hlist_nulls_entry(tw->tw_node.next, typeof(*tw), tw_node) : NULL;
-}
-
 /*
  * Get next listener socket follow cur.  If cur is NULL, get first socket
  * starting from bucket given in st->bucket; when st->bucket is zero the
@@ -2309,10 +2298,9 @@ static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
        return rc;
 }
 
-static inline bool empty_bucket(struct tcp_iter_state *st)
+static inline bool empty_bucket(const struct tcp_iter_state *st)
 {
-       return hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].chain) &&
-               hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].twchain);
+       return hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].chain);
 }
 
 /*
@@ -2329,7 +2317,6 @@ static void *established_get_first(struct seq_file *seq)
        for (; st->bucket <= tcp_hashinfo.ehash_mask; ++st->bucket) {
                struct sock *sk;
                struct hlist_nulls_node *node;
-               struct inet_timewait_sock *tw;
                spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, st->bucket);
 
                /* Lockless fast path for the common case of empty buckets */
@@ -2345,18 +2332,7 @@ static void *established_get_first(struct seq_file *seq)
                        rc = sk;
                        goto out;
                }
-               st->state = TCP_SEQ_STATE_TIME_WAIT;
-               inet_twsk_for_each(tw, node,
-                                  &tcp_hashinfo.ehash[st->bucket].twchain) {
-                       if (tw->tw_family != st->family ||
-                           !net_eq(twsk_net(tw), net)) {
-                               continue;
-                       }
-                       rc = tw;
-                       goto out;
-               }
                spin_unlock_bh(lock);
-               st->state = TCP_SEQ_STATE_ESTABLISHED;
        }
 out:
        return rc;
@@ -2365,7 +2341,6 @@ out:
 static void *established_get_next(struct seq_file *seq, void *cur)
 {
        struct sock *sk = cur;
-       struct inet_timewait_sock *tw;
        struct hlist_nulls_node *node;
        struct tcp_iter_state *st = seq->private;
        struct net *net = seq_file_net(seq);
@@ -2373,45 +2348,16 @@ static void *established_get_next(struct seq_file *seq, void *cur)
        ++st->num;
        ++st->offset;
 
-       if (st->state == TCP_SEQ_STATE_TIME_WAIT) {
-               tw = cur;
-               tw = tw_next(tw);
-get_tw:
-               while (tw && (tw->tw_family != st->family || !net_eq(twsk_net(tw), net))) {
-                       tw = tw_next(tw);
-               }
-               if (tw) {
-                       cur = tw;
-                       goto out;
-               }
-               spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
-               st->state = TCP_SEQ_STATE_ESTABLISHED;
-
-               /* Look for next non empty bucket */
-               st->offset = 0;
-               while (++st->bucket <= tcp_hashinfo.ehash_mask &&
-                               empty_bucket(st))
-                       ;
-               if (st->bucket > tcp_hashinfo.ehash_mask)
-                       return NULL;
-
-               spin_lock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
-               sk = sk_nulls_head(&tcp_hashinfo.ehash[st->bucket].chain);
-       } else
-               sk = sk_nulls_next(sk);
+       sk = sk_nulls_next(sk);
 
        sk_nulls_for_each_from(sk, node) {
                if (sk->sk_family == st->family && net_eq(sock_net(sk), net))
-                       goto found;
+                       return sk;
        }
 
-       st->state = TCP_SEQ_STATE_TIME_WAIT;
-       tw = tw_head(&tcp_hashinfo.ehash[st->bucket].twchain);
-       goto get_tw;
-found:
-       cur = sk;
-out:
-       return cur;
+       spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
+       ++st->bucket;
+       return established_get_first(seq);
 }
 
 static void *established_get_idx(struct seq_file *seq, loff_t pos)
@@ -2464,10 +2410,9 @@ static void *tcp_seek_last_pos(struct seq_file *seq)
                if (rc)
                        break;
                st->bucket = 0;
+               st->state = TCP_SEQ_STATE_ESTABLISHED;
                /* Fallthrough */
        case TCP_SEQ_STATE_ESTABLISHED:
-       case TCP_SEQ_STATE_TIME_WAIT:
-               st->state = TCP_SEQ_STATE_ESTABLISHED;
                if (st->bucket > tcp_hashinfo.ehash_mask)
                        break;
                rc = established_get_first(seq);
@@ -2524,7 +2469,6 @@ static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
                }
                break;
        case TCP_SEQ_STATE_ESTABLISHED:
-       case TCP_SEQ_STATE_TIME_WAIT:
                rc = established_get_next(seq, v);
                break;
        }
@@ -2548,7 +2492,6 @@ static void tcp_seq_stop(struct seq_file *seq, void *v)
                if (v != SEQ_START_TOKEN)
                        spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock);
                break;
-       case TCP_SEQ_STATE_TIME_WAIT:
        case TCP_SEQ_STATE_ESTABLISHED:
                if (v)
                        spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
@@ -2606,10 +2549,10 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req,
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
                " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK%n",
                i,
-               ireq->loc_addr,
+               ireq->ir_loc_addr,
                ntohs(inet_sk(sk)->inet_sport),
-               ireq->rmt_addr,
-               ntohs(ireq->rmt_port),
+               ireq->ir_rmt_addr,
+               ntohs(ireq->ir_rmt_port),
                TCP_SYN_RECV,
                0, 0, /* could print option size, but that is af dependent. */
                1,    /* timers active (only the expire timer) */
@@ -2707,6 +2650,7 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw,
 static int tcp4_seq_show(struct seq_file *seq, void *v)
 {
        struct tcp_iter_state *st;
+       struct sock *sk = v;
        int len;
 
        if (v == SEQ_START_TOKEN) {
@@ -2721,14 +2665,14 @@ static int tcp4_seq_show(struct seq_file *seq, void *v)
        switch (st->state) {
        case TCP_SEQ_STATE_LISTENING:
        case TCP_SEQ_STATE_ESTABLISHED:
-               get_tcp4_sock(v, seq, st->num, &len);
+               if (sk->sk_state == TCP_TIME_WAIT)
+                       get_timewait4_sock(v, seq, st->num, &len);
+               else
+                       get_tcp4_sock(v, seq, st->num, &len);
                break;
        case TCP_SEQ_STATE_OPENREQ:
                get_openreq4(st->syn_wait_sk, v, seq, st->num, st->uid, &len);
                break;
-       case TCP_SEQ_STATE_TIME_WAIT:
-               get_timewait4_sock(v, seq, st->num, &len);
-               break;
        }
        seq_printf(seq, "%*s\n", TMPSZ - 1 - len, "");
 out:
@@ -2806,6 +2750,7 @@ struct proto tcp_prot = {
        .orphan_count           = &tcp_orphan_count,
        .memory_allocated       = &tcp_memory_allocated,
        .memory_pressure        = &tcp_memory_pressure,
+       .sysctl_mem             = sysctl_tcp_mem,
        .sysctl_wmem            = sysctl_tcp_wmem,
        .sysctl_rmem            = sysctl_tcp_rmem,
        .max_header             = MAX_TCP_HEADER,
index 72f7218..991d62a 100644 (file)
@@ -115,12 +115,13 @@ static void tcp_lp_init(struct sock *sk)
  * Will only call newReno CA when away from inference.
  * From TCP-LP's paper, this will be handled in additive increasement.
  */
-static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                             u32 in_flight)
 {
        struct lp *lp = inet_csk_ca(sk);
 
        if (!(lp->flag & LP_WITHIN_INF))
-               tcp_reno_cong_avoid(sk, ack, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
 }
 
 /**
index 559d4ae..03e9154 100644 (file)
@@ -6,15 +6,10 @@
 #include <linux/memcontrol.h>
 #include <linux/module.h>
 
-static inline struct tcp_memcontrol *tcp_from_cgproto(struct cg_proto *cg_proto)
-{
-       return container_of(cg_proto, struct tcp_memcontrol, cg_proto);
-}
-
 static void memcg_tcp_enter_memory_pressure(struct sock *sk)
 {
        if (sk->sk_cgrp->memory_pressure)
-               *sk->sk_cgrp->memory_pressure = 1;
+               sk->sk_cgrp->memory_pressure = 1;
 }
 EXPORT_SYMBOL(memcg_tcp_enter_memory_pressure);
 
@@ -27,34 +22,24 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
         */
        struct res_counter *res_parent = NULL;
        struct cg_proto *cg_proto, *parent_cg;
-       struct tcp_memcontrol *tcp;
        struct mem_cgroup *parent = parent_mem_cgroup(memcg);
-       struct net *net = current->nsproxy->net_ns;
 
        cg_proto = tcp_prot.proto_cgroup(memcg);
        if (!cg_proto)
                return 0;
 
-       tcp = tcp_from_cgproto(cg_proto);
-
-       tcp->tcp_prot_mem[0] = net->ipv4.sysctl_tcp_mem[0];
-       tcp->tcp_prot_mem[1] = net->ipv4.sysctl_tcp_mem[1];
-       tcp->tcp_prot_mem[2] = net->ipv4.sysctl_tcp_mem[2];
-       tcp->tcp_memory_pressure = 0;
+       cg_proto->sysctl_mem[0] = sysctl_tcp_mem[0];
+       cg_proto->sysctl_mem[1] = sysctl_tcp_mem[1];
+       cg_proto->sysctl_mem[2] = sysctl_tcp_mem[2];
+       cg_proto->memory_pressure = 0;
+       cg_proto->memcg = memcg;
 
        parent_cg = tcp_prot.proto_cgroup(parent);
        if (parent_cg)
-               res_parent = parent_cg->memory_allocated;
-
-       res_counter_init(&tcp->tcp_memory_allocated, res_parent);
-       percpu_counter_init(&tcp->tcp_sockets_allocated, 0);
+               res_parent = &parent_cg->memory_allocated;
 
-       cg_proto->enter_memory_pressure = memcg_tcp_enter_memory_pressure;
-       cg_proto->memory_pressure = &tcp->tcp_memory_pressure;
-       cg_proto->sysctl_mem = tcp->tcp_prot_mem;
-       cg_proto->memory_allocated = &tcp->tcp_memory_allocated;
-       cg_proto->sockets_allocated = &tcp->tcp_sockets_allocated;
-       cg_proto->memcg = memcg;
+       res_counter_init(&cg_proto->memory_allocated, res_parent);
+       percpu_counter_init(&cg_proto->sockets_allocated, 0);
 
        return 0;
 }
@@ -63,21 +48,17 @@ EXPORT_SYMBOL(tcp_init_cgroup);
 void tcp_destroy_cgroup(struct mem_cgroup *memcg)
 {
        struct cg_proto *cg_proto;
-       struct tcp_memcontrol *tcp;
 
        cg_proto = tcp_prot.proto_cgroup(memcg);
        if (!cg_proto)
                return;
 
-       tcp = tcp_from_cgproto(cg_proto);
-       percpu_counter_destroy(&tcp->tcp_sockets_allocated);
+       percpu_counter_destroy(&cg_proto->sockets_allocated);
 }
 EXPORT_SYMBOL(tcp_destroy_cgroup);
 
 static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
 {
-       struct net *net = current->nsproxy->net_ns;
-       struct tcp_memcontrol *tcp;
        struct cg_proto *cg_proto;
        u64 old_lim;
        int i;
@@ -90,16 +71,14 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
        if (val > RES_COUNTER_MAX)
                val = RES_COUNTER_MAX;
 
-       tcp = tcp_from_cgproto(cg_proto);
-
-       old_lim = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT);
-       ret = res_counter_set_limit(&tcp->tcp_memory_allocated, val);
+       old_lim = res_counter_read_u64(&cg_proto->memory_allocated, RES_LIMIT);
+       ret = res_counter_set_limit(&cg_proto->memory_allocated, val);
        if (ret)
                return ret;
 
        for (i = 0; i < 3; i++)
-               tcp->tcp_prot_mem[i] = min_t(long, val >> PAGE_SHIFT,
-                                            net->ipv4.sysctl_tcp_mem[i]);
+               cg_proto->sysctl_mem[i] = min_t(long, val >> PAGE_SHIFT,
+                                               sysctl_tcp_mem[i]);
 
        if (val == RES_COUNTER_MAX)
                clear_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
@@ -156,28 +135,24 @@ static int tcp_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft,
 
 static u64 tcp_read_stat(struct mem_cgroup *memcg, int type, u64 default_val)
 {
-       struct tcp_memcontrol *tcp;
        struct cg_proto *cg_proto;
 
        cg_proto = tcp_prot.proto_cgroup(memcg);
        if (!cg_proto)
                return default_val;
 
-       tcp = tcp_from_cgproto(cg_proto);
-       return res_counter_read_u64(&tcp->tcp_memory_allocated, type);
+       return res_counter_read_u64(&cg_proto->memory_allocated, type);
 }
 
 static u64 tcp_read_usage(struct mem_cgroup *memcg)
 {
-       struct tcp_memcontrol *tcp;
        struct cg_proto *cg_proto;
 
        cg_proto = tcp_prot.proto_cgroup(memcg);
        if (!cg_proto)
                return atomic_long_read(&tcp_memory_allocated) << PAGE_SHIFT;
 
-       tcp = tcp_from_cgproto(cg_proto);
-       return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_USAGE);
+       return res_counter_read_u64(&cg_proto->memory_allocated, RES_USAGE);
 }
 
 static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft)
@@ -205,54 +180,25 @@ static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft)
 static int tcp_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event)
 {
        struct mem_cgroup *memcg;
-       struct tcp_memcontrol *tcp;
        struct cg_proto *cg_proto;
 
        memcg = mem_cgroup_from_css(css);
        cg_proto = tcp_prot.proto_cgroup(memcg);
        if (!cg_proto)
                return 0;
-       tcp = tcp_from_cgproto(cg_proto);
 
        switch (event) {
        case RES_MAX_USAGE:
-               res_counter_reset_max(&tcp->tcp_memory_allocated);
+               res_counter_reset_max(&cg_proto->memory_allocated);
                break;
        case RES_FAILCNT:
-               res_counter_reset_failcnt(&tcp->tcp_memory_allocated);
+               res_counter_reset_failcnt(&cg_proto->memory_allocated);
                break;
        }
 
        return 0;
 }
 
-unsigned long long tcp_max_memory(const struct mem_cgroup *memcg)
-{
-       struct tcp_memcontrol *tcp;
-       struct cg_proto *cg_proto;
-
-       cg_proto = tcp_prot.proto_cgroup((struct mem_cgroup *)memcg);
-       if (!cg_proto)
-               return 0;
-
-       tcp = tcp_from_cgproto(cg_proto);
-       return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT);
-}
-
-void tcp_prot_mem(struct mem_cgroup *memcg, long val, int idx)
-{
-       struct tcp_memcontrol *tcp;
-       struct cg_proto *cg_proto;
-
-       cg_proto = tcp_prot.proto_cgroup(memcg);
-       if (!cg_proto)
-               return;
-
-       tcp = tcp_from_cgproto(cg_proto);
-
-       tcp->tcp_prot_mem[idx] = val;
-}
-
 static struct cftype tcp_files[] = {
        {
                .name = "kmem.tcp.limit_in_bytes",
index 52f3c6b..2ab09cb 100644 (file)
@@ -215,13 +215,15 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,
        addr.family = req->rsk_ops->family;
        switch (addr.family) {
        case AF_INET:
-               addr.addr.a4 = inet_rsk(req)->rmt_addr;
+               addr.addr.a4 = inet_rsk(req)->ir_rmt_addr;
                hash = (__force unsigned int) addr.addr.a4;
                break;
+#if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               *(struct in6_addr *)addr.addr.a6 = inet6_rsk(req)->rmt_addr;
-               hash = ipv6_addr_hash(&inet6_rsk(req)->rmt_addr);
+               *(struct in6_addr *)addr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr;
+               hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr);
                break;
+#endif
        default:
                return NULL;
        }
@@ -240,7 +242,6 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,
 
 static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw)
 {
-       struct inet6_timewait_sock *tw6;
        struct tcp_metrics_block *tm;
        struct inetpeer_addr addr;
        unsigned int hash;
@@ -252,11 +253,12 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock
                addr.addr.a4 = tw->tw_daddr;
                hash = (__force unsigned int) addr.addr.a4;
                break;
+#if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               tw6 = inet6_twsk((struct sock *)tw);
-               *(struct in6_addr *)addr.addr.a6 = tw6->tw_v6_daddr;
-               hash = ipv6_addr_hash(&tw6->tw_v6_daddr);
+               *(struct in6_addr *)addr.addr.a6 = tw->tw_v6_daddr;
+               hash = ipv6_addr_hash(&tw->tw_v6_daddr);
                break;
+#endif
        default:
                return NULL;
        }
@@ -288,10 +290,12 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,
                addr.addr.a4 = inet_sk(sk)->inet_daddr;
                hash = (__force unsigned int) addr.addr.a4;
                break;
+#if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6:
-               *(struct in6_addr *)addr.addr.a6 = inet6_sk(sk)->daddr;
-               hash = ipv6_addr_hash(&inet6_sk(sk)->daddr);
+               *(struct in6_addr *)addr.addr.a6 = sk->sk_v6_daddr;
+               hash = ipv6_addr_hash(&sk->sk_v6_daddr);
                break;
+#endif
        default:
                return NULL;
        }
@@ -667,8 +671,9 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
                struct tcp_fastopen_metrics *tfom = &tm->tcpm_fastopen;
 
                write_seqlock_bh(&fastopen_seqlock);
-               tfom->mss = mss;
-               if (cookie->len > 0)
+               if (mss)
+                       tfom->mss = mss;
+               if (cookie && cookie->len > 0)
                        tfom->cookie = *cookie;
                if (syn_lost) {
                        ++tfom->syn_loss;
index 58a3e69..97b6841 100644 (file)
@@ -293,12 +293,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
 #if IS_ENABLED(CONFIG_IPV6)
                if (tw->tw_family == PF_INET6) {
                        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);
-                       tw6->tw_v6_daddr = np->daddr;
-                       tw6->tw_v6_rcv_saddr = np->rcv_saddr;
+                       tw->tw_v6_daddr = sk->sk_v6_daddr;
+                       tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
                        tw->tw_tclass = np->tclass;
                        tw->tw_ipv6only = np->ipv6only;
                }
index 3a7525e..a2b68a1 100644 (file)
 #include <net/tcp.h>
 #include <net/protocol.h>
 
-struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
+struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
                                netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
+       unsigned int sum_truesize = 0;
        struct tcphdr *th;
        unsigned int thlen;
        unsigned int seq;
@@ -56,6 +57,8 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                               SKB_GSO_TCP_ECN |
                               SKB_GSO_TCPV6 |
                               SKB_GSO_GRE |
+                              SKB_GSO_IPIP |
+                              SKB_GSO_SIT |
                               SKB_GSO_MPLS |
                               SKB_GSO_UDP_TUNNEL |
                               0) ||
@@ -102,13 +105,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                if (copy_destructor) {
                        skb->destructor = gso_skb->destructor;
                        skb->sk = gso_skb->sk;
-                       /* {tcp|sock}_wfree() use exact truesize accounting :
-                        * sum(skb->truesize) MUST be exactly be gso_skb->truesize
-                        * So we account mss bytes of 'true size' for each segment.
-                        * The last segment will contain the remaining.
-                        */
-                       skb->truesize = mss;
-                       gso_skb->truesize -= mss;
+                       sum_truesize += skb->truesize;
                }
                skb = skb->next;
                th = tcp_hdr(skb);
@@ -125,7 +122,9 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        if (copy_destructor) {
                swap(gso_skb->sk, skb->sk);
                swap(gso_skb->destructor, skb->destructor);
-               swap(gso_skb->truesize, skb->truesize);
+               sum_truesize += skb->truesize;
+               atomic_add(sum_truesize - gso_skb->truesize,
+                          &skb->sk->sk_wmem_alloc);
        }
 
        delta = htonl(oldlen + (skb_tail_pointer(skb) -
@@ -139,7 +138,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
 out:
        return segs;
 }
-EXPORT_SYMBOL(tcp_tso_segment);
+EXPORT_SYMBOL(tcp_gso_segment);
 
 struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
 {
@@ -320,7 +319,7 @@ static int tcp4_gro_complete(struct sk_buff *skb)
 static const struct net_offload tcpv4_offload = {
        .callbacks = {
                .gso_send_check =       tcp_v4_gso_send_check,
-               .gso_segment    =       tcp_tso_segment,
+               .gso_segment    =       tcp_gso_segment,
                .gro_receive    =       tcp4_gro_receive,
                .gro_complete   =       tcp4_gro_complete,
        },
index 7c83cb8..6728546 100644 (file)
@@ -637,6 +637,8 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
        unsigned int size = 0;
        unsigned int eff_sacks;
 
+       opts->options = 0;
+
 #ifdef CONFIG_TCP_MD5SIG
        *md5 = tp->af_specific->md5_lookup(sk, sk);
        if (unlikely(*md5)) {
@@ -848,15 +850,15 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 
        BUG_ON(!skb || !tcp_skb_pcount(skb));
 
-       /* If congestion control is doing timestamping, we must
-        * take such a timestamp before we potentially clone/copy.
-        */
-       if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP)
-               __net_timestamp(skb);
-
-       if (likely(clone_it)) {
+       if (clone_it) {
                const struct sk_buff *fclone = skb + 1;
 
+               /* If congestion control is doing timestamping, we must
+                * take such a timestamp before we potentially clone/copy.
+                */
+               if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP)
+                       __net_timestamp(skb);
+
                if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
                             fclone->fclone == SKB_FCLONE_CLONE))
                        NET_INC_STATS_BH(sock_net(sk),
@@ -895,8 +897,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 
        skb_orphan(skb);
        skb->sk = sk;
-       skb->destructor = (sysctl_tcp_limit_output_bytes > 0) ?
-                         tcp_wfree : sock_wfree;
+       skb->destructor = tcp_wfree;
        atomic_add(skb->truesize, &sk->sk_wmem_alloc);
 
        /* Build TCP header and checksum it. */
@@ -985,8 +986,10 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
 static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
                                 unsigned int mss_now)
 {
-       if (skb->len <= mss_now || !sk_can_gso(sk) ||
-           skb->ip_summed == CHECKSUM_NONE) {
+       /* Make sure we own this skb before messing gso_size/gso_segs */
+       WARN_ON_ONCE(skb_cloned(skb));
+
+       if (skb->len <= mss_now || skb->ip_summed == CHECKSUM_NONE) {
                /* Avoid the costly divide in the normal
                 * non-TSO case.
                 */
@@ -1066,9 +1069,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
        if (nsize < 0)
                nsize = 0;
 
-       if (skb_cloned(skb) &&
-           skb_is_nonlinear(skb) &&
-           pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+       if (skb_unclone(skb, GFP_ATOMIC))
                return -ENOMEM;
 
        /* Get a new skb... force flag on. */
@@ -1840,7 +1841,6 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
        while ((skb = tcp_send_head(sk))) {
                unsigned int limit;
 
-
                tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
                BUG_ON(!tso_segs);
 
@@ -1869,13 +1869,20 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                                break;
                }
 
-               /* TSQ : sk_wmem_alloc accounts skb truesize,
-                * including skb overhead. But thats OK.
+               /* TCP Small Queues :
+                * Control number of packets in qdisc/devices to two packets / or ~1 ms.
+                * This allows for :
+                *  - better RTT estimation and ACK scheduling
+                *  - faster recovery
+                *  - high rates
                 */
-               if (atomic_read(&sk->sk_wmem_alloc) >= sysctl_tcp_limit_output_bytes) {
+               limit = max(skb->truesize, sk->sk_pacing_rate >> 10);
+
+               if (atomic_read(&sk->sk_wmem_alloc) > limit) {
                        set_bit(TSQ_THROTTLED, &tp->tsq_flags);
                        break;
                }
+
                limit = mss_now;
                if (tso_segs > 1 && !tcp_urg_mode(tp))
                        limit = tcp_mss_split_point(sk, skb, mss_now,
@@ -2337,6 +2344,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                int oldpcount = tcp_skb_pcount(skb);
 
                if (unlikely(oldpcount > 1)) {
+                       if (skb_unclone(skb, GFP_ATOMIC))
+                               return -ENOMEM;
                        tcp_init_tso_segs(sk, skb, cur_mss);
                        tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb));
                }
@@ -2344,21 +2353,6 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
 
        tcp_retrans_try_collapse(sk, skb, cur_mss);
 
-       /* Some Solaris stacks overoptimize and ignore the FIN on a
-        * retransmit when old data is attached.  So strip it off
-        * since it is cheap to do so and saves bytes on the network.
-        */
-       if (skb->len > 0 &&
-           (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) &&
-           tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
-               if (!pskb_trim(skb, 0)) {
-                       /* Reuse, even though it does some unnecessary work */
-                       tcp_init_nondata_skb(skb, TCP_SKB_CB(skb)->end_seq - 1,
-                                            TCP_SKB_CB(skb)->tcp_flags);
-                       skb->ip_summed = CHECKSUM_NONE;
-               }
-       }
-
        /* Make a copy, if the first transmission SKB clone we made
         * is still in somebody's hands, else make a clone.
         */
@@ -2727,8 +2721,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        th->syn = 1;
        th->ack = 1;
        TCP_ECN_make_synack(req, th);
-       th->source = ireq->loc_port;
-       th->dest = ireq->rmt_port;
+       th->source = htons(ireq->ir_num);
+       th->dest = ireq->ir_rmt_port;
        /* Setting of flags are superfluous here for callers (and ECE is
         * not even correctly set)
         */
index 611beab..8b97d71 100644 (file)
@@ -101,22 +101,6 @@ static inline int tcp_probe_avail(void)
                si4.sin_addr.s_addr = inet->inet_##mem##addr;   \
        } while (0)                                             \
 
-#if IS_ENABLED(CONFIG_IPV6)
-#define tcp_probe_copy_fl_to_si6(inet, si6, mem)               \
-       do {                                                    \
-               struct ipv6_pinfo *pi6 = inet->pinet6;          \
-               si6.sin6_family = AF_INET6;                     \
-               si6.sin6_port = inet->inet_##mem##port;         \
-               si6.sin6_addr = pi6->mem##addr;                 \
-               si6.sin6_flowinfo = 0; /* No need here. */      \
-               si6.sin6_scope_id = 0;  /* No need here. */     \
-       } while (0)
-#else
-#define tcp_probe_copy_fl_to_si6(fl, si6, mem)                 \
-       do {                                                    \
-               memset(&si6, 0, sizeof(si6));                   \
-       } while (0)
-#endif
 
 /*
  * Hook inserted to be called before each receive packet.
@@ -147,8 +131,17 @@ static void jtcp_rcv_established(struct sock *sk, struct sk_buff *skb,
                                tcp_probe_copy_fl_to_si4(inet, p->dst.v4, d);
                                break;
                        case AF_INET6:
-                               tcp_probe_copy_fl_to_si6(inet, p->src.v6, s);
-                               tcp_probe_copy_fl_to_si6(inet, p->dst.v6, d);
+                               memset(&p->src.v6, 0, sizeof(p->src.v6));
+                               memset(&p->dst.v6, 0, sizeof(p->dst.v6));
+#if IS_ENABLED(CONFIG_IPV6)
+                               p->src.v6.sin6_family = AF_INET6;
+                               p->src.v6.sin6_port = inet->inet_sport;
+                               p->src.v6.sin6_addr = inet6_sk(sk)->saddr;
+
+                               p->dst.v6.sin6_family = AF_INET6;
+                               p->dst.v6.sin6_port = inet->inet_dport;
+                               p->dst.v6.sin6_addr = sk->sk_v6_daddr;
+#endif
                                break;
                        default:
                                BUG();
index 8ce55b8..19ea6c2 100644 (file)
@@ -15,7 +15,8 @@
 #define TCP_SCALABLE_AI_CNT    50U
 #define TCP_SCALABLE_MD_SCALE  3
 
-static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                                   u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
@@ -23,7 +24,7 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
        else
                tcp_cong_avoid_ai(tp, min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT));
 }
index 4b85e6f..64f0354 100644 (file)
@@ -156,12 +156,16 @@ static bool retransmits_timed_out(struct sock *sk,
 static int tcp_write_timeout(struct sock *sk)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
+       struct tcp_sock *tp = tcp_sk(sk);
        int retry_until;
        bool do_reset, syn_set = false;
 
        if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
-               if (icsk->icsk_retransmits)
+               if (icsk->icsk_retransmits) {
                        dst_negative_advice(sk);
+                       if (tp->syn_fastopen || tp->syn_data)
+                               tcp_fastopen_cache_set(sk, 0, NULL, true);
+               }
                retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
                syn_set = true;
        } else {
@@ -374,9 +378,8 @@ void tcp_retransmit_timer(struct sock *sk)
                }
 #if IS_ENABLED(CONFIG_IPV6)
                else if (sk->sk_family == AF_INET6) {
-                       struct ipv6_pinfo *np = inet6_sk(sk);
                        LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"),
-                                      &np->daddr,
+                                      &sk->sk_v6_daddr,
                                       ntohs(inet->inet_dport), inet->inet_num,
                                       tp->snd_una, tp->snd_nxt);
                }
index 80fa2bf..06cae62 100644 (file)
@@ -163,13 +163,14 @@ static inline u32 tcp_vegas_ssthresh(struct tcp_sock *tp)
        return  min(tp->snd_ssthresh, tp->snd_cwnd-1);
 }
 
-static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                                u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct vegas *vegas = inet_csk_ca(sk);
 
        if (!vegas->doing_vegas_now) {
-               tcp_reno_cong_avoid(sk, ack, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
                return;
        }
 
@@ -194,7 +195,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                        /* We don't have enough RTT samples to do the Vegas
                         * calculation, so we'll behave like Reno.
                         */
-                       tcp_reno_cong_avoid(sk, ack, in_flight);
+                       tcp_reno_cong_avoid(sk, ack, acked, in_flight);
                } else {
                        u32 rtt, diff;
                        u64 target_cwnd;
@@ -243,7 +244,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
 
                        } else if (tp->snd_cwnd <= tp->snd_ssthresh) {
                                /* Slow start.  */
-                               tcp_slow_start(tp);
+                               tcp_slow_start(tp, acked);
                        } else {
                                /* Congestion avoidance. */
 
@@ -283,7 +284,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
        }
        /* Use normal slow start */
        else if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
 
 }
 
index 6c0eea2..0531b99 100644 (file)
@@ -15,10 +15,10 @@ struct vegas {
        u32     baseRTT;        /* the min of all Vegas RTT measurements seen (in usec) */
 };
 
-extern void tcp_vegas_init(struct sock *sk);
-extern void tcp_vegas_state(struct sock *sk, u8 ca_state);
-extern void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us);
-extern void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event);
-extern void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb);
+void tcp_vegas_init(struct sock *sk);
+void tcp_vegas_state(struct sock *sk, u8 ca_state);
+void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us);
+void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event);
+void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb);
 
 #endif /* __TCP_VEGAS_H */
index ac43cd7..326475a 100644 (file)
@@ -114,13 +114,14 @@ static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event)
                tcp_veno_init(sk);
 }
 
-static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                               u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct veno *veno = inet_csk_ca(sk);
 
        if (!veno->doing_veno_now) {
-               tcp_reno_cong_avoid(sk, ack, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
                return;
        }
 
@@ -133,7 +134,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                /* We don't have enough rtt samples to do the Veno
                 * calculation, so we'll behave like Reno.
                 */
-               tcp_reno_cong_avoid(sk, ack, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
        } else {
                u64 target_cwnd;
                u32 rtt;
@@ -152,7 +153,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
 
                if (tp->snd_cwnd <= tp->snd_ssthresh) {
                        /* Slow start.  */
-                       tcp_slow_start(tp);
+                       tcp_slow_start(tp, acked);
                } else {
                        /* Congestion avoidance. */
                        if (veno->diff < beta) {
index 05c3b6f..a347a07 100644 (file)
@@ -69,7 +69,8 @@ static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, s32 rtt_us)
        tcp_vegas_pkts_acked(sk, pkts_acked, rtt_us);
 }
 
-static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
+static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked,
+                               u32 in_flight)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct yeah *yeah = inet_csk_ca(sk);
@@ -78,7 +79,7 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
-               tcp_slow_start(tp);
+               tcp_slow_start(tp, acked);
 
        else if (!yeah->doing_reno_now) {
                /* Scalable */
index 74d2c95..89909dd 100644 (file)
 #include <linux/seq_file.h>
 #include <net/net_namespace.h>
 #include <net/icmp.h>
+#include <net/inet_hashtables.h>
 #include <net/route.h>
 #include <net/checksum.h>
 #include <net/xfrm.h>
@@ -219,7 +220,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
                unsigned short first, last;
                DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN);
 
-               inet_get_local_port_range(&low, &high);
+               inet_get_local_port_range(net, &low, &high);
                remaining = (high - low) + 1;
 
                rand = net_random();
@@ -406,6 +407,18 @@ static inline int compute_score2(struct sock *sk, struct net *net,
        return score;
 }
 
+static unsigned int udp_ehashfn(struct net *net, const __be32 laddr,
+                                const __u16 lport, const __be32 faddr,
+                                const __be16 fport)
+{
+       static u32 udp_ehash_secret __read_mostly;
+
+       net_get_random_once(&udp_ehash_secret, sizeof(udp_ehash_secret));
+
+       return __inet_ehashfn(laddr, lport, faddr, fport,
+                             udp_ehash_secret + net_hash_mix(net));
+}
+
 
 /* called with read_rcu_lock() */
 static struct sock *udp4_lib_lookup2(struct net *net,
@@ -429,8 +442,8 @@ begin:
                        badness = score;
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
-                               hash = inet_ehashfn(net, daddr, hnum,
-                                                   saddr, sport);
+                               hash = udp_ehashfn(net, daddr, hnum,
+                                                  saddr, sport);
                                matches = 1;
                        }
                } else if (score == badness && reuseport) {
@@ -510,8 +523,8 @@ begin:
                        badness = score;
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
-                               hash = inet_ehashfn(net, daddr, hnum,
-                                                   saddr, sport);
+                               hash = udp_ehashfn(net, daddr, hnum,
+                                                  saddr, sport);
                                matches = 1;
                        }
                } else if (score == badness && reuseport) {
@@ -565,6 +578,26 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
 }
 EXPORT_SYMBOL_GPL(udp4_lib_lookup);
 
+static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk,
+                                      __be16 loc_port, __be32 loc_addr,
+                                      __be16 rmt_port, __be32 rmt_addr,
+                                      int dif, unsigned short hnum)
+{
+       struct inet_sock *inet = inet_sk(sk);
+
+       if (!net_eq(sock_net(sk), net) ||
+           udp_sk(sk)->udp_port_hash != hnum ||
+           (inet->inet_daddr && inet->inet_daddr != rmt_addr) ||
+           (inet->inet_dport != rmt_port && inet->inet_dport) ||
+           (inet->inet_rcv_saddr && inet->inet_rcv_saddr != loc_addr) ||
+           ipv6_only_sock(sk) ||
+           (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))
+               return false;
+       if (!ip_mc_sf_allow(sk, loc_addr, rmt_addr, dif))
+               return false;
+       return true;
+}
+
 static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk,
                                             __be16 loc_port, __be32 loc_addr,
                                             __be16 rmt_port, __be32 rmt_addr,
@@ -575,20 +608,11 @@ static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk,
        unsigned short hnum = ntohs(loc_port);
 
        sk_nulls_for_each_from(s, node) {
-               struct inet_sock *inet = inet_sk(s);
-
-               if (!net_eq(sock_net(s), net) ||
-                   udp_sk(s)->udp_port_hash != hnum ||
-                   (inet->inet_daddr && inet->inet_daddr != rmt_addr) ||
-                   (inet->inet_dport != rmt_port && inet->inet_dport) ||
-                   (inet->inet_rcv_saddr &&
-                    inet->inet_rcv_saddr != loc_addr) ||
-                   ipv6_only_sock(s) ||
-                   (s->sk_bound_dev_if && s->sk_bound_dev_if != dif))
-                       continue;
-               if (!ip_mc_sf_allow(s, loc_addr, rmt_addr, dif))
-                       continue;
-               goto found;
+               if (__udp_is_mcast_sock(net, s,
+                                       loc_port, loc_addr,
+                                       rmt_port, rmt_addr,
+                                       dif, hnum))
+                       goto found;
        }
        s = NULL;
 found:
@@ -658,7 +682,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
                break;
        case ICMP_REDIRECT:
                ipv4_sk_redirect(skb, sk);
-               break;
+               goto out;
        }
 
        /*
@@ -855,6 +879,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
        ipc.opt = NULL;
        ipc.tx_flags = 0;
+       ipc.ttl = 0;
+       ipc.tos = -1;
 
        getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
 
@@ -938,7 +964,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                faddr = ipc.opt->opt.faddr;
                connected = 0;
        }
-       tos = RT_TOS(inet->tos);
+       tos = get_rttos(&ipc, inet);
        if (sock_flag(sk, SOCK_LOCALROUTE) ||
            (msg->msg_flags & MSG_DONTROUTE) ||
            (ipc.opt && ipc.opt->opt.is_strictroute)) {
@@ -1403,8 +1429,10 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        int rc;
 
-       if (inet_sk(sk)->inet_daddr)
+       if (inet_sk(sk)->inet_daddr) {
                sock_rps_save_rxhash(sk, skb);
+               sk_mark_napi_id(sk, skb);
+       }
 
        rc = sock_queue_rcv_skb(sk, skb);
        if (rc < 0) {
@@ -1528,7 +1556,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
        rc = 0;
 
-       ipv4_pktinfo_prepare(skb);
+       ipv4_pktinfo_prepare(sk, skb);
        bh_lock_sock(sk);
        if (!sock_owned_by_user(sk))
                rc = __udp_queue_rcv_skb(sk, skb);
@@ -1577,6 +1605,14 @@ static void flush_stack(struct sock **stack, unsigned int count,
                kfree_skb(skb1);
 }
 
+static void udp_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+       struct dst_entry *dst = skb_dst(skb);
+
+       dst_hold(dst);
+       sk->sk_rx_dst = dst;
+}
+
 /*
  *     Multicasts and broadcasts go to each listener.
  *
@@ -1705,16 +1741,32 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (udp4_csum_init(skb, uh, proto))
                goto csum_error;
 
-       if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
-               return __udp4_lib_mcast_deliver(net, skb, uh,
-                               saddr, daddr, udptable);
+       if (skb->sk) {
+               int ret;
+               sk = skb->sk;
+
+               if (unlikely(sk->sk_rx_dst == NULL))
+                       udp_sk_rx_dst_set(sk, skb);
+
+               ret = udp_queue_rcv_skb(sk, skb);
+
+               /* a return value > 0 means to resubmit the input, but
+                * it wants the return to be -protocol, or 0
+                */
+               if (ret > 0)
+                       return -ret;
+               return 0;
+       } else {
+               if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
+                       return __udp4_lib_mcast_deliver(net, skb, uh,
+                                       saddr, daddr, udptable);
 
-       sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
+               sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
+       }
 
        if (sk != NULL) {
                int ret;
 
-               sk_mark_napi_id(sk, skb);
                ret = udp_queue_rcv_skb(sk, skb);
                sock_put(sk);
 
@@ -1768,6 +1820,135 @@ drop:
        return 0;
 }
 
+/* We can only early demux multicast if there is a single matching socket.
+ * If more than one socket found returns NULL
+ */
+static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net,
+                                                 __be16 loc_port, __be32 loc_addr,
+                                                 __be16 rmt_port, __be32 rmt_addr,
+                                                 int dif)
+{
+       struct sock *sk, *result;
+       struct hlist_nulls_node *node;
+       unsigned short hnum = ntohs(loc_port);
+       unsigned int count, slot = udp_hashfn(net, hnum, udp_table.mask);
+       struct udp_hslot *hslot = &udp_table.hash[slot];
+
+       rcu_read_lock();
+begin:
+       count = 0;
+       result = NULL;
+       sk_nulls_for_each_rcu(sk, node, &hslot->head) {
+               if (__udp_is_mcast_sock(net, sk,
+                                       loc_port, loc_addr,
+                                       rmt_port, rmt_addr,
+                                       dif, hnum)) {
+                       result = sk;
+                       ++count;
+               }
+       }
+       /*
+        * if the nulls value we got at the end of this lookup is
+        * not the expected one, we must restart lookup.
+        * We probably met an item that was moved to another chain.
+        */
+       if (get_nulls_value(node) != slot)
+               goto begin;
+
+       if (result) {
+               if (count != 1 ||
+                   unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
+                       result = NULL;
+               else if (unlikely(!__udp_is_mcast_sock(net, result,
+                                                      loc_port, loc_addr,
+                                                      rmt_port, rmt_addr,
+                                                      dif, hnum))) {
+                       sock_put(result);
+                       result = NULL;
+               }
+       }
+       rcu_read_unlock();
+       return result;
+}
+
+/* For unicast we should only early demux connected sockets or we can
+ * break forwarding setups.  The chains here can be long so only check
+ * if the first socket is an exact match and if not move on.
+ */
+static struct sock *__udp4_lib_demux_lookup(struct net *net,
+                                           __be16 loc_port, __be32 loc_addr,
+                                           __be16 rmt_port, __be32 rmt_addr,
+                                           int dif)
+{
+       struct sock *sk, *result;
+       struct hlist_nulls_node *node;
+       unsigned short hnum = ntohs(loc_port);
+       unsigned int hash2 = udp4_portaddr_hash(net, loc_addr, hnum);
+       unsigned int slot2 = hash2 & udp_table.mask;
+       struct udp_hslot *hslot2 = &udp_table.hash2[slot2];
+       INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr)
+       const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum);
+
+       rcu_read_lock();
+       result = NULL;
+       udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) {
+               if (INET_MATCH(sk, net, acookie,
+                              rmt_addr, loc_addr, ports, dif))
+                       result = sk;
+               /* Only check first socket in chain */
+               break;
+       }
+
+       if (result) {
+               if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
+                       result = NULL;
+               else if (unlikely(!INET_MATCH(sk, net, acookie,
+                                             rmt_addr, loc_addr,
+                                             ports, dif))) {
+                       sock_put(result);
+                       result = NULL;
+               }
+       }
+       rcu_read_unlock();
+       return result;
+}
+
+void udp_v4_early_demux(struct sk_buff *skb)
+{
+       const struct iphdr *iph = ip_hdr(skb);
+       const struct udphdr *uh = udp_hdr(skb);
+       struct sock *sk;
+       struct dst_entry *dst;
+       struct net *net = dev_net(skb->dev);
+       int dif = skb->dev->ifindex;
+
+       /* validate the packet */
+       if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr)))
+               return;
+
+       if (skb->pkt_type == PACKET_BROADCAST ||
+           skb->pkt_type == PACKET_MULTICAST)
+               sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
+                                                  uh->source, iph->saddr, dif);
+       else if (skb->pkt_type == PACKET_HOST)
+               sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr,
+                                            uh->source, iph->saddr, dif);
+       else
+               return;
+
+       if (!sk)
+               return;
+
+       skb->sk = sk;
+       skb->destructor = sock_edemux;
+       dst = sk->sk_rx_dst;
+
+       if (dst)
+               dst = dst_check(dst, 0);
+       if (dst)
+               skb_dst_set_noref(skb, dst);
+}
+
 int udp_rcv(struct sk_buff *skb)
 {
        return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP);
index 5a681e2..f3c2789 100644 (file)
@@ -5,30 +5,30 @@
 #include <net/protocol.h>
 #include <net/inet_common.h>
 
-extern int     __udp4_lib_rcv(struct sk_buff *, struct udp_table *, int );
-extern void    __udp4_lib_err(struct sk_buff *, u32, struct udp_table *);
+int __udp4_lib_rcv(struct sk_buff *, struct udp_table *, int);
+void __udp4_lib_err(struct sk_buff *, u32, struct udp_table *);
 
-extern int     udp_v4_get_port(struct sock *sk, unsigned short snum);
+int udp_v4_get_port(struct sock *sk, unsigned short snum);
 
-extern int     udp_setsockopt(struct sock *sk, int level, int optname,
-                              char __user *optval, unsigned int optlen);
-extern int     udp_getsockopt(struct sock *sk, int level, int optname,
-                              char __user *optval, int __user *optlen);
+int udp_setsockopt(struct sock *sk, int level, int optname,
+                  char __user *optval, unsigned int optlen);
+int udp_getsockopt(struct sock *sk, int level, int optname,
+                  char __user *optval, int __user *optlen);
 
 #ifdef CONFIG_COMPAT
-extern int     compat_udp_setsockopt(struct sock *sk, int level, int optname,
-                                     char __user *optval, unsigned int optlen);
-extern int     compat_udp_getsockopt(struct sock *sk, int level, int optname,
-                                     char __user *optval, int __user *optlen);
+int compat_udp_setsockopt(struct sock *sk, int level, int optname,
+                         char __user *optval, unsigned int optlen);
+int compat_udp_getsockopt(struct sock *sk, int level, int optname,
+                         char __user *optval, int __user *optlen);
 #endif
-extern int     udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-                           size_t len, int noblock, int flags, int *addr_len);
-extern int     udp_sendpage(struct sock *sk, struct page *page, int offset,
-                            size_t size, int flags);
-extern int     udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
-extern void    udp_destroy_sock(struct sock *sk);
+int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+               size_t len, int noblock, int flags, int *addr_len);
+int udp_sendpage(struct sock *sk, struct page *page, int offset, size_t size,
+                int flags);
+int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+void udp_destroy_sock(struct sock *sk);
 
 #ifdef CONFIG_PROC_FS
-extern int     udp4_seq_show(struct seq_file *seq, void *v);
+int udp4_seq_show(struct seq_file *seq, void *v);
 #endif
 #endif /* _UDP4_IMPL_H */
index f35ecca..83206de 100644 (file)
@@ -52,6 +52,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 
                if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
+                                     SKB_GSO_IPIP |
                                      SKB_GSO_GRE | SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
index b5663c3..31b1815 100644 (file)
 #include <net/xfrm.h>
 
 /* Informational hook. The decap is still done here. */
-static struct xfrm_tunnel __rcu *rcv_notify_handlers __read_mostly;
+static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly;
 static DEFINE_MUTEX(xfrm4_mode_tunnel_input_mutex);
 
-int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler)
+int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler)
 {
-       struct xfrm_tunnel __rcu **pprev;
-       struct xfrm_tunnel *t;
+       struct xfrm_tunnel_notifier __rcu **pprev;
+       struct xfrm_tunnel_notifier *t;
        int ret = -EEXIST;
        int priority = handler->priority;
 
@@ -50,10 +50,10 @@ err:
 }
 EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_register);
 
-int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler)
+int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler)
 {
-       struct xfrm_tunnel __rcu **pprev;
-       struct xfrm_tunnel *t;
+       struct xfrm_tunnel_notifier __rcu **pprev;
+       struct xfrm_tunnel_notifier *t;
        int ret = -ENOENT;
 
        mutex_lock(&xfrm4_mode_tunnel_input_mutex);
@@ -134,7 +134,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
 
 static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
 {
-       struct xfrm_tunnel *handler;
+       struct xfrm_tunnel_notifier *handler;
        int err = -EINVAL;
 
        if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
index 9a459be..e1a6393 100644 (file)
@@ -104,9 +104,14 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
        const struct iphdr *iph = ip_hdr(skb);
        u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
        struct flowi4 *fl4 = &fl->u.ip4;
+       int oif = 0;
+
+       if (skb_dst(skb))
+               oif = skb_dst(skb)->dev->ifindex;
 
        memset(fl4, 0, sizeof(struct flowi4));
        fl4->flowi4_mark = skb->mark;
+       fl4->flowi4_oif = reverse ? skb->skb_iif : oif;
 
        if (!ip_is_fragment(iph)) {
                switch (iph->protocol) {
@@ -235,7 +240,7 @@ static struct dst_ops xfrm4_dst_ops = {
        .destroy =              xfrm4_dst_destroy,
        .ifdown =               xfrm4_dst_ifdown,
        .local_out =            __ip_local_out,
-       .gc_thresh =            1024,
+       .gc_thresh =            32768,
 };
 
 static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
index 11b13ea..d92e558 100644 (file)
@@ -21,24 +21,6 @@ menuconfig IPV6
 
 if IPV6
 
-config IPV6_PRIVACY
-       bool "IPv6: Privacy Extensions (RFC 3041) support"
-       ---help---
-         Privacy Extensions for Stateless Address Autoconfiguration in IPv6
-         support.  With this option, additional periodically-altered
-         pseudo-random global-scope unicast address(es) will be assigned to
-         your interface(s).
-       
-         We use our standard pseudo-random algorithm to generate the
-          randomized interface identifier, instead of one described in RFC 3041.
-
-         By default the kernel does not generate temporary addresses.
-         To use temporary addresses, do
-       
-               echo 2 >/proc/sys/net/ipv6/conf/all/use_tempaddr 
-
-         See <file:Documentation/networking/ip-sysctl.txt> for details.
-
 config IPV6_ROUTER_PREF
        bool "IPv6: Router Preference (RFC 4191) support"
        ---help---
@@ -153,6 +135,17 @@ config INET6_XFRM_MODE_ROUTEOPTIMIZATION
        ---help---
          Support for MIPv6 route optimization mode.
 
+config IPV6_VTI
+tristate "Virtual (secure) IPv6: tunneling"
+       select IPV6_TUNNEL
+       depends on INET6_XFRM_MODE_TUNNEL
+       ---help---
+       Tunneling means encapsulating data of one protocol type within
+       another protocol and sending it over a channel that understands the
+       encapsulating protocol. This can be used with xfrm mode tunnel to give
+       the notion of a secure tunnel for IPSEC and then use routing protocol
+       on top.
+
 config IPV6_SIT
        tristate "IPv6: IPv6-in-IPv4 tunnel (SIT driver)"
        select INET_TUNNEL
index 470a9c0..17bb830 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
 obj-$(CONFIG_IPV6_MIP6) += mip6.o
 obj-$(CONFIG_NETFILTER)        += netfilter/
 
+obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
 obj-$(CONFIG_IPV6_SIT) += sit.o
 obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
 obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
index d6ff126..542d095 100644 (file)
 #include <linux/if_tunnel.h>
 #include <linux/rtnetlink.h>
 #include <linux/netconf.h>
-
-#ifdef CONFIG_IPV6_PRIVACY
 #include <linux/random.h>
-#endif
-
 #include <linux/uaccess.h>
 #include <asm/unaligned.h>
 
@@ -124,11 +120,9 @@ static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
 }
 #endif
 
-#ifdef CONFIG_IPV6_PRIVACY
 static void __ipv6_regen_rndid(struct inet6_dev *idev);
 static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
 static void ipv6_regen_rndid(unsigned long data);
-#endif
 
 static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
 static int ipv6_count_addresses(struct inet6_dev *idev);
@@ -183,13 +177,11 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
        .rtr_solicits           = MAX_RTR_SOLICITATIONS,
        .rtr_solicit_interval   = RTR_SOLICITATION_INTERVAL,
        .rtr_solicit_delay      = MAX_RTR_SOLICITATION_DELAY,
-#ifdef CONFIG_IPV6_PRIVACY
        .use_tempaddr           = 0,
        .temp_valid_lft         = TEMP_VALID_LIFETIME,
        .temp_prefered_lft      = TEMP_PREFERRED_LIFETIME,
        .regen_max_retry        = REGEN_MAX_RETRY,
        .max_desync_factor      = MAX_DESYNC_FACTOR,
-#endif
        .max_addresses          = IPV6_MAX_ADDRESSES,
        .accept_ra_defrtr       = 1,
        .accept_ra_pinfo        = 1,
@@ -221,13 +213,11 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
        .rtr_solicits           = MAX_RTR_SOLICITATIONS,
        .rtr_solicit_interval   = RTR_SOLICITATION_INTERVAL,
        .rtr_solicit_delay      = MAX_RTR_SOLICITATION_DELAY,
-#ifdef CONFIG_IPV6_PRIVACY
        .use_tempaddr           = 0,
        .temp_valid_lft         = TEMP_VALID_LIFETIME,
        .temp_prefered_lft      = TEMP_PREFERRED_LIFETIME,
        .regen_max_retry        = REGEN_MAX_RETRY,
        .max_desync_factor      = MAX_DESYNC_FACTOR,
-#endif
        .max_addresses          = IPV6_MAX_ADDRESSES,
        .accept_ra_defrtr       = 1,
        .accept_ra_pinfo        = 1,
@@ -371,7 +361,6 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
        }
 #endif
 
-#ifdef CONFIG_IPV6_PRIVACY
        INIT_LIST_HEAD(&ndev->tempaddr_list);
        setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);
        if ((dev->flags&IFF_LOOPBACK) ||
@@ -384,7 +373,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
                in6_dev_hold(ndev);
                ipv6_regen_rndid((unsigned long) ndev);
        }
-#endif
+
        ndev->token = in6addr_any;
 
        if (netif_running(dev) && addrconf_qdisc_ok(dev))
@@ -865,12 +854,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
        /* Add to inet6_dev unicast addr list. */
        ipv6_link_dev_addr(idev, ifa);
 
-#ifdef CONFIG_IPV6_PRIVACY
        if (ifa->flags&IFA_F_TEMPORARY) {
                list_add(&ifa->tmp_list, &idev->tempaddr_list);
                in6_ifa_hold(ifa);
        }
-#endif
 
        in6_ifa_hold(ifa);
        write_unlock(&idev->lock);
@@ -913,7 +900,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        spin_unlock_bh(&addrconf_hash_lock);
 
        write_lock_bh(&idev->lock);
-#ifdef CONFIG_IPV6_PRIVACY
+
        if (ifp->flags&IFA_F_TEMPORARY) {
                list_del(&ifp->tmp_list);
                if (ifp->ifpub) {
@@ -922,7 +909,6 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
                }
                __in6_ifa_put(ifp);
        }
-#endif
 
        list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) {
                if (ifa == ifp) {
@@ -1013,7 +999,6 @@ out:
        in6_ifa_put(ifp);
 }
 
-#ifdef CONFIG_IPV6_PRIVACY
 static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *ift)
 {
        struct inet6_dev *idev = ifp->idev;
@@ -1116,7 +1101,6 @@ retry:
 out:
        return ret;
 }
-#endif
 
 /*
  *     Choose an appropriate source address (RFC3484)
@@ -1131,9 +1115,7 @@ enum {
 #endif
        IPV6_SADDR_RULE_OIF,
        IPV6_SADDR_RULE_LABEL,
-#ifdef CONFIG_IPV6_PRIVACY
        IPV6_SADDR_RULE_PRIVACY,
-#endif
        IPV6_SADDR_RULE_ORCHID,
        IPV6_SADDR_RULE_PREFIX,
        IPV6_SADDR_RULE_MAX
@@ -1247,7 +1229,6 @@ static int ipv6_get_saddr_eval(struct net *net,
                                      &score->ifa->addr, score->addr_type,
                                      score->ifa->idev->dev->ifindex) == dst->label;
                break;
-#ifdef CONFIG_IPV6_PRIVACY
        case IPV6_SADDR_RULE_PRIVACY:
            {
                /* Rule 7: Prefer public address
@@ -1259,7 +1240,6 @@ static int ipv6_get_saddr_eval(struct net *net,
                ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
                break;
            }
-#endif
        case IPV6_SADDR_RULE_ORCHID:
                /* Rule 8-: Prefer ORCHID vs ORCHID or
                 *          non-ORCHID vs non-ORCHID
@@ -1499,6 +1479,33 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
        return false;
 }
 
+/* Compares an address/prefix_len with addresses on device @dev.
+ * If one is found it returns true.
+ */
+bool ipv6_chk_custom_prefix(const struct in6_addr *addr,
+       const unsigned int prefix_len, struct net_device *dev)
+{
+       struct inet6_dev *idev;
+       struct inet6_ifaddr *ifa;
+       bool ret = false;
+
+       rcu_read_lock();
+       idev = __in6_dev_get(dev);
+       if (idev) {
+               read_lock_bh(&idev->lock);
+               list_for_each_entry(ifa, &idev->addr_list, if_list) {
+                       ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len);
+                       if (ret)
+                               break;
+               }
+               read_unlock_bh(&idev->lock);
+       }
+       rcu_read_unlock();
+
+       return ret;
+}
+EXPORT_SYMBOL(ipv6_chk_custom_prefix);
+
 int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev)
 {
        struct inet6_dev *idev;
@@ -1561,7 +1568,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
                if (dad_failed)
                        ipv6_ifa_notify(0, ifp);
                in6_ifa_put(ifp);
-#ifdef CONFIG_IPV6_PRIVACY
        } else if (ifp->flags&IFA_F_TEMPORARY) {
                struct inet6_ifaddr *ifpub;
                spin_lock_bh(&ifp->lock);
@@ -1575,7 +1581,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
                        spin_unlock_bh(&ifp->lock);
                }
                ipv6_del_addr(ifp);
-#endif
        } else
                ipv6_del_addr(ifp);
 }
@@ -1824,7 +1829,6 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
        return err;
 }
 
-#ifdef CONFIG_IPV6_PRIVACY
 /* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
 static void __ipv6_regen_rndid(struct inet6_dev *idev)
 {
@@ -1892,7 +1896,6 @@ static void  __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmp
        if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0)
                __ipv6_regen_rndid(idev);
 }
-#endif
 
 /*
  *     Add prefix route.
@@ -2180,9 +2183,7 @@ ok:
                if (ifp) {
                        int flags;
                        unsigned long now;
-#ifdef CONFIG_IPV6_PRIVACY
                        struct inet6_ifaddr *ift;
-#endif
                        u32 stored_lft;
 
                        /* update lifetime (RFC2462 5.5.3 e) */
@@ -2193,43 +2194,21 @@ ok:
                        else
                                stored_lft = 0;
                        if (!update_lft && !create && stored_lft) {
-                               if (valid_lft > MIN_VALID_LIFETIME ||
-                                   valid_lft > stored_lft)
-                                       update_lft = 1;
-                               else if (stored_lft <= MIN_VALID_LIFETIME) {
-                                       /* valid_lft <= stored_lft is always true */
-                                       /*
-                                        * RFC 4862 Section 5.5.3e:
-                                        * "Note that the preferred lifetime of
-                                        *  the corresponding address is always
-                                        *  reset to the Preferred Lifetime in
-                                        *  the received Prefix Information
-                                        *  option, regardless of whether the
-                                        *  valid lifetime is also reset or
-                                        *  ignored."
-                                        *
-                                        *  So if the preferred lifetime in
-                                        *  this advertisement is different
-                                        *  than what we have stored, but the
-                                        *  valid lifetime is invalid, just
-                                        *  reset prefered_lft.
-                                        *
-                                        *  We must set the valid lifetime
-                                        *  to the stored lifetime since we'll
-                                        *  be updating the timestamp below,
-                                        *  else we'll set it back to the
-                                        *  minimum.
-                                        */
-                                       if (prefered_lft != ifp->prefered_lft) {
-                                               valid_lft = stored_lft;
-                                               update_lft = 1;
-                                       }
-                               } else {
-                                       valid_lft = MIN_VALID_LIFETIME;
-                                       if (valid_lft < prefered_lft)
-                                               prefered_lft = valid_lft;
-                                       update_lft = 1;
-                               }
+                               const u32 minimum_lft = min(
+                                       stored_lft, (u32)MIN_VALID_LIFETIME);
+                               valid_lft = max(valid_lft, minimum_lft);
+
+                               /* RFC4862 Section 5.5.3e:
+                                * "Note that the preferred lifetime of the
+                                *  corresponding address is always reset to
+                                *  the Preferred Lifetime in the received
+                                *  Prefix Information option, regardless of
+                                *  whether the valid lifetime is also reset or
+                                *  ignored."
+                                *
+                                * So we should always update prefered_lft here.
+                                */
+                               update_lft = 1;
                        }
 
                        if (update_lft) {
@@ -2245,7 +2224,6 @@ ok:
                        } else
                                spin_unlock(&ifp->lock);
 
-#ifdef CONFIG_IPV6_PRIVACY
                        read_lock_bh(&in6_dev->lock);
                        /* update all temporary addresses in the list */
                        list_for_each_entry(ift, &in6_dev->tempaddr_list,
@@ -2310,7 +2288,7 @@ ok:
                        } else {
                                read_unlock_bh(&in6_dev->lock);
                        }
-#endif
+
                        in6_ifa_put(ifp);
                        addrconf_verify(0);
                }
@@ -2990,7 +2968,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        if (!how)
                idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
 
-#ifdef CONFIG_IPV6_PRIVACY
        if (how && del_timer(&idev->regen_timer))
                in6_dev_put(idev);
 
@@ -3010,7 +2987,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                in6_ifa_put(ifa);
                write_lock_bh(&idev->lock);
        }
-#endif
 
        while (!list_empty(&idev->addr_list)) {
                ifa = list_first_entry(&idev->addr_list,
@@ -3523,7 +3499,6 @@ restart:
                                        in6_ifa_put(ifp);
                                        goto restart;
                                }
-#ifdef CONFIG_IPV6_PRIVACY
                        } else if ((ifp->flags&IFA_F_TEMPORARY) &&
                                   !(ifp->flags&IFA_F_TENTATIVE)) {
                                unsigned long regen_advance = ifp->idev->cnf.regen_max_retry *
@@ -3551,7 +3526,6 @@ restart:
                                } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
                                        next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;
                                spin_unlock(&ifp->lock);
-#endif
                        } else {
                                /* ifp->prefered_lft <= ifp->valid_lft */
                                if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
@@ -4123,13 +4097,11 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
                jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval);
        array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] =
                jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval);
-#ifdef CONFIG_IPV6_PRIVACY
        array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr;
        array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft;
        array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft;
        array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;
        array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
-#endif
        array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
        array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
        array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
@@ -4823,7 +4795,6 @@ static struct addrconf_sysctl_table
                        .mode           = 0644,
                        .proc_handler   = proc_dointvec_ms_jiffies,
                },
-#ifdef CONFIG_IPV6_PRIVACY
                {
                        .procname       = "use_tempaddr",
                        .data           = &ipv6_devconf.use_tempaddr,
@@ -4859,7 +4830,6 @@ static struct addrconf_sysctl_table
                        .mode           = 0644,
                        .proc_handler   = proc_dointvec,
                },
-#endif
                {
                        .procname       = "max_addresses",
                        .data           = &ipv6_devconf.max_addresses,
index 7c96100..6468bda 100644 (file)
@@ -110,11 +110,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
        int try_loading_module = 0;
        int err;
 
-       if (sock->type != SOCK_RAW &&
-           sock->type != SOCK_DGRAM &&
-           !inet_ehash_secret)
-               build_ehash_secret();
-
        /* Look for the requested type/protocol pair. */
 lookup_protocol:
        err = -ESOCKTNOSUPPORT;
@@ -364,7 +359,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        inet->inet_rcv_saddr = v4addr;
        inet->inet_saddr = v4addr;
 
-       np->rcv_saddr = addr->sin6_addr;
+       sk->sk_v6_rcv_saddr = addr->sin6_addr;
 
        if (!(addr_type & IPV6_ADDR_MULTICAST))
                np->saddr = addr->sin6_addr;
@@ -461,14 +456,14 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
                    peer == 1)
                        return -ENOTCONN;
                sin->sin6_port = inet->inet_dport;
-               sin->sin6_addr = np->daddr;
+               sin->sin6_addr = sk->sk_v6_daddr;
                if (np->sndflow)
                        sin->sin6_flowinfo = np->flow_label;
        } else {
-               if (ipv6_addr_any(&np->rcv_saddr))
+               if (ipv6_addr_any(&sk->sk_v6_rcv_saddr))
                        sin->sin6_addr = np->saddr;
                else
-                       sin->sin6_addr = np->rcv_saddr;
+                       sin->sin6_addr = sk->sk_v6_rcv_saddr;
 
                sin->sin6_port = inet->inet_sport;
        }
@@ -655,7 +650,7 @@ int inet6_sk_rebuild_header(struct sock *sk)
 
                memset(&fl6, 0, sizeof(fl6));
                fl6.flowi6_proto = sk->sk_protocol;
-               fl6.daddr = np->daddr;
+               fl6.daddr = sk->sk_v6_daddr;
                fl6.saddr = np->saddr;
                fl6.flowlabel = np->flow_label;
                fl6.flowi6_oif = sk->sk_bound_dev_if;
@@ -870,8 +865,6 @@ static int __init inet6_init(void)
        if (err)
                goto out_sock_register_fail;
 
-       tcpv6_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;
-
        /*
         *      ipngwg API draft makes clear that the correct semantics
         *      for TCP and UDP is to consider one TCP and UDP instance
@@ -1028,52 +1021,4 @@ out_unregister_tcp_proto:
 }
 module_init(inet6_init);
 
-static void __exit inet6_exit(void)
-{
-       if (disable_ipv6_mod)
-               return;
-
-       /* First of all disallow new sockets creation. */
-       sock_unregister(PF_INET6);
-       /* Disallow any further netlink messages */
-       rtnl_unregister_all(PF_INET6);
-
-       udpv6_exit();
-       udplitev6_exit();
-       tcpv6_exit();
-
-       /* Cleanup code parts. */
-       ipv6_packet_cleanup();
-       ipv6_frag_exit();
-       ipv6_exthdrs_exit();
-       addrconf_cleanup();
-       ip6_flowlabel_cleanup();
-       ndisc_late_cleanup();
-       ip6_route_cleanup();
-#ifdef CONFIG_PROC_FS
-
-       /* Cleanup code parts. */
-       if6_proc_exit();
-       ipv6_misc_proc_exit();
-       udplite6_proc_exit();
-       raw6_proc_exit();
-#endif
-       ipv6_netfilter_fini();
-       ipv6_stub = NULL;
-       igmp6_cleanup();
-       ndisc_cleanup();
-       ip6_mr_cleanup();
-       icmpv6_cleanup();
-       rawv6_exit();
-
-       unregister_pernet_subsys(&inet6_net_ops);
-       proto_unregister(&rawv6_prot);
-       proto_unregister(&udplitev6_prot);
-       proto_unregister(&udpv6_prot);
-       proto_unregister(&tcpv6_prot);
-
-       rcu_barrier(); /* Wait for completion of call_rcu()'s */
-}
-module_exit(inet6_exit);
-
 MODULE_ALIAS_NETPROTO(PF_INET6);
index 73784c3..82e1da3 100644 (file)
@@ -618,8 +618,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
        struct xfrm_state *x;
 
-       if (type != ICMPV6_DEST_UNREACH &&
-           type != ICMPV6_PKT_TOOBIG &&
+       if (type != ICMPV6_PKT_TOOBIG &&
            type != NDISC_REDIRECT)
                return;
 
index 48b6bd2..a454b0f 100644 (file)
@@ -107,16 +107,16 @@ ipv4_connected:
                if (err)
                        goto out;
 
-               ipv6_addr_set_v4mapped(inet->inet_daddr, &np->daddr);
+               ipv6_addr_set_v4mapped(inet->inet_daddr, &sk->sk_v6_daddr);
 
                if (ipv6_addr_any(&np->saddr) ||
                    ipv6_mapped_addr_any(&np->saddr))
                        ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
 
-               if (ipv6_addr_any(&np->rcv_saddr) ||
-                   ipv6_mapped_addr_any(&np->rcv_saddr)) {
+               if (ipv6_addr_any(&sk->sk_v6_rcv_saddr) ||
+                   ipv6_mapped_addr_any(&sk->sk_v6_rcv_saddr)) {
                        ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
-                                              &np->rcv_saddr);
+                                              &sk->sk_v6_rcv_saddr);
                        if (sk->sk_prot->rehash)
                                sk->sk_prot->rehash(sk);
                }
@@ -145,7 +145,7 @@ ipv4_connected:
                }
        }
 
-       np->daddr = *daddr;
+       sk->sk_v6_daddr = *daddr;
        np->flow_label = fl6.flowlabel;
 
        inet->inet_dport = usin->sin6_port;
@@ -156,7 +156,7 @@ ipv4_connected:
         */
 
        fl6.flowi6_proto = sk->sk_protocol;
-       fl6.daddr = np->daddr;
+       fl6.daddr = sk->sk_v6_daddr;
        fl6.saddr = np->saddr;
        fl6.flowi6_oif = sk->sk_bound_dev_if;
        fl6.flowi6_mark = sk->sk_mark;
@@ -183,16 +183,16 @@ ipv4_connected:
        if (ipv6_addr_any(&np->saddr))
                np->saddr = fl6.saddr;
 
-       if (ipv6_addr_any(&np->rcv_saddr)) {
-               np->rcv_saddr = fl6.saddr;
+       if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+               sk->sk_v6_rcv_saddr = fl6.saddr;
                inet->inet_rcv_saddr = LOOPBACK4_IPV6;
                if (sk->sk_prot->rehash)
                        sk->sk_prot->rehash(sk);
        }
 
        ip6_dst_store(sk, dst,
-                     ipv6_addr_equal(&fl6.daddr, &np->daddr) ?
-                     &np->daddr : NULL,
+                     ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ?
+                     &sk->sk_v6_daddr : NULL,
 #ifdef CONFIG_IPV6_SUBTREES
                      ipv6_addr_equal(&fl6.saddr, &np->saddr) ?
                      &np->saddr :
@@ -883,11 +883,10 @@ EXPORT_SYMBOL_GPL(ip6_datagram_send_ctl);
 void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
                             __u16 srcp, __u16 destp, int bucket)
 {
-       struct ipv6_pinfo *np = inet6_sk(sp);
        const struct in6_addr *dest, *src;
 
-       dest  = &np->daddr;
-       src   = &np->rcv_saddr;
+       dest  = &sp->sk_v6_daddr;
+       src   = &sp->sk_v6_rcv_saddr;
        seq_printf(seq,
                   "%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
                   "%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d\n",
index d3618a7..b8719df 100644 (file)
@@ -164,10 +164,9 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
        u8 *iv;
        u8 *tail;
        __be32 *seqhi;
-       struct esp_data *esp = x->data;
 
        /* skb is pure payload to encrypt */
-       aead = esp->aead;
+       aead = x->data;
        alen = crypto_aead_authsize(aead);
 
        tfclen = 0;
@@ -181,8 +180,6 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
        }
        blksize = ALIGN(crypto_aead_blocksize(aead), 4);
        clen = ALIGN(skb->len + 2 + tfclen, blksize);
-       if (esp->padlen)
-               clen = ALIGN(clen, esp->padlen);
        plen = clen - skb->len - tfclen;
 
        err = skb_cow_data(skb, tfclen + plen + alen, &trailer);
@@ -271,8 +268,7 @@ error:
 static int esp_input_done2(struct sk_buff *skb, int err)
 {
        struct xfrm_state *x = xfrm_input_state(skb);
-       struct esp_data *esp = x->data;
-       struct crypto_aead *aead = esp->aead;
+       struct crypto_aead *aead = x->data;
        int alen = crypto_aead_authsize(aead);
        int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
        int elen = skb->len - hlen;
@@ -325,8 +321,7 @@ static void esp_input_done(struct crypto_async_request *base, int err)
 static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct ip_esp_hdr *esph;
-       struct esp_data *esp = x->data;
-       struct crypto_aead *aead = esp->aead;
+       struct crypto_aead *aead = x->data;
        struct aead_request *req;
        struct sk_buff *trailer;
        int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
@@ -414,9 +409,8 @@ out:
 
 static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
 {
-       struct esp_data *esp = x->data;
-       u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
-       u32 align = max_t(u32, blksize, esp->padlen);
+       struct crypto_aead *aead = x->data;
+       u32 blksize = ALIGN(crypto_aead_blocksize(aead), 4);
        unsigned int net_adj;
 
        if (x->props.mode != XFRM_MODE_TUNNEL)
@@ -424,8 +418,8 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
        else
                net_adj = 0;
 
-       return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) -
-                net_adj) & ~(align - 1)) + net_adj - 2;
+       return ((mtu - x->props.header_len - crypto_aead_authsize(aead) -
+                net_adj) & ~(blksize - 1)) + net_adj - 2;
 }
 
 static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
@@ -436,8 +430,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
        struct xfrm_state *x;
 
-       if (type != ICMPV6_DEST_UNREACH &&
-           type != ICMPV6_PKT_TOOBIG &&
+       if (type != ICMPV6_PKT_TOOBIG &&
            type != NDISC_REDIRECT)
                return;
 
@@ -455,18 +448,16 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
 static void esp6_destroy(struct xfrm_state *x)
 {
-       struct esp_data *esp = x->data;
+       struct crypto_aead *aead = x->data;
 
-       if (!esp)
+       if (!aead)
                return;
 
-       crypto_free_aead(esp->aead);
-       kfree(esp);
+       crypto_free_aead(aead);
 }
 
 static int esp_init_aead(struct xfrm_state *x)
 {
-       struct esp_data *esp = x->data;
        struct crypto_aead *aead;
        int err;
 
@@ -475,7 +466,7 @@ static int esp_init_aead(struct xfrm_state *x)
        if (IS_ERR(aead))
                goto error;
 
-       esp->aead = aead;
+       x->data = aead;
 
        err = crypto_aead_setkey(aead, x->aead->alg_key,
                                 (x->aead->alg_key_len + 7) / 8);
@@ -492,7 +483,6 @@ error:
 
 static int esp_init_authenc(struct xfrm_state *x)
 {
-       struct esp_data *esp = x->data;
        struct crypto_aead *aead;
        struct crypto_authenc_key_param *param;
        struct rtattr *rta;
@@ -527,7 +517,7 @@ static int esp_init_authenc(struct xfrm_state *x)
        if (IS_ERR(aead))
                goto error;
 
-       esp->aead = aead;
+       x->data = aead;
 
        keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) +
                 (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param));
@@ -582,7 +572,6 @@ error:
 
 static int esp6_init_state(struct xfrm_state *x)
 {
-       struct esp_data *esp;
        struct crypto_aead *aead;
        u32 align;
        int err;
@@ -590,11 +579,7 @@ static int esp6_init_state(struct xfrm_state *x)
        if (x->encap)
                return -EINVAL;
 
-       esp = kzalloc(sizeof(*esp), GFP_KERNEL);
-       if (esp == NULL)
-               return -ENOMEM;
-
-       x->data = esp;
+       x->data = NULL;
 
        if (x->aead)
                err = esp_init_aead(x);
@@ -604,9 +589,7 @@ static int esp6_init_state(struct xfrm_state *x)
        if (err)
                goto error;
 
-       aead = esp->aead;
-
-       esp->padlen = 0;
+       aead = x->data;
 
        x->props.header_len = sizeof(struct ip_esp_hdr) +
                              crypto_aead_ivsize(aead);
@@ -626,9 +609,7 @@ static int esp6_init_state(struct xfrm_state *x)
        }
 
        align = ALIGN(crypto_aead_blocksize(aead), 4);
-       if (esp->padlen)
-               align = max_t(u32, align, esp->padlen);
-       x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead);
+       x->props.trailer_len = align + 1 + crypto_aead_authsize(aead);
 
 error:
        return err;
index e4311cb..77bb8af 100644 (file)
@@ -70,20 +70,20 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk,
                                      struct flowi6 *fl6,
                                      const struct request_sock *req)
 {
-       struct inet6_request_sock *treq = inet6_rsk(req);
+       struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct in6_addr *final_p, final;
        struct dst_entry *dst;
 
        memset(fl6, 0, sizeof(*fl6));
        fl6->flowi6_proto = IPPROTO_TCP;
-       fl6->daddr = treq->rmt_addr;
+       fl6->daddr = ireq->ir_v6_rmt_addr;
        final_p = fl6_update_dst(fl6, np->opt, &final);
-       fl6->saddr = treq->loc_addr;
-       fl6->flowi6_oif = treq->iif;
+       fl6->saddr = ireq->ir_v6_loc_addr;
+       fl6->flowi6_oif = ireq->ir_iif;
        fl6->flowi6_mark = sk->sk_mark;
-       fl6->fl6_dport = inet_rsk(req)->rmt_port;
-       fl6->fl6_sport = inet_rsk(req)->loc_port;
+       fl6->fl6_dport = ireq->ir_rmt_port;
+       fl6->fl6_sport = htons(ireq->ir_num);
        security_req_classify_flow(req, flowi6_to_flowi(fl6));
 
        dst = ip6_dst_lookup_flow(sk, fl6, final_p, false);
@@ -129,13 +129,13 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk,
                                                     lopt->nr_table_entries)];
             (req = *prev) != NULL;
             prev = &req->dl_next) {
-               const struct inet6_request_sock *treq = inet6_rsk(req);
+               const struct inet_request_sock *ireq = inet_rsk(req);
 
-               if (inet_rsk(req)->rmt_port == rport &&
+               if (ireq->ir_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)) {
+                   ipv6_addr_equal(&ireq->ir_v6_rmt_addr, raddr) &&
+                   ipv6_addr_equal(&ireq->ir_v6_loc_addr, laddr) &&
+                   (!ireq->ir_iif || ireq->ir_iif == iif)) {
                        WARN_ON(req->sk != NULL);
                        *prevp = prev;
                        return req;
@@ -153,8 +153,8 @@ void inet6_csk_reqsk_queue_hash_add(struct sock *sk,
 {
        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,
+       const u32 h = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr,
+                                     inet_rsk(req)->ir_rmt_port,
                                      lopt->hash_rnd, lopt->nr_table_entries);
 
        reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);
@@ -165,11 +165,10 @@ 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;
-       sin6->sin6_addr = np->daddr;
+       sin6->sin6_addr = sk->sk_v6_daddr;
        sin6->sin6_port = inet_sk(sk)->inet_dport;
        /* We do not store received flowlabel for TCP */
        sin6->sin6_flowinfo = 0;
@@ -203,7 +202,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
 
        memset(fl6, 0, sizeof(*fl6));
        fl6->flowi6_proto = sk->sk_protocol;
-       fl6->daddr = np->daddr;
+       fl6->daddr = sk->sk_v6_daddr;
        fl6->saddr = np->saddr;
        fl6->flowlabel = np->flow_label;
        IP6_ECN_flow_xmit(sk, fl6->flowlabel);
@@ -245,7 +244,7 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused)
        skb_dst_set_noref(skb, dst);
 
        /* Restore final destination back after routing done */
-       fl6.daddr = np->daddr;
+       fl6.daddr = sk->sk_v6_daddr;
 
        res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
        rcu_read_unlock();
index 32b4a16..262e13c 100644 (file)
 #include <net/secure_seq.h>
 #include <net/ip.h>
 
+static unsigned int inet6_ehashfn(struct net *net,
+                                 const struct in6_addr *laddr,
+                                 const u16 lport,
+                                 const struct in6_addr *faddr,
+                                 const __be16 fport)
+{
+       static u32 inet6_ehash_secret __read_mostly;
+       static u32 ipv6_hash_secret __read_mostly;
+
+       u32 lhash, fhash;
+
+       net_get_random_once(&inet6_ehash_secret, sizeof(inet6_ehash_secret));
+       net_get_random_once(&ipv6_hash_secret, sizeof(ipv6_hash_secret));
+
+       lhash = (__force u32)laddr->s6_addr32[3];
+       fhash = __ipv6_addr_jhash(faddr, ipv6_hash_secret);
+
+       return __inet6_ehashfn(lhash, lport, fhash, fport,
+                              inet6_ehash_secret + net_hash_mix(net));
+}
+
+static int inet6_sk_ehashfn(const struct sock *sk)
+{
+       const struct inet_sock *inet = inet_sk(sk);
+       const struct in6_addr *laddr = &sk->sk_v6_rcv_saddr;
+       const struct in6_addr *faddr = &sk->sk_v6_daddr;
+       const __u16 lport = inet->inet_num;
+       const __be16 fport = inet->inet_dport;
+       struct net *net = sock_net(sk);
+
+       return inet6_ehashfn(net, laddr, lport, faddr, fport);
+}
+
 int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw)
 {
        struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
@@ -89,43 +122,22 @@ begin:
        sk_nulls_for_each_rcu(sk, node, &head->chain) {
                if (sk->sk_hash != hash)
                        continue;
-               if (likely(INET6_MATCH(sk, net, saddr, daddr, ports, dif))) {
-                       if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
-                               goto begintw;
-                       if (unlikely(!INET6_MATCH(sk, net, saddr, daddr,
-                                                 ports, dif))) {
-                               sock_put(sk);
-                               goto begin;
-                       }
-               goto out;
-               }
-       }
-       if (get_nulls_value(node) != slot)
-               goto begin;
-
-begintw:
-       /* Must check for a TIME_WAIT'er before going to listener hash. */
-       sk_nulls_for_each_rcu(sk, node, &head->twchain) {
-               if (sk->sk_hash != hash)
+               if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif))
                        continue;
-               if (likely(INET6_TW_MATCH(sk, net, saddr, daddr,
-                                         ports, dif))) {
-                       if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
-                               sk = NULL;
-                               goto out;
-                       }
-                       if (unlikely(!INET6_TW_MATCH(sk, net, saddr, daddr,
-                                                    ports, dif))) {
-                               sock_put(sk);
-                               goto begintw;
-                       }
+               if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
                        goto out;
+
+               if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif))) {
+                       sock_gen_put(sk);
+                       goto begin;
                }
+               goto found;
        }
        if (get_nulls_value(node) != slot)
-               goto begintw;
-       sk = NULL;
+               goto begin;
 out:
+       sk = NULL;
+found:
        rcu_read_unlock();
        return sk;
 }
@@ -140,11 +152,10 @@ static inline int compute_score(struct sock *sk, struct net *net,
 
        if (net_eq(sock_net(sk), net) && inet_sk(sk)->inet_num == hnum &&
            sk->sk_family == PF_INET6) {
-               const struct ipv6_pinfo *np = inet6_sk(sk);
 
                score = 1;
-               if (!ipv6_addr_any(&np->rcv_saddr)) {
-                       if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
+               if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+                       if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
                                return -1;
                        score++;
                }
@@ -236,9 +247,8 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
 {
        struct inet_hashinfo *hinfo = death_row->hashinfo;
        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 struct in6_addr *daddr = &sk->sk_v6_rcv_saddr;
+       const struct in6_addr *saddr = &sk->sk_v6_daddr;
        const int dif = sk->sk_bound_dev_if;
        const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
        struct net *net = sock_net(sk);
@@ -248,38 +258,28 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
        spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
        struct sock *sk2;
        const struct hlist_nulls_node *node;
-       struct inet_timewait_sock *tw;
+       struct inet_timewait_sock *tw = NULL;
        int twrefcnt = 0;
 
        spin_lock(lock);
 
-       /* Check TIME-WAIT sockets first. */
-       sk_nulls_for_each(sk2, node, &head->twchain) {
-               if (sk2->sk_hash != hash)
-                       continue;
-
-               if (likely(INET6_TW_MATCH(sk2, net, saddr, daddr,
-                                         ports, dif))) {
-                       tw = inet_twsk(sk2);
-                       if (twsk_unique(sk, sk2, twp))
-                               goto unique;
-                       else
-                               goto not_unique;
-               }
-       }
-       tw = NULL;
-
-       /* And established part... */
        sk_nulls_for_each(sk2, node, &head->chain) {
                if (sk2->sk_hash != hash)
                        continue;
-               if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif)))
+
+               if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif))) {
+                       if (sk2->sk_state == TCP_TIME_WAIT) {
+                               tw = inet_twsk(sk2);
+                               if (twsk_unique(sk, sk2, twp))
+                                       break;
+                       }
                        goto not_unique;
+               }
        }
 
-unique:
        /* Must record num and sport now. Otherwise we will see
-        * in hash table socket with a funny identity. */
+        * in hash table socket with a funny identity.
+        */
        inet->inet_num = lport;
        inet->inet_sport = htons(lport);
        sk->sk_hash = hash;
@@ -312,9 +312,9 @@ not_unique:
 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,
+
+       return secure_ipv6_port_ephemeral(sk->sk_v6_rcv_saddr.s6_addr32,
+                                         sk->sk_v6_daddr.s6_addr32,
                                          inet->inet_dport);
 }
 
index 5bec666..5550a81 100644 (file)
@@ -1529,25 +1529,6 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root,
        fib6_walk(&c.w);
 }
 
-void fib6_clean_all_ro(struct net *net, int (*func)(struct rt6_info *, void *arg),
-                   int prune, void *arg)
-{
-       struct fib6_table *table;
-       struct hlist_head *head;
-       unsigned int h;
-
-       rcu_read_lock();
-       for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
-               head = &net->ipv6.fib_table_hash[h];
-               hlist_for_each_entry_rcu(table, head, tb6_hlist) {
-                       read_lock_bh(&table->tb6_lock);
-                       fib6_clean_tree(net, &table->tb6_root,
-                                       func, prune, arg);
-                       read_unlock_bh(&table->tb6_lock);
-               }
-       }
-       rcu_read_unlock();
-}
 void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
                    int prune, void *arg)
 {
@@ -1782,3 +1763,189 @@ void fib6_gc_cleanup(void)
        unregister_pernet_subsys(&fib6_net_ops);
        kmem_cache_destroy(fib6_node_kmem);
 }
+
+#ifdef CONFIG_PROC_FS
+
+struct ipv6_route_iter {
+       struct seq_net_private p;
+       struct fib6_walker_t w;
+       loff_t skip;
+       struct fib6_table *tbl;
+       __u32 sernum;
+};
+
+static int ipv6_route_seq_show(struct seq_file *seq, void *v)
+{
+       struct rt6_info *rt = v;
+       struct ipv6_route_iter *iter = seq->private;
+
+       seq_printf(seq, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
+
+#ifdef CONFIG_IPV6_SUBTREES
+       seq_printf(seq, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
+#else
+       seq_puts(seq, "00000000000000000000000000000000 00 ");
+#endif
+       if (rt->rt6i_flags & RTF_GATEWAY)
+               seq_printf(seq, "%pi6", &rt->rt6i_gateway);
+       else
+               seq_puts(seq, "00000000000000000000000000000000");
+
+       seq_printf(seq, " %08x %08x %08x %08x %8s\n",
+                  rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
+                  rt->dst.__use, rt->rt6i_flags,
+                  rt->dst.dev ? rt->dst.dev->name : "");
+       iter->w.leaf = NULL;
+       return 0;
+}
+
+static int ipv6_route_yield(struct fib6_walker_t *w)
+{
+       struct ipv6_route_iter *iter = w->args;
+
+       if (!iter->skip)
+               return 1;
+
+       do {
+               iter->w.leaf = iter->w.leaf->dst.rt6_next;
+               iter->skip--;
+               if (!iter->skip && iter->w.leaf)
+                       return 1;
+       } while (iter->w.leaf);
+
+       return 0;
+}
+
+static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter)
+{
+       memset(&iter->w, 0, sizeof(iter->w));
+       iter->w.func = ipv6_route_yield;
+       iter->w.root = &iter->tbl->tb6_root;
+       iter->w.state = FWS_INIT;
+       iter->w.node = iter->w.root;
+       iter->w.args = iter;
+       iter->sernum = iter->w.root->fn_sernum;
+       INIT_LIST_HEAD(&iter->w.lh);
+       fib6_walker_link(&iter->w);
+}
+
+static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
+                                                   struct net *net)
+{
+       unsigned int h;
+       struct hlist_node *node;
+
+       if (tbl) {
+               h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1;
+               node = rcu_dereference_bh(hlist_next_rcu(&tbl->tb6_hlist));
+       } else {
+               h = 0;
+               node = NULL;
+       }
+
+       while (!node && h < FIB6_TABLE_HASHSZ) {
+               node = rcu_dereference_bh(
+                       hlist_first_rcu(&net->ipv6.fib_table_hash[h++]));
+       }
+       return hlist_entry_safe(node, struct fib6_table, tb6_hlist);
+}
+
+static void ipv6_route_check_sernum(struct ipv6_route_iter *iter)
+{
+       if (iter->sernum != iter->w.root->fn_sernum) {
+               iter->sernum = iter->w.root->fn_sernum;
+               iter->w.state = FWS_INIT;
+               iter->w.node = iter->w.root;
+               WARN_ON(iter->w.skip);
+               iter->w.skip = iter->w.count;
+       }
+}
+
+static void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       int r;
+       struct rt6_info *n;
+       struct net *net = seq_file_net(seq);
+       struct ipv6_route_iter *iter = seq->private;
+
+       if (!v)
+               goto iter_table;
+
+       n = ((struct rt6_info *)v)->dst.rt6_next;
+       if (n) {
+               ++*pos;
+               return n;
+       }
+
+iter_table:
+       ipv6_route_check_sernum(iter);
+       read_lock(&iter->tbl->tb6_lock);
+       r = fib6_walk_continue(&iter->w);
+       read_unlock(&iter->tbl->tb6_lock);
+       if (r > 0) {
+               if (v)
+                       ++*pos;
+               return iter->w.leaf;
+       } else if (r < 0) {
+               fib6_walker_unlink(&iter->w);
+               return NULL;
+       }
+       fib6_walker_unlink(&iter->w);
+
+       iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
+       if (!iter->tbl)
+               return NULL;
+
+       ipv6_route_seq_setup_walk(iter);
+       goto iter_table;
+}
+
+static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
+       __acquires(RCU_BH)
+{
+       struct net *net = seq_file_net(seq);
+       struct ipv6_route_iter *iter = seq->private;
+
+       rcu_read_lock_bh();
+       iter->tbl = ipv6_route_seq_next_table(NULL, net);
+       iter->skip = *pos;
+
+       if (iter->tbl) {
+               ipv6_route_seq_setup_walk(iter);
+               return ipv6_route_seq_next(seq, NULL, pos);
+       } else {
+               return NULL;
+       }
+}
+
+static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
+{
+       struct fib6_walker_t *w = &iter->w;
+       return w->node && !(w->state == FWS_U && w->node == w->root);
+}
+
+static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
+       __releases(RCU_BH)
+{
+       struct ipv6_route_iter *iter = seq->private;
+
+       if (ipv6_route_iter_active(iter))
+               fib6_walker_unlink(&iter->w);
+
+       rcu_read_unlock_bh();
+}
+
+static const struct seq_operations ipv6_route_seq_ops = {
+       .start  = ipv6_route_seq_start,
+       .next   = ipv6_route_seq_next,
+       .stop   = ipv6_route_seq_stop,
+       .show   = ipv6_route_seq_show
+};
+
+int ipv6_route_open(struct inode *inode, struct file *file)
+{
+       return seq_open_net(inode, file, &ipv6_route_seq_ops,
+                           sizeof(struct ipv6_route_iter));
+}
+
+#endif /* CONFIG_PROC_FS */
index 46e8843..e7fb710 100644 (file)
@@ -41,7 +41,7 @@
 #define FL_MIN_LINGER  6       /* Minimal linger. It is set to 6sec specified
                                   in old IPv6 RFC. Well, it was reasonable value.
                                 */
-#define FL_MAX_LINGER  60      /* Maximal linger timeout */
+#define FL_MAX_LINGER  150     /* Maximal linger timeout */
 
 /* FL hash table */
 
@@ -345,6 +345,8 @@ static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned lo
        expires = check_linger(expires);
        if (!expires)
                return -EPERM;
+
+       spin_lock_bh(&ip6_fl_lock);
        fl->lastuse = jiffies;
        if (time_before(fl->linger, linger))
                fl->linger = linger;
@@ -352,6 +354,8 @@ static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned lo
                expires = fl->linger;
        if (time_before(fl->expires, fl->lastuse + expires))
                fl->expires = fl->lastuse + expires;
+       spin_unlock_bh(&ip6_fl_lock);
+
        return 0;
 }
 
@@ -453,8 +457,10 @@ static int mem_check(struct sock *sk)
        if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
                return 0;
 
+       rcu_read_lock_bh();
        for_each_sk_fl_rcu(np, sfl)
                count++;
+       rcu_read_unlock_bh();
 
        if (room <= 0 ||
            ((count >= FL_MAX_PER_SOCK ||
@@ -465,34 +471,6 @@ static int mem_check(struct sock *sk)
        return 0;
 }
 
-static bool ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2)
-{
-       if (h1 == h2)
-               return false;
-       if (h1 == NULL || h2 == NULL)
-               return true;
-       if (h1->hdrlen != h2->hdrlen)
-               return true;
-       return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1));
-}
-
-static bool ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2)
-{
-       if (o1 == o2)
-               return false;
-       if (o1 == NULL || o2 == NULL)
-               return true;
-       if (o1->opt_nflen != o2->opt_nflen)
-               return true;
-       if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt))
-               return true;
-       if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt))
-               return true;
-       if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt))
-               return true;
-       return false;
-}
-
 static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
                struct ip6_flowlabel *fl)
 {
@@ -503,6 +481,32 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
        spin_unlock_bh(&ip6_sk_fl_lock);
 }
 
+int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq)
+{
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct ipv6_fl_socklist *sfl;
+
+       rcu_read_lock_bh();
+
+       for_each_sk_fl_rcu(np, sfl) {
+               if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) {
+                       spin_lock_bh(&ip6_fl_lock);
+                       freq->flr_label = sfl->fl->label;
+                       freq->flr_dst = sfl->fl->dst;
+                       freq->flr_share = sfl->fl->share;
+                       freq->flr_expires = (sfl->fl->expires - jiffies) / HZ;
+                       freq->flr_linger = sfl->fl->linger / HZ;
+
+                       spin_unlock_bh(&ip6_fl_lock);
+                       rcu_read_unlock_bh();
+                       return 0;
+               }
+       }
+       rcu_read_unlock_bh();
+
+       return -ENOENT;
+}
+
 int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
 {
        int uninitialized_var(err);
@@ -603,11 +607,6 @@ recheck:
                                     uid_eq(fl1->owner.uid, fl->owner.uid)))
                                        goto release;
 
-                               err = -EINVAL;
-                               if (!ipv6_addr_equal(&fl1->dst, &fl->dst) ||
-                                   ipv6_opt_cmp(fl1->opt, fl->opt))
-                                       goto release;
-
                                err = -ENOMEM;
                                if (sfl1 == NULL)
                                        goto release;
index 6b26e9f..bf4a9a0 100644 (file)
@@ -618,7 +618,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
        struct ip6_tnl *tunnel = netdev_priv(dev);
        struct net_device *tdev;    /* Device to other host */
        struct ipv6hdr  *ipv6h;     /* Our new IP header */
-       unsigned int max_headroom /* The extra header space needed */
+       unsigned int max_headroom = 0; /* The extra header space needed */
        int    gre_hlen;
        struct ipv6_tel_txoption opt;
        int    mtu;
@@ -693,7 +693,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
 
        skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
 
-       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
+       max_headroom += LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
 
        if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
            (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
@@ -976,6 +976,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                if (t->parms.o_flags&GRE_SEQ)
                        addend += 4;
        }
+       t->hlen = addend;
 
        if (p->flags & IP6_TNL_F_CAP_XMIT) {
                int strict = (ipv6_addr_type(&p->raddr) &
@@ -1002,8 +1003,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                }
                ip6_rt_put(rt);
        }
-
-       t->hlen = addend;
 }
 
 static int ip6gre_tnl_change(struct ip6_tnl *t,
@@ -1173,9 +1172,8 @@ done:
 
 static int ip6gre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct ip6_tnl *tunnel = netdev_priv(dev);
        if (new_mtu < 68 ||
-           new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
+           new_mtu > 0xFFF8 - dev->hard_header_len)
                return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
index d82de72..4b85169 100644 (file)
@@ -66,7 +66,6 @@ static int ipv6_gso_send_check(struct sk_buff *skb)
        __skb_pull(skb, sizeof(*ipv6h));
        err = -EPROTONOSUPPORT;
 
-       rcu_read_lock();
        ops = rcu_dereference(inet6_offloads[
                ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
 
@@ -74,7 +73,6 @@ static int ipv6_gso_send_check(struct sk_buff *skb)
                skb_reset_transport_header(skb);
                err = ops->callbacks.gso_send_check(skb);
        }
-       rcu_read_unlock();
 
 out:
        return err;
@@ -92,46 +90,58 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
        u8 *prevhdr;
        int offset = 0;
        bool tunnel;
+       int nhoff;
 
        if (unlikely(skb_shinfo(skb)->gso_type &
                     ~(SKB_GSO_UDP |
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_IPIP |
+                      SKB_GSO_SIT |
                       SKB_GSO_UDP_TUNNEL |
                       SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
                goto out;
 
+       skb_reset_network_header(skb);
+       nhoff = skb_network_header(skb) - skb_mac_header(skb);
        if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
                goto out;
 
-       tunnel = skb->encapsulation;
+       tunnel = SKB_GSO_CB(skb)->encap_level > 0;
+       if (tunnel)
+               features = skb->dev->hw_enc_features & netif_skb_features(skb);
+       SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h);
+
        ipv6h = ipv6_hdr(skb);
        __skb_pull(skb, sizeof(*ipv6h));
        segs = ERR_PTR(-EPROTONOSUPPORT);
 
        proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
-       rcu_read_lock();
+
        ops = rcu_dereference(inet6_offloads[proto]);
        if (likely(ops && ops->callbacks.gso_segment)) {
                skb_reset_transport_header(skb);
                segs = ops->callbacks.gso_segment(skb, features);
        }
-       rcu_read_unlock();
 
        if (IS_ERR(segs))
                goto out;
 
        for (skb = segs; skb; skb = skb->next) {
-               ipv6h = ipv6_hdr(skb);
-               ipv6h->payload_len = htons(skb->len - skb->mac_len -
-                                          sizeof(*ipv6h));
+               ipv6h = (struct ipv6hdr *)(skb_mac_header(skb) + nhoff);
+               ipv6h->payload_len = htons(skb->len - nhoff - sizeof(*ipv6h));
+               if (tunnel) {
+                       skb_reset_inner_headers(skb);
+                       skb->encapsulation = 1;
+               }
+               skb->network_header = (u8 *)ipv6h - skb->head;
+
                if (!tunnel && proto == IPPROTO_UDP) {
                        unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
-                       fptr = (struct frag_hdr *)(skb_network_header(skb) +
-                               unfrag_ip6hlen);
+                       fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen);
                        fptr->frag_off = htons(offset);
                        if (skb->next != NULL)
                                fptr->frag_off |= htons(IP6_MF);
@@ -267,6 +277,13 @@ static struct packet_offload ipv6_packet_offload __read_mostly = {
        },
 };
 
+static const struct net_offload sit_offload = {
+       .callbacks = {
+               .gso_send_check = ipv6_gso_send_check,
+               .gso_segment    = ipv6_gso_segment,
+       },
+};
+
 static int __init ipv6_offload_init(void)
 {
 
@@ -278,6 +295,9 @@ static int __init ipv6_offload_init(void)
                pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__);
 
        dev_add_offload(&ipv6_packet_offload);
+
+       inet_add_offload(&sit_offload, IPPROTO_IPV6);
+
        return 0;
 }
 
index 3a692d5..5e31a90 100644 (file)
@@ -105,7 +105,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
        }
 
        rcu_read_lock_bh();
-       nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+       nexthop = rt6_nexthop((struct rt6_info *)dst);
        neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
        if (unlikely(!neigh))
                neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
@@ -125,7 +125,8 @@ static int ip6_finish_output2(struct sk_buff *skb)
 static int ip6_finish_output(struct sk_buff *skb)
 {
        if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
-           dst_allfrag(skb_dst(skb)))
+           dst_allfrag(skb_dst(skb)) ||
+           (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
                return ip6_fragment(skb, ip6_finish_output2);
        else
                return ip6_finish_output2(skb);
@@ -874,7 +875,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         */
        rt = (struct rt6_info *) *dst;
        rcu_read_lock_bh();
-       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr));
+       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt));
        err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;
        rcu_read_unlock_bh();
 
@@ -1008,6 +1009,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
 
 {
        struct sk_buff *skb;
+       struct frag_hdr fhdr;
        int err;
 
        /* There is support for UDP large send offload by network
@@ -1034,33 +1036,26 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                skb->transport_header = skb->network_header + fragheaderlen;
 
                skb->protocol = htons(ETH_P_IPV6);
-               skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
-       }
-
-       err = skb_append_datato_frags(sk,skb, getfrag, from,
-                                     (length - transhdrlen));
-       if (!err) {
-               struct frag_hdr fhdr;
 
-               /* Specify the length of each IPv6 datagram fragment.
-                * It has to be a multiple of 8.
-                */
-               skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
-                                            sizeof(struct frag_hdr)) & ~7;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
-               ipv6_select_ident(&fhdr, rt);
-               skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
                __skb_queue_tail(&sk->sk_write_queue, skb);
-
-               return 0;
+       } else if (skb_is_gso(skb)) {
+               goto append;
        }
-       /* There is not enough support do UPD LSO,
-        * so follow normal path
-        */
-       kfree_skb(skb);
 
-       return err;
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       /* Specify the length of each IPv6 datagram fragment.
+        * It has to be a multiple of 8.
+        */
+       skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
+                                    sizeof(struct frag_hdr)) & ~7;
+       skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+       ipv6_select_ident(&fhdr, rt);
+       skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+
+append:
+       return skb_append_datato_frags(sk, skb, getfrag, from,
+                                      (length - transhdrlen));
 }
 
 static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src,
@@ -1227,27 +1222,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
         * --yoshfuji
         */
 
-       cork->length += length;
-       if (length > mtu) {
-               int proto = sk->sk_protocol;
-               if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){
-                       ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
-                       return -EMSGSIZE;
-               }
-
-               if (proto == IPPROTO_UDP &&
-                   (rt->dst.dev->features & NETIF_F_UFO)) {
+       if ((length > mtu) && dontfrag && (sk->sk_protocol == IPPROTO_UDP ||
+                                          sk->sk_protocol == IPPROTO_RAW)) {
+               ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
+               return -EMSGSIZE;
+       }
 
-                       err = ip6_ufo_append_data(sk, getfrag, from, length,
-                                                 hh_len, fragheaderlen,
-                                                 transhdrlen, mtu, flags, rt);
-                       if (err)
-                               goto error;
-                       return 0;
-               }
+       skb = skb_peek_tail(&sk->sk_write_queue);
+       cork->length += length;
+       if (((length > mtu) ||
+            (skb && skb_is_gso(skb))) &&
+           (sk->sk_protocol == IPPROTO_UDP) &&
+           (rt->dst.dev->features & NETIF_F_UFO)) {
+               err = ip6_ufo_append_data(sk, getfrag, from, length,
+                                         hh_len, fragheaderlen,
+                                         transhdrlen, mtu, flags, rt);
+               if (err)
+                       goto error;
+               return 0;
        }
 
-       if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
+       if (!skb)
                goto alloc_new_skb;
 
        while (length > 0) {
index 2d8f482..583b77e 100644 (file)
@@ -1430,9 +1430,17 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 static int
 ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
 {
-       if (new_mtu < IPV6_MIN_MTU) {
-               return -EINVAL;
+       struct ip6_tnl *tnl = netdev_priv(dev);
+
+       if (tnl->parms.proto == IPPROTO_IPIP) {
+               if (new_mtu < 68)
+                       return -EINVAL;
+       } else {
+               if (new_mtu < IPV6_MIN_MTU)
+                       return -EINVAL;
        }
+       if (new_mtu > 0xFFF8 - dev->hard_header_len)
+               return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
 }
@@ -1731,8 +1739,6 @@ static void __net_exit ip6_tnl_destroy_tunnels(struct ip6_tnl_net *ip6n)
                }
        }
 
-       t = rtnl_dereference(ip6n->tnls_wc[0]);
-       unregister_netdevice_queue(t->dev, &list);
        unregister_netdevice_many(&list);
 }
 
@@ -1752,6 +1758,7 @@ static int __net_init ip6_tnl_init_net(struct net *net)
        if (!ip6n->fb_tnl_dev)
                goto err_alloc_dev;
        dev_net_set(ip6n->fb_tnl_dev, net);
+       ip6n->fb_tnl_dev->rtnl_link_ops = &ip6_link_ops;
        /* FB netdevice is special: we have one, and only one per netns.
         * Allowing to move it to another netns is clearly unsafe.
         */
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
new file mode 100644 (file)
index 0000000..ed94ba6
--- /dev/null
@@ -0,0 +1,1056 @@
+/*
+ *     IPv6 virtual tunneling interface
+ *
+ *     Copyright (C) 2013 secunet Security Networks AG
+ *
+ *     Author:
+ *     Steffen Klassert <steffen.klassert@secunet.com>
+ *
+ *     Based on:
+ *     net/ipv6/ip6_tunnel.c
+ *
+ *     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/module.h>
+#include <linux/capability.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/sockios.h>
+#include <linux/icmp.h>
+#include <linux/if.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/icmpv6.h>
+#include <linux/init.h>
+#include <linux/route.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/slab.h>
+#include <linux/hash.h>
+
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/ip_tunnels.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/ip6_tunnel.h>
+#include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+
+#define HASH_SIZE_SHIFT  5
+#define HASH_SIZE (1 << HASH_SIZE_SHIFT)
+
+static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
+{
+       u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
+
+       return hash_32(hash, HASH_SIZE_SHIFT);
+}
+
+static int vti6_dev_init(struct net_device *dev);
+static void vti6_dev_setup(struct net_device *dev);
+static struct rtnl_link_ops vti6_link_ops __read_mostly;
+
+static int vti6_net_id __read_mostly;
+struct vti6_net {
+       /* the vti6 tunnel fallback device */
+       struct net_device *fb_tnl_dev;
+       /* lists for storing tunnels in use */
+       struct ip6_tnl __rcu *tnls_r_l[HASH_SIZE];
+       struct ip6_tnl __rcu *tnls_wc[1];
+       struct ip6_tnl __rcu **tnls[2];
+};
+
+static struct net_device_stats *vti6_get_stats(struct net_device *dev)
+{
+       struct pcpu_tstats sum = { 0 };
+       int i;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
+
+               sum.rx_packets += tstats->rx_packets;
+               sum.rx_bytes   += tstats->rx_bytes;
+               sum.tx_packets += tstats->tx_packets;
+               sum.tx_bytes   += tstats->tx_bytes;
+       }
+       dev->stats.rx_packets = sum.rx_packets;
+       dev->stats.rx_bytes   = sum.rx_bytes;
+       dev->stats.tx_packets = sum.tx_packets;
+       dev->stats.tx_bytes   = sum.tx_bytes;
+       return &dev->stats;
+}
+
+#define for_each_vti6_tunnel_rcu(start) \
+       for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
+
+/**
+ * vti6_tnl_lookup - fetch tunnel matching the end-point addresses
+ *   @net: network namespace
+ *   @remote: the address of the tunnel exit-point
+ *   @local: the address of the tunnel entry-point
+ *
+ * Return:
+ *   tunnel matching given end-points if found,
+ *   else fallback tunnel if its device is up,
+ *   else %NULL
+ **/
+static struct ip6_tnl *
+vti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
+               const struct in6_addr *local)
+{
+       unsigned int hash = HASH(remote, local);
+       struct ip6_tnl *t;
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+               if (ipv6_addr_equal(local, &t->parms.laddr) &&
+                   ipv6_addr_equal(remote, &t->parms.raddr) &&
+                   (t->dev->flags & IFF_UP))
+                       return t;
+       }
+       t = rcu_dereference(ip6n->tnls_wc[0]);
+       if (t && (t->dev->flags & IFF_UP))
+               return t;
+
+       return NULL;
+}
+
+/**
+ * vti6_tnl_bucket - get head of list matching given tunnel parameters
+ *   @p: parameters containing tunnel end-points
+ *
+ * Description:
+ *   vti6_tnl_bucket() returns the head of the list matching the
+ *   &struct in6_addr entries laddr and raddr in @p.
+ *
+ * Return: head of IPv6 tunnel list
+ **/
+static struct ip6_tnl __rcu **
+vti6_tnl_bucket(struct vti6_net *ip6n, const struct __ip6_tnl_parm *p)
+{
+       const struct in6_addr *remote = &p->raddr;
+       const struct in6_addr *local = &p->laddr;
+       unsigned int h = 0;
+       int prio = 0;
+
+       if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
+               prio = 1;
+               h = HASH(remote, local);
+       }
+       return &ip6n->tnls[prio][h];
+}
+
+static void
+vti6_tnl_link(struct vti6_net *ip6n, struct ip6_tnl *t)
+{
+       struct ip6_tnl __rcu **tp = vti6_tnl_bucket(ip6n, &t->parms);
+
+       rcu_assign_pointer(t->next , rtnl_dereference(*tp));
+       rcu_assign_pointer(*tp, t);
+}
+
+static void
+vti6_tnl_unlink(struct vti6_net *ip6n, struct ip6_tnl *t)
+{
+       struct ip6_tnl __rcu **tp;
+       struct ip6_tnl *iter;
+
+       for (tp = vti6_tnl_bucket(ip6n, &t->parms);
+            (iter = rtnl_dereference(*tp)) != NULL;
+            tp = &iter->next) {
+               if (t == iter) {
+                       rcu_assign_pointer(*tp, t->next);
+                       break;
+               }
+       }
+}
+
+static void vti6_dev_free(struct net_device *dev)
+{
+       free_percpu(dev->tstats);
+       free_netdev(dev);
+}
+
+static int vti6_tnl_create2(struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net *net = dev_net(dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+       int err;
+
+       err = vti6_dev_init(dev);
+       if (err < 0)
+               goto out;
+
+       err = register_netdevice(dev);
+       if (err < 0)
+               goto out;
+
+       strcpy(t->parms.name, dev->name);
+       dev->rtnl_link_ops = &vti6_link_ops;
+
+       dev_hold(dev);
+       vti6_tnl_link(ip6n, t);
+
+       return 0;
+
+out:
+       return err;
+}
+
+static struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
+{
+       struct net_device *dev;
+       struct ip6_tnl *t;
+       char name[IFNAMSIZ];
+       int err;
+
+       if (p->name[0])
+               strlcpy(name, p->name, IFNAMSIZ);
+       else
+               sprintf(name, "ip6_vti%%d");
+
+       dev = alloc_netdev(sizeof(*t), name, vti6_dev_setup);
+       if (dev == NULL)
+               goto failed;
+
+       dev_net_set(dev, net);
+
+       t = netdev_priv(dev);
+       t->parms = *p;
+       t->net = dev_net(dev);
+
+       err = vti6_tnl_create2(dev);
+       if (err < 0)
+               goto failed_free;
+
+       return t;
+
+failed_free:
+       vti6_dev_free(dev);
+failed:
+       return NULL;
+}
+
+/**
+ * vti6_locate - find or create tunnel matching given parameters
+ *   @net: network namespace
+ *   @p: tunnel parameters
+ *   @create: != 0 if allowed to create new tunnel if no match found
+ *
+ * Description:
+ *   vti6_locate() first tries to locate an existing tunnel
+ *   based on @parms. If this is unsuccessful, but @create is set a new
+ *   tunnel device is created and registered for use.
+ *
+ * Return:
+ *   matching tunnel or NULL
+ **/
+static struct ip6_tnl *vti6_locate(struct net *net, struct __ip6_tnl_parm *p,
+                                  int create)
+{
+       const struct in6_addr *remote = &p->raddr;
+       const struct in6_addr *local = &p->laddr;
+       struct ip6_tnl __rcu **tp;
+       struct ip6_tnl *t;
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       for (tp = vti6_tnl_bucket(ip6n, p);
+            (t = rtnl_dereference(*tp)) != NULL;
+            tp = &t->next) {
+               if (ipv6_addr_equal(local, &t->parms.laddr) &&
+                   ipv6_addr_equal(remote, &t->parms.raddr))
+                       return t;
+       }
+       if (!create)
+               return NULL;
+       return vti6_tnl_create(net, p);
+}
+
+/**
+ * vti6_dev_uninit - tunnel device uninitializer
+ *   @dev: the device to be destroyed
+ *
+ * Description:
+ *   vti6_dev_uninit() removes tunnel from its list
+ **/
+static void vti6_dev_uninit(struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net *net = dev_net(dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       if (dev == ip6n->fb_tnl_dev)
+               RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
+       else
+               vti6_tnl_unlink(ip6n, t);
+       ip6_tnl_dst_reset(t);
+       dev_put(dev);
+}
+
+static int vti6_rcv(struct sk_buff *skb)
+{
+       struct ip6_tnl *t;
+       const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+       rcu_read_lock();
+
+       if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
+                                &ipv6h->daddr)) != NULL) {
+               struct pcpu_tstats *tstats;
+
+               if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) {
+                       rcu_read_unlock();
+                       goto discard;
+               }
+
+               if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+                       rcu_read_unlock();
+                       return 0;
+               }
+
+               if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) {
+                       t->dev->stats.rx_dropped++;
+                       rcu_read_unlock();
+                       goto discard;
+               }
+
+               tstats = this_cpu_ptr(t->dev->tstats);
+               tstats->rx_packets++;
+               tstats->rx_bytes += skb->len;
+
+               skb->mark = 0;
+               secpath_reset(skb);
+               skb->dev = t->dev;
+
+               rcu_read_unlock();
+               return 0;
+       }
+       rcu_read_unlock();
+       return 1;
+
+discard:
+       kfree_skb(skb);
+       return 0;
+}
+
+/**
+ * vti6_addr_conflict - compare packet addresses to tunnel's own
+ *   @t: the outgoing tunnel device
+ *   @hdr: IPv6 header from the incoming packet
+ *
+ * Description:
+ *   Avoid trivial tunneling loop by checking that tunnel exit-point
+ *   doesn't match source of incoming packet.
+ *
+ * Return:
+ *   1 if conflict,
+ *   0 else
+ **/
+static inline bool
+vti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
+{
+       return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
+}
+
+/**
+ * vti6_xmit - send a packet
+ *   @skb: the outgoing socket buffer
+ *   @dev: the outgoing tunnel device
+ **/
+static int vti6_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct net *net = dev_net(dev);
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net_device_stats *stats = &t->dev->stats;
+       struct dst_entry *dst = NULL, *ndst = NULL;
+       struct flowi6 fl6;
+       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+       struct net_device *tdev;
+       int err = -1;
+
+       if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
+           !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
+               return err;
+
+       dst = ip6_tnl_dst_check(t);
+       if (!dst) {
+               memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
+
+               ndst = ip6_route_output(net, NULL, &fl6);
+
+               if (ndst->error)
+                       goto tx_err_link_failure;
+               ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(&fl6), NULL, 0);
+               if (IS_ERR(ndst)) {
+                       err = PTR_ERR(ndst);
+                       ndst = NULL;
+                       goto tx_err_link_failure;
+               }
+               dst = ndst;
+       }
+
+       if (!dst->xfrm || dst->xfrm->props.mode != XFRM_MODE_TUNNEL)
+               goto tx_err_link_failure;
+
+       tdev = dst->dev;
+
+       if (tdev == dev) {
+               stats->collisions++;
+               net_warn_ratelimited("%s: Local routing loop detected!\n",
+                                    t->parms.name);
+               goto tx_err_dst_release;
+       }
+
+
+       skb_dst_drop(skb);
+       skb_dst_set_noref(skb, dst);
+
+       ip6tunnel_xmit(skb, dev);
+       if (ndst) {
+               dev->mtu = dst_mtu(ndst);
+               ip6_tnl_dst_store(t, ndst);
+       }
+
+       return 0;
+tx_err_link_failure:
+       stats->tx_carrier_errors++;
+       dst_link_failure(skb);
+tx_err_dst_release:
+       dst_release(ndst);
+       return err;
+}
+
+static netdev_tx_t
+vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net_device_stats *stats = &t->dev->stats;
+       int ret;
+
+       switch (skb->protocol) {
+       case htons(ETH_P_IPV6):
+               ret = vti6_xmit(skb, dev);
+               break;
+       default:
+               goto tx_err;
+       }
+
+       if (ret < 0)
+               goto tx_err;
+
+       return NETDEV_TX_OK;
+
+tx_err:
+       stats->tx_errors++;
+       stats->tx_dropped++;
+       kfree_skb(skb);
+       return NETDEV_TX_OK;
+}
+
+static void vti6_link_config(struct ip6_tnl *t)
+{
+       struct dst_entry *dst;
+       struct net_device *dev = t->dev;
+       struct __ip6_tnl_parm *p = &t->parms;
+       struct flowi6 *fl6 = &t->fl.u.ip6;
+
+       memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
+       memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
+
+       /* Set up flowi template */
+       fl6->saddr = p->laddr;
+       fl6->daddr = p->raddr;
+       fl6->flowi6_oif = p->link;
+       fl6->flowi6_mark = be32_to_cpu(p->i_key);
+       fl6->flowi6_proto = p->proto;
+       fl6->flowlabel = 0;
+
+       p->flags &= ~(IP6_TNL_F_CAP_XMIT | IP6_TNL_F_CAP_RCV |
+                     IP6_TNL_F_CAP_PER_PACKET);
+       p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
+
+       if (p->flags & IP6_TNL_F_CAP_XMIT && p->flags & IP6_TNL_F_CAP_RCV)
+               dev->flags |= IFF_POINTOPOINT;
+       else
+               dev->flags &= ~IFF_POINTOPOINT;
+
+       dev->iflink = p->link;
+
+       if (p->flags & IP6_TNL_F_CAP_XMIT) {
+
+               dst = ip6_route_output(dev_net(dev), NULL, fl6);
+               if (dst->error)
+                       return;
+
+               dst = xfrm_lookup(dev_net(dev), dst, flowi6_to_flowi(fl6),
+                                 NULL, 0);
+               if (IS_ERR(dst))
+                       return;
+
+               if (dst->dev) {
+                       dev->hard_header_len = dst->dev->hard_header_len;
+
+                       dev->mtu = dst_mtu(dst);
+
+                       if (dev->mtu < IPV6_MIN_MTU)
+                               dev->mtu = IPV6_MIN_MTU;
+               }
+               dst_release(dst);
+       }
+}
+
+/**
+ * vti6_tnl_change - update the tunnel parameters
+ *   @t: tunnel to be changed
+ *   @p: tunnel configuration parameters
+ *
+ * Description:
+ *   vti6_tnl_change() updates the tunnel parameters
+ **/
+static int
+vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
+{
+       t->parms.laddr = p->laddr;
+       t->parms.raddr = p->raddr;
+       t->parms.link = p->link;
+       t->parms.i_key = p->i_key;
+       t->parms.o_key = p->o_key;
+       t->parms.proto = p->proto;
+       ip6_tnl_dst_reset(t);
+       vti6_link_config(t);
+       return 0;
+}
+
+static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
+{
+       struct net *net = dev_net(t->dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+       int err;
+
+       vti6_tnl_unlink(ip6n, t);
+       synchronize_net();
+       err = vti6_tnl_change(t, p);
+       vti6_tnl_link(ip6n, t);
+       netdev_state_change(t->dev);
+       return err;
+}
+
+static void
+vti6_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm2 *u)
+{
+       p->laddr = u->laddr;
+       p->raddr = u->raddr;
+       p->link = u->link;
+       p->i_key = u->i_key;
+       p->o_key = u->o_key;
+       p->proto = u->proto;
+
+       memcpy(p->name, u->name, sizeof(u->name));
+}
+
+static void
+vti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p)
+{
+       u->laddr = p->laddr;
+       u->raddr = p->raddr;
+       u->link = p->link;
+       u->i_key = p->i_key;
+       u->o_key = p->o_key;
+       u->proto = p->proto;
+
+       memcpy(u->name, p->name, sizeof(u->name));
+}
+
+/**
+ * vti6_tnl_ioctl - configure vti6 tunnels from userspace
+ *   @dev: virtual device associated with tunnel
+ *   @ifr: parameters passed from userspace
+ *   @cmd: command to be performed
+ *
+ * Description:
+ *   vti6_ioctl() is used for managing vti6 tunnels
+ *   from userspace.
+ *
+ *   The possible commands are the following:
+ *     %SIOCGETTUNNEL: get tunnel parameters for device
+ *     %SIOCADDTUNNEL: add tunnel matching given tunnel parameters
+ *     %SIOCCHGTUNNEL: change tunnel parameters to those given
+ *     %SIOCDELTUNNEL: delete tunnel
+ *
+ *   The fallback device "ip6_vti0", created during module
+ *   initialization, can be used for creating other tunnel devices.
+ *
+ * Return:
+ *   0 on success,
+ *   %-EFAULT if unable to copy data to or from userspace,
+ *   %-EPERM if current process hasn't %CAP_NET_ADMIN set
+ *   %-EINVAL if passed tunnel parameters are invalid,
+ *   %-EEXIST if changing a tunnel's parameters would cause a conflict
+ *   %-ENODEV if attempting to change or delete a nonexisting device
+ **/
+static int
+vti6_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       int err = 0;
+       struct ip6_tnl_parm2 p;
+       struct __ip6_tnl_parm p1;
+       struct ip6_tnl *t = NULL;
+       struct net *net = dev_net(dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       switch (cmd) {
+       case SIOCGETTUNNEL:
+               if (dev == ip6n->fb_tnl_dev) {
+                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       vti6_parm_from_user(&p1, &p);
+                       t = vti6_locate(net, &p1, 0);
+               } else {
+                       memset(&p, 0, sizeof(p));
+               }
+               if (t == NULL)
+                       t = netdev_priv(dev);
+               vti6_parm_to_user(&p, &t->parms);
+               if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+                       err = -EFAULT;
+               break;
+       case SIOCADDTUNNEL:
+       case SIOCCHGTUNNEL:
+               err = -EPERM;
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+                       break;
+               err = -EFAULT;
+               if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+                       break;
+               err = -EINVAL;
+               if (p.proto != IPPROTO_IPV6  && p.proto != 0)
+                       break;
+               vti6_parm_from_user(&p1, &p);
+               t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
+               if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
+                       if (t != NULL) {
+                               if (t->dev != dev) {
+                                       err = -EEXIST;
+                                       break;
+                               }
+                       } else
+                               t = netdev_priv(dev);
+
+                       err = vti6_update(t, &p1);
+               }
+               if (t) {
+                       err = 0;
+                       vti6_parm_to_user(&p, &t->parms);
+                       if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+                               err = -EFAULT;
+
+               } else
+                       err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+               break;
+       case SIOCDELTUNNEL:
+               err = -EPERM;
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+                       break;
+
+               if (dev == ip6n->fb_tnl_dev) {
+                       err = -EFAULT;
+                       if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+                               break;
+                       err = -ENOENT;
+                       vti6_parm_from_user(&p1, &p);
+                       t = vti6_locate(net, &p1, 0);
+                       if (t == NULL)
+                               break;
+                       err = -EPERM;
+                       if (t->dev == ip6n->fb_tnl_dev)
+                               break;
+                       dev = t->dev;
+               }
+               err = 0;
+               unregister_netdevice(dev);
+               break;
+       default:
+               err = -EINVAL;
+       }
+       return err;
+}
+
+/**
+ * vti6_tnl_change_mtu - change mtu manually for tunnel device
+ *   @dev: virtual device associated with tunnel
+ *   @new_mtu: the new mtu
+ *
+ * Return:
+ *   0 on success,
+ *   %-EINVAL if mtu too small
+ **/
+static int vti6_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (new_mtu < IPV6_MIN_MTU)
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static const struct net_device_ops vti6_netdev_ops = {
+       .ndo_uninit     = vti6_dev_uninit,
+       .ndo_start_xmit = vti6_tnl_xmit,
+       .ndo_do_ioctl   = vti6_ioctl,
+       .ndo_change_mtu = vti6_change_mtu,
+       .ndo_get_stats  = vti6_get_stats,
+};
+
+/**
+ * vti6_dev_setup - setup virtual tunnel device
+ *   @dev: virtual device associated with tunnel
+ *
+ * Description:
+ *   Initialize function pointers and device parameters
+ **/
+static void vti6_dev_setup(struct net_device *dev)
+{
+       struct ip6_tnl *t;
+
+       dev->netdev_ops = &vti6_netdev_ops;
+       dev->destructor = vti6_dev_free;
+
+       dev->type = ARPHRD_TUNNEL6;
+       dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr);
+       dev->mtu = ETH_DATA_LEN;
+       t = netdev_priv(dev);
+       dev->flags |= IFF_NOARP;
+       dev->addr_len = sizeof(struct in6_addr);
+       dev->features |= NETIF_F_NETNS_LOCAL;
+       dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+}
+
+/**
+ * vti6_dev_init_gen - general initializer for all tunnel devices
+ *   @dev: virtual device associated with tunnel
+ **/
+static inline int vti6_dev_init_gen(struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+
+       t->dev = dev;
+       t->net = dev_net(dev);
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
+               return -ENOMEM;
+       return 0;
+}
+
+/**
+ * vti6_dev_init - initializer for all non fallback tunnel devices
+ *   @dev: virtual device associated with tunnel
+ **/
+static int vti6_dev_init(struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       int err = vti6_dev_init_gen(dev);
+
+       if (err)
+               return err;
+       vti6_link_config(t);
+       return 0;
+}
+
+/**
+ * vti6_fb_tnl_dev_init - initializer for fallback tunnel device
+ *   @dev: fallback device
+ *
+ * Return: 0
+ **/
+static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net *net = dev_net(dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+       int err = vti6_dev_init_gen(dev);
+
+       if (err)
+               return err;
+
+       t->parms.proto = IPPROTO_IPV6;
+       dev_hold(dev);
+
+       vti6_link_config(t);
+
+       rcu_assign_pointer(ip6n->tnls_wc[0], t);
+       return 0;
+}
+
+static int vti6_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       return 0;
+}
+
+static void vti6_netlink_parms(struct nlattr *data[],
+                              struct __ip6_tnl_parm *parms)
+{
+       memset(parms, 0, sizeof(*parms));
+
+       if (!data)
+               return;
+
+       if (data[IFLA_VTI_LINK])
+               parms->link = nla_get_u32(data[IFLA_VTI_LINK]);
+
+       if (data[IFLA_VTI_LOCAL])
+               nla_memcpy(&parms->laddr, data[IFLA_VTI_LOCAL],
+                          sizeof(struct in6_addr));
+
+       if (data[IFLA_VTI_REMOTE])
+               nla_memcpy(&parms->raddr, data[IFLA_VTI_REMOTE],
+                          sizeof(struct in6_addr));
+
+       if (data[IFLA_VTI_IKEY])
+               parms->i_key = nla_get_be32(data[IFLA_VTI_IKEY]);
+
+       if (data[IFLA_VTI_OKEY])
+               parms->o_key = nla_get_be32(data[IFLA_VTI_OKEY]);
+}
+
+static int vti6_newlink(struct net *src_net, struct net_device *dev,
+                       struct nlattr *tb[], struct nlattr *data[])
+{
+       struct net *net = dev_net(dev);
+       struct ip6_tnl *nt;
+
+       nt = netdev_priv(dev);
+       vti6_netlink_parms(data, &nt->parms);
+
+       nt->parms.proto = IPPROTO_IPV6;
+
+       if (vti6_locate(net, &nt->parms, 0))
+               return -EEXIST;
+
+       return vti6_tnl_create2(dev);
+}
+
+static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
+                          struct nlattr *data[])
+{
+       struct ip6_tnl *t;
+       struct __ip6_tnl_parm p;
+       struct net *net = dev_net(dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       if (dev == ip6n->fb_tnl_dev)
+               return -EINVAL;
+
+       vti6_netlink_parms(data, &p);
+
+       t = vti6_locate(net, &p, 0);
+
+       if (t) {
+               if (t->dev != dev)
+                       return -EEXIST;
+       } else
+               t = netdev_priv(dev);
+
+       return vti6_update(t, &p);
+}
+
+static size_t vti6_get_size(const struct net_device *dev)
+{
+       return
+               /* IFLA_VTI_LINK */
+               nla_total_size(4) +
+               /* IFLA_VTI_LOCAL */
+               nla_total_size(sizeof(struct in6_addr)) +
+               /* IFLA_VTI_REMOTE */
+               nla_total_size(sizeof(struct in6_addr)) +
+               /* IFLA_VTI_IKEY */
+               nla_total_size(4) +
+               /* IFLA_VTI_OKEY */
+               nla_total_size(4) +
+               0;
+}
+
+static int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+       struct ip6_tnl *tunnel = netdev_priv(dev);
+       struct __ip6_tnl_parm *parm = &tunnel->parms;
+
+       if (nla_put_u32(skb, IFLA_VTI_LINK, parm->link) ||
+           nla_put(skb, IFLA_VTI_LOCAL, sizeof(struct in6_addr),
+                   &parm->laddr) ||
+           nla_put(skb, IFLA_VTI_REMOTE, sizeof(struct in6_addr),
+                   &parm->raddr) ||
+           nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) ||
+           nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = {
+       [IFLA_VTI_LINK]         = { .type = NLA_U32 },
+       [IFLA_VTI_LOCAL]        = { .len = sizeof(struct in6_addr) },
+       [IFLA_VTI_REMOTE]       = { .len = sizeof(struct in6_addr) },
+       [IFLA_VTI_IKEY]         = { .type = NLA_U32 },
+       [IFLA_VTI_OKEY]         = { .type = NLA_U32 },
+};
+
+static struct rtnl_link_ops vti6_link_ops __read_mostly = {
+       .kind           = "vti6",
+       .maxtype        = IFLA_VTI_MAX,
+       .policy         = vti6_policy,
+       .priv_size      = sizeof(struct ip6_tnl),
+       .setup          = vti6_dev_setup,
+       .validate       = vti6_validate,
+       .newlink        = vti6_newlink,
+       .changelink     = vti6_changelink,
+       .get_size       = vti6_get_size,
+       .fill_info      = vti6_fill_info,
+};
+
+static struct xfrm_tunnel_notifier vti6_handler __read_mostly = {
+       .handler        = vti6_rcv,
+       .priority       =       1,
+};
+
+static void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n)
+{
+       int h;
+       struct ip6_tnl *t;
+       LIST_HEAD(list);
+
+       for (h = 0; h < HASH_SIZE; h++) {
+               t = rtnl_dereference(ip6n->tnls_r_l[h]);
+               while (t != NULL) {
+                       unregister_netdevice_queue(t->dev, &list);
+                       t = rtnl_dereference(t->next);
+               }
+       }
+
+       t = rtnl_dereference(ip6n->tnls_wc[0]);
+       unregister_netdevice_queue(t->dev, &list);
+       unregister_netdevice_many(&list);
+}
+
+static int __net_init vti6_init_net(struct net *net)
+{
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+       struct ip6_tnl *t = NULL;
+       int err;
+
+       ip6n->tnls[0] = ip6n->tnls_wc;
+       ip6n->tnls[1] = ip6n->tnls_r_l;
+
+       err = -ENOMEM;
+       ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6_vti0",
+                                       vti6_dev_setup);
+
+       if (!ip6n->fb_tnl_dev)
+               goto err_alloc_dev;
+       dev_net_set(ip6n->fb_tnl_dev, net);
+
+       err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
+       if (err < 0)
+               goto err_register;
+
+       err = register_netdev(ip6n->fb_tnl_dev);
+       if (err < 0)
+               goto err_register;
+
+       t = netdev_priv(ip6n->fb_tnl_dev);
+
+       strcpy(t->parms.name, ip6n->fb_tnl_dev->name);
+       return 0;
+
+err_register:
+       vti6_dev_free(ip6n->fb_tnl_dev);
+err_alloc_dev:
+       return err;
+}
+
+static void __net_exit vti6_exit_net(struct net *net)
+{
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       rtnl_lock();
+       vti6_destroy_tunnels(ip6n);
+       rtnl_unlock();
+}
+
+static struct pernet_operations vti6_net_ops = {
+       .init = vti6_init_net,
+       .exit = vti6_exit_net,
+       .id   = &vti6_net_id,
+       .size = sizeof(struct vti6_net),
+};
+
+/**
+ * vti6_tunnel_init - register protocol and reserve needed resources
+ *
+ * Return: 0 on success
+ **/
+static int __init vti6_tunnel_init(void)
+{
+       int  err;
+
+       err = register_pernet_device(&vti6_net_ops);
+       if (err < 0)
+               goto out_pernet;
+
+       err = xfrm6_mode_tunnel_input_register(&vti6_handler);
+       if (err < 0) {
+               pr_err("%s: can't register vti6\n", __func__);
+               goto out;
+       }
+       err = rtnl_link_register(&vti6_link_ops);
+       if (err < 0)
+               goto rtnl_link_failed;
+
+       return 0;
+
+rtnl_link_failed:
+       xfrm6_mode_tunnel_input_deregister(&vti6_handler);
+out:
+       unregister_pernet_device(&vti6_net_ops);
+out_pernet:
+       return err;
+}
+
+/**
+ * vti6_tunnel_cleanup - free resources and unregister protocol
+ **/
+static void __exit vti6_tunnel_cleanup(void)
+{
+       rtnl_link_unregister(&vti6_link_ops);
+       if (xfrm6_mode_tunnel_input_deregister(&vti6_handler))
+               pr_info("%s: can't deregister vti6\n", __func__);
+
+       unregister_pernet_device(&vti6_net_ops);
+}
+
+module_init(vti6_tunnel_init);
+module_exit(vti6_tunnel_cleanup);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("vti6");
+MODULE_ALIAS_NETDEV("ip6_vti0");
+MODULE_AUTHOR("Steffen Klassert");
+MODULE_DESCRIPTION("IPv6 virtual tunnel interface");
index 5636a91..ce507d9 100644 (file)
@@ -64,8 +64,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                (struct ip_comp_hdr *)(skb->data + offset);
        struct xfrm_state *x;
 
-       if (type != ICMPV6_DEST_UNREACH &&
-           type != ICMPV6_PKT_TOOBIG &&
+       if (type != ICMPV6_PKT_TOOBIG &&
            type != NDISC_REDIRECT)
                return;
 
index d1e2e8e..1c6ce31 100644 (file)
@@ -174,7 +174,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                        }
 
                        if (ipv6_only_sock(sk) ||
-                           !ipv6_addr_v4mapped(&np->daddr)) {
+                           !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
                                retv = -EADDRNOTAVAIL;
                                break;
                        }
@@ -1011,7 +1011,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                                struct in6_pktinfo src_info;
                                src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
                                        np->sticky_pktinfo.ipi6_ifindex;
-                               src_info.ipi6_addr = np->mcast_oif ? np->daddr : np->sticky_pktinfo.ipi6_addr;
+                               src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
                                put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
                        }
                        if (np->rxopt.bits.rxhlim) {
@@ -1026,7 +1026,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                                struct in6_pktinfo src_info;
                                src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
                                        np->sticky_pktinfo.ipi6_ifindex;
-                               src_info.ipi6_addr = np->mcast_oif ? np->daddr : np->sticky_pktinfo.ipi6_addr;
+                               src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
+                                                                    np->sticky_pktinfo.ipi6_addr;
                                put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
                        }
                        if (np->rxopt.bits.rxohlim) {
@@ -1211,6 +1212,34 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                val = np->sndflow;
                break;
 
+       case IPV6_FLOWLABEL_MGR:
+       {
+               struct in6_flowlabel_req freq;
+
+               if (len < sizeof(freq))
+                       return -EINVAL;
+
+               if (copy_from_user(&freq, optval, sizeof(freq)))
+                       return -EFAULT;
+
+               if (freq.flr_action != IPV6_FL_A_GET)
+                       return -EINVAL;
+
+               len = sizeof(freq);
+               memset(&freq, 0, sizeof(freq));
+
+               val = ipv6_flowlabel_opt_get(sk, &freq);
+               if (val < 0)
+                       return val;
+
+               if (put_user(len, optlen))
+                       return -EFAULT;
+               if (copy_to_user(optval, &freq, len))
+                       return -EFAULT;
+
+               return 0;
+       }
+
        case IPV6_ADDR_PREFERENCES:
                val = 0;
 
index 096cd67..d18f9f9 100644 (file)
@@ -2034,7 +2034,7 @@ static void mld_dad_timer_expire(unsigned long data)
                if (idev->mc_dad_count)
                        mld_dad_start_timer(idev, idev->mc_maxdelay);
        }
-       __in6_dev_put(idev);
+       in6_dev_put(idev);
 }
 
 static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
@@ -2379,7 +2379,7 @@ static void mld_gq_timer_expire(unsigned long data)
 
        idev->mc_gq_running = 0;
        mld_send_report(idev, NULL);
-       __in6_dev_put(idev);
+       in6_dev_put(idev);
 }
 
 static void mld_ifc_timer_expire(unsigned long data)
@@ -2392,7 +2392,7 @@ static void mld_ifc_timer_expire(unsigned long data)
                if (idev->mc_ifc_count)
                        mld_ifc_start_timer(idev, idev->mc_maxdelay);
        }
-       __in6_dev_put(idev);
+       in6_dev_put(idev);
 }
 
 static void mld_ifc_event(struct inet6_dev *idev)
index a7f842b..7702f9e 100644 (file)
@@ -25,6 +25,19 @@ config NF_CONNTRACK_IPV6
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config NF_TABLES_IPV6
+       depends on NF_TABLES
+       tristate "IPv6 nf_tables support"
+
+config NFT_CHAIN_ROUTE_IPV6
+       depends on NF_TABLES_IPV6
+       tristate "IPv6 nf_tables route chain support"
+
+config NFT_CHAIN_NAT_IPV6
+       depends on NF_TABLES_IPV6
+       depends on NF_NAT_IPV6 && NFT_NAT
+       tristate "IPv6 nf_tables nat chain support"
+
 config IP6_NF_IPTABLES
        tristate "IP6 tables support (required for filtering)"
        depends on INET && IPV6
index 2b53738..d1b4928 100644 (file)
@@ -23,6 +23,11 @@ obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
 nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
 obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o
 
+# nf_tables
+obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
+obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
+obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
+
 # matches
 obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
 obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
index 44400c2..710238f 100644 (file)
@@ -349,6 +349,11 @@ ip6t_do_table(struct sk_buff *skb,
        local_bh_disable();
        addend = xt_write_recseq_begin();
        private = table->private;
+       /*
+        * Ensure we load private-> members after we've fetched the base
+        * pointer.
+        */
+       smp_read_barrier_depends();
        cpu        = smp_processor_id();
        table_base = private->entries[cpu];
        jumpstack  = (struct ip6t_entry **)private->jumpstack[cpu];
index 56eef30..da00a2e 100644 (file)
@@ -39,7 +39,7 @@ MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv6");
 MODULE_LICENSE("GPL");
 
 /* Send RST reply */
-static void send_reset(struct net *net, struct sk_buff *oldskb)
+static void send_reset(struct net *net, struct sk_buff *oldskb, int hook)
 {
        struct sk_buff *nskb;
        struct tcphdr otcph, *tcph;
@@ -88,8 +88,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
        }
 
        /* Check checksum. */
-       if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP,
-                           skb_checksum(oldskb, tcphoff, otcplen, 0))) {
+       if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) {
                pr_debug("TCP checksum is invalid\n");
                return;
        }
@@ -227,7 +226,7 @@ reject_tg6(struct sk_buff *skb, const struct xt_action_param *par)
                /* Do nothing */
                break;
        case IP6T_TCP_RESET:
-               send_reset(net, skb);
+               send_reset(net, skb, par->hooknum);
                break;
        default:
                net_info_ratelimited("case %u not handled yet\n", reject->with);
index 19cfea8..bf9f612 100644 (file)
@@ -282,7 +282,8 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
        if (th == NULL)
                return NF_DROP;
 
-       synproxy_parse_options(skb, par->thoff, th, &opts);
+       if (!synproxy_parse_options(skb, par->thoff, th, &opts))
+               return NF_DROP;
 
        if (th->syn && !(th->ack || th->fin || th->rst)) {
                /* Initial SYN from client */
@@ -311,7 +312,7 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
        return XT_CONTINUE;
 }
 
-static unsigned int ipv6_synproxy_hook(unsigned int hooknum,
+static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops,
                                       struct sk_buff *skb,
                                       const struct net_device *in,
                                       const struct net_device *out,
@@ -372,7 +373,8 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum,
 
                /* fall through */
        case TCP_CONNTRACK_SYN_SENT:
-               synproxy_parse_options(skb, thoff, th, &opts);
+               if (!synproxy_parse_options(skb, thoff, th, &opts))
+                       return NF_DROP;
 
                if (!th->syn && th->ack &&
                    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
@@ -395,7 +397,9 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum,
                if (!th->syn || !th->ack)
                        break;
 
-               synproxy_parse_options(skb, thoff, th, &opts);
+               if (!synproxy_parse_options(skb, thoff, th, &opts))
+                       return NF_DROP;
+
                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
                        synproxy->tsoff = opts.tsval - synproxy->its;
 
index 29b44b1..ca7f6c1 100644 (file)
@@ -32,13 +32,14 @@ static const struct xt_table packet_filter = {
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6table_filter_hook(unsigned int hook, struct sk_buff *skb,
+ip6table_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
        const struct net *net = dev_net((in != NULL) ? in : out);
 
-       return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_filter);
+       return ip6t_do_table(skb, ops->hooknum, in, out,
+                            net->ipv6.ip6table_filter);
 }
 
 static struct nf_hook_ops *filter_ops __read_mostly;
index c705907..307bbb7 100644 (file)
@@ -76,17 +76,17 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6table_mangle_hook(unsigned int hook, struct sk_buff *skb,
+ip6table_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
-       if (hook == NF_INET_LOCAL_OUT)
+       if (ops->hooknum == NF_INET_LOCAL_OUT)
                return ip6t_mangle_out(skb, out);
-       if (hook == NF_INET_POST_ROUTING)
-               return ip6t_do_table(skb, hook, in, out,
+       if (ops->hooknum == NF_INET_POST_ROUTING)
+               return ip6t_do_table(skb, ops->hooknum, in, out,
                                     dev_net(out)->ipv6.ip6table_mangle);
        /* INPUT/FORWARD */
-       return ip6t_do_table(skb, hook, in, out,
+       return ip6t_do_table(skb, ops->hooknum, in, out,
                             dev_net(in)->ipv6.ip6table_mangle);
 }
 
index 9b076d2..84c7f33 100644 (file)
@@ -63,7 +63,7 @@ static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
 }
 
 static unsigned int
-nf_nat_ipv6_fn(unsigned int hooknum,
+nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
               struct sk_buff *skb,
               const struct net_device *in,
               const struct net_device *out,
@@ -72,7 +72,7 @@ nf_nat_ipv6_fn(unsigned int hooknum,
        struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        struct nf_conn_nat *nat;
-       enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
+       enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
        __be16 frag_off;
        int hdrlen;
        u8 nexthdr;
@@ -111,7 +111,8 @@ nf_nat_ipv6_fn(unsigned int hooknum,
 
                if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
                        if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
-                                                            hooknum, hdrlen))
+                                                            ops->hooknum,
+                                                            hdrlen))
                                return NF_DROP;
                        else
                                return NF_ACCEPT;
@@ -124,14 +125,14 @@ nf_nat_ipv6_fn(unsigned int hooknum,
                if (!nf_nat_initialized(ct, maniptype)) {
                        unsigned int ret;
 
-                       ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
+                       ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct);
                        if (ret != NF_ACCEPT)
                                return ret;
                } else {
                        pr_debug("Already setup manip %s for ct %p\n",
                                 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
                                 ct);
-                       if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+                       if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
                                goto oif_changed;
                }
                break;
@@ -140,11 +141,11 @@ nf_nat_ipv6_fn(unsigned int hooknum,
                /* ESTABLISHED */
                NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
                             ctinfo == IP_CT_ESTABLISHED_REPLY);
-               if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+               if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
                        goto oif_changed;
        }
 
-       return nf_nat_packet(ct, ctinfo, hooknum, skb);
+       return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
 
 oif_changed:
        nf_ct_kill_acct(ct, ctinfo, skb);
@@ -152,7 +153,7 @@ oif_changed:
 }
 
 static unsigned int
-nf_nat_ipv6_in(unsigned int hooknum,
+nf_nat_ipv6_in(const struct nf_hook_ops *ops,
               struct sk_buff *skb,
               const struct net_device *in,
               const struct net_device *out,
@@ -161,7 +162,7 @@ nf_nat_ipv6_in(unsigned int hooknum,
        unsigned int ret;
        struct in6_addr daddr = ipv6_hdr(skb)->daddr;
 
-       ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
+       ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
                skb_dst_drop(skb);
@@ -170,7 +171,7 @@ nf_nat_ipv6_in(unsigned int hooknum,
 }
 
 static unsigned int
-nf_nat_ipv6_out(unsigned int hooknum,
+nf_nat_ipv6_out(const struct nf_hook_ops *ops,
                struct sk_buff *skb,
                const struct net_device *in,
                const struct net_device *out,
@@ -187,7 +188,7 @@ nf_nat_ipv6_out(unsigned int hooknum,
        if (skb->len < sizeof(struct ipv6hdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
+       ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
 #ifdef CONFIG_XFRM
        if (ret != NF_DROP && ret != NF_STOLEN &&
            !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
@@ -209,7 +210,7 @@ nf_nat_ipv6_out(unsigned int hooknum,
 }
 
 static unsigned int
-nf_nat_ipv6_local_fn(unsigned int hooknum,
+nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
                     struct sk_buff *skb,
                     const struct net_device *in,
                     const struct net_device *out,
@@ -224,7 +225,7 @@ nf_nat_ipv6_local_fn(unsigned int hooknum,
        if (skb->len < sizeof(struct ipv6hdr))
                return NF_ACCEPT;
 
-       ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
+       ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
        if (ret != NF_DROP && ret != NF_STOLEN &&
            (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
index 9a626d8..5274740 100644 (file)
@@ -19,13 +19,14 @@ static const struct xt_table packet_raw = {
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6table_raw_hook(unsigned int hook, struct sk_buff *skb,
+ip6table_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                  const struct net_device *in, const struct net_device *out,
                  int (*okfn)(struct sk_buff *))
 {
        const struct net *net = dev_net((in != NULL) ? in : out);
 
-       return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_raw);
+       return ip6t_do_table(skb, ops->hooknum, in, out,
+                            net->ipv6.ip6table_raw);
 }
 
 static struct nf_hook_ops *rawtable_ops __read_mostly;
index ce88d1d..ab3b021 100644 (file)
@@ -36,14 +36,15 @@ static const struct xt_table security_table = {
 };
 
 static unsigned int
-ip6table_security_hook(unsigned int hook, struct sk_buff *skb,
+ip6table_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
                       const struct net_device *in,
                       const struct net_device *out,
                       int (*okfn)(struct sk_buff *))
 {
        const struct net *net = dev_net((in != NULL) ? in : out);
 
-       return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_security);
+       return ip6t_do_table(skb, ops->hooknum, in, out,
+                            net->ipv6.ip6table_security);
 }
 
 static struct nf_hook_ops *sectbl_ops __read_mostly;
index d6e4dd8..4cbc6b2 100644 (file)
@@ -95,7 +95,7 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
        return NF_ACCEPT;
 }
 
-static unsigned int ipv6_helper(unsigned int hooknum,
+static unsigned int ipv6_helper(const struct nf_hook_ops *ops,
                                struct sk_buff *skb,
                                const struct net_device *in,
                                const struct net_device *out,
@@ -133,7 +133,7 @@ static unsigned int ipv6_helper(unsigned int hooknum,
        return helper->help(skb, protoff, ct, ctinfo);
 }
 
-static unsigned int ipv6_confirm(unsigned int hooknum,
+static unsigned int ipv6_confirm(const struct nf_hook_ops *ops,
                                 struct sk_buff *skb,
                                 const struct net_device *in,
                                 const struct net_device *out,
@@ -169,66 +169,16 @@ out:
        return nf_conntrack_confirm(skb);
 }
 
-static unsigned int __ipv6_conntrack_in(struct net *net,
-                                       unsigned int hooknum,
-                                       struct sk_buff *skb,
-                                       const struct net_device *in,
-                                       const struct net_device *out,
-                                       int (*okfn)(struct sk_buff *))
-{
-       struct sk_buff *reasm = skb->nfct_reasm;
-       const struct nf_conn_help *help;
-       struct nf_conn *ct;
-       enum ip_conntrack_info ctinfo;
-
-       /* This packet is fragmented and has reassembled packet. */
-       if (reasm) {
-               /* Reassembled packet isn't parsed yet ? */
-               if (!reasm->nfct) {
-                       unsigned int ret;
-
-                       ret = nf_conntrack_in(net, PF_INET6, hooknum, reasm);
-                       if (ret != NF_ACCEPT)
-                               return ret;
-               }
-
-               /* Conntrack helpers need the entire reassembled packet in the
-                * POST_ROUTING hook. In case of unconfirmed connections NAT
-                * might reassign a helper, so the entire packet is also
-                * required.
-                */
-               ct = nf_ct_get(reasm, &ctinfo);
-               if (ct != NULL && !nf_ct_is_untracked(ct)) {
-                       help = nfct_help(ct);
-                       if ((help && help->helper) || !nf_ct_is_confirmed(ct)) {
-                               nf_conntrack_get_reasm(reasm);
-                               NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm,
-                                              (struct net_device *)in,
-                                              (struct net_device *)out,
-                                              okfn, NF_IP6_PRI_CONNTRACK + 1);
-                               return NF_DROP_ERR(-ECANCELED);
-                       }
-               }
-
-               nf_conntrack_get(reasm->nfct);
-               skb->nfct = reasm->nfct;
-               skb->nfctinfo = reasm->nfctinfo;
-               return NF_ACCEPT;
-       }
-
-       return nf_conntrack_in(net, PF_INET6, hooknum, skb);
-}
-
-static unsigned int ipv6_conntrack_in(unsigned int hooknum,
+static unsigned int ipv6_conntrack_in(const struct nf_hook_ops *ops,
                                      struct sk_buff *skb,
                                      const struct net_device *in,
                                      const struct net_device *out,
                                      int (*okfn)(struct sk_buff *))
 {
-       return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn);
+       return nf_conntrack_in(dev_net(in), PF_INET6, ops->hooknum, skb);
 }
 
-static unsigned int ipv6_conntrack_local(unsigned int hooknum,
+static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops,
                                         struct sk_buff *skb,
                                         const struct net_device *in,
                                         const struct net_device *out,
@@ -239,7 +189,7 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum,
                net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
                return NF_ACCEPT;
        }
-       return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn);
+       return nf_conntrack_in(dev_net(out), PF_INET6, ops->hooknum, skb);
 }
 
 static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
@@ -297,9 +247,9 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
        struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 };
        struct nf_conn *ct;
 
-       tuple.src.u3.in6 = inet6->rcv_saddr;
+       tuple.src.u3.in6 = sk->sk_v6_rcv_saddr;
        tuple.src.u.tcp.port = inet->inet_sport;
-       tuple.dst.u3.in6 = inet6->daddr;
+       tuple.dst.u3.in6 = sk->sk_v6_daddr;
        tuple.dst.u.tcp.port = inet->inet_dport;
        tuple.dst.protonum = sk->sk_protocol;
 
index dffdc1a..767ab8d 100644 (file)
@@ -144,12 +144,24 @@ static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
        return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
 }
 
+static unsigned int nf_hash_frag(__be32 id, const struct in6_addr *saddr,
+                                const struct in6_addr *daddr)
+{
+       u32 c;
+
+       net_get_random_once(&nf_frags.rnd, sizeof(nf_frags.rnd));
+       c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr),
+                        (__force u32)id, nf_frags.rnd);
+       return c & (INETFRAGS_HASHSZ - 1);
+}
+
+
 static unsigned int nf_hashfn(struct inet_frag_queue *q)
 {
        const struct frag_queue *nq;
 
        nq = container_of(q, struct frag_queue, q);
-       return inet6_hash_frag(nq->id, &nq->saddr, &nq->daddr, nf_frags.rnd);
+       return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr);
 }
 
 static void nf_skb_free(struct sk_buff *skb)
@@ -185,7 +197,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id,
        arg.ecn = ecn;
 
        read_lock_bh(&nf_frags.lock);
-       hash = inet6_hash_frag(id, src, dst, nf_frags.rnd);
+       hash = nf_hash_frag(id, src, dst);
 
        q = inet_frag_find(&net->nf_frag.frags, &nf_frags, &arg, hash);
        local_bh_enable();
@@ -621,31 +633,16 @@ ret_orig:
        return skb;
 }
 
-void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
-                       struct net_device *in, struct net_device *out,
-                       int (*okfn)(struct sk_buff *))
+void nf_ct_frag6_consume_orig(struct sk_buff *skb)
 {
        struct sk_buff *s, *s2;
-       unsigned int ret = 0;
 
        for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
-               nf_conntrack_put_reasm(s->nfct_reasm);
-               nf_conntrack_get_reasm(skb);
-               s->nfct_reasm = skb;
-
                s2 = s->next;
                s->next = NULL;
-
-               if (ret != -ECANCELED)
-                       ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s,
-                                            in, out, okfn,
-                                            NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
-               else
-                       kfree_skb(s);
-
+               consume_skb(s);
                s = s2;
        }
-       nf_conntrack_put_reasm(skb);
 }
 
 static int nf_ct_net_init(struct net *net)
index aacd121..7b9a748 100644 (file)
@@ -52,7 +52,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
 
 }
 
-static unsigned int ipv6_defrag(unsigned int hooknum,
+static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
                                struct sk_buff *skb,
                                const struct net_device *in,
                                const struct net_device *out,
@@ -66,7 +66,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
                return NF_ACCEPT;
 #endif
 
-       reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
+       reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(ops->hooknum, skb));
        /* queued */
        if (reasm == NULL)
                return NF_STOLEN;
@@ -75,8 +75,11 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
        if (reasm == skb)
                return NF_ACCEPT;
 
-       nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in,
-                          (struct net_device *)out, okfn);
+       nf_ct_frag6_consume_orig(reasm);
+
+       NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, reasm,
+                      (struct net_device *) in, (struct net_device *) out,
+                      okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
 
        return NF_STOLEN;
 }
diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c
new file mode 100644 (file)
index 0000000..d77db8a
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012-2013 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ipv6.h>
+#include <linux/netfilter_ipv6.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_ipv6.h>
+
+static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
+                                   struct sk_buff *skb,
+                                   const struct net_device *in,
+                                   const struct net_device *out,
+                                   int (*okfn)(struct sk_buff *))
+{
+       struct nft_pktinfo pkt;
+
+       if (unlikely(skb->len < sizeof(struct ipv6hdr))) {
+               if (net_ratelimit())
+                       pr_info("nf_tables_ipv6: ignoring short SOCK_RAW "
+                               "packet\n");
+               return NF_ACCEPT;
+       }
+       if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
+               return NF_DROP;
+
+       return nft_do_chain_pktinfo(&pkt, ops);
+}
+
+static struct nft_af_info nft_af_ipv6 __read_mostly = {
+       .family         = NFPROTO_IPV6,
+       .nhooks         = NF_INET_NUMHOOKS,
+       .owner          = THIS_MODULE,
+       .hooks          = {
+               [NF_INET_LOCAL_OUT]     = nft_ipv6_output,
+       },
+};
+
+static int nf_tables_ipv6_init_net(struct net *net)
+{
+       net->nft.ipv6 = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
+       if (net->nft.ipv6 == NULL)
+               return -ENOMEM;
+
+       memcpy(net->nft.ipv6, &nft_af_ipv6, sizeof(nft_af_ipv6));
+
+       if (nft_register_afinfo(net, net->nft.ipv6) < 0)
+               goto err;
+
+       return 0;
+err:
+       kfree(net->nft.ipv6);
+       return -ENOMEM;
+}
+
+static void nf_tables_ipv6_exit_net(struct net *net)
+{
+       nft_unregister_afinfo(net->nft.ipv6);
+       kfree(net->nft.ipv6);
+}
+
+static struct pernet_operations nf_tables_ipv6_net_ops = {
+       .init   = nf_tables_ipv6_init_net,
+       .exit   = nf_tables_ipv6_exit_net,
+};
+
+static unsigned int
+nft_do_chain_ipv6(const struct nf_hook_ops *ops,
+                 struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 int (*okfn)(struct sk_buff *))
+{
+       struct nft_pktinfo pkt;
+
+       /* malformed packet, drop it */
+       if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
+               return NF_DROP;
+
+       return nft_do_chain_pktinfo(&pkt, ops);
+}
+
+static struct nf_chain_type filter_ipv6 = {
+       .family         = NFPROTO_IPV6,
+       .name           = "filter",
+       .type           = NFT_CHAIN_T_DEFAULT,
+       .hook_mask      = (1 << NF_INET_LOCAL_IN) |
+                         (1 << NF_INET_LOCAL_OUT) |
+                         (1 << NF_INET_FORWARD) |
+                         (1 << NF_INET_PRE_ROUTING) |
+                         (1 << NF_INET_POST_ROUTING),
+       .fn             = {
+               [NF_INET_LOCAL_IN]      = nft_do_chain_ipv6,
+               [NF_INET_LOCAL_OUT]     = nft_ipv6_output,
+               [NF_INET_FORWARD]       = nft_do_chain_ipv6,
+               [NF_INET_PRE_ROUTING]   = nft_do_chain_ipv6,
+               [NF_INET_POST_ROUTING]  = nft_do_chain_ipv6,
+       },
+};
+
+static int __init nf_tables_ipv6_init(void)
+{
+       nft_register_chain_type(&filter_ipv6);
+       return register_pernet_subsys(&nf_tables_ipv6_net_ops);
+}
+
+static void __exit nf_tables_ipv6_exit(void)
+{
+       unregister_pernet_subsys(&nf_tables_ipv6_net_ops);
+       nft_unregister_chain_type(&filter_ipv6);
+}
+
+module_init(nf_tables_ipv6_init);
+module_exit(nf_tables_ipv6_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_FAMILY(AF_INET6);
diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c
new file mode 100644 (file)
index 0000000..e86dcd7
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_ipv6.h>
+#include <net/netfilter/nf_nat_l3proto.h>
+#include <net/ipv6.h>
+
+/*
+ * IPv6 NAT chains
+ */
+
+static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
+                             struct sk_buff *skb,
+                             const struct net_device *in,
+                             const struct net_device *out,
+                             int (*okfn)(struct sk_buff *))
+{
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+       struct nf_conn_nat *nat;
+       enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
+       __be16 frag_off;
+       int hdrlen;
+       u8 nexthdr;
+       struct nft_pktinfo pkt;
+       unsigned int ret;
+
+       if (ct == NULL || nf_ct_is_untracked(ct))
+               return NF_ACCEPT;
+
+       nat = nfct_nat(ct);
+       if (nat == NULL) {
+               /* Conntrack module was loaded late, can't add extension. */
+               if (nf_ct_is_confirmed(ct))
+                       return NF_ACCEPT;
+               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
+               if (nat == NULL)
+                       return NF_ACCEPT;
+       }
+
+       switch (ctinfo) {
+       case IP_CT_RELATED:
+       case IP_CT_RELATED + IP_CT_IS_REPLY:
+               nexthdr = ipv6_hdr(skb)->nexthdr;
+               hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
+                                         &nexthdr, &frag_off);
+
+               if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
+                       if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
+                                                          ops->hooknum,
+                                                          hdrlen))
+                               return NF_DROP;
+                       else
+                               return NF_ACCEPT;
+               }
+               /* Fall through */
+       case IP_CT_NEW:
+               if (nf_nat_initialized(ct, maniptype))
+                       break;
+
+               nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
+
+               ret = nft_do_chain_pktinfo(&pkt, ops);
+               if (ret != NF_ACCEPT)
+                       return ret;
+               if (!nf_nat_initialized(ct, maniptype)) {
+                       ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
+                       if (ret != NF_ACCEPT)
+                               return ret;
+               }
+       default:
+               break;
+       }
+
+       return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
+}
+
+static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops,
+                                     struct sk_buff *skb,
+                                     const struct net_device *in,
+                                     const struct net_device *out,
+                                     int (*okfn)(struct sk_buff *))
+{
+       struct in6_addr daddr = ipv6_hdr(skb)->daddr;
+       unsigned int ret;
+
+       ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
+       if (ret != NF_DROP && ret != NF_STOLEN &&
+           ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
+               skb_dst_drop(skb);
+
+       return ret;
+}
+
+static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops,
+                                      struct sk_buff *skb,
+                                      const struct net_device *in,
+                                      const struct net_device *out,
+                                      int (*okfn)(struct sk_buff *))
+{
+       enum ip_conntrack_info ctinfo __maybe_unused;
+       const struct nf_conn *ct __maybe_unused;
+       unsigned int ret;
+
+       ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
+#ifdef CONFIG_XFRM
+       if (ret != NF_DROP && ret != NF_STOLEN &&
+           !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
+           (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
+               enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+
+               if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
+                                     &ct->tuplehash[!dir].tuple.dst.u3) ||
+                   (ct->tuplehash[dir].tuple.src.u.all !=
+                    ct->tuplehash[!dir].tuple.dst.u.all))
+                       if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
+                               ret = NF_DROP;
+       }
+#endif
+       return ret;
+}
+
+static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops,
+                                 struct sk_buff *skb,
+                                 const struct net_device *in,
+                                 const struct net_device *out,
+                                 int (*okfn)(struct sk_buff *))
+{
+       enum ip_conntrack_info ctinfo;
+       const struct nf_conn *ct;
+       unsigned int ret;
+
+       ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
+       if (ret != NF_DROP && ret != NF_STOLEN &&
+           (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
+               enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+
+               if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
+                                     &ct->tuplehash[!dir].tuple.src.u3)) {
+                       if (ip6_route_me_harder(skb))
+                               ret = NF_DROP;
+               }
+#ifdef CONFIG_XFRM
+               else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
+                        ct->tuplehash[dir].tuple.dst.u.all !=
+                        ct->tuplehash[!dir].tuple.src.u.all)
+                       if (nf_xfrm_me_harder(skb, AF_INET6))
+                               ret = NF_DROP;
+#endif
+       }
+       return ret;
+}
+
+static struct nf_chain_type nft_chain_nat_ipv6 = {
+       .family         = NFPROTO_IPV6,
+       .name           = "nat",
+       .type           = NFT_CHAIN_T_NAT,
+       .hook_mask      = (1 << NF_INET_PRE_ROUTING) |
+                         (1 << NF_INET_POST_ROUTING) |
+                         (1 << NF_INET_LOCAL_OUT) |
+                         (1 << NF_INET_LOCAL_IN),
+       .fn             = {
+               [NF_INET_PRE_ROUTING]   = nf_nat_ipv6_prerouting,
+               [NF_INET_POST_ROUTING]  = nf_nat_ipv6_postrouting,
+               [NF_INET_LOCAL_OUT]     = nf_nat_ipv6_output,
+               [NF_INET_LOCAL_IN]      = nf_nat_ipv6_fn,
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init nft_chain_nat_ipv6_init(void)
+{
+       int err;
+
+       err = nft_register_chain_type(&nft_chain_nat_ipv6);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static void __exit nft_chain_nat_ipv6_exit(void)
+{
+       nft_unregister_chain_type(&nft_chain_nat_ipv6);
+}
+
+module_init(nft_chain_nat_ipv6_init);
+module_exit(nft_chain_nat_ipv6_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
+MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat");
diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c
new file mode 100644 (file)
index 0000000..3fe40f0
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_ipv6.h>
+#include <net/route.h>
+
+static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
+                                       struct sk_buff *skb,
+                                       const struct net_device *in,
+                                       const struct net_device *out,
+                                       int (*okfn)(struct sk_buff *))
+{
+       unsigned int ret;
+       struct nft_pktinfo pkt;
+       struct in6_addr saddr, daddr;
+       u_int8_t hop_limit;
+       u32 mark, flowlabel;
+
+       /* malformed packet, drop it */
+       if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
+               return NF_DROP;
+
+       /* save source/dest address, mark, hoplimit, flowlabel, priority */
+       memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr));
+       memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr));
+       mark = skb->mark;
+       hop_limit = ipv6_hdr(skb)->hop_limit;
+
+       /* flowlabel and prio (includes version, which shouldn't change either */
+       flowlabel = *((u32 *)ipv6_hdr(skb));
+
+       ret = nft_do_chain_pktinfo(&pkt, ops);
+       if (ret != NF_DROP && ret != NF_QUEUE &&
+           (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) ||
+            memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) ||
+            skb->mark != mark ||
+            ipv6_hdr(skb)->hop_limit != hop_limit ||
+            flowlabel != *((u_int32_t *)ipv6_hdr(skb))))
+               return ip6_route_me_harder(skb) == 0 ? ret : NF_DROP;
+
+       return ret;
+}
+
+static struct nf_chain_type nft_chain_route_ipv6 = {
+       .family         = NFPROTO_IPV6,
+       .name           = "route",
+       .type           = NFT_CHAIN_T_ROUTE,
+       .hook_mask      = (1 << NF_INET_LOCAL_OUT),
+       .fn             = {
+                [NF_INET_LOCAL_OUT]    = nf_route_table_hook,
+        },
+        .me            = THIS_MODULE,
+};
+
+static int __init nft_chain_route_init(void)
+{
+       return nft_register_chain_type(&nft_chain_route_ipv6);
+}
+
+static void __exit nft_chain_route_exit(void)
+{
+       nft_unregister_chain_type(&nft_chain_route_ipv6);
+}
+
+module_init(nft_chain_route_init);
+module_exit(nft_chain_route_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_CHAIN(AF_INET6, "route");
index 18f19df..8815e31 100644 (file)
@@ -116,7 +116,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        } else {
                if (sk->sk_state != TCP_ESTABLISHED)
                        return -EDESTADDRREQ;
-               daddr = &np->daddr;
+               daddr = &sk->sk_v6_daddr;
        }
 
        if (!iif)
index 58916bb..3c00842 100644 (file)
@@ -77,20 +77,19 @@ static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk,
 
        sk_for_each_from(sk)
                if (inet_sk(sk)->inet_num == num) {
-                       struct ipv6_pinfo *np = inet6_sk(sk);
 
                        if (!net_eq(sock_net(sk), net))
                                continue;
 
-                       if (!ipv6_addr_any(&np->daddr) &&
-                           !ipv6_addr_equal(&np->daddr, rmt_addr))
+                       if (!ipv6_addr_any(&sk->sk_v6_daddr) &&
+                           !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))
                                continue;
 
                        if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
                                continue;
 
-                       if (!ipv6_addr_any(&np->rcv_saddr)) {
-                               if (ipv6_addr_equal(&np->rcv_saddr, loc_addr))
+                       if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+                               if (ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr))
                                        goto found;
                                if (is_multicast &&
                                    inet6_mc_check(sk, loc_addr, rmt_addr))
@@ -302,7 +301,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        }
 
        inet->inet_rcv_saddr = inet->inet_saddr = v4addr;
-       np->rcv_saddr = addr->sin6_addr;
+       sk->sk_v6_rcv_saddr = addr->sin6_addr;
        if (!(addr_type & IPV6_ADDR_MULTICAST))
                np->saddr = addr->sin6_addr;
        err = 0;
@@ -335,8 +334,10 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
                ip6_sk_update_pmtu(skb, sk, info);
                harderr = (np->pmtudisc == IPV6_PMTUDISC_DO);
        }
-       if (type == NDISC_REDIRECT)
+       if (type == NDISC_REDIRECT) {
                ip6_sk_redirect(skb, sk);
+               return;
+       }
        if (np->recverr) {
                u8 *payload = skb->data;
                if (!inet->hdrincl)
@@ -802,8 +803,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                 * sk->sk_dst_cache.
                 */
                if (sk->sk_state == TCP_ESTABLISHED &&
-                   ipv6_addr_equal(daddr, &np->daddr))
-                       daddr = &np->daddr;
+                   ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
+                       daddr = &sk->sk_v6_daddr;
 
                if (addr_len >= sizeof(struct sockaddr_in6) &&
                    sin6->sin6_scope_id &&
@@ -814,7 +815,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                        return -EDESTADDRREQ;
 
                proto = inet->inet_num;
-               daddr = &np->daddr;
+               daddr = &sk->sk_v6_daddr;
                fl6.flowlabel = np->flow_label;
        }
 
index 1aeb473..cc85a9b 100644 (file)
@@ -82,24 +82,24 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
  * callers should be careful not to use the hash value outside the ipfrag_lock
  * as doing so could race with ipfrag_hash_rnd being recalculated.
  */
-unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
-                            const struct in6_addr *daddr, u32 rnd)
+static unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
+                                   const struct in6_addr *daddr)
 {
        u32 c;
 
+       net_get_random_once(&ip6_frags.rnd, sizeof(ip6_frags.rnd));
        c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr),
-                        (__force u32)id, rnd);
+                        (__force u32)id, ip6_frags.rnd);
 
        return c & (INETFRAGS_HASHSZ - 1);
 }
-EXPORT_SYMBOL_GPL(inet6_hash_frag);
 
 static unsigned int ip6_hashfn(struct inet_frag_queue *q)
 {
        struct frag_queue *fq;
 
        fq = container_of(q, struct frag_queue, q);
-       return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr, ip6_frags.rnd);
+       return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr);
 }
 
 bool ip6_frag_match(struct inet_frag_queue *q, void *a)
@@ -193,7 +193,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src,
        arg.ecn = ecn;
 
        read_lock(&ip6_frags.lock);
-       hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
+       hash = inet6_hash_frag(id, src, dst);
 
        q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash);
        if (IS_ERR_OR_NULL(q)) {
index c979dd9..7faa9d5 100644 (file)
@@ -476,6 +476,24 @@ out:
 }
 
 #ifdef CONFIG_IPV6_ROUTER_PREF
+struct __rt6_probe_work {
+       struct work_struct work;
+       struct in6_addr target;
+       struct net_device *dev;
+};
+
+static void rt6_probe_deferred(struct work_struct *w)
+{
+       struct in6_addr mcaddr;
+       struct __rt6_probe_work *work =
+               container_of(w, struct __rt6_probe_work, work);
+
+       addrconf_addr_solict_mult(&work->target, &mcaddr);
+       ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL);
+       dev_put(work->dev);
+       kfree(w);
+}
+
 static void rt6_probe(struct rt6_info *rt)
 {
        struct neighbour *neigh;
@@ -499,17 +517,23 @@ static void rt6_probe(struct rt6_info *rt)
 
        if (!neigh ||
            time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
-               struct in6_addr mcaddr;
-               struct in6_addr *target;
+               struct __rt6_probe_work *work;
+
+               work = kmalloc(sizeof(*work), GFP_ATOMIC);
 
-               if (neigh) {
+               if (neigh && work)
                        neigh->updated = jiffies;
+
+               if (neigh)
                        write_unlock(&neigh->lock);
-               }
 
-               target = (struct in6_addr *)&rt->rt6i_gateway;
-               addrconf_addr_solict_mult(target, &mcaddr);
-               ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL);
+               if (work) {
+                       INIT_WORK(&work->work, rt6_probe_deferred);
+                       work->target = rt->rt6i_gateway;
+                       dev_hold(rt->dst.dev);
+                       work->dev = rt->dst.dev;
+                       schedule_work(&work->work);
+               }
        } else {
 out:
                write_unlock(&neigh->lock);
@@ -595,7 +619,7 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
                goto out;
 
        m = rt6_score_route(rt, oif, strict);
-       if (m == RT6_NUD_FAIL_SOFT && !IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) {
+       if (m == RT6_NUD_FAIL_SOFT) {
                match_do_rr = true;
                m = 0; /* lowest valid score */
        } else if (m < 0) {
@@ -707,8 +731,11 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
                prefix = &prefix_buf;
        }
 
-       rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr,
-                               dev->ifindex);
+       if (rinfo->prefix_len == 0)
+               rt = rt6_get_dflt_router(gwaddr, dev);
+       else
+               rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
+                                       gwaddr, dev->ifindex);
 
        if (rt && !lifetime) {
                ip6_del_rt(rt);
@@ -847,12 +874,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort,
        rt = ip6_rt_copy(ort, daddr);
 
        if (rt) {
-               if (!(rt->rt6i_flags & RTF_GATEWAY)) {
-                       if (ort->rt6i_dst.plen != 128 &&
-                           ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
-                               rt->rt6i_flags |= RTF_ANYCAST;
-                       rt->rt6i_gateway = *daddr;
-               }
+               if (ort->rt6i_dst.plen != 128 &&
+                   ipv6_addr_equal(&ort->rt6i_dst.addr, daddr))
+                       rt->rt6i_flags |= RTF_ANYCAST;
 
                rt->rt6i_flags |= RTF_CACHE;
 
@@ -1064,10 +1088,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
        if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev)))
                return NULL;
 
-       if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
-               return dst;
+       if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
+               return NULL;
 
-       return NULL;
+       if (rt6_check_expired(rt))
+               return NULL;
+
+       return dst;
 }
 
 static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
@@ -1137,7 +1164,6 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
        memset(&fl6, 0, sizeof(fl6));
        fl6.flowi6_oif = oif;
        fl6.flowi6_mark = mark;
-       fl6.flowi6_flags = 0;
        fl6.daddr = iph->daddr;
        fl6.saddr = iph->saddr;
        fl6.flowlabel = ip6_flowinfo(iph);
@@ -1236,7 +1262,6 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
        memset(&fl6, 0, sizeof(fl6));
        fl6.flowi6_oif = oif;
        fl6.flowi6_mark = mark;
-       fl6.flowi6_flags = 0;
        fl6.daddr = iph->daddr;
        fl6.saddr = iph->saddr;
        fl6.flowlabel = ip6_flowinfo(iph);
@@ -1258,7 +1283,6 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
        memset(&fl6, 0, sizeof(fl6));
        fl6.flowi6_oif = oif;
        fl6.flowi6_mark = mark;
-       fl6.flowi6_flags = 0;
        fl6.daddr = msg->dest;
        fl6.saddr = iph->daddr;
 
@@ -1338,6 +1362,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
        rt->dst.flags |= DST_HOST;
        rt->dst.output  = ip6_output;
        atomic_set(&rt->dst.__refcnt, 1);
+       rt->rt6i_gateway  = fl6->daddr;
        rt->rt6i_dst.addr = fl6->daddr;
        rt->rt6i_dst.plen = 128;
        rt->rt6i_idev     = idev;
@@ -1873,7 +1898,10 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
                        in6_dev_hold(rt->rt6i_idev);
                rt->dst.lastuse = jiffies;
 
-               rt->rt6i_gateway = ort->rt6i_gateway;
+               if (ort->rt6i_flags & RTF_GATEWAY)
+                       rt->rt6i_gateway = ort->rt6i_gateway;
+               else
+                       rt->rt6i_gateway = *dest;
                rt->rt6i_flags = ort->rt6i_flags;
                if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
                    (RTF_DEFAULT | RTF_ADDRCONF))
@@ -2160,6 +2188,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
        else
                rt->rt6i_flags |= RTF_LOCAL;
 
+       rt->rt6i_gateway  = *addr;
        rt->rt6i_dst.addr = *addr;
        rt->rt6i_dst.plen = 128;
        rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL);
@@ -2800,56 +2829,12 @@ static int ip6_route_dev_notify(struct notifier_block *this,
 
 #ifdef CONFIG_PROC_FS
 
-struct rt6_proc_arg
-{
-       char *buffer;
-       int offset;
-       int length;
-       int skip;
-       int len;
-};
-
-static int rt6_info_route(struct rt6_info *rt, void *p_arg)
-{
-       struct seq_file *m = p_arg;
-
-       seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
-
-#ifdef CONFIG_IPV6_SUBTREES
-       seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
-#else
-       seq_puts(m, "00000000000000000000000000000000 00 ");
-#endif
-       if (rt->rt6i_flags & RTF_GATEWAY) {
-               seq_printf(m, "%pi6", &rt->rt6i_gateway);
-       } else {
-               seq_puts(m, "00000000000000000000000000000000");
-       }
-       seq_printf(m, " %08x %08x %08x %08x %8s\n",
-                  rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
-                  rt->dst.__use, rt->rt6i_flags,
-                  rt->dst.dev ? rt->dst.dev->name : "");
-       return 0;
-}
-
-static int ipv6_route_show(struct seq_file *m, void *v)
-{
-       struct net *net = (struct net *)m->private;
-       fib6_clean_all_ro(net, rt6_info_route, 0, m);
-       return 0;
-}
-
-static int ipv6_route_open(struct inode *inode, struct file *file)
-{
-       return single_open_net(inode, file, ipv6_route_show);
-}
-
 static const struct file_operations ipv6_route_proc_fops = {
        .owner          = THIS_MODULE,
        .open           = ipv6_route_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
-       .release        = single_release_net,
+       .release        = seq_release_net,
 };
 
 static int rt6_stats_seq_show(struct seq_file *seq, void *v)
index 7ee5cb9..3a9038d 100644 (file)
@@ -566,6 +566,70 @@ static inline bool is_spoofed_6rd(struct ip_tunnel *tunnel, const __be32 v4addr,
        return false;
 }
 
+/* Checks if an address matches an address on the tunnel interface.
+ * Used to detect the NAT of proto 41 packets and let them pass spoofing test.
+ * Long story:
+ * This function is called after we considered the packet as spoofed
+ * in is_spoofed_6rd.
+ * We may have a router that is doing NAT for proto 41 packets
+ * for an internal station. Destination a.a.a.a/PREFIX:bbbb:bbbb
+ * will be translated to n.n.n.n/PREFIX:bbbb:bbbb. And is_spoofed_6rd
+ * function will return true, dropping the packet.
+ * But, we can still check if is spoofed against the IP
+ * addresses associated with the interface.
+ */
+static bool only_dnatted(const struct ip_tunnel *tunnel,
+       const struct in6_addr *v6dst)
+{
+       int prefix_len;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+       prefix_len = tunnel->ip6rd.prefixlen + 32
+               - tunnel->ip6rd.relay_prefixlen;
+#else
+       prefix_len = 48;
+#endif
+       return ipv6_chk_custom_prefix(v6dst, prefix_len, tunnel->dev);
+}
+
+/* Returns true if a packet is spoofed */
+static bool packet_is_spoofed(struct sk_buff *skb,
+                             const struct iphdr *iph,
+                             struct ip_tunnel *tunnel)
+{
+       const struct ipv6hdr *ipv6h;
+
+       if (tunnel->dev->priv_flags & IFF_ISATAP) {
+               if (!isatap_chksrc(skb, iph, tunnel))
+                       return true;
+
+               return false;
+       }
+
+       if (tunnel->dev->flags & IFF_POINTOPOINT)
+               return false;
+
+       ipv6h = ipv6_hdr(skb);
+
+       if (unlikely(is_spoofed_6rd(tunnel, iph->saddr, &ipv6h->saddr))) {
+               net_warn_ratelimited("Src spoofed %pI4/%pI6c -> %pI4/%pI6c\n",
+                                    &iph->saddr, &ipv6h->saddr,
+                                    &iph->daddr, &ipv6h->daddr);
+               return true;
+       }
+
+       if (likely(!is_spoofed_6rd(tunnel, iph->daddr, &ipv6h->daddr)))
+               return false;
+
+       if (only_dnatted(tunnel, &ipv6h->daddr))
+               return false;
+
+       net_warn_ratelimited("Dst spoofed %pI4/%pI6c -> %pI4/%pI6c\n",
+                            &iph->saddr, &ipv6h->saddr,
+                            &iph->daddr, &ipv6h->daddr);
+       return true;
+}
+
 static int ipip6_rcv(struct sk_buff *skb)
 {
        const struct iphdr *iph = ip_hdr(skb);
@@ -586,19 +650,9 @@ static int ipip6_rcv(struct sk_buff *skb)
                IPCB(skb)->flags = 0;
                skb->protocol = htons(ETH_P_IPV6);
 
-               if (tunnel->dev->priv_flags & IFF_ISATAP) {
-                       if (!isatap_chksrc(skb, iph, tunnel)) {
-                               tunnel->dev->stats.rx_errors++;
-                               goto out;
-                       }
-               } else if (!(tunnel->dev->flags&IFF_POINTOPOINT)) {
-                       if (is_spoofed_6rd(tunnel, iph->saddr,
-                                          &ipv6_hdr(skb)->saddr) ||
-                           is_spoofed_6rd(tunnel, iph->daddr,
-                                          &ipv6_hdr(skb)->daddr)) {
-                               tunnel->dev->stats.rx_errors++;
-                               goto out;
-                       }
+               if (packet_is_spoofed(skb, iph, tunnel)) {
+                       tunnel->dev->stats.rx_errors++;
+                       goto out;
                }
 
                __skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
@@ -748,7 +802,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
 
                if (neigh == NULL) {
-                       net_dbg_ratelimited("sit: nexthop == NULL\n");
+                       net_dbg_ratelimited("nexthop == NULL\n");
                        goto tx_error;
                }
 
@@ -777,7 +831,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr);
 
                if (neigh == NULL) {
-                       net_dbg_ratelimited("sit: nexthop == NULL\n");
+                       net_dbg_ratelimited("nexthop == NULL\n");
                        goto tx_error;
                }
 
@@ -879,10 +933,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                ttl = iph6->hop_limit;
        tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
 
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = iptunnel_handle_offloads(skb, false, SKB_GSO_SIT);
+       if (IS_ERR(skb))
+               goto out;
 
        err = iptunnel_xmit(rt, skb, fl4.saddr, fl4.daddr, IPPROTO_IPV6, tos,
                            ttl, df, !net_eq(tunnel->net, dev_net(dev)));
@@ -892,8 +945,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
 tx_error_icmp:
        dst_link_failure(skb);
 tx_error:
-       dev->stats.tx_errors++;
        dev_kfree_skb(skb);
+out:
+       dev->stats.tx_errors++;
        return NETDEV_TX_OK;
 }
 
@@ -902,13 +956,15 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        struct ip_tunnel *tunnel = netdev_priv(dev);
        const struct iphdr  *tiph = &tunnel->parms.iph;
 
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP);
+       if (IS_ERR(skb))
+               goto out;
 
        ip_tunnel_xmit(skb, dev, tiph, IPPROTO_IPIP);
        return NETDEV_TX_OK;
+out:
+       dev->stats.tx_errors++;
+       return NETDEV_TX_OK;
 }
 
 static netdev_tx_t sit_tunnel_xmit(struct sk_buff *skb,
@@ -1238,6 +1294,12 @@ static void ipip6_dev_free(struct net_device *dev)
        free_netdev(dev);
 }
 
+#define SIT_FEATURES (NETIF_F_SG          | \
+                     NETIF_F_FRAGLIST     | \
+                     NETIF_F_HIGHDMA      | \
+                     NETIF_F_GSO_SOFTWARE | \
+                     NETIF_F_HW_CSUM)
+
 static void ipip6_tunnel_setup(struct net_device *dev)
 {
        dev->netdev_ops         = &ipip6_netdev_ops;
@@ -1251,6 +1313,8 @@ static void ipip6_tunnel_setup(struct net_device *dev)
        dev->iflink             = 0;
        dev->addr_len           = 4;
        dev->features           |= NETIF_F_LLTX;
+       dev->features           |= SIT_FEATURES;
+       dev->hw_features        |= SIT_FEATURES;
 }
 
 static int ipip6_tunnel_init(struct net_device *dev)
@@ -1612,6 +1676,7 @@ static int __net_init sit_init_net(struct net *net)
                goto err_alloc_dev;
        }
        dev_net_set(sitn->fb_tunnel_dev, net);
+       sitn->fb_tunnel_dev->rtnl_link_ops = &sit_link_ops;
        /* FB netdevice is special: we have one, and only one per netns.
         * Allowing to move it to another netns is clearly unsafe.
         */
@@ -1646,7 +1711,6 @@ static void __net_exit sit_exit_net(struct net *net)
 
        rtnl_lock();
        sit_destroy_tunnels(sitn, &list);
-       unregister_netdevice_queue(sitn->fb_tunnel_dev, &list);
        unregister_netdevice_many(&list);
        rtnl_unlock();
 }
index bf63ac8..535a3ad 100644 (file)
 #define COOKIEBITS 24  /* Upper bits store count */
 #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
 
-/* Table must be sorted. */
+static u32 syncookie6_secret[2][16-4+SHA_DIGEST_WORDS];
+
+/* RFC 2460, Section 8.3:
+ * [ipv6 tcp] MSS must be computed as the maximum packet size minus 60 [..]
+ *
+ * Due to IPV6_MIN_MTU=1280 the lowest possible MSS is 1220, which allows
+ * using higher values than ipv4 tcp syncookies.
+ * The other values are chosen based on ethernet (1500 and 9k MTU), plus
+ * one that accounts for common encap (PPPoe) overhead. Table must be sorted.
+ */
 static __u16 const msstab[] = {
-       64,
-       512,
-       536,
-       1280 - 60,
+       1280 - 60, /* IPV6_MIN_MTU - 60 */
        1480 - 60,
        1500 - 60,
-       4460 - 60,
        9000 - 60,
 };
 
-/*
- * This (misnamed) value is the age of syncookie which is permitted.
- * Its ideal value should be dependent on TCP_TIMEOUT_INIT and
- * sysctl_tcp_retries1. It's a rather complicated formula (exponential
- * backoff) to compute at runtime so it's currently hardcoded here.
- */
-#define COUNTER_TRIES 4
-
 static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
                                           struct request_sock *req,
                                           struct dst_entry *dst)
@@ -66,14 +63,18 @@ static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS],
 static u32 cookie_hash(const struct in6_addr *saddr, const struct in6_addr *daddr,
                       __be16 sport, __be16 dport, u32 count, int c)
 {
-       __u32 *tmp = __get_cpu_var(ipv6_cookie_scratch);
+       __u32 *tmp;
+
+       net_get_random_once(syncookie6_secret, sizeof(syncookie6_secret));
+
+       tmp  = __get_cpu_var(ipv6_cookie_scratch);
 
        /*
         * we have 320 bits of information to hash, copy in the remaining
-        * 192 bits required for sha_transform, from the syncookie_secret
+        * 192 bits required for sha_transform, from the syncookie6_secret
         * and overwrite the digest with the secret
         */
-       memcpy(tmp + 10, syncookie_secret[c], 44);
+       memcpy(tmp + 10, syncookie6_secret[c], 44);
        memcpy(tmp, saddr, 16);
        memcpy(tmp + 4, daddr, 16);
        tmp[8] = ((__force u32)sport << 16) + (__force u32)dport;
@@ -86,8 +87,9 @@ static u32 cookie_hash(const struct in6_addr *saddr, const struct in6_addr *dadd
 static __u32 secure_tcp_syn_cookie(const struct in6_addr *saddr,
                                   const struct in6_addr *daddr,
                                   __be16 sport, __be16 dport, __u32 sseq,
-                                  __u32 count, __u32 data)
+                                  __u32 data)
 {
+       u32 count = tcp_cookie_time();
        return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
                sseq + (count << COOKIEBITS) +
                ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
@@ -96,15 +98,14 @@ static __u32 secure_tcp_syn_cookie(const struct in6_addr *saddr,
 
 static __u32 check_tcp_syn_cookie(__u32 cookie, const struct in6_addr *saddr,
                                  const struct in6_addr *daddr, __be16 sport,
-                                 __be16 dport, __u32 sseq, __u32 count,
-                                 __u32 maxdiff)
+                                 __be16 dport, __u32 sseq)
 {
-       __u32 diff;
+       __u32 diff, count = tcp_cookie_time();
 
        cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
 
        diff = (count - (cookie >> COOKIEBITS)) & ((__u32) -1 >> COOKIEBITS);
-       if (diff >= maxdiff)
+       if (diff >= MAX_SYNCOOKIE_AGE)
                return (__u32)-1;
 
        return (cookie -
@@ -125,8 +126,7 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
        *mssp = msstab[mssind];
 
        return secure_tcp_syn_cookie(&iph->saddr, &iph->daddr, th->source,
-                                    th->dest, ntohl(th->seq),
-                                    jiffies / (HZ * 60), mssind);
+                                    th->dest, ntohl(th->seq), mssind);
 }
 EXPORT_SYMBOL_GPL(__cookie_v6_init_sequence);
 
@@ -146,8 +146,7 @@ int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th,
 {
        __u32 seq = ntohl(th->seq) - 1;
        __u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr,
-                                           th->source, th->dest, seq,
-                                           jiffies / (HZ * 60), COUNTER_TRIES);
+                                           th->source, th->dest, seq);
 
        return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
 }
@@ -157,7 +156,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_options_received tcp_opt;
        struct inet_request_sock *ireq;
-       struct inet6_request_sock *ireq6;
        struct tcp_request_sock *treq;
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
@@ -194,7 +192,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                goto out;
 
        ireq = inet_rsk(req);
-       ireq6 = inet6_rsk(req);
        treq = tcp_rsk(req);
        treq->listener = NULL;
 
@@ -202,22 +199,22 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                goto out_free;
 
        req->mss = mss;
-       ireq->rmt_port = th->source;
-       ireq->loc_port = th->dest;
-       ireq6->rmt_addr = ipv6_hdr(skb)->saddr;
-       ireq6->loc_addr = ipv6_hdr(skb)->daddr;
+       ireq->ir_rmt_port = th->source;
+       ireq->ir_num = ntohs(th->dest);
+       ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
+       ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
        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;
+               ireq->pktopts = skb;
        }
 
-       ireq6->iif = sk->sk_bound_dev_if;
+       ireq->ir_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);
+           ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
+               ireq->ir_iif = inet6_iif(skb);
 
        req->expires = 0UL;
        req->num_retrans = 0;
@@ -241,12 +238,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                struct flowi6 fl6;
                memset(&fl6, 0, sizeof(fl6));
                fl6.flowi6_proto = IPPROTO_TCP;
-               fl6.daddr = ireq6->rmt_addr;
+               fl6.daddr = ireq->ir_v6_rmt_addr;
                final_p = fl6_update_dst(&fl6, np->opt, &final);
-               fl6.saddr = ireq6->loc_addr;
+               fl6.saddr = ireq->ir_v6_loc_addr;
                fl6.flowi6_oif = sk->sk_bound_dev_if;
                fl6.flowi6_mark = sk->sk_mark;
-               fl6.fl6_dport = inet_rsk(req)->rmt_port;
+               fl6.fl6_dport = ireq->ir_rmt_port;
                fl6.fl6_sport = inet_sk(sk)->inet_sport;
                security_req_classify_flow(req, flowi6_to_flowi(&fl6));
 
index 5c71501..0740f93 100644 (file)
@@ -192,13 +192,13 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        }
 
        if (tp->rx_opt.ts_recent_stamp &&
-           !ipv6_addr_equal(&np->daddr, &usin->sin6_addr)) {
+           !ipv6_addr_equal(&sk->sk_v6_daddr, &usin->sin6_addr)) {
                tp->rx_opt.ts_recent = 0;
                tp->rx_opt.ts_recent_stamp = 0;
                tp->write_seq = 0;
        }
 
-       np->daddr = usin->sin6_addr;
+       sk->sk_v6_daddr = usin->sin6_addr;
        np->flow_label = fl6.flowlabel;
 
        /*
@@ -237,17 +237,17 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                } else {
                        ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
                        ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
-                                              &np->rcv_saddr);
+                                              &sk->sk_v6_rcv_saddr);
                }
 
                return err;
        }
 
-       if (!ipv6_addr_any(&np->rcv_saddr))
-               saddr = &np->rcv_saddr;
+       if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr))
+               saddr = &sk->sk_v6_rcv_saddr;
 
        fl6.flowi6_proto = IPPROTO_TCP;
-       fl6.daddr = np->daddr;
+       fl6.daddr = sk->sk_v6_daddr;
        fl6.saddr = saddr ? *saddr : np->saddr;
        fl6.flowi6_oif = sk->sk_bound_dev_if;
        fl6.flowi6_mark = sk->sk_mark;
@@ -266,7 +266,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        if (saddr == NULL) {
                saddr = &fl6.saddr;
-               np->rcv_saddr = *saddr;
+               sk->sk_v6_rcv_saddr = *saddr;
        }
 
        /* set the source address */
@@ -279,7 +279,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        rt = (struct rt6_info *) dst;
        if (tcp_death_row.sysctl_tw_recycle &&
            !tp->rx_opt.ts_recent_stamp &&
-           ipv6_addr_equal(&rt->rt6i_dst.addr, &np->daddr))
+           ipv6_addr_equal(&rt->rt6i_dst.addr, &sk->sk_v6_daddr))
                tcp_fetch_timewait_stamp(sk, dst);
 
        icsk->icsk_ext_hdr_len = 0;
@@ -298,7 +298,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        if (!tp->write_seq && likely(!tp->repair))
                tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,
-                                                            np->daddr.s6_addr32,
+                                                            sk->sk_v6_daddr.s6_addr32,
                                                             inet->inet_sport,
                                                             inet->inet_dport);
 
@@ -465,7 +465,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct request_sock *req,
                              u16 queue_mapping)
 {
-       struct inet6_request_sock *treq = inet6_rsk(req);
+       struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sk_buff * skb;
        int err = -ENOMEM;
@@ -477,9 +477,10 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
        skb = tcp_make_synack(sk, dst, req, NULL);
 
        if (skb) {
-               __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr);
+               __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr,
+                                   &ireq->ir_v6_rmt_addr);
 
-               fl6->daddr = treq->rmt_addr;
+               fl6->daddr = ireq->ir_v6_rmt_addr;
                skb_set_queue_mapping(skb, queue_mapping);
                err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
                err = net_xmit_eval(err);
@@ -502,7 +503,7 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req)
 
 static void tcp_v6_reqsk_destructor(struct request_sock *req)
 {
-       kfree_skb(inet6_rsk(req)->pktopts);
+       kfree_skb(inet_rsk(req)->pktopts);
 }
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -515,13 +516,13 @@ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
 static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk,
                                                struct sock *addr_sk)
 {
-       return tcp_v6_md5_do_lookup(sk, &inet6_sk(addr_sk)->daddr);
+       return tcp_v6_md5_do_lookup(sk, &addr_sk->sk_v6_daddr);
 }
 
 static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk,
                                                      struct request_sock *req)
 {
-       return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);
+       return tcp_v6_md5_do_lookup(sk, &inet_rsk(req)->ir_v6_rmt_addr);
 }
 
 static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
@@ -621,10 +622,10 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
 
        if (sk) {
                saddr = &inet6_sk(sk)->saddr;
-               daddr = &inet6_sk(sk)->daddr;
+               daddr = &sk->sk_v6_daddr;
        } else if (req) {
-               saddr = &inet6_rsk(req)->loc_addr;
-               daddr = &inet6_rsk(req)->rmt_addr;
+               saddr = &inet_rsk(req)->ir_v6_loc_addr;
+               daddr = &inet_rsk(req)->ir_v6_rmt_addr;
        } else {
                const struct ipv6hdr *ip6h = ipv6_hdr(skb);
                saddr = &ip6h->saddr;
@@ -949,7 +950,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_options_received tmp_opt;
        struct request_sock *req;
-       struct inet6_request_sock *treq;
+       struct inet_request_sock *ireq;
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        __u32 isn = TCP_SKB_CB(skb)->when;
@@ -994,25 +995,25 @@ 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 = inet6_rsk(req);
-       treq->rmt_addr = ipv6_hdr(skb)->saddr;
-       treq->loc_addr = ipv6_hdr(skb)->daddr;
+       ireq = inet_rsk(req);
+       ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
+       ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
        if (!want_cookie || tmp_opt.tstamp_ok)
                TCP_ECN_create_request(req, skb, sock_net(sk));
 
-       treq->iif = sk->sk_bound_dev_if;
+       ireq->ir_iif = sk->sk_bound_dev_if;
 
        /* So that link locals have meaning */
        if (!sk->sk_bound_dev_if &&
-           ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
-               treq->iif = inet6_iif(skb);
+           ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
+               ireq->ir_iif = inet6_iif(skb);
 
        if (!isn) {
                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);
-                       treq->pktopts = skb;
+                       ireq->pktopts = skb;
                }
 
                if (want_cookie) {
@@ -1051,7 +1052,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                         * to the moment of synflood.
                         */
                        LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n",
-                                      &treq->rmt_addr, ntohs(tcp_hdr(skb)->source));
+                                      &ireq->ir_v6_rmt_addr, ntohs(tcp_hdr(skb)->source));
                        goto drop_and_release;
                }
 
@@ -1086,7 +1087,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 inet6_request_sock *treq;
+       struct inet_request_sock *ireq;
        struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
        struct tcp6_sock *newtcp6sk;
        struct inet_sock *newinet;
@@ -1116,11 +1117,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
                memcpy(newnp, np, sizeof(struct ipv6_pinfo));
 
-               ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr);
+               ipv6_addr_set_v4mapped(newinet->inet_daddr, &newsk->sk_v6_daddr);
 
                ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr);
 
-               newnp->rcv_saddr = newnp->saddr;
+               newsk->sk_v6_rcv_saddr = newnp->saddr;
 
                inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
                newsk->sk_backlog_rcv = tcp_v4_do_rcv;
@@ -1151,7 +1152,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                return newsk;
        }
 
-       treq = inet6_rsk(req);
+       ireq = inet_rsk(req);
 
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
@@ -1185,10 +1186,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        memcpy(newnp, np, sizeof(struct ipv6_pinfo));
 
-       newnp->daddr = treq->rmt_addr;
-       newnp->saddr = treq->loc_addr;
-       newnp->rcv_saddr = treq->loc_addr;
-       newsk->sk_bound_dev_if = treq->iif;
+       newsk->sk_v6_daddr = ireq->ir_v6_rmt_addr;
+       newnp->saddr = ireq->ir_v6_loc_addr;
+       newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr;
+       newsk->sk_bound_dev_if = ireq->ir_iif;
 
        /* Now IPv6 options...
 
@@ -1203,11 +1204,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        /* Clone pktoptions received with SYN */
        newnp->pktoptions = NULL;
-       if (treq->pktopts != NULL) {
-               newnp->pktoptions = skb_clone(treq->pktopts,
+       if (ireq->pktopts != NULL) {
+               newnp->pktoptions = skb_clone(ireq->pktopts,
                                              sk_gfp_atomic(sk, GFP_ATOMIC));
-               consume_skb(treq->pktopts);
-               treq->pktopts = NULL;
+               consume_skb(ireq->pktopts);
+               ireq->pktopts = NULL;
                if (newnp->pktoptions)
                        skb_set_owner_r(newnp->pktoptions, newsk);
        }
@@ -1244,13 +1245,13 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Copy over the MD5 key from the original socket */
-       if ((key = tcp_v6_md5_do_lookup(sk, &newnp->daddr)) != NULL) {
+       if ((key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr)) != NULL) {
                /* We're using one, so create a matching key
                 * on the newsk structure. If we fail to get
                 * memory, then we end up not copying the key
                 * across. Shucks.
                 */
-               tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr,
+               tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
                               AF_INET6, key->key, key->keylen,
                               sk_gfp_atomic(sk, GFP_ATOMIC));
        }
@@ -1722,8 +1723,8 @@ static void get_openreq6(struct seq_file *seq,
                         const struct sock *sk, struct request_sock *req, int i, kuid_t uid)
 {
        int ttd = req->expires - jiffies;
-       const struct in6_addr *src = &inet6_rsk(req)->loc_addr;
-       const struct in6_addr *dest = &inet6_rsk(req)->rmt_addr;
+       const struct in6_addr *src = &inet_rsk(req)->ir_v6_loc_addr;
+       const struct in6_addr *dest = &inet_rsk(req)->ir_v6_rmt_addr;
 
        if (ttd < 0)
                ttd = 0;
@@ -1734,10 +1735,10 @@ static void get_openreq6(struct seq_file *seq,
                   i,
                   src->s6_addr32[0], src->s6_addr32[1],
                   src->s6_addr32[2], src->s6_addr32[3],
-                  ntohs(inet_rsk(req)->loc_port),
+                  inet_rsk(req)->ir_num,
                   dest->s6_addr32[0], dest->s6_addr32[1],
                   dest->s6_addr32[2], dest->s6_addr32[3],
-                  ntohs(inet_rsk(req)->rmt_port),
+                  ntohs(inet_rsk(req)->ir_rmt_port),
                   TCP_SYN_RECV,
                   0,0, /* could print option size, but that is af dependent. */
                   1,   /* timers active (only the expire timer) */
@@ -1758,10 +1759,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
        const struct inet_sock *inet = inet_sk(sp);
        const struct tcp_sock *tp = tcp_sk(sp);
        const struct inet_connection_sock *icsk = inet_csk(sp);
-       const struct ipv6_pinfo *np = inet6_sk(sp);
 
-       dest  = &np->daddr;
-       src   = &np->rcv_saddr;
+       dest  = &sp->sk_v6_daddr;
+       src   = &sp->sk_v6_rcv_saddr;
        destp = ntohs(inet->inet_dport);
        srcp  = ntohs(inet->inet_sport);
 
@@ -1810,11 +1810,10 @@ static void get_timewait6_sock(struct seq_file *seq,
 {
        const struct in6_addr *dest, *src;
        __u16 destp, srcp;
-       const struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw);
-       long delta = tw->tw_ttd - jiffies;
+       s32 delta = tw->tw_ttd - inet_tw_time_stamp();
 
-       dest = &tw6->tw_v6_daddr;
-       src  = &tw6->tw_v6_rcv_saddr;
+       dest = &tw->tw_v6_daddr;
+       src  = &tw->tw_v6_rcv_saddr;
        destp = ntohs(tw->tw_dport);
        srcp  = ntohs(tw->tw_sport);
 
@@ -1834,6 +1833,7 @@ static void get_timewait6_sock(struct seq_file *seq,
 static int tcp6_seq_show(struct seq_file *seq, void *v)
 {
        struct tcp_iter_state *st;
+       struct sock *sk = v;
 
        if (v == SEQ_START_TOKEN) {
                seq_puts(seq,
@@ -1849,14 +1849,14 @@ static int tcp6_seq_show(struct seq_file *seq, void *v)
        switch (st->state) {
        case TCP_SEQ_STATE_LISTENING:
        case TCP_SEQ_STATE_ESTABLISHED:
-               get_tcp6_sock(seq, v, st->num);
+               if (sk->sk_state == TCP_TIME_WAIT)
+                       get_timewait6_sock(seq, v, st->num);
+               else
+                       get_tcp6_sock(seq, v, st->num);
                break;
        case TCP_SEQ_STATE_OPENREQ:
                get_openreq6(seq, st->syn_wait_sk, v, st->num, st->uid);
                break;
-       case TCP_SEQ_STATE_TIME_WAIT:
-               get_timewait6_sock(seq, v, st->num);
-               break;
        }
 out:
        return 0;
@@ -1929,6 +1929,7 @@ struct proto tcpv6_prot = {
        .memory_allocated       = &tcp_memory_allocated,
        .memory_pressure        = &tcp_memory_pressure,
        .orphan_count           = &tcp_orphan_count,
+       .sysctl_mem             = sysctl_tcp_mem,
        .sysctl_wmem            = sysctl_tcp_wmem,
        .sysctl_rmem            = sysctl_tcp_rmem,
        .max_header             = MAX_TCP_HEADER,
index 2ec6bf6..c1097c7 100644 (file)
@@ -83,7 +83,7 @@ static int tcp6_gro_complete(struct sk_buff *skb)
 static const struct net_offload tcpv6_offload = {
        .callbacks = {
                .gso_send_check =       tcp_v6_gso_send_check,
-               .gso_segment    =       tcp_tso_segment,
+               .gso_segment    =       tcp_gso_segment,
                .gro_receive    =       tcp6_gro_receive,
                .gro_complete   =       tcp6_gro_complete,
        },
index f405815..f3893e8 100644 (file)
 #include <trace/events/skb.h>
 #include "udp_impl.h"
 
+static unsigned int udp6_ehashfn(struct net *net,
+                                 const struct in6_addr *laddr,
+                                 const u16 lport,
+                                 const struct in6_addr *faddr,
+                                 const __be16 fport)
+{
+       static u32 udp6_ehash_secret __read_mostly;
+       static u32 udp_ipv6_hash_secret __read_mostly;
+
+       u32 lhash, fhash;
+
+       net_get_random_once(&udp6_ehash_secret,
+                           sizeof(udp6_ehash_secret));
+       net_get_random_once(&udp_ipv6_hash_secret,
+                           sizeof(udp_ipv6_hash_secret));
+
+       lhash = (__force u32)laddr->s6_addr32[3];
+       fhash = __ipv6_addr_jhash(faddr, udp_ipv6_hash_secret);
+
+       return __inet6_ehashfn(lhash, lport, fhash, fport,
+                              udp_ipv6_hash_secret + net_hash_mix(net));
+}
+
 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 = inet6_rcv_saddr(sk2);
-       __be32 sk1_rcv_saddr = sk_rcv_saddr(sk);
-       __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);
        int sk_ipv6only = ipv6_only_sock(sk);
        int sk2_ipv6only = inet_v6_ipv6only(sk2);
-       int addr_type = ipv6_addr_type(sk_rcv_saddr6);
+       int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr);
        int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
 
        /* if both are mapped, treat as IPv4 */
        if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED)
                return (!sk2_ipv6only &&
-                       (!sk1_rcv_saddr || !sk2_rcv_saddr ||
-                         sk1_rcv_saddr == sk2_rcv_saddr));
+                       (!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr ||
+                         sk->sk_rcv_saddr == sk2->sk_rcv_saddr));
 
        if (addr_type2 == IPV6_ADDR_ANY &&
            !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
@@ -79,7 +99,7 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
                return 1;
 
        if (sk2_rcv_saddr6 &&
-           ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
+           ipv6_addr_equal(&sk->sk_v6_rcv_saddr, sk2_rcv_saddr6))
                return 1;
 
        return 0;
@@ -107,7 +127,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum)
        unsigned int hash2_nulladdr =
                udp6_portaddr_hash(sock_net(sk), &in6addr_any, snum);
        unsigned int hash2_partial =
-               udp6_portaddr_hash(sock_net(sk), &inet6_sk(sk)->rcv_saddr, 0);
+               udp6_portaddr_hash(sock_net(sk), &sk->sk_v6_rcv_saddr, 0);
 
        /* precompute partial secondary hash */
        udp_sk(sk)->udp_portaddr_hash = hash2_partial;
@@ -117,7 +137,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum)
 static void udp_v6_rehash(struct sock *sk)
 {
        u16 new_hash = udp6_portaddr_hash(sock_net(sk),
-                                         &inet6_sk(sk)->rcv_saddr,
+                                         &sk->sk_v6_rcv_saddr,
                                          inet_sk(sk)->inet_num);
 
        udp_lib_rehash(sk, new_hash);
@@ -133,7 +153,6 @@ static inline int compute_score(struct sock *sk, struct net *net,
 
        if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
                        sk->sk_family == PF_INET6) {
-               struct ipv6_pinfo *np = inet6_sk(sk);
                struct inet_sock *inet = inet_sk(sk);
 
                score = 0;
@@ -142,13 +161,13 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                return -1;
                        score++;
                }
-               if (!ipv6_addr_any(&np->rcv_saddr)) {
-                       if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
+               if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+                       if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
                                return -1;
                        score++;
                }
-               if (!ipv6_addr_any(&np->daddr)) {
-                       if (!ipv6_addr_equal(&np->daddr, saddr))
+               if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
+                       if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
                                return -1;
                        score++;
                }
@@ -171,10 +190,9 @@ static inline int compute_score2(struct sock *sk, struct net *net,
 
        if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
                        sk->sk_family == PF_INET6) {
-               struct ipv6_pinfo *np = inet6_sk(sk);
                struct inet_sock *inet = inet_sk(sk);
 
-               if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
+               if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
                        return -1;
                score = 0;
                if (inet->inet_dport) {
@@ -182,8 +200,8 @@ static inline int compute_score2(struct sock *sk, struct net *net,
                                return -1;
                        score++;
                }
-               if (!ipv6_addr_any(&np->daddr)) {
-                       if (!ipv6_addr_equal(&np->daddr, saddr))
+               if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
+                       if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
                                return -1;
                        score++;
                }
@@ -219,8 +237,8 @@ begin:
                        badness = score;
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
-                               hash = inet6_ehashfn(net, daddr, hnum,
-                                                    saddr, sport);
+                               hash = udp6_ehashfn(net, daddr, hnum,
+                                                   saddr, sport);
                                matches = 1;
                        } else if (score == SCORE2_MAX)
                                goto exact_match;
@@ -300,8 +318,8 @@ begin:
                        badness = score;
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
-                               hash = inet6_ehashfn(net, daddr, hnum,
-                                                    saddr, sport);
+                               hash = udp6_ehashfn(net, daddr, hnum,
+                                                   saddr, sport);
                                matches = 1;
                        }
                } else if (score == badness && reuseport) {
@@ -525,8 +543,10 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        if (type == ICMPV6_PKT_TOOBIG)
                ip6_sk_update_pmtu(skb, sk, info);
-       if (type == NDISC_REDIRECT)
+       if (type == NDISC_REDIRECT) {
                ip6_sk_redirect(skb, sk);
+               goto out;
+       }
 
        np = inet6_sk(sk);
 
@@ -549,8 +569,10 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        int rc;
 
-       if (!ipv6_addr_any(&inet6_sk(sk)->daddr))
+       if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
                sock_rps_save_rxhash(sk, skb);
+               sk_mark_napi_id(sk, skb);
+       }
 
        rc = sock_queue_rcv_skb(sk, skb);
        if (rc < 0) {
@@ -688,20 +710,19 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
 
                if (udp_sk(s)->udp_port_hash == num &&
                    s->sk_family == PF_INET6) {
-                       struct ipv6_pinfo *np = inet6_sk(s);
                        if (inet->inet_dport) {
                                if (inet->inet_dport != rmt_port)
                                        continue;
                        }
-                       if (!ipv6_addr_any(&np->daddr) &&
-                           !ipv6_addr_equal(&np->daddr, rmt_addr))
+                       if (!ipv6_addr_any(&sk->sk_v6_daddr) &&
+                           !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))
                                continue;
 
                        if (s->sk_bound_dev_if && s->sk_bound_dev_if != dif)
                                continue;
 
-                       if (!ipv6_addr_any(&np->rcv_saddr)) {
-                               if (!ipv6_addr_equal(&np->rcv_saddr, loc_addr))
+                       if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+                               if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr))
                                        continue;
                        }
                        if (!inet6_mc_check(s, loc_addr, rmt_addr))
@@ -844,7 +865,6 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (sk != NULL) {
                int ret;
 
-               sk_mark_napi_id(sk, skb);
                ret = udpv6_queue_rcv_skb(sk, skb);
                sock_put(sk);
 
@@ -1062,7 +1082,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        } else if (!up->pending) {
                if (sk->sk_state != TCP_ESTABLISHED)
                        return -EDESTADDRREQ;
-               daddr = &np->daddr;
+               daddr = &sk->sk_v6_daddr;
        } else
                daddr = NULL;
 
@@ -1132,8 +1152,8 @@ do_udp_sendmsg:
                 * sk->sk_dst_cache.
                 */
                if (sk->sk_state == TCP_ESTABLISHED &&
-                   ipv6_addr_equal(daddr, &np->daddr))
-                       daddr = &np->daddr;
+                   ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
+                       daddr = &sk->sk_v6_daddr;
 
                if (addr_len >= sizeof(struct sockaddr_in6) &&
                    sin6->sin6_scope_id &&
@@ -1144,7 +1164,7 @@ do_udp_sendmsg:
                        return -EDESTADDRREQ;
 
                fl6.fl6_dport = inet->inet_dport;
-               daddr = &np->daddr;
+               daddr = &sk->sk_v6_daddr;
                fl6.flowlabel = np->flow_label;
                connected = 1;
        }
@@ -1223,9 +1243,6 @@ do_udp_sendmsg:
        if (tclass < 0)
                tclass = np->tclass;
 
-       if (dontfrag < 0)
-               dontfrag = np->dontfrag;
-
        if (msg->msg_flags&MSG_CONFIRM)
                goto do_confirm;
 back_from_confirm:
@@ -1244,6 +1261,8 @@ back_from_confirm:
        up->pending = AF_INET6;
 
 do_append_data:
+       if (dontfrag < 0)
+               dontfrag = np->dontfrag;
        up->len += ulen;
        getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;
        err = ip6_append_data(sk, getfrag, msg->msg_iov, ulen,
@@ -1260,8 +1279,8 @@ do_append_data:
        if (dst) {
                if (connected) {
                        ip6_dst_store(sk, dst,
-                                     ipv6_addr_equal(&fl6.daddr, &np->daddr) ?
-                                     &np->daddr : NULL,
+                                     ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ?
+                                     &sk->sk_v6_daddr : NULL,
 #ifdef CONFIG_IPV6_SUBTREES
                                      ipv6_addr_equal(&fl6.saddr, &np->saddr) ?
                                      &np->saddr :
index 4691ed5..c779c3c 100644 (file)
@@ -7,33 +7,32 @@
 #include <net/inet_common.h>
 #include <net/transp_v6.h>
 
-extern int     __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int );
-extern void    __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *,
-                              u8 , u8 , int , __be32 , struct udp_table *);
+int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int);
+void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int,
+                   __be32, struct udp_table *);
 
-extern int     udp_v6_get_port(struct sock *sk, unsigned short snum);
+int udp_v6_get_port(struct sock *sk, unsigned short snum);
 
-extern int     udpv6_getsockopt(struct sock *sk, int level, int optname,
-                                char __user *optval, int __user *optlen);
-extern int     udpv6_setsockopt(struct sock *sk, int level, int optname,
-                                char __user *optval, unsigned int optlen);
+int udpv6_getsockopt(struct sock *sk, int level, int optname,
+                    char __user *optval, int __user *optlen);
+int udpv6_setsockopt(struct sock *sk, int level, int optname,
+                    char __user *optval, unsigned int optlen);
 #ifdef CONFIG_COMPAT
-extern int     compat_udpv6_setsockopt(struct sock *sk, int level, int optname,
-                                       char __user *optval, unsigned int optlen);
-extern int     compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
-                                      char __user *optval, int __user *optlen);
+int compat_udpv6_setsockopt(struct sock *sk, int level, int optname,
+                           char __user *optval, unsigned int optlen);
+int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
+                           char __user *optval, int __user *optlen);
 #endif
-extern int     udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
-                             struct msghdr *msg, size_t len);
-extern int     udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
-                             struct msghdr *msg, size_t len,
-                             int noblock, int flags, int *addr_len);
-extern int     udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb);
-extern void    udpv6_destroy_sock(struct sock *sk);
+int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                 size_t len);
+int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                 size_t len, int noblock, int flags, int *addr_len);
+int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+void udpv6_destroy_sock(struct sock *sk);
 
-extern void udp_v6_clear_sk(struct sock *sk, int size);
+void udp_v6_clear_sk(struct sock *sk, int size);
 
 #ifdef CONFIG_PROC_FS
-extern int     udp6_seq_show(struct seq_file *seq, void *v);
+int udp6_seq_show(struct seq_file *seq, void *v);
 #endif
 #endif /* _UDP6_IMPL_H */
index 6055951..e7359f9 100644 (file)
@@ -64,6 +64,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                                      SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
                                      SKB_GSO_GRE |
+                                     SKB_GSO_IPIP |
+                                     SKB_GSO_SIT |
                                      SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
@@ -88,7 +90,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
 
                /* Check if there is enough headroom to insert fragment header. */
                tnl_hlen = skb_tnl_header_len(skb);
-               if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) {
+               if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) {
                        if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
                                goto out;
                }
index 4770d51..cb04f7a 100644 (file)
 #include <net/ipv6.h>
 #include <net/xfrm.h>
 
+/* Informational hook. The decap is still done here. */
+static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly;
+static DEFINE_MUTEX(xfrm6_mode_tunnel_input_mutex);
+
+int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler)
+{
+       struct xfrm_tunnel_notifier __rcu **pprev;
+       struct xfrm_tunnel_notifier *t;
+       int ret = -EEXIST;
+       int priority = handler->priority;
+
+       mutex_lock(&xfrm6_mode_tunnel_input_mutex);
+
+       for (pprev = &rcv_notify_handlers;
+            (t = rcu_dereference_protected(*pprev,
+            lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL;
+            pprev = &t->next) {
+               if (t->priority > priority)
+                       break;
+               if (t->priority == priority)
+                       goto err;
+
+       }
+
+       handler->next = *pprev;
+       rcu_assign_pointer(*pprev, handler);
+
+       ret = 0;
+
+err:
+       mutex_unlock(&xfrm6_mode_tunnel_input_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_register);
+
+int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler)
+{
+       struct xfrm_tunnel_notifier __rcu **pprev;
+       struct xfrm_tunnel_notifier *t;
+       int ret = -ENOENT;
+
+       mutex_lock(&xfrm6_mode_tunnel_input_mutex);
+       for (pprev = &rcv_notify_handlers;
+            (t = rcu_dereference_protected(*pprev,
+            lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL;
+            pprev = &t->next) {
+               if (t == handler) {
+                       *pprev = handler->next;
+                       ret = 0;
+                       break;
+               }
+       }
+       mutex_unlock(&xfrm6_mode_tunnel_input_mutex);
+       synchronize_net();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_deregister);
+
 static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
 {
        const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
@@ -63,8 +122,15 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
        return 0;
 }
 
+#define for_each_input_rcu(head, handler)      \
+       for (handler = rcu_dereference(head);   \
+            handler != NULL;                   \
+            handler = rcu_dereference(handler->next))
+
+
 static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
 {
+       struct xfrm_tunnel_notifier *handler;
        int err = -EINVAL;
 
        if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6)
@@ -72,6 +138,9 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
        if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
                goto out;
 
+       for_each_input_rcu(rcv_notify_handlers, handler)
+               handler->handler(skb);
+
        err = skb_unclone(skb, GFP_ATOMIC);
        if (err)
                goto out;
index 23ed03d..5f8e128 100644 (file)
@@ -135,9 +135,14 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
        struct ipv6_opt_hdr *exthdr;
        const unsigned char *nh = skb_network_header(skb);
        u8 nexthdr = nh[IP6CB(skb)->nhoff];
+       int oif = 0;
+
+       if (skb_dst(skb))
+               oif = skb_dst(skb)->dev->ifindex;
 
        memset(fl6, 0, sizeof(struct flowi6));
        fl6->flowi6_mark = skb->mark;
+       fl6->flowi6_oif = reverse ? skb->skb_iif : oif;
 
        fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
        fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
@@ -284,7 +289,7 @@ static struct dst_ops xfrm6_dst_ops = {
        .destroy =              xfrm6_dst_destroy,
        .ifdown =               xfrm6_dst_ifdown,
        .local_out =            __ip6_local_out,
-       .gc_thresh =            1024,
+       .gc_thresh =            32768,
 };
 
 static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
index 564eb0b..8d65bb9 100644 (file)
@@ -509,16 +509,11 @@ typedef struct irnet_ctrl_channel
  */
 
 /* -------------------------- IRDA PART -------------------------- */
-extern int
-       irda_irnet_create(irnet_socket *);      /* Initialise a IrNET socket */
-extern int
-       irda_irnet_connect(irnet_socket *);     /* Try to connect over IrDA */
-extern void
-       irda_irnet_destroy(irnet_socket *);     /* Teardown  a IrNET socket */
-extern int
-       irda_irnet_init(void);          /* Initialise IrDA part of IrNET */
-extern void
-       irda_irnet_cleanup(void);       /* Teardown IrDA part of IrNET */
+int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */
+int irda_irnet_connect(irnet_socket *);        /* Try to connect over IrDA */
+void irda_irnet_destroy(irnet_socket *);       /* Teardown an IrNET socket */
+int irda_irnet_init(void);             /* Initialise IrDA part of IrNET */
+void irda_irnet_cleanup(void);         /* Teardown IrDA part of IrNET */
 
 /**************************** VARIABLES ****************************/
 
index 9d58537..911ef03 100644 (file)
@@ -1098,7 +1098,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
 
        x->id.proto = proto;
        x->id.spi = sa->sadb_sa_spi;
-       x->props.replay_window = sa->sadb_sa_replay;
+       x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay,
+                                       (sizeof(x->replay.bitmap) * 8));
        if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
                x->props.flags |= XFRM_STATE_NOECN;
        if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
index feae495..9af77d9 100644 (file)
@@ -115,6 +115,11 @@ struct l2tp_net {
 static void l2tp_session_set_header_len(struct l2tp_session *session, int version);
 static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
 
+static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
+{
+       return sk->sk_user_data;
+}
+
 static inline struct l2tp_net *l2tp_pernet(struct net *net)
 {
        BUG_ON(!net);
@@ -504,7 +509,7 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
                return 0;
 
 #if IS_ENABLED(CONFIG_IPV6)
-       if (sk->sk_family == PF_INET6) {
+       if (sk->sk_family == PF_INET6 && !l2tp_tunnel(sk)->v4mapped) {
                if (!uh->check) {
                        LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n");
                        return 1;
@@ -1128,7 +1133,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
        /* Queue the packet to IP for output */
        skb->local_df = 1;
 #if IS_ENABLED(CONFIG_IPV6)
-       if (skb->sk->sk_family == PF_INET6)
+       if (skb->sk->sk_family == PF_INET6 && !tunnel->v4mapped)
                error = inet6_csk_xmit(skb, NULL);
        else
 #endif
@@ -1176,7 +1181,7 @@ static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb,
            !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) {
                __wsum csum = skb_checksum(skb, 0, udp_len, 0);
                skb->ip_summed = CHECKSUM_UNNECESSARY;
-               uh->check = csum_ipv6_magic(&np->saddr, &np->daddr, udp_len,
+               uh->check = csum_ipv6_magic(&np->saddr, &sk->sk_v6_daddr, udp_len,
                                            IPPROTO_UDP, csum);
                if (uh->check == 0)
                        uh->check = CSUM_MANGLED_0;
@@ -1184,7 +1189,7 @@ static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb,
                skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum_start = skb_transport_header(skb) - skb->head;
                skb->csum_offset = offsetof(struct udphdr, check);
-               uh->check = ~csum_ipv6_magic(&np->saddr, &np->daddr,
+               uh->check = ~csum_ipv6_magic(&np->saddr, &sk->sk_v6_daddr,
                                             udp_len, IPPROTO_UDP, 0);
        }
 }
@@ -1255,7 +1260,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
 
                /* Calculate UDP checksum if configured to do so */
 #if IS_ENABLED(CONFIG_IPV6)
-               if (sk->sk_family == PF_INET6)
+               if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
                        l2tp_xmit_ipv6_csum(sk, skb, udp_len);
                else
 #endif
@@ -1304,10 +1309,9 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb);
  */
 static void l2tp_tunnel_destruct(struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel;
+       struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);
        struct l2tp_net *pn;
 
-       tunnel = sk->sk_user_data;
        if (tunnel == NULL)
                goto end;
 
@@ -1675,7 +1679,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
        }
 
        /* Check if this socket has already been prepped */
-       tunnel = (struct l2tp_tunnel *)sk->sk_user_data;
+       tunnel = l2tp_tunnel(sk);
        if (tunnel != NULL) {
                /* This socket has already been prepped */
                err = -EBUSY;
@@ -1704,6 +1708,24 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
        if (cfg != NULL)
                tunnel->debug = cfg->debug;
 
+#if IS_ENABLED(CONFIG_IPV6)
+       if (sk->sk_family == PF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+
+               if (ipv6_addr_v4mapped(&np->saddr) &&
+                   ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
+                       struct inet_sock *inet = inet_sk(sk);
+
+                       tunnel->v4mapped = true;
+                       inet->inet_saddr = np->saddr.s6_addr32[3];
+                       inet->inet_rcv_saddr = sk->sk_v6_rcv_saddr.s6_addr32[3];
+                       inet->inet_daddr = sk->sk_v6_daddr.s6_addr32[3];
+               } else {
+                       tunnel->v4mapped = false;
+               }
+       }
+#endif
+
        /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
        tunnel->encap = encap;
        if (encap == L2TP_ENCAPTYPE_UDP) {
@@ -1712,7 +1734,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
                udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
                udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;
 #if IS_ENABLED(CONFIG_IPV6)
-               if (sk->sk_family == PF_INET6)
+               if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
                        udpv6_encap_enable();
                else
 #endif
index 66a559b..1ee9f69 100644 (file)
@@ -194,6 +194,9 @@ struct l2tp_tunnel {
        struct sock             *sock;          /* Parent socket */
        int                     fd;             /* Parent fd, if tunnel socket
                                                 * was created by userspace */
+#if IS_ENABLED(CONFIG_IPV6)
+       bool                    v4mapped;
+#endif
 
        struct work_struct      del_work;
 
@@ -235,29 +238,40 @@ out:
        return tunnel;
 }
 
-extern struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel);
-extern void l2tp_tunnel_sock_put(struct sock *sk);
-extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id);
-extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
-extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
-extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
-extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
-
-extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp);
-extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
-extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
-extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
-extern void __l2tp_session_unhash(struct l2tp_session *session);
-extern int l2tp_session_delete(struct l2tp_session *session);
-extern void l2tp_session_free(struct l2tp_session *session);
-extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
-extern int l2tp_session_queue_purge(struct l2tp_session *session);
-extern int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb);
-
-extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len);
-
-extern int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops);
-extern void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
+struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel);
+void l2tp_tunnel_sock_put(struct sock *sk);
+struct l2tp_session *l2tp_session_find(struct net *net,
+                                      struct l2tp_tunnel *tunnel,
+                                      u32 session_id);
+struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
+struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
+struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
+struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
+
+int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id,
+                      u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg,
+                      struct l2tp_tunnel **tunnelp);
+void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
+int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
+struct l2tp_session *l2tp_session_create(int priv_size,
+                                        struct l2tp_tunnel *tunnel,
+                                        u32 session_id, u32 peer_session_id,
+                                        struct l2tp_session_cfg *cfg);
+void __l2tp_session_unhash(struct l2tp_session *session);
+int l2tp_session_delete(struct l2tp_session *session);
+void l2tp_session_free(struct l2tp_session *session);
+void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
+                     unsigned char *ptr, unsigned char *optr, u16 hdrflags,
+                     int length, int (*payload_hook)(struct sk_buff *skb));
+int l2tp_session_queue_purge(struct l2tp_session *session);
+int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb);
+
+int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb,
+                 int hdr_len);
+
+int l2tp_nl_register_ops(enum l2tp_pwtype pw_type,
+                        const struct l2tp_nl_cmd_ops *ops);
+void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
 
 /* Session reference counts. Incremented when code obtains a reference
  * to a session.
index 072d720..2d6760a 100644 (file)
@@ -127,9 +127,10 @@ static void l2tp_dfs_seq_tunnel_show(struct seq_file *m, void *v)
 
 #if IS_ENABLED(CONFIG_IPV6)
                if (tunnel->sock->sk_family == AF_INET6) {
-                       struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
+                       const struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
+
                        seq_printf(m, " from %pI6c to %pI6c\n",
-                               &np->saddr, &np->daddr);
+                               &np->saddr, &tunnel->sock->sk_v6_daddr);
                } else
 #endif
                seq_printf(m, " from %pI4 to %pI4\n",
index b8a6039..cfd6530 100644 (file)
@@ -63,7 +63,7 @@ static struct sock *__l2tp_ip6_bind_lookup(struct net *net,
        struct sock *sk;
 
        sk_for_each_bound(sk, &l2tp_ip6_bind_table) {
-               struct in6_addr *addr = inet6_rcv_saddr(sk);
+               const struct in6_addr *addr = inet6_rcv_saddr(sk);
                struct l2tp_ip6_sock *l2tp = l2tp_ip6_sk(sk);
 
                if (l2tp == NULL)
@@ -331,7 +331,7 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        rcu_read_unlock();
 
        inet->inet_rcv_saddr = inet->inet_saddr = v4addr;
-       np->rcv_saddr = addr->l2tp_addr;
+       sk->sk_v6_rcv_saddr = addr->l2tp_addr;
        np->saddr = addr->l2tp_addr;
 
        l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id;
@@ -421,14 +421,14 @@ static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
                if (!lsk->peer_conn_id)
                        return -ENOTCONN;
                lsa->l2tp_conn_id = lsk->peer_conn_id;
-               lsa->l2tp_addr = np->daddr;
+               lsa->l2tp_addr = sk->sk_v6_daddr;
                if (np->sndflow)
                        lsa->l2tp_flowinfo = np->flow_label;
        } else {
-               if (ipv6_addr_any(&np->rcv_saddr))
+               if (ipv6_addr_any(&sk->sk_v6_rcv_saddr))
                        lsa->l2tp_addr = np->saddr;
                else
-                       lsa->l2tp_addr = np->rcv_saddr;
+                       lsa->l2tp_addr = sk->sk_v6_rcv_saddr;
 
                lsa->l2tp_conn_id = lsk->conn_id;
        }
@@ -537,8 +537,8 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
                 * sk->sk_dst_cache.
                 */
                if (sk->sk_state == TCP_ESTABLISHED &&
-                   ipv6_addr_equal(daddr, &np->daddr))
-                       daddr = &np->daddr;
+                   ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
+                       daddr = &sk->sk_v6_daddr;
 
                if (addr_len >= sizeof(struct sockaddr_in6) &&
                    lsa->l2tp_scope_id &&
@@ -548,7 +548,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
                if (sk->sk_state != TCP_ESTABLISHED)
                        return -EDESTADDRREQ;
 
-               daddr = &np->daddr;
+               daddr = &sk->sk_v6_daddr;
                fl6.flowlabel = np->flow_label;
        }
 
index 0825ff2..be446d5 100644 (file)
@@ -306,8 +306,8 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla
                if (np) {
                        if (nla_put(skb, L2TP_ATTR_IP6_SADDR, sizeof(np->saddr),
                                    &np->saddr) ||
-                           nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(np->daddr),
-                                   &np->daddr))
+                           nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(sk->sk_v6_daddr),
+                                   &sk->sk_v6_daddr))
                                goto nla_put_failure;
                } else
 #endif
index 5ebee2d..ffda81e 100644 (file)
@@ -353,7 +353,9 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
                goto error_put_sess_tun;
        }
 
+       local_bh_disable();
        l2tp_xmit_skb(session, skb, session->hdr_len);
+       local_bh_enable();
 
        sock_put(ps->tunnel_sock);
        sock_put(sk);
@@ -422,7 +424,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        skb->data[0] = ppph[0];
        skb->data[1] = ppph[1];
 
+       local_bh_disable();
        l2tp_xmit_skb(session, skb, session->hdr_len);
+       local_bh_enable();
 
        sock_put(sk_tun);
        sock_put(sk);
@@ -906,8 +910,8 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
 #if IS_ENABLED(CONFIG_IPV6)
        } else if ((tunnel->version == 2) &&
                   (tunnel->sock->sk_family == AF_INET6)) {
-               struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
                struct sockaddr_pppol2tpin6 sp;
+
                len = sizeof(sp);
                memset(&sp, 0, len);
                sp.sa_family    = AF_PPPOX;
@@ -920,13 +924,13 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
                sp.pppol2tp.d_session = session->peer_session_id;
                sp.pppol2tp.addr.sin6_family = AF_INET6;
                sp.pppol2tp.addr.sin6_port = inet->inet_dport;
-               memcpy(&sp.pppol2tp.addr.sin6_addr, &np->daddr,
-                      sizeof(np->daddr));
+               memcpy(&sp.pppol2tp.addr.sin6_addr, &tunnel->sock->sk_v6_daddr,
+                      sizeof(tunnel->sock->sk_v6_daddr));
                memcpy(uaddr, &sp, len);
        } else if ((tunnel->version == 3) &&
                   (tunnel->sock->sk_family == AF_INET6)) {
-               struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
                struct sockaddr_pppol2tpv3in6 sp;
+
                len = sizeof(sp);
                memset(&sp, 0, len);
                sp.sa_family    = AF_PPPOX;
@@ -939,8 +943,8 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
                sp.pppol2tp.d_session = session->peer_session_id;
                sp.pppol2tp.addr.sin6_family = AF_INET6;
                sp.pppol2tp.addr.sin6_port = inet->inet_dport;
-               memcpy(&sp.pppol2tp.addr.sin6_addr, &np->daddr,
-                      sizeof(np->daddr));
+               memcpy(&sp.pppol2tp.addr.sin6_addr, &tunnel->sock->sk_v6_daddr,
+                      sizeof(tunnel->sock->sk_v6_daddr));
                memcpy(uaddr, &sp, len);
 #endif
        } else if (tunnel->version == 3) {
index 54563ad..355cc3b 100644 (file)
@@ -154,6 +154,7 @@ static void lapb_t1timer_expiry(unsigned long param)
                        } else {
                                lapb->n2count++;
                                lapb_requeue_frames(lapb);
+                               lapb_kick(lapb);
                        }
                        break;
 
index 62535fe..97b5dca 100644 (file)
@@ -4,6 +4,7 @@ config MAC80211
        select CRYPTO
        select CRYPTO_ARC4
        select CRYPTO_AES
+       select CRYPTO_CCM
        select CRC32
        select AVERAGE
        ---help---
@@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG
 
          Do not select this option.
 
+config MAC80211_MESH_CSA_DEBUG
+       bool "Verbose mesh channel switch debugging"
+       depends on MAC80211_DEBUG_MENU
+       depends on MAC80211_MESH
+       ---help---
+         Selecting this option causes mac80211 to print out very verbose mesh
+         channel switch debugging messages (when mac80211 is taking part in a
+         mesh network).
+
+         Do not select this option.
+
 config MAC80211_MESH_PS_DEBUG
        bool "Verbose mesh powersave debugging"
        depends on MAC80211_DEBUG_MENU
index be7614b..7c7df47 100644 (file)
@@ -2,6 +2,8 @@
  * Copyright 2003-2004, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  *
+ * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
  * 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 "key.h"
 #include "aes_ccm.h"
 
-static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic)
 {
-       int i;
-       u8 *b_0, *aad, *b, *s_0;
-
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-       aad = scratch + 4 * AES_BLOCK_SIZE;
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-
-       crypto_cipher_encrypt_one(tfm, b, b_0);
+       struct scatterlist assoc, pt, ct[2];
+       struct {
+               struct aead_request     req;
+               u8                      priv[crypto_aead_reqsize(tfm)];
+       } aead_req;
 
-       /* Extra Authenticate-only data (always two AES blocks) */
-       for (i = 0; i < AES_BLOCK_SIZE; i++)
-               aad[i] ^= b[i];
-       crypto_cipher_encrypt_one(tfm, b, aad);
+       memset(&aead_req, 0, sizeof(aead_req));
 
-       aad += AES_BLOCK_SIZE;
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-       for (i = 0; i < AES_BLOCK_SIZE; i++)
-               aad[i] ^= b[i];
-       crypto_cipher_encrypt_one(tfm, a, aad);
+       aead_request_set_tfm(&aead_req.req, tfm);
+       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+       aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
 
-       /* Mask out bits from auth-only-b_0 */
-       b_0[0] &= 0x07;
-
-       /* S_0 is used to encrypt T (= MIC) */
-       b_0[14] = 0;
-       b_0[15] = 0;
-       crypto_cipher_encrypt_one(tfm, s_0, b_0);
+       crypto_aead_encrypt(&aead_req.req);
 }
 
-
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-                              u8 *data, size_t data_len,
-                              u8 *cdata, u8 *mic)
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic)
 {
-       int i, j, last_len, num_blocks;
-       u8 *pos, *cpos, *b, *s_0, *e, *b_0;
-
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-       e = scratch + 2 * AES_BLOCK_SIZE;
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-       num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-       last_len = data_len % AES_BLOCK_SIZE;
-       aes_ccm_prepare(tfm, scratch, b);
-
-       /* Process payload blocks */
-       pos = data;
-       cpos = cdata;
-       for (j = 1; j <= num_blocks; j++) {
-               int blen = (j == num_blocks && last_len) ?
-                       last_len : AES_BLOCK_SIZE;
-
-               /* Authentication followed by encryption */
-               for (i = 0; i < blen; i++)
-                       b[i] ^= pos[i];
-               crypto_cipher_encrypt_one(tfm, b, b);
-
-               b_0[14] = (j >> 8) & 0xff;
-               b_0[15] = j & 0xff;
-               crypto_cipher_encrypt_one(tfm, e, b_0);
-               for (i = 0; i < blen; i++)
-                       *cpos++ = *pos++ ^ e[i];
-       }
-
-       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
-               mic[i] = b[i] ^ s_0[i];
+       struct scatterlist assoc, pt, ct[2];
+       struct {
+               struct aead_request     req;
+               u8                      priv[crypto_aead_reqsize(tfm)];
+       } aead_req;
+
+       memset(&aead_req, 0, sizeof(aead_req));
+
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+
+       aead_request_set_tfm(&aead_req.req, tfm);
+       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
+       aead_request_set_crypt(&aead_req.req, ct, &pt,
+                              data_len + IEEE80211_CCMP_MIC_LEN, b_0);
+
+       return crypto_aead_decrypt(&aead_req.req);
 }
 
-
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-                             u8 *cdata, size_t data_len, u8 *mic, u8 *data)
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
 {
-       int i, j, last_len, num_blocks;
-       u8 *pos, *cpos, *b, *s_0, *a, *b_0;
-
-       b = scratch;
-       s_0 = scratch + AES_BLOCK_SIZE;
-       a = scratch + 2 * AES_BLOCK_SIZE;
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
-       num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-       last_len = data_len % AES_BLOCK_SIZE;
-       aes_ccm_prepare(tfm, scratch, a);
-
-       /* Process payload blocks */
-       cpos = cdata;
-       pos = data;
-       for (j = 1; j <= num_blocks; j++) {
-               int blen = (j == num_blocks && last_len) ?
-                       last_len : AES_BLOCK_SIZE;
-
-               /* Decryption followed by authentication */
-               b_0[14] = (j >> 8) & 0xff;
-               b_0[15] = j & 0xff;
-               crypto_cipher_encrypt_one(tfm, b, b_0);
-               for (i = 0; i < blen; i++) {
-                       *pos = *cpos++ ^ b[i];
-                       a[i] ^= *pos++;
-               }
-               crypto_cipher_encrypt_one(tfm, a, a);
-       }
-
-       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
-               if ((mic[i] ^ s_0[i]) != a[i])
-                       return -1;
-       }
-
-       return 0;
-}
+       struct crypto_aead *tfm;
+       int err;
 
+       tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm))
+               return tfm;
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
-{
-       struct crypto_cipher *tfm;
+       err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+       if (!err)
+               err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
+       if (!err)
+               return tfm;
 
-       tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
-       if (!IS_ERR(tfm))
-               crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
-
-       return tfm;
+       crypto_free_aead(tfm);
+       return ERR_PTR(err);
 }
 
-
-void ieee80211_aes_key_free(struct crypto_cipher *tfm)
+void ieee80211_aes_key_free(struct crypto_aead *tfm)
 {
-       crypto_free_cipher(tfm);
+       crypto_free_aead(tfm);
 }
index 5b7d744..2c7ab19 100644 (file)
 
 #include <linux/crypto.h>
 
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
-void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
-                              u8 *data, size_t data_len,
-                              u8 *cdata, u8 *mic);
-int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
-                             u8 *cdata, size_t data_len,
-                             u8 *mic, u8 *data);
-void ieee80211_aes_key_free(struct crypto_cipher *tfm);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_key_free(struct crypto_aead *tfm);
 
 #endif /* AES_CCM_H */
index 629dee7..95667b0 100644 (file)
@@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        sdata->vif.csa_active = false;
        cancel_work_sync(&sdata->csa_finalize_work);
+       cancel_work_sync(&sdata->u.ap.request_smps_work);
 
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1342,8 +1343,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                sta->plink_state = params->plink_state;
 
                                ieee80211_mps_sta_status_update(sta);
-                               changed |=
-                                     ieee80211_mps_local_status_update(sdata);
+                               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                               NL80211_MESH_POWER_UNKNOWN);
                                break;
                        default:
                                /*  nothing  */
@@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        mutex_unlock(&local->sta_mtx);
 
+       if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           sta->known_smps_mode != sta->sdata->bss->req_smps &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sta->sdata,
+                      "%pM just authorized and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sta->sdata,
+                       sta->sdata->bss->req_smps,
+                       sta->sta.addr,
+                       sta->sdata->vif.bss_conf.bssid);
+       }
+
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
                ieee80211_recalc_ps(local, -1);
@@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
 }
 #endif
 
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode)
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode)
+{
+       struct sta_info *sta;
+       enum ieee80211_smps_mode old_req;
+       int i;
+
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
+               return -EINVAL;
+
+       if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+               return 0;
+
+       old_req = sdata->u.ap.req_smps;
+       sdata->u.ap.req_smps = smps_mode;
+
+       /* AUTOMATIC doesn't mean much for AP - don't allow it */
+       if (old_req == smps_mode ||
+           smps_mode == IEEE80211_SMPS_AUTOMATIC)
+               return 0;
+
+        /* If no associated stations, there's no need to do anything */
+       if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
+               sdata->smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+               return 0;
+       }
+
+       ht_dbg(sdata,
+              "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+              smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
+
+       mutex_lock(&sdata->local->sta_mtx);
+       for (i = 0; i < STA_HASH_SIZE; i++) {
+               for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
+                               lockdep_is_held(&sdata->local->sta_mtx));
+                    sta;
+                    sta = rcu_dereference_protected(sta->hnext,
+                               lockdep_is_held(&sdata->local->sta_mtx))) {
+                       /*
+                        * Only stations associated to our AP and
+                        * associated VLANs
+                        */
+                       if (sta->sdata->bss != &sdata->u.ap)
+                               continue;
+
+                       /* This station doesn't support MIMO - skip it */
+                       if (sta_info_tx_streams(sta) == 1)
+                               continue;
+
+                       /*
+                        * Don't wake up a STA just to send the action frame
+                        * unless we are getting more restrictive.
+                        */
+                       if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+                           !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                                          smps_mode)) {
+                               ht_dbg(sdata,
+                                      "Won't send SMPS to sleeping STA %pM\n",
+                                      sta->sta.addr);
+                               continue;
+                       }
+
+                       /*
+                        * If the STA is not authorized, wait until it gets
+                        * authorized and the action frame will be sent then.
+                        */
+                       if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                               continue;
+
+                       ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+                       ieee80211_send_smps_action(sdata, smps_mode,
+                                                  sta->sta.addr,
+                                                  sdata->vif.bss_conf.bssid);
+               }
+       }
+       mutex_unlock(&sdata->local->sta_mtx);
+
+       sdata->smps_mode = smps_mode;
+       ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+
+       return 0;
+}
+
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode)
 {
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
@@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
+               return -EINVAL;
+
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
 
@@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 
        /* no change, but if automatic follow powersave */
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
        sdata_unlock(sdata);
 
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
@@ -2860,35 +2962,55 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
-       int err, changed;
+       int err, changed = 0;
 
        if (!ieee80211_sdata_running(sdata))
                return;
 
-       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
-               return;
-
        sdata->radar_required = sdata->csa_radar_required;
        err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
                                           &changed);
        if (WARN_ON(err < 0))
                return;
 
-       err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
-       if (err < 0)
-               return;
+       if (!local->use_chanctx) {
+               local->_oper_chandef = local->csa_chandef;
+               ieee80211_hw_config(local, 0);
+       }
 
-       changed |= err;
-       kfree(sdata->u.ap.next_beacon);
-       sdata->u.ap.next_beacon = NULL;
+       ieee80211_bss_info_change_notify(sdata, changed);
+
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+               if (err < 0)
+                       return;
+               changed |= err;
+               kfree(sdata->u.ap.next_beacon);
+               sdata->u.ap.next_beacon = NULL;
+
+               ieee80211_bss_info_change_notify(sdata, err);
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ieee80211_ibss_finish_csa(sdata);
+               break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               err = ieee80211_mesh_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               break;
+#endif
+       default:
+               WARN_ON(1);
+               return;
+       }
        sdata->vif.csa_active = false;
 
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
        cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
 }
 
@@ -2899,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
 
        if (!list_empty(&local->roc_list) || local->scanning)
@@ -2936,20 +3059,76 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.csa_active)
                return -EBUSY;
 
-       /* only handle AP for now. */
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
+               sdata->csa_counter_offset_beacon =
+                       params->counter_offset_beacon;
+               sdata->csa_counter_offset_presp = params->counter_offset_presp;
+               sdata->u.ap.next_beacon =
+                       cfg80211_beacon_dup(&params->beacon_after);
+               if (!sdata->u.ap.next_beacon)
+                       return -ENOMEM;
+
+               err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+               if (err < 0) {
+                       kfree(sdata->u.ap.next_beacon);
+                       return err;
+               }
                break;
+       case NL80211_IFTYPE_ADHOC:
+               if (!sdata->vif.bss_conf.ibss_joined)
+                       return -EINVAL;
+
+               if (params->chandef.width != sdata->u.ibss.chandef.width)
+                       return -EINVAL;
+
+               switch (params->chandef.width) {
+               case NL80211_CHAN_WIDTH_40:
+                       if (cfg80211_get_chandef_type(&params->chandef) !=
+                           cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
+                               return -EINVAL;
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
+               case NL80211_CHAN_WIDTH_20_NOHT:
+               case NL80211_CHAN_WIDTH_20:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               /* changes into another band are not supported */
+               if (sdata->u.ibss.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+
+               err = ieee80211_ibss_csa_beacon(sdata, params);
+               if (err < 0)
+                       return err;
+               break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               ifmsh = &sdata->u.mesh;
+
+               if (!ifmsh->mesh_id)
+                       return -EINVAL;
+
+               if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
+                       return -EINVAL;
+
+               /* changes into another band are not supported */
+               if (sdata->vif.bss_conf.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+
+               err = ieee80211_mesh_csa_beacon(sdata, params, true);
+               if (err < 0)
+                       return err;
+               break;
+#endif
        default:
                return -EOPNOTSUPP;
        }
 
-       sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
-       if (!sdata->u.ap.next_beacon)
-               return -ENOMEM;
-
-       sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
-       sdata->csa_counter_offset_presp = params->counter_offset_presp;
        sdata->csa_radar_required = params->radar_required;
 
        if (params->block_tx)
@@ -2957,10 +3136,6 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
-       if (err < 0)
-               return err;
-
        local->csa_chandef = params->chandef;
        sdata->vif.csa_active = true;
 
@@ -3014,7 +3189,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                        need_offchan = true;
                if (!ieee80211_is_action(mgmt->frame_control) ||
                    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
-                   mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
+                   mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
+                   mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
                        break;
                rcu_read_lock();
                sta = sta_info_get(sdata, mgmt->da);
index 3a4764b..03ba6b5 100644 (file)
@@ -453,11 +453,6 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
        chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
        drv_change_chanctx(local, ctx, chanctx_changed);
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = *chandef;
-               ieee80211_hw_config(local, 0);
-       }
-
        ieee80211_recalc_chanctx_chantype(local, ctx);
        ieee80211_recalc_smps_chanctx(local, ctx);
        ieee80211_recalc_radar_chanctx(local, ctx);
index 4ccc5ed..493d680 100644 (file)
 #define MAC80211_MESH_SYNC_DEBUG 0
 #endif
 
+#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
+#define MAC80211_MESH_CSA_DEBUG 1
+#else
+#define MAC80211_MESH_CSA_DEBUG 0
+#endif
+
 #ifdef CONFIG_MAC80211_MESH_PS_DEBUG
 #define MAC80211_MESH_PS_DEBUG 1
 #else
@@ -157,6 +163,10 @@ do {                                                                       \
        _sdata_dbg(MAC80211_MESH_SYNC_DEBUG,                            \
                   sdata, fmt, ##__VA_ARGS__)
 
+#define mcsa_dbg(sdata, fmt, ...)                                      \
+       _sdata_dbg(MAC80211_MESH_CSA_DEBUG,                             \
+                  sdata, fmt, ##__VA_ARGS__)
+
 #define mps_dbg(sdata, fmt, ...)                                       \
        _sdata_dbg(MAC80211_MESH_PS_DEBUG,                              \
                   sdata, fmt, ##__VA_ARGS__)
index b0e32d6..5c090e4 100644 (file)
@@ -103,54 +103,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
        if (!buf)
                return 0;
 
-       sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
+       sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
        if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
-               sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
+               sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
        if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
-               sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
+               sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
        if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
-               sf += snprintf(buf + sf, mxln - sf,
-                              "HOST_BCAST_PS_BUFFERING\n");
+               sf += scnprintf(buf + sf, mxln - sf,
+                               "HOST_BCAST_PS_BUFFERING\n");
        if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
-               sf += snprintf(buf + sf, mxln - sf,
-                              "2GHZ_SHORT_SLOT_INCAPABLE\n");
+               sf += scnprintf(buf + sf, mxln - sf,
+                               "2GHZ_SHORT_SLOT_INCAPABLE\n");
        if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
-               sf += snprintf(buf + sf, mxln - sf,
-                              "2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
+               sf += scnprintf(buf + sf, mxln - sf,
+                               "2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
        if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
-               sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
-               sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
        if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
-               sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n");
+               sf += scnprintf(buf + sf, mxln - sf,
+                               "NEED_DTIM_BEFORE_ASSOC\n");
        if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
-               sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
        if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
-               sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
+               sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
-               sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
        if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-               sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
+               sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-               sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
        if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
-               sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
+               sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
-               sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
-               sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n");
+               sf += scnprintf(buf + sf, mxln - sf,
+                               "SUPPORTS_DYNAMIC_SMPS\n");
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
-               sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
        if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
-               sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
+               sf += scnprintf(buf + sf, mxln - sf,
+                               "REPORTS_TX_ACK_STATUS\n");
        if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
-               sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
+               sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
-               sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
+               sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
        if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
-               sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
+               sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
        if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)
-               sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
+               sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
 
        rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
        kfree(buf);
index cafe614..04b5a14 100644 (file)
@@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
             smps_mode == IEEE80211_SMPS_AUTOMATIC))
                return -EINVAL;
 
-       /* supported only on managed interfaces for now */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+           sdata->vif.type != NL80211_IFTYPE_AP)
                return -EOPNOTSUPP;
 
        sdata_lock(sdata);
-       err = __ieee80211_request_smps(sdata, smps_mode);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               err = __ieee80211_request_smps_mgd(sdata, smps_mode);
+       else
+               err = __ieee80211_request_smps_ap(sdata, smps_mode);
        sdata_unlock(sdata);
 
        return err;
@@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
 static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
                                     char *buf, int buflen)
 {
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
-               return -EOPNOTSUPP;
-
-       return snprintf(buf, buflen, "request: %s\nused: %s\n",
-                       smps_modes[sdata->u.mgd.req_smps],
-                       smps_modes[sdata->smps_mode]);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                               smps_modes[sdata->u.mgd.req_smps],
+                               smps_modes[sdata->smps_mode]);
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                               smps_modes[sdata->u.ap.req_smps],
+                               smps_modes[sdata->smps_mode]);
+       return -EINVAL;
 }
 
 static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
@@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 {
        DEBUGFS_ADD(num_mcast_sta);
+       DEBUGFS_ADD_MODE(smps, 0600);
        DEBUGFS_ADD(num_sta_ps);
        DEBUGFS_ADD(dtim_count);
        DEBUGFS_ADD(num_buffered_multicast);
index b3ea11f..5d03c47 100644 (file)
@@ -1085,4 +1085,31 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
        }
 }
 
+static inline int drv_join_ibss(struct ieee80211_local *local,
+                               struct ieee80211_sub_if_data *sdata)
+{
+       int ret = 0;
+
+       might_sleep();
+       check_sdata_in_driver(sdata);
+
+       trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf);
+       if (local->ops->join_ibss)
+               ret = local->ops->join_ibss(&local->hw, &sdata->vif);
+       trace_drv_return_int(local, ret);
+       return ret;
+}
+
+static inline void drv_leave_ibss(struct ieee80211_local *local,
+                                 struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+       check_sdata_in_driver(sdata);
+
+       trace_drv_leave_ibss(local, sdata);
+       if (local->ops->leave_ibss)
+               local->ops->leave_ibss(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index 529bf58..9a8be8f 100644 (file)
@@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-void ieee80211_request_smps_work(struct work_struct *work)
+void ieee80211_request_smps_mgd_work(struct work_struct *work)
 {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.request_smps_work);
 
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
+       sdata_unlock(sdata);
+}
+
+void ieee80211_request_smps_ap_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.ap.request_smps_work);
+
+       sdata_lock(sdata);
+       __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
        sdata_unlock(sdata);
 }
 
@@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
-       if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+       if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
+                        vif->type != NL80211_IFTYPE_AP))
                return;
 
        if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
-       if (sdata->u.mgd.driver_smps_mode == smps_mode)
-               return;
-
-       sdata->u.mgd.driver_smps_mode = smps_mode;
-
-       ieee80211_queue_work(&sdata->local->hw,
-                            &sdata->u.mgd.request_smps_work);
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               if (sdata->u.mgd.driver_smps_mode == smps_mode)
+                       return;
+               sdata->u.mgd.driver_smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.mgd.request_smps_work);
+       } else {
+               /* AUTOMATIC is meaningless in AP mode */
+               if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
+                       return;
+               if (sdata->u.ap.driver_smps_mode == smps_mode)
+                       return;
+               sdata->u.ap.driver_smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.ap.request_smps_work);
+       }
 }
 /* this might change ... don't want non-open drivers using it */
 EXPORT_SYMBOL_GPL(ieee80211_request_smps);
index a12afe7..531be04 100644 (file)
@@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                           const int beacon_int, const u32 basic_rates,
                           const u16 capability, u64 tsf,
                           struct cfg80211_chan_def *chandef,
-                          bool *have_higher_than_11mbit)
+                          bool *have_higher_than_11mbit,
+                          struct cfg80211_csa_settings *csa_settings)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
@@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                    2 + 8 /* max Supported Rates */ +
                    3 /* max DS params */ +
                    4 /* IBSS params */ +
+                   5 /* Channel Switch Announcement */ +
                    2 + (IEEE80211_MAX_SUPP_RATES - 8) +
                    2 + sizeof(struct ieee80211_ht_cap) +
                    2 + sizeof(struct ieee80211_ht_operation) +
@@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
        *pos++ = 0;
        *pos++ = 0;
 
+       if (csa_settings) {
+               *pos++ = WLAN_EID_CHANNEL_SWITCH;
+               *pos++ = 3;
+               *pos++ = csa_settings->block_tx ? 1 : 0;
+               *pos++ = ieee80211_frequency_to_channel(
+                               csa_settings->chandef.chan->center_freq);
+               sdata->csa_counter_offset_beacon = (pos - presp->head);
+               *pos++ = csa_settings->count;
+       }
+
        /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
        if (rates_n > 8) {
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
@@ -217,6 +229,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct beacon_data *presp;
        enum nl80211_bss_scan_width scan_width;
        bool have_higher_than_11mbit;
+       bool radar_required = false;
+       int err;
 
        sdata_assert_lock(sdata);
 
@@ -235,6 +249,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                ieee80211_bss_info_change_notify(sdata,
                                                 BSS_CHANGED_IBSS |
                                                 BSS_CHANGED_BEACON_ENABLED);
+               drv_leave_ibss(local, sdata);
        }
 
        presp = rcu_dereference_protected(ifibss->presp,
@@ -259,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                }
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
+               /* check again for downgraded chandef */
+               if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, beacons forbidden\n");
+                       return;
+               }
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &chandef);
+       if (err > 0) {
+               if (!ifibss->userspace_handles_dfs) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, DFS channel without control program\n");
+                       return;
+               }
+               radar_required = true;
        }
 
        ieee80211_vif_release_channel(sdata);
@@ -276,13 +308,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
                                           capability, tsf, &chandef,
-                                          &have_higher_than_11mbit);
+                                          &have_higher_than_11mbit, NULL);
        if (!presp)
                return;
 
        rcu_assign_pointer(ifibss->presp, presp);
        mgmt = (void *)presp->head;
 
+       sdata->radar_required = radar_required;
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
@@ -317,11 +350,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        else
                sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
 
+       ieee80211_set_wmm_default(sdata, true);
+
        sdata->vif.bss_conf.ibss_joined = true;
        sdata->vif.bss_conf.ibss_creator = creator;
-       ieee80211_bss_info_change_notify(sdata, bss_change);
 
-       ieee80211_set_wmm_default(sdata, true);
+       err = drv_join_ibss(local, sdata);
+       if (err) {
+               sdata->vif.bss_conf.ibss_joined = false;
+               sdata->vif.bss_conf.ibss_creator = false;
+               sdata->vif.bss_conf.enable_beacon = false;
+               sdata->vif.bss_conf.ssid_len = 0;
+               RCU_INIT_POINTER(ifibss->presp, NULL);
+               kfree_rcu(presp, rcu_head);
+               ieee80211_vif_release_channel(sdata);
+               sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
+                          err);
+               return;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, bss_change);
 
        ifibss->state = IEEE80211_IBSS_MLME_JOINED;
        mod_timer(&ifibss->timer,
@@ -416,6 +464,115 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                  tsf, false);
 }
 
+int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       struct beacon_data *presp, *old_presp;
+       struct cfg80211_bss *cbss;
+       const struct cfg80211_bss_ies *ies;
+       u16 capability;
+       u64 tsf;
+       int ret = 0;
+
+       sdata_assert_lock(sdata);
+
+       capability = WLAN_CAPABILITY_IBSS;
+
+       if (ifibss->privacy)
+               capability |= WLAN_CAPABILITY_PRIVACY;
+
+       cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
+                               ifibss->bssid, ifibss->ssid,
+                               ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
+                               WLAN_CAPABILITY_PRIVACY,
+                               capability);
+
+       if (WARN_ON(!cbss)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       rcu_read_lock();
+       ies = rcu_dereference(cbss->ies);
+       tsf = ies->tsf;
+       rcu_read_unlock();
+       cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
+
+       old_presp = rcu_dereference_protected(ifibss->presp,
+                                         lockdep_is_held(&sdata->wdev.mtx));
+
+       presp = ieee80211_ibss_build_presp(sdata,
+                                          sdata->vif.bss_conf.beacon_int,
+                                          sdata->vif.bss_conf.basic_rates,
+                                          capability, tsf, &ifibss->chandef,
+                                          NULL, csa_settings);
+       if (!presp) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       rcu_assign_pointer(ifibss->presp, presp);
+       if (old_presp)
+               kfree_rcu(old_presp, rcu_head);
+
+       /* it might not send the beacon for a while. send an action frame
+        * immediately to announce the channel switch.
+        */
+       if (csa_settings)
+               ieee80211_send_action_csa(sdata, csa_settings);
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+ out:
+       return ret;
+}
+
+int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       struct cfg80211_bss *cbss;
+       int err;
+       u16 capability;
+
+       sdata_lock(sdata);
+       /* update cfg80211 bss information with the new channel */
+       if (!is_zero_ether_addr(ifibss->bssid)) {
+               capability = WLAN_CAPABILITY_IBSS;
+
+               if (ifibss->privacy)
+                       capability |= WLAN_CAPABILITY_PRIVACY;
+
+               cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
+                                       ifibss->chandef.chan,
+                                       ifibss->bssid, ifibss->ssid,
+                                       ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
+                                       WLAN_CAPABILITY_PRIVACY,
+                                       capability);
+               /* XXX: should not really modify cfg80211 data */
+               if (cbss) {
+                       cbss->channel = sdata->local->csa_chandef.chan;
+                       cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
+               }
+       }
+
+       ifibss->chandef = sdata->local->csa_chandef;
+
+       /* generate the beacon */
+       err = ieee80211_ibss_csa_beacon(sdata, NULL);
+       sdata_unlock(sdata);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+       cancel_work_sync(&ifibss->csa_connection_drop_work);
+}
+
 static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
        __acquires(RCU)
 {
@@ -499,6 +656,315 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        return ieee80211_ibss_finish_sta(sta);
 }
 
+static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       int active = 0;
+       struct sta_info *sta;
+
+       sdata_assert_lock(sdata);
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (sta->sdata == sdata &&
+                   time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
+                              jiffies)) {
+                       active++;
+                       break;
+               }
+       }
+
+       rcu_read_unlock();
+
+       return active;
+}
+
+static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       struct ieee80211_local *local = sdata->local;
+       struct cfg80211_bss *cbss;
+       struct beacon_data *presp;
+       struct sta_info *sta;
+       int active_ibss;
+       u16 capability;
+
+       active_ibss = ieee80211_sta_active_ibss(sdata);
+
+       if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
+               capability = WLAN_CAPABILITY_IBSS;
+
+               if (ifibss->privacy)
+                       capability |= WLAN_CAPABILITY_PRIVACY;
+
+               cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
+                                       ifibss->bssid, ifibss->ssid,
+                                       ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
+                                       WLAN_CAPABILITY_PRIVACY,
+                                       capability);
+
+               if (cbss) {
+                       cfg80211_unlink_bss(local->hw.wiphy, cbss);
+                       cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
+               }
+       }
+
+       ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+
+       sta_info_flush(sdata);
+
+       spin_lock_bh(&ifibss->incomplete_lock);
+       while (!list_empty(&ifibss->incomplete_stations)) {
+               sta = list_first_entry(&ifibss->incomplete_stations,
+                                      struct sta_info, list);
+               list_del(&sta->list);
+               spin_unlock_bh(&ifibss->incomplete_lock);
+
+               sta_info_free(local, sta);
+               spin_lock_bh(&ifibss->incomplete_lock);
+       }
+       spin_unlock_bh(&ifibss->incomplete_lock);
+
+       netif_carrier_off(sdata->dev);
+
+       sdata->vif.bss_conf.ibss_joined = false;
+       sdata->vif.bss_conf.ibss_creator = false;
+       sdata->vif.bss_conf.enable_beacon = false;
+       sdata->vif.bss_conf.ssid_len = 0;
+
+       /* remove beacon */
+       presp = rcu_dereference_protected(ifibss->presp,
+                                         lockdep_is_held(&sdata->wdev.mtx));
+       RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
+       if (presp)
+               kfree_rcu(presp, rcu_head);
+
+       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
+                                               BSS_CHANGED_IBSS);
+       drv_leave_ibss(local, sdata);
+       ieee80211_vif_release_channel(sdata);
+}
+
+static void ieee80211_csa_connection_drop_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.ibss.csa_connection_drop_work);
+
+       ieee80211_ibss_disconnect(sdata);
+       synchronize_rcu();
+       skb_queue_purge(&sdata->skb_queue);
+
+       /* trigger a scan to find another IBSS network to join */
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+}
+
+static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       int err;
+
+       /* if the current channel is a DFS channel, mark the channel as
+        * unavailable.
+        */
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &ifibss->chandef);
+       if (err > 0)
+               cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
+                                    GFP_ATOMIC);
+}
+
+static bool
+ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+                                 struct ieee802_11_elems *elems,
+                                 bool beacon)
+{
+       struct cfg80211_csa_settings params;
+       struct ieee80211_csa_ie csa_ie;
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
+       enum nl80211_channel_type ch_type;
+       int err, num_chanctx;
+       u32 sta_flags;
+
+       if (sdata->vif.csa_active)
+               return true;
+
+       if (!sdata->vif.bss_conf.ibss_joined)
+               return false;
+
+       sta_flags = IEEE80211_STA_DISABLE_VHT;
+       switch (ifibss->chandef.width) {
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               sta_flags |= IEEE80211_STA_DISABLE_HT;
+               /* fall through */
+       case NL80211_CHAN_WIDTH_20:
+               sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
+               break;
+       default:
+               break;
+       }
+
+       memset(&params, 0, sizeof(params));
+       memset(&csa_ie, 0, sizeof(csa_ie));
+       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+                                          ifibss->chandef.chan->band,
+                                          sta_flags, ifibss->bssid, &csa_ie);
+       /* can't switch to destination channel, fail */
+       if (err < 0)
+               goto disconnect;
+
+       /* did not contain a CSA */
+       if (err)
+               return false;
+
+       params.count = csa_ie.count;
+       params.chandef = csa_ie.chandef;
+
+       if (ifibss->chandef.chan->band != params.chandef.chan->band)
+               goto disconnect;
+
+       switch (ifibss->chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_40:
+               /* keep our current HT mode (HT20/HT40+/HT40-), even if
+                * another mode  has been announced. The mode is not adopted
+                * within the beacon while doing CSA and we should therefore
+                * keep the mode which we announce.
+                */
+               ch_type = cfg80211_get_chandef_type(&ifibss->chandef);
+               cfg80211_chandef_create(&params.chandef, params.chandef.chan,
+                                       ch_type);
+               break;
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
+               if (params.chandef.width != ifibss->chandef.width) {
+                       sdata_info(sdata,
+                                  "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+                                  ifibss->bssid,
+                                  params.chandef.chan->center_freq,
+                                  params.chandef.width,
+                                  params.chandef.center_freq1,
+                                  params.chandef.center_freq2);
+                       goto disconnect;
+               }
+               break;
+       default:
+               /* should not happen, sta_flags should prevent VHT modes. */
+               WARN_ON(1);
+               goto disconnect;
+       }
+
+       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
+               sdata_info(sdata,
+                          "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+                          ifibss->bssid,
+                          params.chandef.chan->center_freq,
+                          params.chandef.width,
+                          params.chandef.center_freq1,
+                          params.chandef.center_freq2);
+               goto disconnect;
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &params.chandef);
+       if (err < 0)
+               goto disconnect;
+       if (err) {
+               /* IBSS-DFS only allowed with a control program */
+               if (!ifibss->userspace_handles_dfs)
+                       goto disconnect;
+
+               params.radar_required = true;
+       }
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!chanctx_conf) {
+               rcu_read_unlock();
+               goto disconnect;
+       }
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       if (chanctx->refcount > 1) {
+               rcu_read_unlock();
+               goto disconnect;
+       }
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
+               num_chanctx++;
+
+       if (num_chanctx > 1) {
+               rcu_read_unlock();
+               goto disconnect;
+       }
+       rcu_read_unlock();
+
+       /* all checks done, now perform the channel switch. */
+       ibss_dbg(sdata,
+                "received channel switch announcement to go to channel %d MHz\n",
+                params.chandef.chan->center_freq);
+
+       params.block_tx = !!csa_ie.mode;
+
+       ieee80211_ibss_csa_beacon(sdata, &params);
+       sdata->csa_radar_required = params.radar_required;
+
+       if (params.block_tx)
+               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       sdata->local->csa_chandef = params.chandef;
+       sdata->vif.csa_active = true;
+
+       ieee80211_bss_info_change_notify(sdata, err);
+       drv_channel_switch_beacon(sdata, &params.chandef);
+
+       ieee80211_ibss_csa_mark_radar(sdata);
+
+       return true;
+disconnect:
+       ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
+       ieee80211_queue_work(&sdata->local->hw,
+                            &ifibss->csa_connection_drop_work);
+
+       ieee80211_ibss_csa_mark_radar(sdata);
+
+       return true;
+}
+
+static void
+ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               struct ieee80211_rx_status *rx_status,
+                               struct ieee802_11_elems *elems)
+{
+       int required_len;
+
+       if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+               return;
+
+       /* CSA is the only action we handle for now */
+       if (mgmt->u.action.u.measurement.action_code !=
+           WLAN_ACTION_SPCT_CHL_SWITCH)
+               return;
+
+       required_len = IEEE80211_MIN_ACTION_SIZE +
+                      sizeof(mgmt->u.action.u.chan_switch);
+       if (len < required_len)
+               return;
+
+       ieee80211_ibss_process_chanswitch(sdata, elems, false);
+}
+
 static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
                                          struct ieee80211_mgmt *mgmt,
                                          size_t len)
@@ -661,10 +1127,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 
        /* check if we need to merge IBSS */
 
-       /* we use a fixed BSSID */
-       if (sdata->u.ibss.fixed_bssid)
-               goto put_bss;
-
        /* not an IBSS */
        if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
                goto put_bss;
@@ -680,10 +1142,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                sdata->u.ibss.ssid_len))
                goto put_bss;
 
+       /* process channel switch */
+       if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
+               goto put_bss;
+
        /* same BSSID */
        if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
                goto put_bss;
 
+       /* we use a fixed BSSID */
+       if (sdata->u.ibss.fixed_bssid)
+               goto put_bss;
+
        if (ieee80211_have_rx_timestamp(rx_status)) {
                /* time when timestamp field was received */
                rx_timestamp =
@@ -775,30 +1245,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        ieee80211_queue_work(&local->hw, &sdata->work);
 }
 
-static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-       int active = 0;
-       struct sta_info *sta;
-
-       sdata_assert_lock(sdata);
-
-       rcu_read_lock();
-
-       list_for_each_entry_rcu(sta, &local->sta_list, list) {
-               if (sta->sdata == sdata &&
-                   time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
-                              jiffies)) {
-                       active++;
-                       break;
-               }
-       }
-
-       rcu_read_unlock();
-
-       return active;
-}
-
 static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -1076,6 +1522,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
        u16 fc;
+       struct ieee802_11_elems elems;
+       int ies_len;
 
        rx_status = IEEE80211_SKB_RXCB(skb);
        mgmt = (struct ieee80211_mgmt *) skb->data;
@@ -1101,6 +1549,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        case IEEE80211_STYPE_DEAUTH:
                ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
                break;
+       case IEEE80211_STYPE_ACTION:
+               switch (mgmt->u.action.category) {
+               case WLAN_CATEGORY_SPECTRUM_MGMT:
+                       ies_len = skb->len -
+                                 offsetof(struct ieee80211_mgmt,
+                                          u.action.u.chan_switch.variable);
+
+                       if (ies_len < 0)
+                               break;
+
+                       ieee802_11_parse_elems(
+                               mgmt->u.action.u.chan_switch.variable,
+                               ies_len, true, &elems);
+
+                       if (elems.parse_error)
+                               break;
+
+                       ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
+                                                       rx_status, &elems);
+                       break;
+               }
        }
 
  mgmt_out:
@@ -1167,6 +1636,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
                    (unsigned long) sdata);
        INIT_LIST_HEAD(&ifibss->incomplete_stations);
        spin_lock_init(&ifibss->incomplete_lock);
+       INIT_WORK(&ifibss->csa_connection_drop_work,
+                 ieee80211_csa_connection_drop_work);
 }
 
 /* scan finished notification */
@@ -1202,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 
        sdata->u.ibss.privacy = params->privacy;
        sdata->u.ibss.control_port = params->control_port;
+       sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
        sdata->u.ibss.basic_rates = params->basic_rates;
 
        /* fix basic_rates if channel does not support these rates */
@@ -1265,73 +1737,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-       struct ieee80211_local *local = sdata->local;
-       struct cfg80211_bss *cbss;
-       u16 capability;
-       int active_ibss;
-       struct sta_info *sta;
-       struct beacon_data *presp;
-
-       active_ibss = ieee80211_sta_active_ibss(sdata);
-
-       if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
-               capability = WLAN_CAPABILITY_IBSS;
 
-               if (ifibss->privacy)
-                       capability |= WLAN_CAPABILITY_PRIVACY;
-
-               cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
-                                       ifibss->bssid, ifibss->ssid,
-                                       ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
-                                       WLAN_CAPABILITY_PRIVACY,
-                                       capability);
-
-               if (cbss) {
-                       cfg80211_unlink_bss(local->hw.wiphy, cbss);
-                       cfg80211_put_bss(local->hw.wiphy, cbss);
-               }
-       }
-
-       ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
-       memset(ifibss->bssid, 0, ETH_ALEN);
+       ieee80211_ibss_disconnect(sdata);
        ifibss->ssid_len = 0;
-
-       sta_info_flush(sdata);
-
-       spin_lock_bh(&ifibss->incomplete_lock);
-       while (!list_empty(&ifibss->incomplete_stations)) {
-               sta = list_first_entry(&ifibss->incomplete_stations,
-                                      struct sta_info, list);
-               list_del(&sta->list);
-               spin_unlock_bh(&ifibss->incomplete_lock);
-
-               sta_info_free(local, sta);
-               spin_lock_bh(&ifibss->incomplete_lock);
-       }
-       spin_unlock_bh(&ifibss->incomplete_lock);
-
-       netif_carrier_off(sdata->dev);
+       memset(ifibss->bssid, 0, ETH_ALEN);
 
        /* remove beacon */
        kfree(sdata->u.ibss.ie);
-       presp = rcu_dereference_protected(ifibss->presp,
-                                         lockdep_is_held(&sdata->wdev.mtx));
-       RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
 
        /* on the next join, re-program HT parameters */
        memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
        memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
 
-       sdata->vif.bss_conf.ibss_joined = false;
-       sdata->vif.bss_conf.ibss_creator = false;
-       sdata->vif.bss_conf.enable_beacon = false;
-       sdata->vif.bss_conf.ssid_len = 0;
-       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
-       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
-                                               BSS_CHANGED_IBSS);
-       ieee80211_vif_release_channel(sdata);
        synchronize_rcu();
-       kfree(presp);
 
        skb_queue_purge(&sdata->skb_queue);
 
index 0764095..29dc505 100644 (file)
@@ -262,6 +262,10 @@ struct ieee80211_if_ap {
 
        struct ps_data ps;
        atomic_t num_mcast_sta; /* number of stations receiving multicast */
+       enum ieee80211_smps_mode req_smps, /* requested smps mode */
+                        driver_smps_mode; /* smps mode request */
+
+       struct work_struct request_smps_work;
 };
 
 struct ieee80211_if_wds {
@@ -322,7 +326,6 @@ struct ieee80211_roc_work {
 
 /* flags used in struct ieee80211_if_managed.flags */
 enum ieee80211_sta_flags {
-       IEEE80211_STA_BEACON_POLL       = BIT(0),
        IEEE80211_STA_CONNECTION_POLL   = BIT(1),
        IEEE80211_STA_CONTROL_PORT      = BIT(2),
        IEEE80211_STA_DISABLE_HT        = BIT(4),
@@ -488,6 +491,7 @@ struct ieee80211_if_managed {
 
 struct ieee80211_if_ibss {
        struct timer_list timer;
+       struct work_struct csa_connection_drop_work;
 
        unsigned long last_scan_completed;
 
@@ -498,6 +502,7 @@ struct ieee80211_if_ibss {
        bool privacy;
 
        bool control_port;
+       bool userspace_handles_dfs;
 
        u8 bssid[ETH_ALEN] __aligned(2);
        u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -539,6 +544,11 @@ struct ieee80211_mesh_sync_ops {
        /* add other framework functions here */
 };
 
+struct mesh_csa_settings {
+       struct rcu_head rcu_head;
+       struct cfg80211_csa_settings settings;
+};
+
 struct ieee80211_if_mesh {
        struct timer_list housekeeping_timer;
        struct timer_list mesh_path_timer;
@@ -599,6 +609,11 @@ struct ieee80211_if_mesh {
        int ps_peers_light_sleep;
        int ps_peers_deep_sleep;
        struct ps_data ps;
+       /* Channel Switching Support */
+       struct mesh_csa_settings __rcu *csa;
+       bool chsw_init;
+       u8 chsw_ttl;
+       u16 pre_value;
 };
 
 #ifdef CONFIG_MAC80211_MESH
@@ -1207,6 +1222,14 @@ struct ieee80211_ra_tid {
        u16 tid;
 };
 
+/* this struct holds the value parsing from channel switch IE  */
+struct ieee80211_csa_ie {
+       struct cfg80211_chan_def chandef;
+       u8 mode;
+       u8 count;
+       u8 ttl;
+};
+
 /* Parsed Information Elements */
 struct ieee802_11_elems {
        const u8 *ie_start;
@@ -1243,6 +1266,7 @@ struct ieee802_11_elems {
        const struct ieee80211_timeout_interval_ie *timeout_int;
        const u8 *opmode_notif;
        const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
 
        /* length of them, respectively */
        u8 ssid_len;
@@ -1334,11 +1358,19 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
 void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
+int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings);
+int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
 
 /* mesh code */
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action);
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
@@ -1435,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
                               enum ieee80211_smps_mode smps, const u8 *da,
                               const u8 *bssid);
-void ieee80211_request_smps_work(struct work_struct *work);
+void ieee80211_request_smps_ap_work(struct work_struct *work);
+void ieee80211_request_smps_mgd_work(struct work_struct *work);
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new);
 
 void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
@@ -1485,6 +1520,28 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
                                       struct ieee80211_mgmt *mgmt,
                                       size_t len);
+/**
+ * ieee80211_parse_ch_switch_ie - parses channel switch IEs
+ * @sdata: the sdata of the interface which has received the frame
+ * @elems: parsed 802.11 elements received with the frame
+ * @beacon: indicates if the frame was a beacon or probe response
+ * @current_band: indicates the current band
+ * @sta_flags: contains information about own capabilities and restrictions
+ *     to decide which channel switch announcements can be accepted. Only the
+ *     following subset of &enum ieee80211_sta_flags are evaluated:
+ *     %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
+ *     %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
+ *     %IEEE80211_STA_DISABLE_160MHZ.
+ * @bssid: the currently connected bssid (for reporting)
+ * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
+       All of them will be filled with if success only.
+ * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
+ */
+int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
+                                struct ieee802_11_elems *elems, bool beacon,
+                                enum ieee80211_band current_band,
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie);
 
 /* Suspend/resume and hw reconfiguration */
 int ieee80211_reconfig(struct ieee80211_local *local);
@@ -1630,8 +1687,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
 u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode);
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode);
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 
 size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
@@ -1658,6 +1717,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
                                  const struct ieee80211_ht_operation *ht_oper,
                                  struct cfg80211_chan_def *chandef);
+u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
 
 int __must_check
 ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
@@ -1686,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data);
 void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 void ieee80211_dfs_radar_detected_work(struct work_struct *work);
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index fcecd63..ff101ea 100644 (file)
@@ -766,6 +766,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.type == NL80211_IFTYPE_STATION)
                ieee80211_mgd_stop(sdata);
 
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               ieee80211_ibss_stop(sdata);
+
+
        /*
         * Remove all stations associated with this interface.
         *
@@ -1289,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        case NL80211_IFTYPE_AP:
                skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
                INIT_LIST_HEAD(&sdata->u.ap.vlans);
+               INIT_WORK(&sdata->u.ap.request_smps_work,
+                         ieee80211_request_smps_ap_work);
                sdata->vif.bss_conf.bssid = sdata->vif.addr;
+               sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                type = NL80211_IFTYPE_STATION;
index 620677e..3e51dd7 100644 (file)
@@ -879,7 +879,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
                                  keyconf->keylen, keyconf->key,
                                  0, NULL);
        if (IS_ERR(key))
-               return ERR_PTR(PTR_ERR(key));
+               return ERR_CAST(key);
 
        if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
                key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
index 036d57e..aaae0ed 100644 (file)
@@ -83,7 +83,7 @@ struct ieee80211_key {
                         * Management frames.
                         */
                        u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
-                       struct crypto_cipher *tfm;
+                       struct crypto_aead *tfm;
                        u32 replays; /* dot11RSNAStatsCCMPReplays */
                } ccmp;
                struct {
index 707ac61..896fe3b 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/unaligned.h>
 #include "ieee80211_i.h"
 #include "mesh.h"
+#include "driver-ops.h"
 
 static int mesh_allocated;
 static struct kmem_cache *rm_cache;
@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct mesh_csa_settings *csa;
        enum ieee80211_band band;
        u8 *pos;
        struct ieee80211_sub_if_data *sdata;
@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 
        head_len = hdr_len +
                   2 + /* NULL SSID */
+                  /* Channel Switch Announcement */
+                  2 + sizeof(struct ieee80211_channel_sw_ie) +
+                  /* Mesh Channel Swith Parameters */
+                  2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
                   2 + 8 + /* supported rates */
                   2 + 3; /* DS params */
        tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        *pos++ = WLAN_EID_SSID;
        *pos++ = 0x0;
 
+       rcu_read_lock();
+       csa = rcu_dereference(ifmsh->csa);
+       if (csa) {
+               __le16 pre_value;
+
+               pos = skb_put(skb, 13);
+               memset(pos, 0, 13);
+               *pos++ = WLAN_EID_CHANNEL_SWITCH;
+               *pos++ = 3;
+               *pos++ = 0x0;
+               *pos++ = ieee80211_frequency_to_channel(
+                               csa->settings.chandef.chan->center_freq);
+               sdata->csa_counter_offset_beacon = hdr_len + 6;
+               *pos++ = csa->settings.count;
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
+               *pos++ = 6;
+               if (ifmsh->chsw_init) {
+                       *pos++ = ifmsh->mshcfg.dot11MeshTTL;
+                       *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               } else {
+                       *pos++ = ifmsh->chsw_ttl;
+               }
+               *pos++ |= csa->settings.block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
+               pos += 2;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);
+               pos += 2;
+       }
+       rcu_read_unlock();
+
        if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
            mesh_add_ds_params_ie(sdata, skb))
                goto out_free;
@@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_configure_filter(local);
 }
 
+static bool
+ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
+                                struct ieee802_11_elems *elems, bool beacon)
+{
+       struct cfg80211_csa_settings params;
+       struct ieee80211_csa_ie csa_ie;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       int err, num_chanctx;
+       u32 sta_flags;
+
+       if (sdata->vif.csa_active)
+               return true;
+
+       if (!ifmsh->mesh_id)
+               return false;
+
+       sta_flags = IEEE80211_STA_DISABLE_VHT;
+       switch (sdata->vif.bss_conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               sta_flags |= IEEE80211_STA_DISABLE_HT;
+       case NL80211_CHAN_WIDTH_20:
+               sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
+               break;
+       default:
+               break;
+       }
+
+       memset(&params, 0, sizeof(params));
+       memset(&csa_ie, 0, sizeof(csa_ie));
+       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+                                          sta_flags, sdata->vif.addr,
+                                          &csa_ie);
+       if (err < 0)
+               return false;
+       if (err)
+               return false;
+
+       params.chandef = csa_ie.chandef;
+       params.count = csa_ie.count;
+
+       if (sdata->vif.bss_conf.chandef.chan->band !=
+           params.chandef.chan->band)
+               return false;
+
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
+                                    IEEE80211_CHAN_DISABLED)) {
+               sdata_info(sdata,
+                          "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
+                          sdata->vif.addr,
+                          params.chandef.chan->center_freq,
+                          params.chandef.width,
+                          params.chandef.center_freq1,
+                          params.chandef.center_freq2);
+               return false;
+       }
+
+       err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
+                                           &params.chandef);
+       if (err < 0)
+               return false;
+       if (err) {
+               params.radar_required = true;
+               /* TODO: DFS not (yet) supported */
+               return false;
+       }
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!chanctx_conf)
+               goto failed_chswitch;
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       if (chanctx->refcount > 1)
+               goto failed_chswitch;
+
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
+               num_chanctx++;
+
+       if (num_chanctx > 1)
+               goto failed_chswitch;
+
+       rcu_read_unlock();
+
+       mcsa_dbg(sdata,
+                "received channel switch announcement to go to channel %d MHz\n",
+                params.chandef.chan->center_freq);
+
+       params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
+       if (beacon)
+               ifmsh->chsw_ttl = csa_ie.ttl - 1;
+       else
+               ifmsh->chsw_ttl = 0;
+
+       if (ifmsh->chsw_ttl > 0)
+               if (ieee80211_mesh_csa_beacon(sdata, &params, false) < 0)
+                       return false;
+
+       sdata->csa_radar_required = params.radar_required;
+
+       if (params.block_tx)
+               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       sdata->local->csa_chandef = params.chandef;
+       sdata->vif.csa_active = true;
+
+       ieee80211_bss_info_change_notify(sdata, err);
+       drv_channel_switch_beacon(sdata, &params.chandef);
+
+       return true;
+failed_chswitch:
+       rcu_read_unlock();
+       return false;
+}
+
 static void
 ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
                            struct ieee80211_mgmt *mgmt, size_t len)
@@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
        if (ifmsh->sync_ops)
                ifmsh->sync_ops->rx_bcn_presp(sdata,
                        stype, mgmt, &elems, rx_status);
+
+       if (!ifmsh->chsw_init)
+               ieee80211_mesh_process_chnswitch(sdata, &elems, true);
+}
+
+int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct mesh_csa_settings *tmp_csa_settings;
+       int ret = 0;
+
+       /* Reset the TTL value and Initiator flag */
+       ifmsh->chsw_init = false;
+       ifmsh->chsw_ttl = 0;
+
+       /* Remove the CSA and MCSP elements from the beacon */
+       tmp_csa_settings = rcu_dereference(ifmsh->csa);
+       rcu_assign_pointer(ifmsh->csa, NULL);
+       kfree_rcu(tmp_csa_settings, rcu_head);
+       ret = ieee80211_mesh_rebuild_beacon(sdata);
+       if (ret)
+               return -EINVAL;
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+
+       mcsa_dbg(sdata, "complete switching to center freq %d MHz",
+                sdata->vif.bss_conf.chandef.chan->center_freq);
+       return 0;
+}
+
+int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct mesh_csa_settings *tmp_csa_settings;
+       int ret = 0;
+
+       tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
+                                  GFP_ATOMIC);
+       if (!tmp_csa_settings)
+               return -ENOMEM;
+
+       memcpy(&tmp_csa_settings->settings, csa_settings,
+              sizeof(struct cfg80211_csa_settings));
+
+       rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
+
+       ret = ieee80211_mesh_rebuild_beacon(sdata);
+       if (ret) {
+               tmp_csa_settings = rcu_dereference(ifmsh->csa);
+               rcu_assign_pointer(ifmsh->csa, NULL);
+               kfree_rcu(tmp_csa_settings, rcu_head);
+               return ret;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+
+       if (csa_action)
+               ieee80211_send_action_csa(sdata, csa_settings);
+
+       return 0;
+}
+
+static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
+                              struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_mgmt *mgmt_fwd;
+       struct sk_buff *skb;
+       struct ieee80211_local *local = sdata->local;
+       u8 *pos = mgmt->u.action.u.chan_switch.variable;
+       size_t offset_ttl;
+
+       skb = dev_alloc_skb(local->tx_headroom + len);
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, local->tx_headroom);
+       mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
+
+       /* offset_ttl is based on whether the secondary channel
+        * offset is available or not. Substract 1 from the mesh TTL
+        * and disable the initiator flag before forwarding.
+        */
+       offset_ttl = (len < 42) ? 7 : 10;
+       *(pos + offset_ttl) -= 1;
+       *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+       sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
+
+       memcpy(mgmt_fwd, mgmt, len);
+       eth_broadcast_addr(mgmt_fwd->da);
+       memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
+
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+}
+
+static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
+                             struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       struct ieee802_11_elems elems;
+       u16 pre_value;
+       bool fwd_csa = true;
+       size_t baselen;
+       u8 *pos, ttl;
+
+       if (mgmt->u.action.u.measurement.action_code !=
+           WLAN_ACTION_SPCT_CHL_SWITCH)
+               return;
+
+       pos = mgmt->u.action.u.chan_switch.variable;
+       baselen = offsetof(struct ieee80211_mgmt,
+                          u.action.u.chan_switch.variable);
+       ieee802_11_parse_elems(pos, len - baselen, false, &elems);
+
+       ttl = elems.mesh_chansw_params_ie->mesh_ttl;
+       if (!--ttl)
+               fwd_csa = false;
+
+       pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
+       if (ifmsh->pre_value >= pre_value)
+               return;
+
+       ifmsh->pre_value = pre_value;
+
+       if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
+               mcsa_dbg(sdata, "Failed to process CSA action frame");
+               return;
+       }
+
+       /* forward or re-broadcast the CSA frame */
+       if (fwd_csa) {
+               if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
+                       mcsa_dbg(sdata, "Failed to forward the CSA frame");
+       }
 }
 
 static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
@@ -939,6 +1234,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
                if (mesh_action_is_path_sel(mgmt))
                        mesh_rx_path_sel_frame(sdata, mgmt, len);
                break;
+       case WLAN_CATEGORY_SPECTRUM_MGMT:
+               mesh_rx_csa_frame(sdata, mgmt, len);
+               break;
        }
 }
 
@@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
                    (unsigned long) sdata);
 
        ifmsh->accepting_plinks = true;
-       ifmsh->preq_id = 0;
-       ifmsh->sn = 0;
-       ifmsh->num_gates = 0;
        atomic_set(&ifmsh->mpaths, 0);
        mesh_rmc_init(sdata);
        ifmsh->last_preq = jiffies;
        ifmsh->next_perr = jiffies;
+       ifmsh->chsw_init = false;
        /* Allocate all mesh structures when creating the first mesh interface. */
        if (!mesh_allocated)
                ieee80211s_init();
index 6b65d50..4301aa5 100644 (file)
@@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
        mesh_path_flush_by_nexthop(sta);
 
        ieee80211_mps_sta_status_update(sta);
-       changed |= ieee80211_mps_local_status_update(sdata);
+       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                       NL80211_MESH_POWER_UNKNOWN);
 
        return changed;
 }
index 22290a9..0f79b78 100644 (file)
@@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+       if (sta->local_pm == pm)
+               return 0;
+
        mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
                pm, sta->sta.addr);
 
@@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
 
        do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
 
+       /* clear the MPSP flags for non-peers or active STA */
+       if (sta->plink_state != NL80211_PLINK_ESTAB) {
+               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+               clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+       } else if (!do_buffer) {
+               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+       }
+
        /* Don't let the same PS state be set twice */
        if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
                return;
@@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
        } else {
                ieee80211_sta_ps_deliver_wakeup(sta);
        }
-
-       /* clear the MPSP flags for non-peers or active STA */
-       if (sta->plink_state != NL80211_PLINK_ESTAB) {
-               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
-               clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
-       } else if (!do_buffer) {
-               clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
-       }
 }
 
 static void mps_set_sta_peer_pm(struct sta_info *sta,
@@ -444,8 +447,7 @@ static void mpsp_qos_null_append(struct sta_info *sta,
  */
 static void mps_frame_deliver(struct sta_info *sta, int n_frames)
 {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local = sta->sdata->local;
        int ac;
        struct sk_buff_head frames;
        struct sk_buff *skb;
@@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
 }
 
 /**
- * ieee80211_mps_frame_release - release buffered frames in response to beacon
+ * ieee80211_mps_frame_release - release frames buffered due to mesh power save
  *
  * @sta: mesh STA
- * @elems: beacon IEs
+ * @elems: IEs of beacon or probe response
  *
  * For peers if we have individually-addressed frames buffered or the peer
  * indicates buffered frames, send a corresponding MPSP trigger frame. Since
@@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
            (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
                return;
 
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-               buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
-                               skb_queue_len(&sta->tx_filtered[ac]);
+       if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
+                                       skb_queue_len(&sta->tx_filtered[ac]);
 
        if (!has_buffered && !buffer_local)
                return;
index 54ebc81..d7504ab 100644 (file)
@@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
        return (1 << ecw) - 1;
 }
 
-static u32 chandef_downgrade(struct cfg80211_chan_def *c)
-{
-       u32 ret;
-       int tmp;
-
-       switch (c->width) {
-       case NL80211_CHAN_WIDTH_20:
-               c->width = NL80211_CHAN_WIDTH_20_NOHT;
-               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
-               break;
-       case NL80211_CHAN_WIDTH_40:
-               c->width = NL80211_CHAN_WIDTH_20;
-               c->center_freq1 = c->chan->center_freq;
-               ret = IEEE80211_STA_DISABLE_40MHZ |
-                     IEEE80211_STA_DISABLE_VHT;
-               break;
-       case NL80211_CHAN_WIDTH_80:
-               tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
-               /* n_P40 */
-               tmp /= 2;
-               /* freq_P40 */
-               c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
-               c->width = NL80211_CHAN_WIDTH_40;
-               ret = IEEE80211_STA_DISABLE_VHT;
-               break;
-       case NL80211_CHAN_WIDTH_80P80:
-               c->center_freq2 = 0;
-               c->width = NL80211_CHAN_WIDTH_80;
-               ret = IEEE80211_STA_DISABLE_80P80MHZ |
-                     IEEE80211_STA_DISABLE_160MHZ;
-               break;
-       case NL80211_CHAN_WIDTH_160:
-               /* n_P20 */
-               tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
-               /* n_P80 */
-               tmp /= 4;
-               c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
-               c->width = NL80211_CHAN_WIDTH_80;
-               ret = IEEE80211_STA_DISABLE_80P80MHZ |
-                     IEEE80211_STA_DISABLE_160MHZ;
-               break;
-       default:
-       case NL80211_CHAN_WIDTH_20_NOHT:
-               WARN_ON_ONCE(1);
-               c->width = NL80211_CHAN_WIDTH_20_NOHT;
-               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
-               break;
-       case NL80211_CHAN_WIDTH_5:
-       case NL80211_CHAN_WIDTH_10:
-               WARN_ON_ONCE(1);
-               /* keep c->width */
-               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
-               break;
-       }
-
-       WARN_ON_ONCE(!cfg80211_chandef_valid(c));
-
-       return ret;
-}
-
 static u32
 ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_supported_band *sband,
@@ -352,7 +292,7 @@ out:
                        break;
                }
 
-               ret |= chandef_downgrade(chandef);
+               ret |= ieee80211_chandef_downgrade(chandef);
        }
 
        if (chandef->width != vht_chandef.width && !tracking)
@@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
         */
        if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
            chandef.width == NL80211_CHAN_WIDTH_80P80)
-               flags |= chandef_downgrade(&chandef);
+               flags |= ieee80211_chandef_downgrade(&chandef);
        if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
            chandef.width == NL80211_CHAN_WIDTH_160)
-               flags |= chandef_downgrade(&chandef);
+               flags |= ieee80211_chandef_downgrade(&chandef);
        if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
            chandef.width > NL80211_CHAN_WIDTH_20)
-               flags |= chandef_downgrade(&chandef);
+               flags |= ieee80211_chandef_downgrade(&chandef);
 
        if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
                return 0;
@@ -893,8 +833,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
        if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-       if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-                           IEEE80211_STA_CONNECTION_POLL))
+       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
 
        ieee80211_tx_skb(sdata, skb);
@@ -937,6 +876,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       u32 changed = 0;
+       int ret;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -945,24 +886,39 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        if (!ifmgd->associated)
                goto out;
 
-       local->_oper_chandef = local->csa_chandef;
+       ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
+                                          &changed);
+       if (ret) {
+               sdata_info(sdata,
+                          "vif channel switch failed, disconnecting\n");
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               goto out;
+       }
 
-       if (!local->ops->channel_switch) {
-               /* call "hw_config" only if doing sw channel switch */
-               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
-       } else {
-               /* update the device channel directly */
-               local->hw.conf.chandef = local->_oper_chandef;
+       if (!local->use_chanctx) {
+               local->_oper_chandef = local->csa_chandef;
+               /* Call "hw_config" only if doing sw channel switch.
+                * Otherwise update the channel directly
+                */
+               if (!local->ops->channel_switch)
+                       ieee80211_hw_config(local, 0);
+               else
+                       local->hw.conf.chandef = local->_oper_chandef;
        }
 
        /* XXX: shouldn't really modify cfg80211-owned data! */
-       ifmgd->associated->channel = local->_oper_chandef.chan;
+       ifmgd->associated->channel = local->csa_chandef.chan;
 
        /* XXX: wait for a beacon first? */
        ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       ieee80211_bss_info_change_notify(sdata, changed);
+
  out:
+       sdata->vif.csa_active = false;
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
        sdata_unlock(sdata);
 }
@@ -1000,20 +956,10 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct cfg80211_bss *cbss = ifmgd->associated;
-       struct ieee80211_bss *bss;
        struct ieee80211_chanctx *chanctx;
-       enum ieee80211_band new_band;
-       int new_freq;
-       u8 new_chan_no;
-       u8 count;
-       u8 mode;
-       struct ieee80211_channel *new_chan;
-       struct cfg80211_chan_def new_chandef = {};
-       struct cfg80211_chan_def new_vht_chandef = {};
-       const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
-       const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
-       const struct ieee80211_ht_operation *ht_oper;
-       int secondary_channel_offset = -1;
+       enum ieee80211_band current_band;
+       struct ieee80211_csa_ie csa_ie;
+       int res;
 
        sdata_assert_lock(sdata);
 
@@ -1027,181 +973,53 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
                return;
 
-       sec_chan_offs = elems->sec_chan_offs;
-       wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
-       ht_oper = elems->ht_operation;
-
-       if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
-                           IEEE80211_STA_DISABLE_40MHZ)) {
-               sec_chan_offs = NULL;
-               wide_bw_chansw_ie = NULL;
-               /* only used for bandwidth here */
-               ht_oper = NULL;
-       }
-
-       if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
-               wide_bw_chansw_ie = NULL;
-
-       if (elems->ext_chansw_ie) {
-               if (!ieee80211_operating_class_to_band(
-                               elems->ext_chansw_ie->new_operating_class,
-                               &new_band)) {
-                       sdata_info(sdata,
-                                  "cannot understand ECSA IE operating class %d, disconnecting\n",
-                                  elems->ext_chansw_ie->new_operating_class);
-                       ieee80211_queue_work(&local->hw,
-                                            &ifmgd->csa_connection_drop_work);
-               }
-               new_chan_no = elems->ext_chansw_ie->new_ch_num;
-               count = elems->ext_chansw_ie->count;
-               mode = elems->ext_chansw_ie->mode;
-       } else if (elems->ch_switch_ie) {
-               new_band = cbss->channel->band;
-               new_chan_no = elems->ch_switch_ie->new_ch_num;
-               count = elems->ch_switch_ie->count;
-               mode = elems->ch_switch_ie->mode;
-       } else {
-               /* nothing here we understand */
+       current_band = cbss->channel->band;
+       memset(&csa_ie, 0, sizeof(csa_ie));
+       res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+                                          ifmgd->flags,
+                                          ifmgd->associated->bssid, &csa_ie);
+       if (res < 0)
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+       if (res)
                return;
-       }
-
-       bss = (void *)cbss->priv;
 
-       new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
-       new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-       if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
+       if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
+                                    IEEE80211_CHAN_DISABLED)) {
                sdata_info(sdata,
-                          "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
-                          ifmgd->associated->bssid, new_freq);
+                          "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+                          ifmgd->associated->bssid,
+                          csa_ie.chandef.chan->center_freq,
+                          csa_ie.chandef.width, csa_ie.chandef.center_freq1,
+                          csa_ie.chandef.center_freq2);
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                return;
        }
 
-       if (!beacon && sec_chan_offs) {
-               secondary_channel_offset = sec_chan_offs->sec_chan_offs;
-       } else if (beacon && ht_oper) {
-               secondary_channel_offset =
-                       ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
-       } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
-               /*
-                * If it's not a beacon, HT is enabled and the IE not present,
-                * it's 20 MHz, 802.11-2012 8.5.2.6:
-                *      This element [the Secondary Channel Offset Element] is
-                *      present when switching to a 40 MHz channel. It may be
-                *      present when switching to a 20 MHz channel (in which
-                *      case the secondary channel offset is set to SCN).
-                */
-               secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
-       }
-
-       switch (secondary_channel_offset) {
-       default:
-               /* secondary_channel_offset was present but is invalid */
-       case IEEE80211_HT_PARAM_CHA_SEC_NONE:
-               cfg80211_chandef_create(&new_chandef, new_chan,
-                                       NL80211_CHAN_HT20);
-               break;
-       case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-               cfg80211_chandef_create(&new_chandef, new_chan,
-                                       NL80211_CHAN_HT40PLUS);
-               break;
-       case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-               cfg80211_chandef_create(&new_chandef, new_chan,
-                                       NL80211_CHAN_HT40MINUS);
-               break;
-       case -1:
-               cfg80211_chandef_create(&new_chandef, new_chan,
-                                       NL80211_CHAN_NO_HT);
-               /* keep width for 5/10 MHz channels */
-               switch (sdata->vif.bss_conf.chandef.width) {
-               case NL80211_CHAN_WIDTH_5:
-               case NL80211_CHAN_WIDTH_10:
-                       new_chandef.width = sdata->vif.bss_conf.chandef.width;
-                       break;
-               default:
-                       break;
-               }
-               break;
-       }
+       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+       sdata->vif.csa_active = true;
 
-       if (wide_bw_chansw_ie) {
-               new_vht_chandef.chan = new_chan;
-               new_vht_chandef.center_freq1 =
-                       ieee80211_channel_to_frequency(
-                               wide_bw_chansw_ie->new_center_freq_seg0,
-                               new_band);
+       mutex_lock(&local->chanctx_mtx);
+       if (local->use_chanctx) {
+               u32 num_chanctx = 0;
+               list_for_each_entry(chanctx, &local->chanctx_list, list)
+                      num_chanctx++;
 
-               switch (wide_bw_chansw_ie->new_channel_width) {
-               default:
-                       /* hmmm, ignore VHT and use HT if present */
-               case IEEE80211_VHT_CHANWIDTH_USE_HT:
-                       new_vht_chandef.chan = NULL;
-                       break;
-               case IEEE80211_VHT_CHANWIDTH_80MHZ:
-                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
-                       break;
-               case IEEE80211_VHT_CHANWIDTH_160MHZ:
-                       new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
-                       break;
-               case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-                       /* field is otherwise reserved */
-                       new_vht_chandef.center_freq2 =
-                               ieee80211_channel_to_frequency(
-                                       wide_bw_chansw_ie->new_center_freq_seg1,
-                                       new_band);
-                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
-                       break;
-               }
-               if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
-                   new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
-                       chandef_downgrade(&new_vht_chandef);
-               if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
-                   new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
-                       chandef_downgrade(&new_vht_chandef);
-               if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
-                   new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
-                       chandef_downgrade(&new_vht_chandef);
-       }
-
-       /* if VHT data is there validate & use it */
-       if (new_vht_chandef.chan) {
-               if (!cfg80211_chandef_compatible(&new_vht_chandef,
-                                                &new_chandef)) {
+               if (num_chanctx > 1 ||
+                   !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
                        sdata_info(sdata,
-                                  "AP %pM CSA has inconsistent channel data, disconnecting\n",
-                                  ifmgd->associated->bssid);
+                                  "not handling chan-switch with channel contexts\n");
                        ieee80211_queue_work(&local->hw,
                                             &ifmgd->csa_connection_drop_work);
+                       mutex_unlock(&local->chanctx_mtx);
                        return;
                }
-               new_chandef = new_vht_chandef;
        }
 
-       if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
-                                    IEEE80211_CHAN_DISABLED)) {
-               sdata_info(sdata,
-                          "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
-                          ifmgd->associated->bssid, new_freq,
-                          new_chandef.width, new_chandef.center_freq1,
-                          new_chandef.center_freq2);
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               return;
-       }
-
-       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
-
-       if (local->use_chanctx) {
-               sdata_info(sdata,
-                          "not handling channel switch with channel contexts\n");
+       if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
-               return;
-       }
-
-       mutex_lock(&local->chanctx_mtx);
-       if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
                mutex_unlock(&local->chanctx_mtx);
                return;
        }
@@ -1217,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       local->csa_chandef = new_chandef;
+       local->csa_chandef = csa_ie.chandef;
 
-       if (mode)
+       if (csa_ie.mode)
                ieee80211_stop_queues_by_reason(&local->hw,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1228,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                /* use driver's channel switch callback */
                struct ieee80211_channel_switch ch_switch = {
                        .timestamp = timestamp,
-                       .block_tx = mode,
-                       .chandef = new_chandef,
-                       .count = count,
+                       .block_tx = csa_ie.mode,
+                       .chandef = csa_ie.chandef,
+                       .count = csa_ie.count,
                };
 
                drv_channel_switch(local, &ch_switch);
@@ -1238,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        /* channel switch handled in software */
-       if (count <= 1)
+       if (csa_ie.count <= 1)
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
 }
 
 static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -1374,8 +1192,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
        if (!mgd->associated)
                return false;
 
-       if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
-                         IEEE80211_STA_CONNECTION_POLL))
+       if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
                return false;
 
        if (!mgd->have_beacon)
@@ -1691,8 +1508,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
 {
        lockdep_assert_held(&sdata->local->mtx);
 
-       sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-                               IEEE80211_STA_BEACON_POLL);
+       sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
        ieee80211_run_deferred_scan(sdata->local);
 }
 
@@ -1954,11 +1770,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
 
        mutex_lock(&local->mtx);
-       if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-                             IEEE80211_STA_CONNECTION_POLL))) {
-               mutex_unlock(&local->mtx);
-               return;
-       }
+       if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
+               goto out;
 
        __ieee80211_stop_poll(sdata);
 
@@ -2094,15 +1907,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
         * because otherwise we would reset the timer every time and
         * never check whether we received a probe response!
         */
-       if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-                           IEEE80211_STA_CONNECTION_POLL))
+       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
                already = true;
 
-       if (beacon)
-               ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
-       else
-               ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
-
        mutex_unlock(&sdata->local->mtx);
 
        if (already)
@@ -2174,6 +1981,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
                               true, frame_buf);
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+       sdata->vif.csa_active = false;
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -3061,17 +2869,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
+       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
                mlme_dbg_ratelimited(sdata,
                                     "cancelling AP probe due to a received beacon\n");
-               mutex_lock(&local->mtx);
-               ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
-               ieee80211_run_deferred_scan(local);
-               mutex_unlock(&local->mtx);
-
-               mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local, -1);
-               mutex_unlock(&local->iflist_mtx);
+               ieee80211_reset_ap_probe(sdata);
        }
 
        /*
@@ -3544,8 +3345,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
        } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
                run_again(sdata, ifmgd->assoc_data->timeout);
 
-       if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-                           IEEE80211_STA_CONNECTION_POLL) &&
+       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
            ifmgd->associated) {
                u8 bssid[ETH_ALEN];
                int max_tries;
@@ -3698,7 +3498,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
                  ieee80211_beacon_connection_loss_work);
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
-       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
+       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -3877,7 +3677,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                return ret;
 
        while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
-               ifmgd->flags |= chandef_downgrade(&chandef);
+               ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
                ret = ieee80211_vif_use_channel(sdata, &chandef,
                                                IEEE80211_CHANCTX_SHARED);
        }
index 5dedc56..505bc0d 100644 (file)
@@ -144,8 +144,8 @@ void rate_control_deinitialize(struct ieee80211_local *local);
 
 /* Rate control algorithms */
 #ifdef CONFIG_MAC80211_RC_PID
-extern int rc80211_pid_init(void);
-extern void rc80211_pid_exit(void);
+int rc80211_pid_init(void);
+void rc80211_pid_exit(void);
 #else
 static inline int rc80211_pid_init(void)
 {
@@ -157,8 +157,8 @@ static inline void rc80211_pid_exit(void)
 #endif
 
 #ifdef CONFIG_MAC80211_RC_MINSTREL
-extern int rc80211_minstrel_init(void);
-extern void rc80211_minstrel_exit(void);
+int rc80211_minstrel_init(void);
+void rc80211_minstrel_exit(void);
 #else
 static inline int rc80211_minstrel_init(void)
 {
@@ -170,8 +170,8 @@ static inline void rc80211_minstrel_exit(void)
 #endif
 
 #ifdef CONFIG_MAC80211_RC_MINSTREL_HT
-extern int rc80211_minstrel_ht_init(void);
-extern void rc80211_minstrel_ht_exit(void);
+int rc80211_minstrel_ht_init(void);
+void rc80211_minstrel_ht_exit(void);
 #else
 static inline int rc80211_minstrel_ht_init(void)
 {
index 8b5f7ef..7fa1b36 100644 (file)
@@ -203,6 +203,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
        memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
        mi->max_prob_rate = tmp_prob_rate;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       /* use fixed index if set */
+       if (mp->fixed_rate_idx != -1) {
+               mi->max_tp_rate[0] = mp->fixed_rate_idx;
+               mi->max_tp_rate[1] = mp->fixed_rate_idx;
+               mi->max_prob_rate = mp->fixed_rate_idx;
+       }
+#endif
+
        /* Reset update timer */
        mi->stats_update = jiffies;
 
@@ -310,6 +319,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
        /* increase sum packet counter */
        mi->packet_count++;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       if (mp->fixed_rate_idx != -1)
+               return;
+#endif
+
        delta = (mi->packet_count * sampling_ratio / 100) -
                        (mi->sample_count + mi->sample_deferred / 2);
 
index 7c323f2..5d60779 100644 (file)
@@ -365,6 +365,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
                }
        }
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       /* use fixed index if set */
+       if (mp->fixed_rate_idx != -1) {
+               mi->max_tp_rate = mp->fixed_rate_idx;
+               mi->max_tp_rate2 = mp->fixed_rate_idx;
+               mi->max_prob_rate = mp->fixed_rate_idx;
+       }
+#endif
 
        mi->stats_update = jiffies;
 }
@@ -774,6 +782,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
        info->flags |= mi->tx_flags;
        minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       if (mp->fixed_rate_idx != -1)
+               return;
+#endif
+
        /* Don't use EAPOL frames for sampling on non-mrr hw */
        if (mp->hw->max_rates == 1 &&
            (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
@@ -781,16 +794,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
        else
                sample_idx = minstrel_get_sample_rate(mp, mi);
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* use fixed index if set */
-       if (mp->fixed_rate_idx != -1) {
-               mi->max_tp_rate = mp->fixed_rate_idx;
-               mi->max_tp_rate2 = mp->fixed_rate_idx;
-               mi->max_prob_rate = mp->fixed_rate_idx;
-               sample_idx = -1;
-       }
-#endif
-
        mi->total_packets++;
 
        /* wraparound */
index c97a065..6ff1346 100644 (file)
@@ -167,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
         * provide large enough buffers. */
        length = length < RC_PID_PRINT_BUF_SIZE ?
                 length : RC_PID_PRINT_BUF_SIZE;
-       p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
+       p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
        switch (ev->type) {
        case RC_PID_EVENT_TYPE_TX_STATUS:
-               p += snprintf(pb + p, length - p, "tx_status %u %u",
-                             !(ev->data.flags & IEEE80211_TX_STAT_ACK),
-                             ev->data.tx_status.status.rates[0].idx);
+               p += scnprintf(pb + p, length - p, "tx_status %u %u",
+                              !(ev->data.flags & IEEE80211_TX_STAT_ACK),
+                              ev->data.tx_status.status.rates[0].idx);
                break;
        case RC_PID_EVENT_TYPE_RATE_CHANGE:
-               p += snprintf(pb + p, length - p, "rate_change %d %d",
-                             ev->data.index, ev->data.rate);
+               p += scnprintf(pb + p, length - p, "rate_change %d %d",
+                              ev->data.index, ev->data.rate);
                break;
        case RC_PID_EVENT_TYPE_TX_RATE:
-               p += snprintf(pb + p, length - p, "tx_rate %d %d",
-                             ev->data.index, ev->data.rate);
+               p += scnprintf(pb + p, length - p, "tx_rate %d %d",
+                              ev->data.index, ev->data.rate);
                break;
        case RC_PID_EVENT_TYPE_PF_SAMPLE:
-               p += snprintf(pb + p, length - p,
-                             "pf_sample %d %d %d %d",
-                             ev->data.pf_sample, ev->data.prop_err,
-                             ev->data.int_err, ev->data.der_err);
+               p += scnprintf(pb + p, length - p,
+                              "pf_sample %d %d %d %d",
+                              ev->data.pf_sample, ev->data.prop_err,
+                              ev->data.int_err, ev->data.der_err);
                break;
        }
-       p += snprintf(pb + p, length - p, "\n");
+       p += scnprintf(pb + p, length - p, "\n");
 
        spin_unlock_irqrestore(&events->lock, status);
 
index 674eac1..caecef8 100644 (file)
@@ -995,8 +995,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
                                rx->sta->num_duplicates++;
                        }
                        return RX_DROP_UNUSABLE;
-               } else
+               } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
                        rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
+               }
        }
 
        if (unlikely(rx->skb->len < 16)) {
@@ -2402,7 +2403,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                return RX_DROP_UNUSABLE;
 
        if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
-           mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
+           mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED &&
+           mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
                return RX_DROP_UNUSABLE;
 
        if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
@@ -2566,31 +2568,49 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 
                goto queue;
        case WLAN_CATEGORY_SPECTRUM_MGMT:
-               if (status->band != IEEE80211_BAND_5GHZ)
-                       break;
-
-               if (sdata->vif.type != NL80211_IFTYPE_STATION)
-                       break;
-
                /* verify action_code is present */
                if (len < IEEE80211_MIN_ACTION_SIZE + 1)
                        break;
 
                switch (mgmt->u.action.u.measurement.action_code) {
                case WLAN_ACTION_SPCT_MSR_REQ:
+                       if (status->band != IEEE80211_BAND_5GHZ)
+                               break;
+
                        if (len < (IEEE80211_MIN_ACTION_SIZE +
                                   sizeof(mgmt->u.action.u.measurement)))
                                break;
+
+                       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                               break;
+
                        ieee80211_process_measurement_req(sdata, mgmt, len);
                        goto handled;
-               case WLAN_ACTION_SPCT_CHL_SWITCH:
-                       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               case WLAN_ACTION_SPCT_CHL_SWITCH: {
+                       u8 *bssid;
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.chan_switch)))
+                               break;
+
+                       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+                           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                                break;
 
-                       if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid))
+                       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                               bssid = sdata->u.mgd.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                               bssid = sdata->u.ibss.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+                               bssid = mgmt->sa;
+                       else
+                               break;
+
+                       if (!ether_addr_equal(mgmt->bssid, bssid))
                                break;
 
                        goto queue;
+                       }
                }
                break;
        case WLAN_CATEGORY_SA_QUERY:
index d2d17a4..5ad66a8 100644 (file)
@@ -394,8 +394,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
                return false;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-           sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
-                                 IEEE80211_STA_CONNECTION_POLL))
+           sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
                return false;
 
        return true;
index 578eea3..a40da20 100644 (file)
 #include "sta_info.h"
 #include "wme.h"
 
+int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
+                                struct ieee802_11_elems *elems, bool beacon,
+                                enum ieee80211_band current_band,
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie)
+{
+       enum ieee80211_band new_band;
+       int new_freq;
+       u8 new_chan_no;
+       struct ieee80211_channel *new_chan;
+       struct cfg80211_chan_def new_vht_chandef = {};
+       const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
+       const struct ieee80211_ht_operation *ht_oper;
+       int secondary_channel_offset = -1;
+
+       sec_chan_offs = elems->sec_chan_offs;
+       wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
+       ht_oper = elems->ht_operation;
+
+       if (sta_flags & (IEEE80211_STA_DISABLE_HT |
+                        IEEE80211_STA_DISABLE_40MHZ)) {
+               sec_chan_offs = NULL;
+               wide_bw_chansw_ie = NULL;
+               /* only used for bandwidth here */
+               ht_oper = NULL;
+       }
+
+       if (sta_flags & IEEE80211_STA_DISABLE_VHT)
+               wide_bw_chansw_ie = NULL;
+
+       if (elems->ext_chansw_ie) {
+               if (!ieee80211_operating_class_to_band(
+                               elems->ext_chansw_ie->new_operating_class,
+                               &new_band)) {
+                       sdata_info(sdata,
+                                  "cannot understand ECSA IE operating class %d, disconnecting\n",
+                                  elems->ext_chansw_ie->new_operating_class);
+                       return -EINVAL;
+               }
+               new_chan_no = elems->ext_chansw_ie->new_ch_num;
+               csa_ie->count = elems->ext_chansw_ie->count;
+               csa_ie->mode = elems->ext_chansw_ie->mode;
+       } else if (elems->ch_switch_ie) {
+               new_band = current_band;
+               new_chan_no = elems->ch_switch_ie->new_ch_num;
+               csa_ie->count = elems->ch_switch_ie->count;
+               csa_ie->mode = elems->ch_switch_ie->mode;
+       } else {
+               /* nothing here we understand */
+               return 1;
+       }
+
+       /* Mesh Channel Switch Parameters Element */
+       if (elems->mesh_chansw_params_ie) {
+               csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
+               csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
+       }
+
+       new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
+       new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+       if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
+               sdata_info(sdata,
+                          "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
+                          bssid, new_freq);
+               return -EINVAL;
+       }
+
+       if (!beacon && sec_chan_offs) {
+               secondary_channel_offset = sec_chan_offs->sec_chan_offs;
+       } else if (beacon && ht_oper) {
+               secondary_channel_offset =
+                       ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
+       } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
+               /* If it's not a beacon, HT is enabled and the IE not present,
+                * it's 20 MHz, 802.11-2012 8.5.2.6:
+                *      This element [the Secondary Channel Offset Element] is
+                *      present when switching to a 40 MHz channel. It may be
+                *      present when switching to a 20 MHz channel (in which
+                *      case the secondary channel offset is set to SCN).
+                */
+               secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+       }
+
+       switch (secondary_channel_offset) {
+       default:
+               /* secondary_channel_offset was present but is invalid */
+       case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+                                       NL80211_CHAN_HT20);
+               break;
+       case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+                                       NL80211_CHAN_HT40PLUS);
+               break;
+       case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+                                       NL80211_CHAN_HT40MINUS);
+               break;
+       case -1:
+               cfg80211_chandef_create(&csa_ie->chandef, new_chan,
+                                       NL80211_CHAN_NO_HT);
+               /* keep width for 5/10 MHz channels */
+               switch (sdata->vif.bss_conf.chandef.width) {
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
+                       csa_ie->chandef.width =
+                               sdata->vif.bss_conf.chandef.width;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       }
+
+       if (wide_bw_chansw_ie) {
+               new_vht_chandef.chan = new_chan;
+               new_vht_chandef.center_freq1 =
+                       ieee80211_channel_to_frequency(
+                               wide_bw_chansw_ie->new_center_freq_seg0,
+                               new_band);
+
+               switch (wide_bw_chansw_ie->new_channel_width) {
+               default:
+                       /* hmmm, ignore VHT and use HT if present */
+               case IEEE80211_VHT_CHANWIDTH_USE_HT:
+                       new_vht_chandef.chan = NULL;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_80MHZ:
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_160MHZ:
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+                       /* field is otherwise reserved */
+                       new_vht_chandef.center_freq2 =
+                               ieee80211_channel_to_frequency(
+                                       wide_bw_chansw_ie->new_center_freq_seg1,
+                                       new_band);
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+                       break;
+               }
+               if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
+                   new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
+                       ieee80211_chandef_downgrade(&new_vht_chandef);
+               if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
+                   new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
+                       ieee80211_chandef_downgrade(&new_vht_chandef);
+               if (sta_flags & IEEE80211_STA_DISABLE_40MHZ &&
+                   new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
+                       ieee80211_chandef_downgrade(&new_vht_chandef);
+       }
+
+       /* if VHT data is there validate & use it */
+       if (new_vht_chandef.chan) {
+               if (!cfg80211_chandef_compatible(&new_vht_chandef,
+                                                &csa_ie->chandef)) {
+                       sdata_info(sdata,
+                                  "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
+                                  bssid);
+                       return -EINVAL;
+               }
+               csa_ie->chandef = new_vht_chandef;
+       }
+
+       return 0;
+}
+
 static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
                                        struct ieee80211_msrment_ie *request_ie,
                                        const u8 *da, const u8 *bssid,
index aeb967a..1eb66e2 100644 (file)
@@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 
        sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+       if (sdata->vif.type == NL80211_IFTYPE_AP ||
+           sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               struct ieee80211_supported_band *sband =
+                       local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+               u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
+                               IEEE80211_HT_CAP_SM_PS_SHIFT;
+               /*
+                * Assume that hostapd advertises our caps in the beacon and
+                * this is the known_smps_mode for a station that just assciated
+                */
+               switch (smps) {
+               case WLAN_HT_SMPS_CONTROL_DISABLED:
+                       sta->known_smps_mode = IEEE80211_SMPS_OFF;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_STATIC:
+                       sta->known_smps_mode = IEEE80211_SMPS_STATIC;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+                       sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       break;
+               default:
+                       WARN_ON(1);
+               }
+       }
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
@@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
 
+       /* This station just woke up and isn't aware of our SMPS state */
+       if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                          sdata->smps_mode) &&
+           sta->known_smps_mode != sdata->bss->req_smps &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sdata,
+                      "%pM just woke up and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sdata, sdata->bss->req_smps,
+                                          sta->sta.addr,
+                                          sdata->vif.bss_conf.bssid);
+       }
+
        local->total_ps_buffered -= buffered;
 
        sta_info_recalc_tim(sta);
@@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta,
 
        return 0;
 }
+
+u8 sta_info_tx_streams(struct sta_info *sta)
+{
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap;
+       u8 rx_streams;
+
+       if (!sta->sta.ht_cap.ht_supported)
+               return 1;
+
+       if (sta->sta.vht_cap.vht_supported) {
+               int i;
+               u16 tx_mcs_map =
+                       le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map);
+
+               for (i = 7; i >= 0; i--)
+                       if ((tx_mcs_map & (0x3 << (i * 2))) !=
+                           IEEE80211_VHT_MCS_NOT_SUPPORTED)
+                               return i + 1;
+       }
+
+       if (ht_cap->mcs.rx_mask[3])
+               rx_streams = 4;
+       else if (ht_cap->mcs.rx_mask[2])
+               rx_streams = 3;
+       else if (ht_cap->mcs.rx_mask[1])
+               rx_streams = 2;
+       else
+               rx_streams = 1;
+
+       if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF))
+               return rx_streams;
+
+       return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+                       >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+}
index 4208dbd..3ef06a2 100644 (file)
@@ -301,6 +301,8 @@ struct sta_ampdu_mlme {
  * @chains: chains ever used for RX from this station
  * @chain_signal_last: last signal (per chain)
  * @chain_signal_avg: signal average (per chain)
+ * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
+ *     AP only.
  */
 struct sta_info {
        /* General information, mostly static */
@@ -411,6 +413,8 @@ struct sta_info {
        unsigned int lost_packets;
        unsigned int beacon_loss_count;
 
+       enum ieee80211_smps_mode known_smps_mode;
+
        /* keep last! */
        struct ieee80211_sta sta;
 };
@@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta,
                          struct rate_info *rinfo);
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time);
+u8 sta_info_tx_streams(struct sta_info *sta);
 
 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
index 78dc2e9..52a152b 100644 (file)
@@ -194,29 +194,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
        if (ieee80211_is_action(mgmt->frame_control) &&
            mgmt->u.action.category == WLAN_CATEGORY_HT &&
            mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
-           sdata->vif.type == NL80211_IFTYPE_STATION &&
            ieee80211_sdata_running(sdata)) {
-               /*
-                * This update looks racy, but isn't -- if we come
-                * here we've definitely got a station that we're
-                * talking to, and on a managed interface that can
-                * only be the AP. And the only other place updating
-                * this variable in managed mode is before association.
-                */
+               enum ieee80211_smps_mode smps_mode;
+
                switch (mgmt->u.action.u.ht_smps.smps_control) {
                case WLAN_HT_SMPS_CONTROL_DYNAMIC:
-                       sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_STATIC:
-                       sdata->smps_mode = IEEE80211_SMPS_STATIC;
+                       smps_mode = IEEE80211_SMPS_STATIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_DISABLED:
                default: /* shouldn't happen since we don't send that */
-                       sdata->smps_mode = IEEE80211_SMPS_OFF;
+                       smps_mode = IEEE80211_SMPS_OFF;
                        break;
                }
 
-               ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       /*
+                        * This update looks racy, but isn't -- if we come
+                        * here we've definitely got a station that we're
+                        * talking to, and on a managed interface that can
+                        * only be the AP. And the only other place updating
+                        * this variable in managed mode is before association.
+                        */
+                       sdata->smps_mode = smps_mode;
+                       ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               } else if (sdata->vif.type == NL80211_IFTYPE_AP ||
+                          sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+                       sta->known_smps_mode = smps_mode;
+               }
        }
 }
 
index 1aba645..d4cee98 100644 (file)
@@ -77,13 +77,13 @@ DECLARE_EVENT_CLASS(local_sdata_addr_evt,
        TP_STRUCT__entry(
                LOCAL_ENTRY
                VIF_ENTRY
-               __array(char, addr, 6)
+               __array(char, addr, ETH_ALEN)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
                VIF_ASSIGN;
-               memcpy(__entry->addr, sdata->vif.addr, 6);
+               memcpy(__entry->addr, sdata->vif.addr, ETH_ALEN);
        ),
 
        TP_printk(
@@ -1475,6 +1475,41 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
 );
 #endif
 
+TRACE_EVENT(drv_join_ibss,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_bss_conf *info),
+
+       TP_ARGS(local, sdata, info),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(u8, dtimper)
+               __field(u16, bcnint)
+               __dynamic_array(u8, ssid, info->ssid_len);
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->dtimper = info->dtim_period;
+               __entry->bcnint = info->beacon_int;
+               memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT,
+               LOCAL_PR_ARG, VIF_PR_ARG
+       )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
index 70b5a05..c558b24 100644 (file)
@@ -1367,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        return 0;
 }
 
+bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_tx_data tx;
+
+       if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+               return false;
+
+       info->band = band;
+       info->control.vif = vif;
+       info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
+
+       if (invoke_tx_handlers(&tx))
+               return false;
+
+       if (sta) {
+               if (tx.sta)
+                       *sta = &tx.sta->sta;
+               else
+                       *sta = NULL;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
+
 /*
  * Returns false if the frame couldn't be transmitted but was queued instead.
  */
@@ -1982,7 +2011,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
         * EAPOL frames from the local station.
         */
        if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
-                    !is_multicast_ether_addr(hdr.addr1) && !authorized &&
+                    !multicast && !authorized &&
                     (cpu_to_be16(ethertype) != sdata->control_port_protocol ||
                      !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -2358,15 +2387,35 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
        struct probe_resp *resp;
        int counter_offset_beacon = sdata->csa_counter_offset_beacon;
        int counter_offset_presp = sdata->csa_counter_offset_presp;
+       u8 *beacon_data;
+       size_t beacon_data_len;
+
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+               beacon_data = beacon->tail;
+               beacon_data_len = beacon->tail_len;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+               break;
+       default:
+               return;
+       }
+       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
+               return;
 
        /* warn if the driver did not check for/react to csa completeness */
-       if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
+       if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
                return;
 
-       ((u8 *)beacon->tail)[counter_offset_beacon]--;
+       beacon_data[counter_offset_beacon]--;
 
-       if (sdata->vif.type == NL80211_IFTYPE_AP &&
-           counter_offset_presp) {
+       if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
                rcu_read_lock();
                resp = rcu_dereference(sdata->u.ap.probe_resp);
 
@@ -2401,6 +2450,24 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
                        goto out;
                beacon_data = beacon->tail;
                beacon_data_len = beacon->tail_len;
+       } else if (vif->type == NL80211_IFTYPE_ADHOC) {
+               struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+               beacon = rcu_dereference(ifibss->presp);
+               if (!beacon)
+                       goto out;
+
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+       } else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+               beacon = rcu_dereference(ifmsh->beacon);
+               if (!beacon)
+                       goto out;
+
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
        } else {
                WARN_ON(1);
                goto out;
@@ -2485,6 +2552,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                if (!presp)
                        goto out;
 
+               if (sdata->vif.csa_active)
+                       ieee80211_update_csa(sdata, presp);
+
+
                skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
                if (!skb)
                        goto out;
@@ -2502,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                if (!bcn)
                        goto out;
 
+               if (sdata->vif.csa_active)
+                       ieee80211_update_csa(sdata, bcn);
+
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(
                                                sdata);
index 69e4ef5..592a181 100644 (file)
@@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
                if (!sdata->dev)
                        continue;
 
-               if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-                       continue;
-
                if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
                    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
                        continue;
@@ -567,18 +564,15 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
                                        IEEE80211_QUEUE_STOP_REASON_FLUSH);
 }
 
-void ieee80211_iterate_active_interfaces(
-       struct ieee80211_hw *hw, u32 iter_flags,
-       void (*iterator)(void *data, u8 *mac,
-                        struct ieee80211_vif *vif),
-       void *data)
+static void __iterate_active_interfaces(struct ieee80211_local *local,
+                                       u32 iter_flags,
+                                       void (*iterator)(void *data, u8 *mac,
+                                               struct ieee80211_vif *vif),
+                                       void *data)
 {
-       struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
 
-       mutex_lock(&local->iflist_mtx);
-
-       list_for_each_entry(sdata, &local->interfaces, list) {
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_MONITOR:
                        if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
@@ -597,13 +591,25 @@ void ieee80211_iterate_active_interfaces(
                                 &sdata->vif);
        }
 
-       sdata = rcu_dereference_protected(local->monitor_sdata,
-                                         lockdep_is_held(&local->iflist_mtx));
+       sdata = rcu_dereference_check(local->monitor_sdata,
+                                     lockdep_is_held(&local->iflist_mtx) ||
+                                     lockdep_rtnl_is_held());
        if (sdata &&
            (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
             sdata->flags & IEEE80211_SDATA_IN_DRIVER))
                iterator(data, sdata->vif.addr, &sdata->vif);
+}
+
+void ieee80211_iterate_active_interfaces(
+       struct ieee80211_hw *hw, u32 iter_flags,
+       void (*iterator)(void *data, u8 *mac,
+                        struct ieee80211_vif *vif),
+       void *data)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
 
+       mutex_lock(&local->iflist_mtx);
+       __iterate_active_interfaces(local, iter_flags, iterator, data);
        mutex_unlock(&local->iflist_mtx);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
@@ -615,38 +621,26 @@ void ieee80211_iterate_active_interfaces_atomic(
        void *data)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata;
 
        rcu_read_lock();
+       __iterate_active_interfaces(local, iter_flags, iterator, data);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
 
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               switch (sdata->vif.type) {
-               case NL80211_IFTYPE_MONITOR:
-                       if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
-                               continue;
-                       break;
-               case NL80211_IFTYPE_AP_VLAN:
-                       continue;
-               default:
-                       break;
-               }
-               if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
-                   !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
-                       continue;
-               if (ieee80211_sdata_running(sdata))
-                       iterator(data, sdata->vif.addr,
-                                &sdata->vif);
-       }
+void ieee80211_iterate_active_interfaces_rtnl(
+       struct ieee80211_hw *hw, u32 iter_flags,
+       void (*iterator)(void *data, u8 *mac,
+                        struct ieee80211_vif *vif),
+       void *data)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
 
-       sdata = rcu_dereference(local->monitor_sdata);
-       if (sdata &&
-           (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
-            sdata->flags & IEEE80211_SDATA_IN_DRIVER))
-               iterator(data, sdata->vif.addr, &sdata->vif);
+       ASSERT_RTNL();
 
-       rcu_read_unlock();
+       __iterate_active_interfaces(local, iter_flags, iterator, data);
 }
-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
 
 /*
  * Nothing should have been stuffed into the workqueue during
@@ -746,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                case WLAN_EID_TIMEOUT_INTERVAL:
                case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
+               case WLAN_EID_CHAN_SWITCH_PARAM:
                /*
                 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
                 * that if the content gets bigger it might be needed more than once
@@ -911,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                        }
                        elems->sec_chan_offs = (void *)pos;
                        break;
+               case WLAN_EID_CHAN_SWITCH_PARAM:
+                       if (elen !=
+                           sizeof(*elems->mesh_chansw_params_ie)) {
+                               elem_parse_failed = true;
+                               break;
+                       }
+                       elems->mesh_chansw_params_ie = (void *)pos;
+                       break;
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
                        if (!action ||
                            elen != sizeof(*elems->wide_bw_chansw_ie)) {
@@ -1007,14 +1010,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
         */
        enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
 
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               /* Set defaults according to 802.11-2007 Table 7-37 */
-               aCWmax = 1023;
-               if (use_11b)
-                       aCWmin = 31;
-               else
-                       aCWmin = 15;
+       /* Set defaults according to 802.11-2007 Table 7-37 */
+       aCWmax = 1023;
+       if (use_11b)
+               aCWmin = 31;
+       else
+               aCWmin = 15;
+
+       /* Confiure old 802.11b/g medium access rules. */
+       qparam.cw_max = aCWmax;
+       qparam.cw_min = aCWmin;
+       qparam.txop = 0;
+       qparam.aifs = 2;
 
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               /* Update if QoS is enabled. */
                if (enable_qos) {
                        switch (ac) {
                        case IEEE80211_AC_BK:
@@ -1050,12 +1060,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                                qparam.aifs = 2;
                                break;
                        }
-               } else {
-                       /* Confiure old 802.11b/g medium access rules. */
-                       qparam.cw_max = aCWmax;
-                       qparam.cw_min = aCWmin;
-                       qparam.txop = 0;
-                       qparam.aifs = 2;
                }
 
                qparam.uapsd = false;
@@ -1084,8 +1088,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_mgmt *mgmt;
        int err;
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 6 + extra_len);
+       /* 24 + 6 = header + auth_algo + auth_transaction + status_code */
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
        if (!skb)
                return;
 
@@ -2296,3 +2300,175 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
        ieee80211_queue_work(hw, &local->radar_detected_work);
 }
 EXPORT_SYMBOL(ieee80211_radar_detected);
+
+u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
+{
+       u32 ret;
+       int tmp;
+
+       switch (c->width) {
+       case NL80211_CHAN_WIDTH_20:
+               c->width = NL80211_CHAN_WIDTH_20_NOHT;
+               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               c->width = NL80211_CHAN_WIDTH_20;
+               c->center_freq1 = c->chan->center_freq;
+               ret = IEEE80211_STA_DISABLE_40MHZ |
+                     IEEE80211_STA_DISABLE_VHT;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+               /* n_P40 */
+               tmp /= 2;
+               /* freq_P40 */
+               c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
+               c->width = NL80211_CHAN_WIDTH_40;
+               ret = IEEE80211_STA_DISABLE_VHT;
+               break;
+       case NL80211_CHAN_WIDTH_80P80:
+               c->center_freq2 = 0;
+               c->width = NL80211_CHAN_WIDTH_80;
+               ret = IEEE80211_STA_DISABLE_80P80MHZ |
+                     IEEE80211_STA_DISABLE_160MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_160:
+               /* n_P20 */
+               tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+               /* n_P80 */
+               tmp /= 4;
+               c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
+               c->width = NL80211_CHAN_WIDTH_80;
+               ret = IEEE80211_STA_DISABLE_80P80MHZ |
+                     IEEE80211_STA_DISABLE_160MHZ;
+               break;
+       default:
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               WARN_ON_ONCE(1);
+               c->width = NL80211_CHAN_WIDTH_20_NOHT;
+               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+               break;
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
+               WARN_ON_ONCE(1);
+               /* keep c->width */
+               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+               break;
+       }
+
+       WARN_ON_ONCE(!cfg80211_chandef_valid(c));
+
+       return ret;
+}
+
+/*
+ * Returns true if smps_mode_new is strictly more restrictive than
+ * smps_mode_old.
+ */
+bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new)
+{
+       if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
+                        smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
+               return false;
+
+       switch (smps_mode_old) {
+       case IEEE80211_SMPS_STATIC:
+               return false;
+       case IEEE80211_SMPS_DYNAMIC:
+               return smps_mode_new == IEEE80211_SMPS_STATIC;
+       case IEEE80211_SMPS_OFF:
+               return smps_mode_new != IEEE80211_SMPS_OFF;
+       default:
+               WARN_ON(1);
+       }
+
+       return false;
+}
+
+int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings)
+{
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_local *local = sdata->local;
+       int freq;
+       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
+                              sizeof(mgmt->u.action.u.chan_switch);
+       u8 *pos;
+
+       if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       skb = dev_alloc_skb(local->tx_headroom + hdr_len +
+                           5 + /* channel switch announcement element */
+                           3 + /* secondary channel offset element */
+                           8); /* mesh channel switch parameters element */
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, local->tx_headroom);
+       mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
+       memset(mgmt, 0, hdr_len);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       eth_broadcast_addr(mgmt->da);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+       } else {
+               struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+               memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
+       }
+       mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+       mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
+       pos = skb_put(skb, 5);
+       *pos++ = WLAN_EID_CHANNEL_SWITCH;                       /* EID */
+       *pos++ = 3;                                             /* IE length */
+       *pos++ = csa_settings->block_tx ? 1 : 0;                /* CSA mode */
+       freq = csa_settings->chandef.chan->center_freq;
+       *pos++ = ieee80211_frequency_to_channel(freq);          /* channel */
+       *pos++ = csa_settings->count;                           /* count */
+
+       if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
+               enum nl80211_channel_type ch_type;
+
+               skb_put(skb, 3);
+               *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;     /* EID */
+               *pos++ = 1;                                     /* IE length */
+               ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
+               if (ch_type == NL80211_CHAN_HT40PLUS)
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+               else
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+       }
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+               __le16 pre_value;
+
+               skb_put(skb, 8);
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;            /* EID */
+               *pos++ = 6;                                     /* IE length */
+               *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;     /* Mesh TTL */
+               *pos = 0x00;    /* Mesh Flag: Tx Restrict, Initiator, Reason */
+               *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               *pos++ |= csa_settings->block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
+               pos += 2;
+               if (!ifmsh->pre_value)
+                       ifmsh->pre_value = 1;
+               else
+                       ifmsh->pre_value++;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);             /* Precedence Value */
+               pos += 2;
+               ifmsh->chsw_init = true;
+       }
+
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+}
index 97c2894..de01127 100644 (file)
@@ -185,13 +185,13 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
        if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
                vht_cap->cap |= cap_info &
                                (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
-                                IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
                                 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
        }
 
        if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
                vht_cap->cap |= cap_info &
-                               IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+                               (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                                IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX);
 
        if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
                vht_cap->cap |= cap_info &
index c9edfcb..d657282 100644 (file)
@@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
 }
 
 
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
                                int encrypted)
 {
        __le16 mask_fc;
        int a4_included, mgmt;
        u8 qos_tid;
-       u8 *b_0, *aad;
-       u16 data_len, len_a;
+       u16 len_a;
        unsigned int hdrlen;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       memset(scratch, 0, 6 * AES_BLOCK_SIZE);
-
-       b_0 = scratch + 3 * AES_BLOCK_SIZE;
-       aad = scratch + 4 * AES_BLOCK_SIZE;
-
        /*
         * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
         * Retry, PwrMgt, MoreData; set Protected
@@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
        else
                qos_tid = 0;
 
-       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
-       if (encrypted)
-               data_len -= IEEE80211_CCMP_MIC_LEN;
+       /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
+        * mode authentication are not allowed to collide, yet both are derived
+        * from this vector b_0. We only set L := 1 here to indicate that the
+        * data size can be represented in (L+1) bytes. The CCM layer will take
+        * care of storing the data length in the top (L+1) bytes and setting
+        * and clearing the other bits as is required to derive the two IVs.
+        */
+       b_0[0] = 0x1;
 
-       /* First block, b_0 */
-       b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
        /* Nonce: Nonce Flags | A2 | PN
         * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
         */
        b_0[1] = qos_tid | (mgmt << 4);
        memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
        memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
-       /* l(m) */
-       put_unaligned_be16(data_len, &b_0[14]);
 
        /* AAD (extra authenticate-only data) / masked 802.11 header
         * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
@@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        u8 *pos;
        u8 pn[6];
        u64 pn64;
-       u8 scratch[6 * AES_BLOCK_SIZE];
+       u8 aad[2 * AES_BLOCK_SIZE];
+       u8 b_0[AES_BLOCK_SIZE];
 
        if (info->control.hw_key &&
            !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
@@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
                return 0;
 
        pos += IEEE80211_CCMP_HDR_LEN;
-       ccmp_special_blocks(skb, pn, scratch, 0);
-       ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
-                                 pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+       ccmp_special_blocks(skb, pn, b_0, aad, 0);
+       ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
+                                 skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 
        return 0;
 }
@@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        }
 
        if (!(status->flag & RX_FLAG_DECRYPTED)) {
-               u8 scratch[6 * AES_BLOCK_SIZE];
+               u8 aad[2 * AES_BLOCK_SIZE];
+               u8 b_0[AES_BLOCK_SIZE];
                /* hardware didn't decrypt/verify MIC */
-               ccmp_special_blocks(skb, pn, scratch, 1);
+               ccmp_special_blocks(skb, pn, b_0, aad, 1);
 
                if (ieee80211_aes_ccm_decrypt(
-                           key->u.ccmp.tfm, scratch,
+                           key->u.ccmp.tfm, b_0, aad,
                            skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
                            data_len,
-                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
-                           skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
+                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
                        return RX_DROP_UNUSABLE;
        }
 
index b7c7f81..52ae664 100644 (file)
@@ -174,8 +174,7 @@ ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 
        if (!ops || !ops->xmit || !ops->ed || !ops->start ||
            !ops->stop || !ops->set_channel) {
-               printk(KERN_ERR
-                      "undefined IEEE802.15.4 device operations\n");
+               pr_err("undefined IEEE802.15.4 device operations\n");
                return NULL;
        }
 
@@ -201,8 +200,7 @@ ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 
        phy = wpan_phy_alloc(priv_size);
        if (!phy) {
-               printk(KERN_ERR
-                      "failure to allocate master IEEE802.15.4 device\n");
+               pr_err("failure to allocate master IEEE802.15.4 device\n");
                return NULL;
        }
 
index 2ca2f4d..e24bcf9 100644 (file)
@@ -208,6 +208,8 @@ static int mac802154_header_create(struct sk_buff *skb,
        head[1] = fc >> 8;
 
        memcpy(skb_push(skb, pos), head, pos);
+       skb_reset_mac_header(skb);
+       skb->mac_len = pos;
 
        return pos;
 }
index 1bec121..851cd88 100644 (file)
@@ -33,6 +33,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
+                                 SKB_GSO_IPIP |
                                  SKB_GSO_MPLS)))
                goto out;
 
index 6e839b6..48acec1 100644 (file)
@@ -413,6 +413,58 @@ config NETFILTER_SYNPROXY
 
 endif # NF_CONNTRACK
 
+config NF_TABLES
+       depends on NETFILTER_NETLINK
+       tristate "Netfilter nf_tables support"
+
+config NFT_EXTHDR
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables IPv6 exthdr module"
+
+config NFT_META
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables meta module"
+
+config NFT_CT
+       depends on NF_TABLES
+       depends on NF_CONNTRACK
+       tristate "Netfilter nf_tables conntrack module"
+
+config NFT_RBTREE
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables rbtree set module"
+
+config NFT_HASH
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables hash set module"
+
+config NFT_COUNTER
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables counter module"
+
+config NFT_LOG
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables log module"
+
+config NFT_LIMIT
+       depends on NF_TABLES
+       tristate "Netfilter nf_tables limit module"
+
+config NFT_NAT
+       depends on NF_TABLES
+       depends on NF_CONNTRACK
+       depends on NF_NAT
+       tristate "Netfilter nf_tables nat module"
+
+config NFT_COMPAT
+       depends on NF_TABLES
+       depends on NETFILTER_XTABLES
+       tristate "Netfilter x_tables over nf_tables module"
+       help
+         This is required if you intend to use any of existing
+         x_tables match/target extensions over the nf_tables
+         framework.
+
 config NETFILTER_XTABLES
        tristate "Netfilter Xtables support (required for ip_tables)"
        default m if NETFILTER_ADVANCED=n
index c3a0a12..394483b 100644 (file)
@@ -64,6 +64,24 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
 # SYNPROXY
 obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
 
+# nf_tables
+nf_tables-objs += nf_tables_core.o nf_tables_api.o
+nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o
+nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
+
+obj-$(CONFIG_NF_TABLES)                += nf_tables.o
+obj-$(CONFIG_NFT_COMPAT)       += nft_compat.o
+obj-$(CONFIG_NFT_EXTHDR)       += nft_exthdr.o
+obj-$(CONFIG_NFT_META)         += nft_meta.o
+obj-$(CONFIG_NFT_CT)           += nft_ct.o
+obj-$(CONFIG_NFT_LIMIT)                += nft_limit.o
+obj-$(CONFIG_NFT_NAT)          += nft_nat.o
+#nf_tables-objs                        += nft_meta_target.o
+obj-$(CONFIG_NFT_RBTREE)       += nft_rbtree.o
+obj-$(CONFIG_NFT_HASH)         += nft_hash.o
+obj-$(CONFIG_NFT_COUNTER)      += nft_counter.o
+obj-$(CONFIG_NFT_LOG)          += nft_log.o
+
 # generic X tables 
 obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
 
index 593b16e..1fbab0c 100644 (file)
@@ -146,7 +146,7 @@ unsigned int nf_iterate(struct list_head *head,
                /* Optimization: we don't need to hold module
                   reference here, since function can't sleep. --RR */
 repeat:
-               verdict = (*elemp)->hook(hook, skb, indev, outdev, okfn);
+               verdict = (*elemp)->hook(*elemp, skb, indev, outdev, okfn);
                if (verdict != NF_ACCEPT) {
 #ifdef CONFIG_NETFILTER_DEBUG
                        if (unlikely((verdict & NF_VERDICT_MASK)
index ba36c28..a2d6263 100644 (file)
@@ -1,7 +1,7 @@
 menuconfig IP_SET
        tristate "IP set support"
        depends on INET && NETFILTER
-       depends on NETFILTER_NETLINK
+       select NETFILTER_NETLINK
        help
          This option adds IP set support to the kernel.
          In order to define and use the sets, you need the userspace utility
@@ -90,6 +90,15 @@ config IP_SET_HASH_IPPORTNET
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_SET_HASH_NETPORTNET
+       tristate "hash:net,port,net set support"
+       depends on IP_SET
+       help
+         This option adds the hash:net,port,net set type support, by which
+         one can store two IPv4/IPv6 subnets, and a protocol/port in a set.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config IP_SET_HASH_NET
        tristate "hash:net set support"
        depends on IP_SET
@@ -99,6 +108,15 @@ config IP_SET_HASH_NET
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_SET_HASH_NETNET
+       tristate "hash:net,net set support"
+       depends on IP_SET
+       help
+         This option adds the hash:net,net  set type support, by which
+         one can store IPv4/IPv6 network address/prefix pairs in a set.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config IP_SET_HASH_NETPORT
        tristate "hash:net,port set support"
        depends on IP_SET
index 6e965ec..44b2d38 100644 (file)
@@ -20,6 +20,8 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
 obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
 obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o
 obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o
+obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o
+obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o
 
 # list types
 obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o
index 2524337..f2c7d83 100644 (file)
@@ -8,38 +8,32 @@
 #ifndef __IP_SET_BITMAP_IP_GEN_H
 #define __IP_SET_BITMAP_IP_GEN_H
 
-#define CONCAT(a, b)           a##b
-#define TOKEN(a,b)             CONCAT(a, b)
-
-#define mtype_do_test          TOKEN(MTYPE, _do_test)
-#define mtype_gc_test          TOKEN(MTYPE, _gc_test)
-#define mtype_is_filled                TOKEN(MTYPE, _is_filled)
-#define mtype_do_add           TOKEN(MTYPE, _do_add)
-#define mtype_do_del           TOKEN(MTYPE, _do_del)
-#define mtype_do_list          TOKEN(MTYPE, _do_list)
-#define mtype_do_head          TOKEN(MTYPE, _do_head)
-#define mtype_adt_elem         TOKEN(MTYPE, _adt_elem)
-#define mtype_add_timeout      TOKEN(MTYPE, _add_timeout)
-#define mtype_gc_init          TOKEN(MTYPE, _gc_init)
-#define mtype_kadt             TOKEN(MTYPE, _kadt)
-#define mtype_uadt             TOKEN(MTYPE, _uadt)
-#define mtype_destroy          TOKEN(MTYPE, _destroy)
-#define mtype_flush            TOKEN(MTYPE, _flush)
-#define mtype_head             TOKEN(MTYPE, _head)
-#define mtype_same_set         TOKEN(MTYPE, _same_set)
-#define mtype_elem             TOKEN(MTYPE, _elem)
-#define mtype_test             TOKEN(MTYPE, _test)
-#define mtype_add              TOKEN(MTYPE, _add)
-#define mtype_del              TOKEN(MTYPE, _del)
-#define mtype_list             TOKEN(MTYPE, _list)
-#define mtype_gc               TOKEN(MTYPE, _gc)
+#define mtype_do_test          IPSET_TOKEN(MTYPE, _do_test)
+#define mtype_gc_test          IPSET_TOKEN(MTYPE, _gc_test)
+#define mtype_is_filled                IPSET_TOKEN(MTYPE, _is_filled)
+#define mtype_do_add           IPSET_TOKEN(MTYPE, _do_add)
+#define mtype_ext_cleanup      IPSET_TOKEN(MTYPE, _ext_cleanup)
+#define mtype_do_del           IPSET_TOKEN(MTYPE, _do_del)
+#define mtype_do_list          IPSET_TOKEN(MTYPE, _do_list)
+#define mtype_do_head          IPSET_TOKEN(MTYPE, _do_head)
+#define mtype_adt_elem         IPSET_TOKEN(MTYPE, _adt_elem)
+#define mtype_add_timeout      IPSET_TOKEN(MTYPE, _add_timeout)
+#define mtype_gc_init          IPSET_TOKEN(MTYPE, _gc_init)
+#define mtype_kadt             IPSET_TOKEN(MTYPE, _kadt)
+#define mtype_uadt             IPSET_TOKEN(MTYPE, _uadt)
+#define mtype_destroy          IPSET_TOKEN(MTYPE, _destroy)
+#define mtype_flush            IPSET_TOKEN(MTYPE, _flush)
+#define mtype_head             IPSET_TOKEN(MTYPE, _head)
+#define mtype_same_set         IPSET_TOKEN(MTYPE, _same_set)
+#define mtype_elem             IPSET_TOKEN(MTYPE, _elem)
+#define mtype_test             IPSET_TOKEN(MTYPE, _test)
+#define mtype_add              IPSET_TOKEN(MTYPE, _add)
+#define mtype_del              IPSET_TOKEN(MTYPE, _del)
+#define mtype_list             IPSET_TOKEN(MTYPE, _list)
+#define mtype_gc               IPSET_TOKEN(MTYPE, _gc)
 #define mtype                  MTYPE
 
-#define ext_timeout(e, m)      \
-       (unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
-#define ext_counter(e, m)      \
-       (struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER])
-#define get_ext(map, id)       ((map)->extensions + (map)->dsize * (id))
+#define get_ext(set, map, id)  ((map)->extensions + (set)->dsize * (id))
 
 static void
 mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
@@ -49,10 +43,21 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
        init_timer(&map->gc);
        map->gc.data = (unsigned long) set;
        map->gc.function = gc;
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
+static void
+mtype_ext_cleanup(struct ip_set *set)
+{
+       struct mtype *map = set->data;
+       u32 id;
+
+       for (id = 0; id < map->elements; id++)
+               if (test_bit(id, map->members))
+                       ip_set_ext_destroy(set, get_ext(set, map, id));
+}
+
 static void
 mtype_destroy(struct ip_set *set)
 {
@@ -62,8 +67,11 @@ mtype_destroy(struct ip_set *set)
                del_timer_sync(&map->gc);
 
        ip_set_free(map->members);
-       if (map->dsize)
+       if (set->dsize) {
+               if (set->extensions & IPSET_EXT_DESTROY)
+                       mtype_ext_cleanup(set);
                ip_set_free(map->extensions);
+       }
        kfree(map);
 
        set->data = NULL;
@@ -74,6 +82,8 @@ mtype_flush(struct ip_set *set)
 {
        struct mtype *map = set->data;
 
+       if (set->extensions & IPSET_EXT_DESTROY)
+               mtype_ext_cleanup(set);
        memset(map->members, 0, map->memsize);
 }
 
@@ -91,12 +101,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
            nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
                          htonl(sizeof(*map) +
                                map->memsize +
-                               map->dsize * map->elements)) ||
-           (SET_WITH_TIMEOUT(set) &&
-            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
-           (SET_WITH_COUNTER(set) &&
-            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
-                          htonl(IPSET_FLAG_WITH_COUNTERS))))
+                               set->dsize * map->elements)))
+               goto nla_put_failure;
+       if (unlikely(ip_set_put_flags(skb, set)))
                goto nla_put_failure;
        ipset_nest_end(skb, nested);
 
@@ -111,16 +118,16 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 {
        struct mtype *map = set->data;
        const struct mtype_adt_elem *e = value;
-       void *x = get_ext(map, e->id);
-       int ret = mtype_do_test(e, map);
+       void *x = get_ext(set, map, e->id);
+       int ret = mtype_do_test(e, map, set->dsize);
 
        if (ret <= 0)
                return ret;
        if (SET_WITH_TIMEOUT(set) &&
-           ip_set_timeout_expired(ext_timeout(x, map)))
+           ip_set_timeout_expired(ext_timeout(x, set)))
                return 0;
        if (SET_WITH_COUNTER(set))
-               ip_set_update_counter(ext_counter(x, map), ext, mext, flags);
+               ip_set_update_counter(ext_counter(x, set), ext, mext, flags);
        return 1;
 }
 
@@ -130,26 +137,30 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 {
        struct mtype *map = set->data;
        const struct mtype_adt_elem *e = value;
-       void *x = get_ext(map, e->id);
-       int ret = mtype_do_add(e, map, flags);
+       void *x = get_ext(set, map, e->id);
+       int ret = mtype_do_add(e, map, flags, set->dsize);
 
        if (ret == IPSET_ADD_FAILED) {
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(x, map)))
+                   ip_set_timeout_expired(ext_timeout(x, set)))
                        ret = 0;
                else if (!(flags & IPSET_FLAG_EXIST))
                        return -IPSET_ERR_EXIST;
+               /* Element is re-added, cleanup extensions */
+               ip_set_ext_destroy(set, x);
        }
 
        if (SET_WITH_TIMEOUT(set))
 #ifdef IP_SET_BITMAP_STORED_TIMEOUT
-               mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret);
+               mtype_add_timeout(ext_timeout(x, set), e, ext, set, map, ret);
 #else
-               ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
+               ip_set_timeout_set(ext_timeout(x, set), ext->timeout);
 #endif
 
        if (SET_WITH_COUNTER(set))
-               ip_set_init_counter(ext_counter(x, map), ext);
+               ip_set_init_counter(ext_counter(x, set), ext);
+       if (SET_WITH_COMMENT(set))
+               ip_set_init_comment(ext_comment(x, set), ext);
        return 0;
 }
 
@@ -159,16 +170,27 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 {
        struct mtype *map = set->data;
        const struct mtype_adt_elem *e = value;
-       const void *x = get_ext(map, e->id);
+       void *x = get_ext(set, map, e->id);
 
-       if (mtype_do_del(e, map) ||
-           (SET_WITH_TIMEOUT(set) &&
-            ip_set_timeout_expired(ext_timeout(x, map))))
+       if (mtype_do_del(e, map))
+               return -IPSET_ERR_EXIST;
+
+       ip_set_ext_destroy(set, x);
+       if (SET_WITH_TIMEOUT(set) &&
+           ip_set_timeout_expired(ext_timeout(x, set)))
                return -IPSET_ERR_EXIST;
 
        return 0;
 }
 
+#ifndef IP_SET_BITMAP_STORED_TIMEOUT
+static inline bool
+mtype_is_filled(const struct mtype_elem *x)
+{
+       return true;
+}
+#endif
+
 static int
 mtype_list(const struct ip_set *set,
           struct sk_buff *skb, struct netlink_callback *cb)
@@ -176,20 +198,21 @@ mtype_list(const struct ip_set *set,
        struct mtype *map = set->data;
        struct nlattr *adt, *nested;
        void *x;
-       u32 id, first = cb->args[2];
+       u32 id, first = cb->args[IPSET_CB_ARG0];
 
        adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
        if (!adt)
                return -EMSGSIZE;
-       for (; cb->args[2] < map->elements; cb->args[2]++) {
-               id = cb->args[2];
-               x = get_ext(map, id);
+       for (; cb->args[IPSET_CB_ARG0] < map->elements;
+            cb->args[IPSET_CB_ARG0]++) {
+               id = cb->args[IPSET_CB_ARG0];
+               x = get_ext(set, map, id);
                if (!test_bit(id, map->members) ||
                    (SET_WITH_TIMEOUT(set) &&
 #ifdef IP_SET_BITMAP_STORED_TIMEOUT
                     mtype_is_filled((const struct mtype_elem *) x) &&
 #endif
-                    ip_set_timeout_expired(ext_timeout(x, map))))
+                    ip_set_timeout_expired(ext_timeout(x, set))))
                        continue;
                nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
                if (!nested) {
@@ -199,40 +222,27 @@ mtype_list(const struct ip_set *set,
                        } else
                                goto nla_put_failure;
                }
-               if (mtype_do_list(skb, map, id))
+               if (mtype_do_list(skb, map, id, set->dsize))
                        goto nla_put_failure;
-               if (SET_WITH_TIMEOUT(set)) {
-#ifdef IP_SET_BITMAP_STORED_TIMEOUT
-                       if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                         htonl(ip_set_timeout_stored(map, id,
-                                                       ext_timeout(x, map)))))
-                               goto nla_put_failure;
-#else
-                       if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                         htonl(ip_set_timeout_get(
-                                                       ext_timeout(x, map)))))
-                               goto nla_put_failure;
-#endif
-               }
-               if (SET_WITH_COUNTER(set) &&
-                   ip_set_put_counter(skb, ext_counter(x, map)))
+               if (ip_set_put_extensions(skb, set, x,
+                   mtype_is_filled((const struct mtype_elem *) x)))
                        goto nla_put_failure;
                ipset_nest_end(skb, nested);
        }
        ipset_nest_end(skb, adt);
 
        /* Set listing finished */
-       cb->args[2] = 0;
+       cb->args[IPSET_CB_ARG0] = 0;
 
        return 0;
 
 nla_put_failure:
        nla_nest_cancel(skb, nested);
-       ipset_nest_end(skb, adt);
        if (unlikely(id == first)) {
-               cb->args[2] = 0;
+               cb->args[IPSET_CB_ARG0] = 0;
                return -EMSGSIZE;
        }
+       ipset_nest_end(skb, adt);
        return 0;
 }
 
@@ -241,21 +251,23 @@ mtype_gc(unsigned long ul_set)
 {
        struct ip_set *set = (struct ip_set *) ul_set;
        struct mtype *map = set->data;
-       const void *x;
+       void *x;
        u32 id;
 
        /* We run parallel with other readers (test element)
         * but adding/deleting new entries is locked out */
        read_lock_bh(&set->lock);
        for (id = 0; id < map->elements; id++)
-               if (mtype_gc_test(id, map)) {
-                       x = get_ext(map, id);
-                       if (ip_set_timeout_expired(ext_timeout(x, map)))
+               if (mtype_gc_test(id, map, set->dsize)) {
+                       x = get_ext(set, map, id);
+                       if (ip_set_timeout_expired(ext_timeout(x, set))) {
                                clear_bit(id, map->members);
+                               ip_set_ext_destroy(set, x);
+                       }
                }
        read_unlock_bh(&set->lock);
 
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
index f1a8128..6f1f9f4 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counter support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counter support added */
+#define IPSET_TYPE_REV_MAX     2       /* Comment support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("bitmap:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_bitmap:ip");
 
 #define MTYPE          bitmap_ip
@@ -44,10 +45,7 @@ struct bitmap_ip {
        u32 elements;           /* number of max elements in the set */
        u32 hosts;              /* number of hosts in a subnet */
        size_t memsize;         /* members size */
-       size_t dsize;           /* extensions struct size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
        u8 netmask;             /* subnet netmask */
-       u32 timeout;            /* timeout parameter */
        struct timer_list gc;   /* garbage collection */
 };
 
@@ -65,20 +63,21 @@ ip_to_id(const struct bitmap_ip *m, u32 ip)
 /* Common functions */
 
 static inline int
-bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
+bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e,
+                 struct bitmap_ip *map, size_t dsize)
 {
        return !!test_bit(e->id, map->members);
 }
 
 static inline int
-bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map)
+bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map, size_t dsize)
 {
        return !!test_bit(id, map->members);
 }
 
 static inline int
 bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map,
-                u32 flags)
+                u32 flags, size_t dsize)
 {
        return !!test_and_set_bit(e->id, map->members);
 }
@@ -90,7 +89,8 @@ bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
 }
 
 static inline int
-bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id)
+bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id,
+                 size_t dsize)
 {
        return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
                        htonl(map->first_ip + id * map->hosts));
@@ -113,7 +113,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct bitmap_ip *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_ip_adt_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        u32 ip;
 
        ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
@@ -131,9 +131,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 {
        struct bitmap_ip *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
-       u32 ip, ip_to;
+       u32 ip = 0, ip_to = 0;
        struct bitmap_ip_adt_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        int ret = 0;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -200,7 +200,7 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
        return x->first_ip == y->first_ip &&
               x->last_ip == y->last_ip &&
               x->netmask == y->netmask &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
@@ -209,25 +209,6 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
 struct bitmap_ip_elem {
 };
 
-/* Timeout variant */
-
-struct bitmap_ipt_elem {
-       unsigned long timeout;
-};
-
-/* Plain variant with counter */
-
-struct bitmap_ipc_elem {
-       struct ip_set_counter counter;
-};
-
-/* Timeout variant with counter */
-
-struct bitmap_ipct_elem {
-       unsigned long timeout;
-       struct ip_set_counter counter;
-};
-
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
@@ -240,8 +221,8 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
        map->members = ip_set_alloc(map->memsize);
        if (!map->members)
                return false;
-       if (map->dsize) {
-               map->extensions = ip_set_alloc(map->dsize * elements);
+       if (set->dsize) {
+               map->extensions = ip_set_alloc(set->dsize * elements);
                if (!map->extensions) {
                        kfree(map->members);
                        return false;
@@ -252,7 +233,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
        map->elements = elements;
        map->hosts = hosts;
        map->netmask = netmask;
-       map->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        set->data = map;
        set->family = NFPROTO_IPV4;
@@ -261,10 +242,11 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
 }
 
 static int
-bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+                u32 flags)
 {
        struct bitmap_ip *map;
-       u32 first_ip, last_ip, hosts, cadt_flags = 0;
+       u32 first_ip = 0, last_ip = 0, hosts;
        u64 elements;
        u8 netmask = 32;
        int ret;
@@ -336,61 +318,15 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 
        map->memsize = bitmap_bytes(0, elements - 1);
        set->variant = &bitmap_ip;
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map->dsize = sizeof(struct bitmap_ipct_elem);
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct bitmap_ipct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipct_elem, counter);
-
-                       if (!init_map_ip(set, map, first_ip, last_ip,
-                                        elements, hosts, netmask)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-
-                       map->timeout = ip_set_timeout_uget(
-                               tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-
-                       bitmap_ip_gc_init(set, bitmap_ip_gc);
-               } else {
-                       map->dsize = sizeof(struct bitmap_ipc_elem);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipc_elem, counter);
-
-                       if (!init_map_ip(set, map, first_ip, last_ip,
-                                        elements, hosts, netmask)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map->dsize = sizeof(struct bitmap_ipt_elem);
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct bitmap_ipt_elem, timeout);
-
-               if (!init_map_ip(set, map, first_ip, last_ip,
-                                elements, hosts, netmask)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-
-               map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
-
+       set->dsize = ip_set_elem_len(set, tb, 0);
+       if (!init_map_ip(set, map, first_ip, last_ip,
+                        elements, hosts, netmask)) {
+               kfree(map);
+               return -ENOMEM;
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                bitmap_ip_gc_init(set, bitmap_ip_gc);
-       } else {
-               map->dsize = 0;
-               if (!init_map_ip(set, map, first_ip, last_ip,
-                                elements, hosts, netmask)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
        }
        return 0;
 }
@@ -401,8 +337,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
        .features       = IPSET_TYPE_IP,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_IPV4,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = bitmap_ip_create,
        .create_policy  = {
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
@@ -420,6 +356,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 3b30e0b..740eabe 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counter support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counter support added */
+#define IPSET_TYPE_REV_MAX     2       /* Comment support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("bitmap:ip,mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_bitmap:ip,mac");
 
 #define MTYPE          bitmap_ipmac
@@ -48,11 +49,8 @@ struct bitmap_ipmac {
        u32 first_ip;           /* host byte order, included in range */
        u32 last_ip;            /* host byte order, included in range */
        u32 elements;           /* number of max elements in the set */
-       u32 timeout;            /* timeout value */
-       struct timer_list gc;   /* garbage collector */
        size_t memsize;         /* members size */
-       size_t dsize;           /* size of element */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
+       struct timer_list gc;   /* garbage collector */
 };
 
 /* ADT structure for generic function args */
@@ -82,13 +80,13 @@ get_elem(void *extensions, u16 id, size_t dsize)
 
 static inline int
 bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
-                    const struct bitmap_ipmac *map)
+                    const struct bitmap_ipmac *map, size_t dsize)
 {
        const struct bitmap_ipmac_elem *elem;
 
        if (!test_bit(e->id, map->members))
                return 0;
-       elem = get_elem(map->extensions, e->id, map->dsize);
+       elem = get_elem(map->extensions, e->id, dsize);
        if (elem->filled == MAC_FILLED)
                return e->ether == NULL ||
                       ether_addr_equal(e->ether, elem->ether);
@@ -97,13 +95,13 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
 }
 
 static inline int
-bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map)
+bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize)
 {
        const struct bitmap_ipmac_elem *elem;
 
        if (!test_bit(id, map->members))
                return 0;
-       elem = get_elem(map->extensions, id, map->dsize);
+       elem = get_elem(map->extensions, id, dsize);
        /* Timer not started for the incomplete elements */
        return elem->filled == MAC_FILLED;
 }
@@ -117,13 +115,13 @@ bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem)
 static inline int
 bitmap_ipmac_add_timeout(unsigned long *timeout,
                         const struct bitmap_ipmac_adt_elem *e,
-                        const struct ip_set_ext *ext,
+                        const struct ip_set_ext *ext, struct ip_set *set,
                         struct bitmap_ipmac *map, int mode)
 {
        u32 t = ext->timeout;
 
        if (mode == IPSET_ADD_START_STORED_TIMEOUT) {
-               if (t == map->timeout)
+               if (t == set->timeout)
                        /* Timeout was not specified, get stored one */
                        t = *timeout;
                ip_set_timeout_set(timeout, t);
@@ -142,11 +140,11 @@ bitmap_ipmac_add_timeout(unsigned long *timeout,
 
 static inline int
 bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e,
-                   struct bitmap_ipmac *map, u32 flags)
+                   struct bitmap_ipmac *map, u32 flags, size_t dsize)
 {
        struct bitmap_ipmac_elem *elem;
 
-       elem = get_elem(map->extensions, e->id, map->dsize);
+       elem = get_elem(map->extensions, e->id, dsize);
        if (test_and_set_bit(e->id, map->members)) {
                if (elem->filled == MAC_FILLED) {
                        if (e->ether && (flags & IPSET_FLAG_EXIST))
@@ -178,22 +176,12 @@ bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e,
        return !test_and_clear_bit(e->id, map->members);
 }
 
-static inline unsigned long
-ip_set_timeout_stored(struct bitmap_ipmac *map, u32 id, unsigned long *timeout)
-{
-       const struct bitmap_ipmac_elem *elem =
-               get_elem(map->extensions, id, map->dsize);
-
-       return elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) :
-                                           *timeout;
-}
-
 static inline int
 bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map,
-                    u32 id)
+                    u32 id, size_t dsize)
 {
        const struct bitmap_ipmac_elem *elem =
-               get_elem(map->extensions, id, map->dsize);
+               get_elem(map->extensions, id, dsize);
 
        return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
                               htonl(map->first_ip + id)) ||
@@ -216,7 +204,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct bitmap_ipmac *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_ipmac_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        u32 ip;
 
        /* MAC can be src only */
@@ -245,8 +233,8 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct bitmap_ipmac *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_ipmac_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
-       u32 ip;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0;
        int ret = 0;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -285,43 +273,12 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
 
        return x->first_ip == y->first_ip &&
               x->last_ip == y->last_ip &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
 /* Plain variant */
 
-/* Timeout variant */
-
-struct bitmap_ipmact_elem {
-       struct {
-               unsigned char ether[ETH_ALEN];
-               unsigned char filled;
-       } __attribute__ ((aligned));
-       unsigned long timeout;
-};
-
-/* Plain variant with counter */
-
-struct bitmap_ipmacc_elem {
-       struct {
-               unsigned char ether[ETH_ALEN];
-               unsigned char filled;
-       } __attribute__ ((aligned));
-       struct ip_set_counter counter;
-};
-
-/* Timeout variant with counter */
-
-struct bitmap_ipmacct_elem {
-       struct {
-               unsigned char ether[ETH_ALEN];
-               unsigned char filled;
-       } __attribute__ ((aligned));
-       unsigned long timeout;
-       struct ip_set_counter counter;
-};
-
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip,mac type of sets */
@@ -330,11 +287,11 @@ static bool
 init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
               u32 first_ip, u32 last_ip, u32 elements)
 {
-       map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize);
+       map->members = ip_set_alloc(map->memsize);
        if (!map->members)
                return false;
-       if (map->dsize) {
-               map->extensions = ip_set_alloc(map->dsize * elements);
+       if (set->dsize) {
+               map->extensions = ip_set_alloc(set->dsize * elements);
                if (!map->extensions) {
                        kfree(map->members);
                        return false;
@@ -343,7 +300,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
        map->first_ip = first_ip;
        map->last_ip = last_ip;
        map->elements = elements;
-       map->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        set->data = map;
        set->family = NFPROTO_IPV4;
@@ -352,10 +309,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
 }
 
 static int
-bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
+bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
                    u32 flags)
 {
-       u32 first_ip, last_ip, cadt_flags = 0;
+       u32 first_ip = 0, last_ip = 0;
        u64 elements;
        struct bitmap_ipmac *map;
        int ret;
@@ -399,57 +356,15 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
 
        map->memsize = bitmap_bytes(0, elements - 1);
        set->variant = &bitmap_ipmac;
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map->dsize = sizeof(struct bitmap_ipmacct_elem);
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct bitmap_ipmacct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipmacct_elem, counter);
-
-                       if (!init_map_ipmac(set, map, first_ip, last_ip,
-                                           elements)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-                       map->timeout = ip_set_timeout_uget(
-                               tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
-               } else {
-                       map->dsize = sizeof(struct bitmap_ipmacc_elem);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_ipmacc_elem, counter);
-
-                       if (!init_map_ipmac(set, map, first_ip, last_ip,
-                                           elements)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map->dsize = sizeof(struct bitmap_ipmact_elem);
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct bitmap_ipmact_elem, timeout);
-
-               if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-               map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
+       set->dsize = ip_set_elem_len(set, tb,
+                                    sizeof(struct bitmap_ipmac_elem));
+       if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
+               kfree(map);
+               return -ENOMEM;
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
-       } else {
-               map->dsize = sizeof(struct bitmap_ipmac_elem);
-
-               if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-               set->variant = &bitmap_ipmac;
        }
        return 0;
 }
@@ -460,8 +375,8 @@ static struct ip_set_type bitmap_ipmac_type = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_MAC,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_IPV4,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = bitmap_ipmac_create,
        .create_policy  = {
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
@@ -478,6 +393,7 @@ static struct ip_set_type bitmap_ipmac_type = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 8207d1f..cf99676 100644 (file)
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counter support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counter support added */
+#define IPSET_TYPE_REV_MAX     2       /* Comment support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("bitmap:port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_bitmap:port");
 
 #define MTYPE          bitmap_port
@@ -38,9 +39,6 @@ struct bitmap_port {
        u16 last_port;          /* host byte order, included in range */
        u32 elements;           /* number of max elements in the set */
        size_t memsize;         /* members size */
-       size_t dsize;           /* extensions struct size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
-       u32 timeout;            /* timeout parameter */
        struct timer_list gc;   /* garbage collection */
 };
 
@@ -59,20 +57,20 @@ port_to_id(const struct bitmap_port *m, u16 port)
 
 static inline int
 bitmap_port_do_test(const struct bitmap_port_adt_elem *e,
-                   const struct bitmap_port *map)
+                   const struct bitmap_port *map, size_t dsize)
 {
        return !!test_bit(e->id, map->members);
 }
 
 static inline int
-bitmap_port_gc_test(u16 id, const struct bitmap_port *map)
+bitmap_port_gc_test(u16 id, const struct bitmap_port *map, size_t dsize)
 {
        return !!test_bit(id, map->members);
 }
 
 static inline int
 bitmap_port_do_add(const struct bitmap_port_adt_elem *e,
-                  struct bitmap_port *map, u32 flags)
+                  struct bitmap_port *map, u32 flags, size_t dsize)
 {
        return !!test_and_set_bit(e->id, map->members);
 }
@@ -85,7 +83,8 @@ bitmap_port_do_del(const struct bitmap_port_adt_elem *e,
 }
 
 static inline int
-bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id)
+bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id,
+                   size_t dsize)
 {
        return nla_put_net16(skb, IPSET_ATTR_PORT,
                             htons(map->first_port + id));
@@ -106,7 +105,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct bitmap_port *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_port_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        __be16 __port;
        u16 port = 0;
 
@@ -131,7 +130,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
        struct bitmap_port *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct bitmap_port_adt_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port;       /* wraparound */
        u16 port_to;
        int ret = 0;
@@ -191,7 +190,7 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
 
        return x->first_port == y->first_port &&
               x->last_port == y->last_port &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
@@ -200,25 +199,6 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
 struct bitmap_port_elem {
 };
 
-/* Timeout variant */
-
-struct bitmap_portt_elem {
-       unsigned long timeout;
-};
-
-/* Plain variant with counter */
-
-struct bitmap_portc_elem {
-       struct ip_set_counter counter;
-};
-
-/* Timeout variant with counter */
-
-struct bitmap_portct_elem {
-       unsigned long timeout;
-       struct ip_set_counter counter;
-};
-
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
@@ -230,8 +210,8 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
        map->members = ip_set_alloc(map->memsize);
        if (!map->members)
                return false;
-       if (map->dsize) {
-               map->extensions = ip_set_alloc(map->dsize * map->elements);
+       if (set->dsize) {
+               map->extensions = ip_set_alloc(set->dsize * map->elements);
                if (!map->extensions) {
                        kfree(map->members);
                        return false;
@@ -239,7 +219,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
        }
        map->first_port = first_port;
        map->last_port = last_port;
-       map->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        set->data = map;
        set->family = NFPROTO_UNSPEC;
@@ -248,11 +228,11 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
 }
 
 static int
-bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+                  u32 flags)
 {
        struct bitmap_port *map;
        u16 first_port, last_port;
-       u32 cadt_flags = 0;
 
        if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
                     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
@@ -274,55 +254,16 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
                return -ENOMEM;
 
        map->elements = last_port - first_port + 1;
-       map->memsize = map->elements * sizeof(unsigned long);
+       map->memsize = bitmap_bytes(0, map->elements);
        set->variant = &bitmap_port;
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map->dsize = sizeof(struct bitmap_portct_elem);
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct bitmap_portct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_portct_elem, counter);
-                       if (!init_map_port(set, map, first_port, last_port)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-
-                       map->timeout =
-                               ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       bitmap_port_gc_init(set, bitmap_port_gc);
-               } else {
-                       map->dsize = sizeof(struct bitmap_portc_elem);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct bitmap_portc_elem, counter);
-                       if (!init_map_port(set, map, first_port, last_port)) {
-                               kfree(map);
-                               return -ENOMEM;
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map->dsize = sizeof(struct bitmap_portt_elem);
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct bitmap_portt_elem, timeout);
-               if (!init_map_port(set, map, first_port, last_port)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-
-               map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
+       set->dsize = ip_set_elem_len(set, tb, 0);
+       if (!init_map_port(set, map, first_port, last_port)) {
+               kfree(map);
+               return -ENOMEM;
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                bitmap_port_gc_init(set, bitmap_port_gc);
-       } else {
-               map->dsize = 0;
-               if (!init_map_port(set, map, first_port, last_port)) {
-                       kfree(map);
-                       return -ENOMEM;
-               }
-
        }
        return 0;
 }
@@ -333,8 +274,8 @@ static struct ip_set_type bitmap_port_type = {
        .features       = IPSET_TYPE_PORT,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = bitmap_port_create,
        .create_policy  = {
                [IPSET_ATTR_PORT]       = { .type = NLA_U16 },
@@ -349,6 +290,7 @@ static struct ip_set_type bitmap_port_type = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index f2e30fb..bac7e01 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/spinlock.h>
 #include <linux/rculist.h>
 #include <net/netlink.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #include <linux/netfilter.h>
 #include <linux/netfilter/x_tables.h>
@@ -27,8 +29,17 @@ static LIST_HEAD(ip_set_type_list);          /* all registered set types */
 static DEFINE_MUTEX(ip_set_type_mutex);                /* protects ip_set_type_list */
 static DEFINE_RWLOCK(ip_set_ref_lock);         /* protects the set refs */
 
-static struct ip_set * __rcu *ip_set_list;     /* all individual sets */
-static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */
+struct ip_set_net {
+       struct ip_set * __rcu *ip_set_list;     /* all individual sets */
+       ip_set_id_t     ip_set_max;     /* max number of sets */
+       int             is_deleted;     /* deleted by ip_set_net_exit */
+};
+static int ip_set_net_id __read_mostly;
+
+static inline struct ip_set_net *ip_set_pernet(struct net *net)
+{
+       return net_generic(net, ip_set_net_id);
+}
 
 #define IP_SET_INC     64
 #define STREQ(a, b)    (strncmp(a, b, IPSET_MAXNAMELEN) == 0)
@@ -45,8 +56,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
 /* When the nfnl mutex is held: */
 #define nfnl_dereference(p)            \
        rcu_dereference_protected(p, 1)
-#define nfnl_set(id)                   \
-       nfnl_dereference(ip_set_list)[id]
+#define nfnl_set(inst, id)                     \
+       nfnl_dereference((inst)->ip_set_list)[id]
 
 /*
  * The set types are implemented in modules and registered set types
@@ -315,6 +326,60 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
 }
 EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
 
+typedef void (*destroyer)(void *);
+/* ipset data extension types, in size order */
+
+const struct ip_set_ext_type ip_set_extensions[] = {
+       [IPSET_EXT_ID_COUNTER] = {
+               .type   = IPSET_EXT_COUNTER,
+               .flag   = IPSET_FLAG_WITH_COUNTERS,
+               .len    = sizeof(struct ip_set_counter),
+               .align  = __alignof__(struct ip_set_counter),
+       },
+       [IPSET_EXT_ID_TIMEOUT] = {
+               .type   = IPSET_EXT_TIMEOUT,
+               .len    = sizeof(unsigned long),
+               .align  = __alignof__(unsigned long),
+       },
+       [IPSET_EXT_ID_COMMENT] = {
+               .type    = IPSET_EXT_COMMENT | IPSET_EXT_DESTROY,
+               .flag    = IPSET_FLAG_WITH_COMMENT,
+               .len     = sizeof(struct ip_set_comment),
+               .align   = __alignof__(struct ip_set_comment),
+               .destroy = (destroyer) ip_set_comment_free,
+       },
+};
+EXPORT_SYMBOL_GPL(ip_set_extensions);
+
+static inline bool
+add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[])
+{
+       return ip_set_extensions[id].flag ?
+               (flags & ip_set_extensions[id].flag) :
+               !!tb[IPSET_ATTR_TIMEOUT];
+}
+
+size_t
+ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len)
+{
+       enum ip_set_ext_id id;
+       size_t offset = 0;
+       u32 cadt_flags = 0;
+
+       if (tb[IPSET_ATTR_CADT_FLAGS])
+               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+       for (id = 0; id < IPSET_EXT_ID_MAX; id++) {
+               if (!add_extension(id, cadt_flags, tb))
+                       continue;
+               offset += ALIGN(len + offset, ip_set_extensions[id].align);
+               set->offset[id] = offset;
+               set->extensions |= ip_set_extensions[id].type;
+               offset += ip_set_extensions[id].len;
+       }
+       return len + offset;
+}
+EXPORT_SYMBOL_GPL(ip_set_elem_len);
+
 int
 ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
                      struct ip_set_ext *ext)
@@ -334,6 +399,12 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
                        ext->packets = be64_to_cpu(nla_get_be64(
                                                   tb[IPSET_ATTR_PACKETS]));
        }
+       if (tb[IPSET_ATTR_COMMENT]) {
+               if (!(set->extensions & IPSET_EXT_COMMENT))
+                       return -IPSET_ERR_COMMENT;
+               ext->comment = ip_set_comment_uget(tb[IPSET_ATTR_COMMENT]);
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ip_set_get_extensions);
@@ -374,13 +445,14 @@ __ip_set_put(struct ip_set *set)
  */
 
 static inline struct ip_set *
-ip_set_rcu_get(ip_set_id_t index)
+ip_set_rcu_get(struct net *net, ip_set_id_t index)
 {
        struct ip_set *set;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
        rcu_read_lock();
        /* ip_set_list itself needs to be protected */
-       set = rcu_dereference(ip_set_list)[index];
+       set = rcu_dereference(inst->ip_set_list)[index];
        rcu_read_unlock();
 
        return set;
@@ -390,7 +462,8 @@ int
 ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
            const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(index);
+       struct ip_set *set = ip_set_rcu_get(
+                       dev_net(par->in ? par->in : par->out), index);
        int ret = 0;
 
        BUG_ON(set == NULL);
@@ -428,7 +501,8 @@ int
 ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
           const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(index);
+       struct ip_set *set = ip_set_rcu_get(
+                       dev_net(par->in ? par->in : par->out), index);
        int ret;
 
        BUG_ON(set == NULL);
@@ -450,7 +524,8 @@ int
 ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
           const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
-       struct ip_set *set = ip_set_rcu_get(index);
+       struct ip_set *set = ip_set_rcu_get(
+                       dev_net(par->in ? par->in : par->out), index);
        int ret = 0;
 
        BUG_ON(set == NULL);
@@ -474,14 +549,15 @@ EXPORT_SYMBOL_GPL(ip_set_del);
  *
  */
 ip_set_id_t
-ip_set_get_byname(const char *name, struct ip_set **set)
+ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
 {
        ip_set_id_t i, index = IPSET_INVALID_ID;
        struct ip_set *s;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
        rcu_read_lock();
-       for (i = 0; i < ip_set_max; i++) {
-               s = rcu_dereference(ip_set_list)[i];
+       for (i = 0; i < inst->ip_set_max; i++) {
+               s = rcu_dereference(inst->ip_set_list)[i];
                if (s != NULL && STREQ(s->name, name)) {
                        __ip_set_get(s);
                        index = i;
@@ -501,17 +577,26 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname);
  * to be valid, after calling this function.
  *
  */
-void
-ip_set_put_byindex(ip_set_id_t index)
+
+static inline void
+__ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index)
 {
        struct ip_set *set;
 
        rcu_read_lock();
-       set = rcu_dereference(ip_set_list)[index];
+       set = rcu_dereference(inst->ip_set_list)[index];
        if (set != NULL)
                __ip_set_put(set);
        rcu_read_unlock();
 }
+
+void
+ip_set_put_byindex(struct net *net, ip_set_id_t index)
+{
+       struct ip_set_net *inst = ip_set_pernet(net);
+
+       __ip_set_put_byindex(inst, index);
+}
 EXPORT_SYMBOL_GPL(ip_set_put_byindex);
 
 /*
@@ -522,9 +607,9 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex);
  *
  */
 const char *
-ip_set_name_byindex(ip_set_id_t index)
+ip_set_name_byindex(struct net *net, ip_set_id_t index)
 {
-       const struct ip_set *set = ip_set_rcu_get(index);
+       const struct ip_set *set = ip_set_rcu_get(net, index);
 
        BUG_ON(set == NULL);
        BUG_ON(set->ref == 0);
@@ -546,14 +631,15 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex);
  * The nfnl mutex is used in the function.
  */
 ip_set_id_t
-ip_set_nfnl_get(const char *name)
+ip_set_nfnl_get(struct net *net, const char *name)
 {
        ip_set_id_t i, index = IPSET_INVALID_ID;
        struct ip_set *s;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
        nfnl_lock(NFNL_SUBSYS_IPSET);
-       for (i = 0; i < ip_set_max; i++) {
-               s = nfnl_set(i);
+       for (i = 0; i < inst->ip_set_max; i++) {
+               s = nfnl_set(inst, i);
                if (s != NULL && STREQ(s->name, name)) {
                        __ip_set_get(s);
                        index = i;
@@ -573,15 +659,16 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get);
  * The nfnl mutex is used in the function.
  */
 ip_set_id_t
-ip_set_nfnl_get_byindex(ip_set_id_t index)
+ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index)
 {
        struct ip_set *set;
+       struct ip_set_net *inst = ip_set_pernet(net);
 
-       if (index > ip_set_max)
+       if (index > inst->ip_set_max)
                return IPSET_INVALID_ID;
 
        nfnl_lock(NFNL_SUBSYS_IPSET);
-       set = nfnl_set(index);
+       set = nfnl_set(inst, index);
        if (set)
                __ip_set_get(set);
        else
@@ -600,13 +687,17 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex);
  * The nfnl mutex is used in the function.
  */
 void
-ip_set_nfnl_put(ip_set_id_t index)
+ip_set_nfnl_put(struct net *net, ip_set_id_t index)
 {
        struct ip_set *set;
+       struct ip_set_net *inst = ip_set_pernet(net);
+
        nfnl_lock(NFNL_SUBSYS_IPSET);
-       set = nfnl_set(index);
-       if (set != NULL)
-               __ip_set_put(set);
+       if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */
+               set = nfnl_set(inst, index);
+               if (set != NULL)
+                       __ip_set_put(set);
+       }
        nfnl_unlock(NFNL_SUBSYS_IPSET);
 }
 EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
@@ -664,14 +755,14 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = {
 };
 
 static struct ip_set *
-find_set_and_id(const char *name, ip_set_id_t *id)
+find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id)
 {
        struct ip_set *set = NULL;
        ip_set_id_t i;
 
        *id = IPSET_INVALID_ID;
-       for (i = 0; i < ip_set_max; i++) {
-               set = nfnl_set(i);
+       for (i = 0; i < inst->ip_set_max; i++) {
+               set = nfnl_set(inst, i);
                if (set != NULL && STREQ(set->name, name)) {
                        *id = i;
                        break;
@@ -681,22 +772,23 @@ find_set_and_id(const char *name, ip_set_id_t *id)
 }
 
 static inline struct ip_set *
-find_set(const char *name)
+find_set(struct ip_set_net *inst, const char *name)
 {
        ip_set_id_t id;
 
-       return find_set_and_id(name, &id);
+       return find_set_and_id(inst, name, &id);
 }
 
 static int
-find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set)
+find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
+            struct ip_set **set)
 {
        struct ip_set *s;
        ip_set_id_t i;
 
        *index = IPSET_INVALID_ID;
-       for (i = 0;  i < ip_set_max; i++) {
-               s = nfnl_set(i);
+       for (i = 0;  i < inst->ip_set_max; i++) {
+               s = nfnl_set(inst, i);
                if (s == NULL) {
                        if (*index == IPSET_INVALID_ID)
                                *index = i;
@@ -725,6 +817,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
              const struct nlmsghdr *nlh,
              const struct nlattr * const attr[])
 {
+       struct net *net = sock_net(ctnl);
+       struct ip_set_net *inst = ip_set_pernet(net);
        struct ip_set *set, *clash = NULL;
        ip_set_id_t index = IPSET_INVALID_ID;
        struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {};
@@ -783,7 +877,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
                goto put_out;
        }
 
-       ret = set->type->create(set, tb, flags);
+       ret = set->type->create(net, set, tb, flags);
        if (ret != 0)
                goto put_out;
 
@@ -794,7 +888,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
         * by the nfnl mutex. Find the first free index in ip_set_list
         * and check clashing.
         */
-       ret = find_free_id(set->name, &index, &clash);
+       ret = find_free_id(inst, set->name, &index, &clash);
        if (ret == -EEXIST) {
                /* If this is the same set and requested, ignore error */
                if ((flags & IPSET_FLAG_EXIST) &&
@@ -807,9 +901,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
                goto cleanup;
        } else if (ret == -IPSET_ERR_MAX_SETS) {
                struct ip_set **list, **tmp;
-               ip_set_id_t i = ip_set_max + IP_SET_INC;
+               ip_set_id_t i = inst->ip_set_max + IP_SET_INC;
 
-               if (i < ip_set_max || i == IPSET_INVALID_ID)
+               if (i < inst->ip_set_max || i == IPSET_INVALID_ID)
                        /* Wraparound */
                        goto cleanup;
 
@@ -817,14 +911,14 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
                if (!list)
                        goto cleanup;
                /* nfnl mutex is held, both lists are valid */
-               tmp = nfnl_dereference(ip_set_list);
-               memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max);
-               rcu_assign_pointer(ip_set_list, list);
+               tmp = nfnl_dereference(inst->ip_set_list);
+               memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max);
+               rcu_assign_pointer(inst->ip_set_list, list);
                /* Make sure all current packets have passed through */
                synchronize_net();
                /* Use new list */
-               index = ip_set_max;
-               ip_set_max = i;
+               index = inst->ip_set_max;
+               inst->ip_set_max = i;
                kfree(tmp);
                ret = 0;
        } else if (ret)
@@ -834,7 +928,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
         * Finally! Add our shiny new set to the list, and be done.
         */
        pr_debug("create: '%s' created with index %u!\n", set->name, index);
-       nfnl_set(index) = set;
+       nfnl_set(inst, index) = set;
 
        return ret;
 
@@ -857,12 +951,12 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
 };
 
 static void
-ip_set_destroy_set(ip_set_id_t index)
+ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index)
 {
-       struct ip_set *set = nfnl_set(index);
+       struct ip_set *set = nfnl_set(inst, index);
 
        pr_debug("set: %s\n",  set->name);
-       nfnl_set(index) = NULL;
+       nfnl_set(inst, index) = NULL;
 
        /* Must call it without holding any lock */
        set->variant->destroy(set);
@@ -875,6 +969,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
               const struct nlmsghdr *nlh,
               const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *s;
        ip_set_id_t i;
        int ret = 0;
@@ -894,21 +989,22 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
         */
        read_lock_bh(&ip_set_ref_lock);
        if (!attr[IPSET_ATTR_SETNAME]) {
-               for (i = 0; i < ip_set_max; i++) {
-                       s = nfnl_set(i);
+               for (i = 0; i < inst->ip_set_max; i++) {
+                       s = nfnl_set(inst, i);
                        if (s != NULL && s->ref) {
                                ret = -IPSET_ERR_BUSY;
                                goto out;
                        }
                }
                read_unlock_bh(&ip_set_ref_lock);
-               for (i = 0; i < ip_set_max; i++) {
-                       s = nfnl_set(i);
+               for (i = 0; i < inst->ip_set_max; i++) {
+                       s = nfnl_set(inst, i);
                        if (s != NULL)
-                               ip_set_destroy_set(i);
+                               ip_set_destroy_set(inst, i);
                }
        } else {
-               s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i);
+               s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
+                                   &i);
                if (s == NULL) {
                        ret = -ENOENT;
                        goto out;
@@ -918,7 +1014,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
                }
                read_unlock_bh(&ip_set_ref_lock);
 
-               ip_set_destroy_set(i);
+               ip_set_destroy_set(inst, i);
        }
        return 0;
 out:
@@ -943,6 +1039,7 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
             const struct nlmsghdr *nlh,
             const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *s;
        ip_set_id_t i;
 
@@ -950,13 +1047,13 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
                return -IPSET_ERR_PROTOCOL;
 
        if (!attr[IPSET_ATTR_SETNAME]) {
-               for (i = 0; i < ip_set_max; i++) {
-                       s = nfnl_set(i);
+               for (i = 0; i < inst->ip_set_max; i++) {
+                       s = nfnl_set(inst, i);
                        if (s != NULL)
                                ip_set_flush_set(s);
                }
        } else {
-               s = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+               s = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
                if (s == NULL)
                        return -ENOENT;
 
@@ -982,6 +1079,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
              const struct nlmsghdr *nlh,
              const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set, *s;
        const char *name2;
        ip_set_id_t i;
@@ -992,7 +1090,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
                     attr[IPSET_ATTR_SETNAME2] == NULL))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1003,8 +1101,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
        }
 
        name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
-       for (i = 0; i < ip_set_max; i++) {
-               s = nfnl_set(i);
+       for (i = 0; i < inst->ip_set_max; i++) {
+               s = nfnl_set(inst, i);
                if (s != NULL && STREQ(s->name, name2)) {
                        ret = -IPSET_ERR_EXIST_SETNAME2;
                        goto out;
@@ -1031,6 +1129,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *from, *to;
        ip_set_id_t from_id, to_id;
        char from_name[IPSET_MAXNAMELEN];
@@ -1040,11 +1139,13 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
                     attr[IPSET_ATTR_SETNAME2] == NULL))
                return -IPSET_ERR_PROTOCOL;
 
-       from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id);
+       from = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]),
+                              &from_id);
        if (from == NULL)
                return -ENOENT;
 
-       to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id);
+       to = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME2]),
+                            &to_id);
        if (to == NULL)
                return -IPSET_ERR_EXIST_SETNAME2;
 
@@ -1061,8 +1162,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
 
        write_lock_bh(&ip_set_ref_lock);
        swap(from->ref, to->ref);
-       nfnl_set(from_id) = to;
-       nfnl_set(to_id) = from;
+       nfnl_set(inst, from_id) = to;
+       nfnl_set(inst, to_id) = from;
        write_unlock_bh(&ip_set_ref_lock);
 
        return 0;
@@ -1081,9 +1182,12 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
 static int
 ip_set_dump_done(struct netlink_callback *cb)
 {
-       if (cb->args[2]) {
-               pr_debug("release set %s\n", nfnl_set(cb->args[1])->name);
-               ip_set_put_byindex((ip_set_id_t) cb->args[1]);
+       struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET];
+       if (cb->args[IPSET_CB_ARG0]) {
+               pr_debug("release set %s\n",
+                        nfnl_set(inst, cb->args[IPSET_CB_INDEX])->name);
+               __ip_set_put_byindex(inst,
+                       (ip_set_id_t) cb->args[IPSET_CB_INDEX]);
        }
        return 0;
 }
@@ -1101,7 +1205,7 @@ dump_attrs(struct nlmsghdr *nlh)
 }
 
 static int
-dump_init(struct netlink_callback *cb)
+dump_init(struct netlink_callback *cb, struct ip_set_net *inst)
 {
        struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
        int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
@@ -1114,21 +1218,22 @@ dump_init(struct netlink_callback *cb)
        nla_parse(cda, IPSET_ATTR_CMD_MAX,
                  attr, nlh->nlmsg_len - min_len, ip_set_setname_policy);
 
-       /* cb->args[0] : dump single set/all sets
-        *         [1] : set index
-        *         [..]: type specific
+       /* cb->args[IPSET_CB_NET]:      net namespace
+        *         [IPSET_CB_DUMP]:     dump single set/all sets
+        *         [IPSET_CB_INDEX]:    set index
+        *         [IPSET_CB_ARG0]:     type specific
         */
 
        if (cda[IPSET_ATTR_SETNAME]) {
                struct ip_set *set;
 
-               set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]),
+               set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]),
                                      &index);
                if (set == NULL)
                        return -ENOENT;
 
                dump_type = DUMP_ONE;
-               cb->args[1] = index;
+               cb->args[IPSET_CB_INDEX] = index;
        } else
                dump_type = DUMP_ALL;
 
@@ -1136,7 +1241,8 @@ dump_init(struct netlink_callback *cb)
                u32 f = ip_set_get_h32(cda[IPSET_ATTR_FLAGS]);
                dump_type |= (f << 16);
        }
-       cb->args[0] = dump_type;
+       cb->args[IPSET_CB_NET] = (unsigned long)inst;
+       cb->args[IPSET_CB_DUMP] = dump_type;
 
        return 0;
 }
@@ -1148,11 +1254,12 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
        struct ip_set *set = NULL;
        struct nlmsghdr *nlh = NULL;
        unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0;
+       struct ip_set_net *inst = ip_set_pernet(sock_net(skb->sk));
        u32 dump_type, dump_flags;
        int ret = 0;
 
-       if (!cb->args[0]) {
-               ret = dump_init(cb);
+       if (!cb->args[IPSET_CB_DUMP]) {
+               ret = dump_init(cb, inst);
                if (ret < 0) {
                        nlh = nlmsg_hdr(cb->skb);
                        /* We have to create and send the error message
@@ -1163,18 +1270,19 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
                }
        }
 
-       if (cb->args[1] >= ip_set_max)
+       if (cb->args[IPSET_CB_INDEX] >= inst->ip_set_max)
                goto out;
 
-       dump_type = DUMP_TYPE(cb->args[0]);
-       dump_flags = DUMP_FLAGS(cb->args[0]);
-       max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
+       dump_type = DUMP_TYPE(cb->args[IPSET_CB_DUMP]);
+       dump_flags = DUMP_FLAGS(cb->args[IPSET_CB_DUMP]);
+       max = dump_type == DUMP_ONE ? cb->args[IPSET_CB_INDEX] + 1
+                                   : inst->ip_set_max;
 dump_last:
-       pr_debug("args[0]: %u %u args[1]: %ld\n",
-                dump_type, dump_flags, cb->args[1]);
-       for (; cb->args[1] < max; cb->args[1]++) {
-               index = (ip_set_id_t) cb->args[1];
-               set = nfnl_set(index);
+       pr_debug("dump type, flag: %u %u index: %ld\n",
+                dump_type, dump_flags, cb->args[IPSET_CB_INDEX]);
+       for (; cb->args[IPSET_CB_INDEX] < max; cb->args[IPSET_CB_INDEX]++) {
+               index = (ip_set_id_t) cb->args[IPSET_CB_INDEX];
+               set = nfnl_set(inst, index);
                if (set == NULL) {
                        if (dump_type == DUMP_ONE) {
                                ret = -ENOENT;
@@ -1190,7 +1298,7 @@ dump_last:
                     !!(set->type->features & IPSET_DUMP_LAST)))
                        continue;
                pr_debug("List set: %s\n", set->name);
-               if (!cb->args[2]) {
+               if (!cb->args[IPSET_CB_ARG0]) {
                        /* Start listing: make sure set won't be destroyed */
                        pr_debug("reference set\n");
                        __ip_set_get(set);
@@ -1207,7 +1315,7 @@ dump_last:
                        goto nla_put_failure;
                if (dump_flags & IPSET_FLAG_LIST_SETNAME)
                        goto next_set;
-               switch (cb->args[2]) {
+               switch (cb->args[IPSET_CB_ARG0]) {
                case 0:
                        /* Core header data */
                        if (nla_put_string(skb, IPSET_ATTR_TYPENAME,
@@ -1227,7 +1335,7 @@ dump_last:
                        read_lock_bh(&set->lock);
                        ret = set->variant->list(set, skb, cb);
                        read_unlock_bh(&set->lock);
-                       if (!cb->args[2])
+                       if (!cb->args[IPSET_CB_ARG0])
                                /* Set is done, proceed with next one */
                                goto next_set;
                        goto release_refcount;
@@ -1236,8 +1344,8 @@ dump_last:
        /* If we dump all sets, continue with dumping last ones */
        if (dump_type == DUMP_ALL) {
                dump_type = DUMP_LAST;
-               cb->args[0] = dump_type | (dump_flags << 16);
-               cb->args[1] = 0;
+               cb->args[IPSET_CB_DUMP] = dump_type | (dump_flags << 16);
+               cb->args[IPSET_CB_INDEX] = 0;
                goto dump_last;
        }
        goto out;
@@ -1246,15 +1354,15 @@ nla_put_failure:
        ret = -EFAULT;
 next_set:
        if (dump_type == DUMP_ONE)
-               cb->args[1] = IPSET_INVALID_ID;
+               cb->args[IPSET_CB_INDEX] = IPSET_INVALID_ID;
        else
-               cb->args[1]++;
+               cb->args[IPSET_CB_INDEX]++;
 release_refcount:
        /* If there was an error or set is done, release set */
-       if (ret || !cb->args[2]) {
-               pr_debug("release set %s\n", nfnl_set(index)->name);
-               ip_set_put_byindex(index);
-               cb->args[2] = 0;
+       if (ret || !cb->args[IPSET_CB_ARG0]) {
+               pr_debug("release set %s\n", nfnl_set(inst, index)->name);
+               __ip_set_put_byindex(inst, index);
+               cb->args[IPSET_CB_ARG0] = 0;
        }
 out:
        if (nlh) {
@@ -1356,6 +1464,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set;
        struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
        const struct nlattr *nla;
@@ -1374,7 +1483,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
                       attr[IPSET_ATTR_LINENO] == NULL))))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1410,6 +1519,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
            const struct nlmsghdr *nlh,
            const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set;
        struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
        const struct nlattr *nla;
@@ -1428,7 +1538,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
                       attr[IPSET_ATTR_LINENO] == NULL))))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1464,6 +1574,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
             const struct nlmsghdr *nlh,
             const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        struct ip_set *set;
        struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {};
        int ret = 0;
@@ -1474,7 +1585,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
                     !flag_nested(attr[IPSET_ATTR_DATA])))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1499,6 +1610,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
              const struct nlmsghdr *nlh,
              const struct nlattr * const attr[])
 {
+       struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
        const struct ip_set *set;
        struct sk_buff *skb2;
        struct nlmsghdr *nlh2;
@@ -1508,7 +1620,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
                     attr[IPSET_ATTR_SETNAME] == NULL))
                return -IPSET_ERR_PROTOCOL;
 
-       set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+       set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME]));
        if (set == NULL)
                return -ENOENT;
 
@@ -1733,8 +1845,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
        unsigned int *op;
        void *data;
        int copylen = *len, ret = 0;
+       struct net *net = sock_net(sk);
+       struct ip_set_net *inst = ip_set_pernet(net);
 
-       if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+       if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
        if (optval != SO_IP_SET)
                return -EBADF;
@@ -1783,22 +1897,39 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
                }
                req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
                nfnl_lock(NFNL_SUBSYS_IPSET);
-               find_set_and_id(req_get->set.name, &id);
+               find_set_and_id(inst, req_get->set.name, &id);
                req_get->set.index = id;
                nfnl_unlock(NFNL_SUBSYS_IPSET);
                goto copy;
        }
+       case IP_SET_OP_GET_FNAME: {
+               struct ip_set_req_get_set_family *req_get = data;
+               ip_set_id_t id;
+
+               if (*len != sizeof(struct ip_set_req_get_set_family)) {
+                       ret = -EINVAL;
+                       goto done;
+               }
+               req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
+               nfnl_lock(NFNL_SUBSYS_IPSET);
+               find_set_and_id(inst, req_get->set.name, &id);
+               req_get->set.index = id;
+               if (id != IPSET_INVALID_ID)
+                       req_get->family = nfnl_set(inst, id)->family;
+               nfnl_unlock(NFNL_SUBSYS_IPSET);
+               goto copy;
+       }
        case IP_SET_OP_GET_BYINDEX: {
                struct ip_set_req_get_set *req_get = data;
                struct ip_set *set;
 
                if (*len != sizeof(struct ip_set_req_get_set) ||
-                   req_get->set.index >= ip_set_max) {
+                   req_get->set.index >= inst->ip_set_max) {
                        ret = -EINVAL;
                        goto done;
                }
                nfnl_lock(NFNL_SUBSYS_IPSET);
-               set = nfnl_set(req_get->set.index);
+               set = nfnl_set(inst, req_get->set.index);
                strncpy(req_get->set.name, set ? set->name : "",
                        IPSET_MAXNAMELEN);
                nfnl_unlock(NFNL_SUBSYS_IPSET);
@@ -1827,49 +1958,81 @@ static struct nf_sockopt_ops so_set __read_mostly = {
        .owner          = THIS_MODULE,
 };
 
-static int __init
-ip_set_init(void)
+static int __net_init
+ip_set_net_init(struct net *net)
 {
+       struct ip_set_net *inst = ip_set_pernet(net);
        struct ip_set **list;
-       int ret;
 
-       if (max_sets)
-               ip_set_max = max_sets;
-       if (ip_set_max >= IPSET_INVALID_ID)
-               ip_set_max = IPSET_INVALID_ID - 1;
+       inst->ip_set_max = max_sets ? max_sets : CONFIG_IP_SET_MAX;
+       if (inst->ip_set_max >= IPSET_INVALID_ID)
+               inst->ip_set_max = IPSET_INVALID_ID - 1;
 
-       list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
+       list = kzalloc(sizeof(struct ip_set *) * inst->ip_set_max, GFP_KERNEL);
        if (!list)
                return -ENOMEM;
+       inst->is_deleted = 0;
+       rcu_assign_pointer(inst->ip_set_list, list);
+       pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
+       return 0;
+}
 
-       rcu_assign_pointer(ip_set_list, list);
-       ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
+static void __net_exit
+ip_set_net_exit(struct net *net)
+{
+       struct ip_set_net *inst = ip_set_pernet(net);
+
+       struct ip_set *set = NULL;
+       ip_set_id_t i;
+
+       inst->is_deleted = 1; /* flag for ip_set_nfnl_put */
+
+       for (i = 0; i < inst->ip_set_max; i++) {
+               set = nfnl_set(inst, i);
+               if (set != NULL)
+                       ip_set_destroy_set(inst, i);
+       }
+       kfree(rcu_dereference_protected(inst->ip_set_list, 1));
+}
+
+static struct pernet_operations ip_set_net_ops = {
+       .init   = ip_set_net_init,
+       .exit   = ip_set_net_exit,
+       .id     = &ip_set_net_id,
+       .size   = sizeof(struct ip_set_net)
+};
+
+
+static int __init
+ip_set_init(void)
+{
+       int ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
        if (ret != 0) {
                pr_err("ip_set: cannot register with nfnetlink.\n");
-               kfree(list);
                return ret;
        }
        ret = nf_register_sockopt(&so_set);
        if (ret != 0) {
                pr_err("SO_SET registry failed: %d\n", ret);
                nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
-               kfree(list);
                return ret;
        }
-
-       pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL);
+       ret = register_pernet_subsys(&ip_set_net_ops);
+       if (ret) {
+               pr_err("ip_set: cannot register pernet_subsys.\n");
+               nf_unregister_sockopt(&so_set);
+               nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
+               return ret;
+       }
        return 0;
 }
 
 static void __exit
 ip_set_fini(void)
 {
-       struct ip_set **list = rcu_dereference_protected(ip_set_list, 1);
-
-       /* There can't be any existing set */
+       unregister_pernet_subsys(&ip_set_net_ops);
        nf_unregister_sockopt(&so_set);
        nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
-       kfree(list);
        pr_debug("these are the famous last words\n");
 }
 
index dac156f..29fb01d 100644 (file)
@@ -102,9 +102,25 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src,
        int protocol = iph->protocol;
 
        /* See comments at tcp_match in ip_tables.c */
-       if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET))
+       if (protocol <= 0)
                return false;
 
+       if (ntohs(iph->frag_off) & IP_OFFSET)
+               switch (protocol) {
+               case IPPROTO_TCP:
+               case IPPROTO_SCTP:
+               case IPPROTO_UDP:
+               case IPPROTO_UDPLITE:
+               case IPPROTO_ICMP:
+                       /* Port info not available for fragment offset > 0 */
+                       return false;
+               default:
+                       /* Other protocols doesn't have ports,
+                          so we can match fragments */
+                       *proto = protocol;
+                       return true;
+               }
+
        return get_port(skb, protocol, protooff, src, port, proto);
 }
 EXPORT_SYMBOL_GPL(ip_set_get_ip4_port);
index 707bc52..be6932a 100644 (file)
@@ -15,8 +15,7 @@
 #define rcu_dereference_bh(p)  rcu_dereference(p)
 #endif
 
-#define CONCAT(a, b)           a##b
-#define TOKEN(a, b)            CONCAT(a, b)
+#define rcu_dereference_bh_nfnl(p)     rcu_dereference_bh_check(p, 1)
 
 /* Hashing which uses arrays to resolve clashing. The hash table is resized
  * (doubled) when searching becomes too long.
@@ -78,10 +77,14 @@ struct htable {
 
 #define hbucket(h, i)          (&((h)->bucket[i]))
 
+#ifndef IPSET_NET_COUNT
+#define IPSET_NET_COUNT                1
+#endif
+
 /* Book-keeping of the prefixes added to the set */
 struct net_prefixes {
-       u8 cidr;                /* the different cidr values in the set */
-       u32 nets;               /* number of elements per cidr */
+       u32 nets[IPSET_NET_COUNT]; /* number of elements per cidr */
+       u8 cidr[IPSET_NET_COUNT];  /* the different cidr values in the set */
 };
 
 /* Compute the hash table size */
@@ -114,23 +117,6 @@ htable_bits(u32 hashsize)
        return bits;
 }
 
-/* Destroy the hashtable part of the set */
-static void
-ahash_destroy(struct htable *t)
-{
-       struct hbucket *n;
-       u32 i;
-
-       for (i = 0; i < jhash_size(t->htable_bits); i++) {
-               n = hbucket(t, i);
-               if (n->size)
-                       /* FIXME: use slab cache */
-                       kfree(n->value);
-       }
-
-       ip_set_free(t);
-}
-
 static int
 hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 {
@@ -156,30 +142,30 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 }
 
 #ifdef IP_SET_HASH_WITH_NETS
+#if IPSET_NET_COUNT > 1
+#define __CIDR(cidr, i)                (cidr[i])
+#else
+#define __CIDR(cidr, i)                (cidr)
+#endif
 #ifdef IP_SET_HASH_WITH_NETS_PACKED
 /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
-#define CIDR(cidr)             (cidr + 1)
+#define CIDR(cidr, i)          (__CIDR(cidr, i) + 1)
 #else
-#define CIDR(cidr)             (cidr)
+#define CIDR(cidr, i)          (__CIDR(cidr, i))
 #endif
 
 #define SET_HOST_MASK(family)  (family == AF_INET ? 32 : 128)
 
 #ifdef IP_SET_HASH_WITH_MULTI
-#define NETS_LENGTH(family)    (SET_HOST_MASK(family) + 1)
+#define NLEN(family)           (SET_HOST_MASK(family) + 1)
 #else
-#define NETS_LENGTH(family)    SET_HOST_MASK(family)
+#define NLEN(family)           SET_HOST_MASK(family)
 #endif
 
 #else
-#define NETS_LENGTH(family)    0
+#define NLEN(family)           0
 #endif /* IP_SET_HASH_WITH_NETS */
 
-#define ext_timeout(e, h)      \
-(unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT])
-#define ext_counter(e, h)      \
-(struct ip_set_counter *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_COUNTER])
-
 #endif /* _IP_SET_HASH_GEN_H */
 
 /* Family dependent templates */
@@ -194,6 +180,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 #undef mtype_data_next
 #undef mtype_elem
 
+#undef mtype_ahash_destroy
+#undef mtype_ext_cleanup
 #undef mtype_add_cidr
 #undef mtype_del_cidr
 #undef mtype_ahash_memsize
@@ -220,41 +208,43 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 
 #undef HKEY
 
-#define mtype_data_equal       TOKEN(MTYPE, _data_equal)
+#define mtype_data_equal       IPSET_TOKEN(MTYPE, _data_equal)
 #ifdef IP_SET_HASH_WITH_NETS
-#define mtype_do_data_match    TOKEN(MTYPE, _do_data_match)
+#define mtype_do_data_match    IPSET_TOKEN(MTYPE, _do_data_match)
 #else
 #define mtype_do_data_match(d) 1
 #endif
-#define mtype_data_set_flags   TOKEN(MTYPE, _data_set_flags)
-#define mtype_data_reset_flags TOKEN(MTYPE, _data_reset_flags)
-#define mtype_data_netmask     TOKEN(MTYPE, _data_netmask)
-#define mtype_data_list                TOKEN(MTYPE, _data_list)
-#define mtype_data_next                TOKEN(MTYPE, _data_next)
-#define mtype_elem             TOKEN(MTYPE, _elem)
-#define mtype_add_cidr         TOKEN(MTYPE, _add_cidr)
-#define mtype_del_cidr         TOKEN(MTYPE, _del_cidr)
-#define mtype_ahash_memsize    TOKEN(MTYPE, _ahash_memsize)
-#define mtype_flush            TOKEN(MTYPE, _flush)
-#define mtype_destroy          TOKEN(MTYPE, _destroy)
-#define mtype_gc_init          TOKEN(MTYPE, _gc_init)
-#define mtype_same_set         TOKEN(MTYPE, _same_set)
-#define mtype_kadt             TOKEN(MTYPE, _kadt)
-#define mtype_uadt             TOKEN(MTYPE, _uadt)
+#define mtype_data_set_flags   IPSET_TOKEN(MTYPE, _data_set_flags)
+#define mtype_data_reset_elem  IPSET_TOKEN(MTYPE, _data_reset_elem)
+#define mtype_data_reset_flags IPSET_TOKEN(MTYPE, _data_reset_flags)
+#define mtype_data_netmask     IPSET_TOKEN(MTYPE, _data_netmask)
+#define mtype_data_list                IPSET_TOKEN(MTYPE, _data_list)
+#define mtype_data_next                IPSET_TOKEN(MTYPE, _data_next)
+#define mtype_elem             IPSET_TOKEN(MTYPE, _elem)
+#define mtype_ahash_destroy    IPSET_TOKEN(MTYPE, _ahash_destroy)
+#define mtype_ext_cleanup      IPSET_TOKEN(MTYPE, _ext_cleanup)
+#define mtype_add_cidr         IPSET_TOKEN(MTYPE, _add_cidr)
+#define mtype_del_cidr         IPSET_TOKEN(MTYPE, _del_cidr)
+#define mtype_ahash_memsize    IPSET_TOKEN(MTYPE, _ahash_memsize)
+#define mtype_flush            IPSET_TOKEN(MTYPE, _flush)
+#define mtype_destroy          IPSET_TOKEN(MTYPE, _destroy)
+#define mtype_gc_init          IPSET_TOKEN(MTYPE, _gc_init)
+#define mtype_same_set         IPSET_TOKEN(MTYPE, _same_set)
+#define mtype_kadt             IPSET_TOKEN(MTYPE, _kadt)
+#define mtype_uadt             IPSET_TOKEN(MTYPE, _uadt)
 #define mtype                  MTYPE
 
-#define mtype_elem             TOKEN(MTYPE, _elem)
-#define mtype_add              TOKEN(MTYPE, _add)
-#define mtype_del              TOKEN(MTYPE, _del)
-#define mtype_test_cidrs       TOKEN(MTYPE, _test_cidrs)
-#define mtype_test             TOKEN(MTYPE, _test)
-#define mtype_expire           TOKEN(MTYPE, _expire)
-#define mtype_resize           TOKEN(MTYPE, _resize)
-#define mtype_head             TOKEN(MTYPE, _head)
-#define mtype_list             TOKEN(MTYPE, _list)
-#define mtype_gc               TOKEN(MTYPE, _gc)
-#define mtype_variant          TOKEN(MTYPE, _variant)
-#define mtype_data_match       TOKEN(MTYPE, _data_match)
+#define mtype_add              IPSET_TOKEN(MTYPE, _add)
+#define mtype_del              IPSET_TOKEN(MTYPE, _del)
+#define mtype_test_cidrs       IPSET_TOKEN(MTYPE, _test_cidrs)
+#define mtype_test             IPSET_TOKEN(MTYPE, _test)
+#define mtype_expire           IPSET_TOKEN(MTYPE, _expire)
+#define mtype_resize           IPSET_TOKEN(MTYPE, _resize)
+#define mtype_head             IPSET_TOKEN(MTYPE, _head)
+#define mtype_list             IPSET_TOKEN(MTYPE, _list)
+#define mtype_gc               IPSET_TOKEN(MTYPE, _gc)
+#define mtype_variant          IPSET_TOKEN(MTYPE, _variant)
+#define mtype_data_match       IPSET_TOKEN(MTYPE, _data_match)
 
 #ifndef HKEY_DATALEN
 #define HKEY_DATALEN           sizeof(struct mtype_elem)
@@ -269,13 +259,10 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 
 /* The generic hash structure */
 struct htype {
-       struct htable *table;   /* the hash table */
+       struct htable __rcu *table; /* the hash table */
        u32 maxelem;            /* max elements in the hash */
        u32 elements;           /* current element (vs timeout) */
        u32 initval;            /* random jhash init value */
-       u32 timeout;            /* timeout value, if enabled */
-       size_t dsize;           /* data struct size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
        struct timer_list gc;   /* garbage collection when timeout enabled */
        struct mtype_elem next; /* temporary storage for uadd */
 #ifdef IP_SET_HASH_WITH_MULTI
@@ -297,49 +284,49 @@ struct htype {
 /* Network cidr size book keeping when the hash stores different
  * sized networks */
 static void
-mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length)
+mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 {
        int i, j;
 
        /* Add in increasing prefix order, so larger cidr first */
-       for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) {
+       for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) {
                if (j != -1)
                        continue;
-               else if (h->nets[i].cidr < cidr)
+               else if (h->nets[i].cidr[n] < cidr)
                        j = i;
-               else if (h->nets[i].cidr == cidr) {
-                       h->nets[i].nets++;
+               else if (h->nets[i].cidr[n] == cidr) {
+                       h->nets[i].nets[n]++;
                        return;
                }
        }
        if (j != -1) {
                for (; i > j; i--) {
-                       h->nets[i].cidr = h->nets[i - 1].cidr;
-                       h->nets[i].nets = h->nets[i - 1].nets;
+                       h->nets[i].cidr[n] = h->nets[i - 1].cidr[n];
+                       h->nets[i].nets[n] = h->nets[i - 1].nets[n];
                }
        }
-       h->nets[i].cidr = cidr;
-       h->nets[i].nets = 1;
+       h->nets[i].cidr[n] = cidr;
+       h->nets[i].nets[n] = 1;
 }
 
 static void
-mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
+mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 {
        u8 i, j, net_end = nets_length - 1;
 
        for (i = 0; i < nets_length; i++) {
-               if (h->nets[i].cidr != cidr)
+               if (h->nets[i].cidr[n] != cidr)
                        continue;
-                if (h->nets[i].nets > 1 || i == net_end ||
-                    h->nets[i + 1].nets == 0) {
-                        h->nets[i].nets--;
+                if (h->nets[i].nets[n] > 1 || i == net_end ||
+                    h->nets[i + 1].nets[n] == 0) {
+                        h->nets[i].nets[n]--;
                         return;
                 }
-                for (j = i; j < net_end && h->nets[j].nets; j++) {
-                       h->nets[j].cidr = h->nets[j + 1].cidr;
-                       h->nets[j].nets = h->nets[j + 1].nets;
+                for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
+                       h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
+                       h->nets[j].nets[n] = h->nets[j + 1].nets[n];
                 }
-                h->nets[j].nets = 0;
+                h->nets[j].nets[n] = 0;
                 return;
        }
 }
@@ -347,10 +334,10 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
 
 /* Calculate the actual memory size of the set data */
 static size_t
-mtype_ahash_memsize(const struct htype *h, u8 nets_length)
+mtype_ahash_memsize(const struct htype *h, const struct htable *t,
+                   u8 nets_length, size_t dsize)
 {
        u32 i;
-       struct htable *t = h->table;
        size_t memsize = sizeof(*h)
                         + sizeof(*t)
 #ifdef IP_SET_HASH_WITH_NETS
@@ -359,35 +346,70 @@ mtype_ahash_memsize(const struct htype *h, u8 nets_length)
                         + jhash_size(t->htable_bits) * sizeof(struct hbucket);
 
        for (i = 0; i < jhash_size(t->htable_bits); i++)
-               memsize += t->bucket[i].size * h->dsize;
+               memsize += t->bucket[i].size * dsize;
 
        return memsize;
 }
 
+/* Get the ith element from the array block n */
+#define ahash_data(n, i, dsize)        \
+       ((struct mtype_elem *)((n)->value + ((i) * (dsize))))
+
+static void
+mtype_ext_cleanup(struct ip_set *set, struct hbucket *n)
+{
+       int i;
+
+       for (i = 0; i < n->pos; i++)
+               ip_set_ext_destroy(set, ahash_data(n, i, set->dsize));
+}
+
 /* Flush a hash type of set: destroy all elements */
 static void
 mtype_flush(struct ip_set *set)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t;
        struct hbucket *n;
        u32 i;
 
+       t = rcu_dereference_bh_nfnl(h->table);
        for (i = 0; i < jhash_size(t->htable_bits); i++) {
                n = hbucket(t, i);
                if (n->size) {
+                       if (set->extensions & IPSET_EXT_DESTROY)
+                               mtype_ext_cleanup(set, n);
                        n->size = n->pos = 0;
                        /* FIXME: use slab cache */
                        kfree(n->value);
                }
        }
 #ifdef IP_SET_HASH_WITH_NETS
-       memset(h->nets, 0, sizeof(struct net_prefixes)
-                          * NETS_LENGTH(set->family));
+       memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family));
 #endif
        h->elements = 0;
 }
 
+/* Destroy the hashtable part of the set */
+static void
+mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy)
+{
+       struct hbucket *n;
+       u32 i;
+
+       for (i = 0; i < jhash_size(t->htable_bits); i++) {
+               n = hbucket(t, i);
+               if (n->size) {
+                       if (set->extensions & IPSET_EXT_DESTROY && ext_destroy)
+                               mtype_ext_cleanup(set, n);
+                       /* FIXME: use slab cache */
+                       kfree(n->value);
+               }
+       }
+
+       ip_set_free(t);
+}
+
 /* Destroy a hash type of set */
 static void
 mtype_destroy(struct ip_set *set)
@@ -397,7 +419,7 @@ mtype_destroy(struct ip_set *set)
        if (set->extensions & IPSET_EXT_TIMEOUT)
                del_timer_sync(&h->gc);
 
-       ahash_destroy(h->table);
+       mtype_ahash_destroy(set, rcu_dereference_bh_nfnl(h->table), true);
 #ifdef IP_SET_HASH_WITH_RBTREE
        rbtree_destroy(&h->rbtree);
 #endif
@@ -414,10 +436,10 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
        init_timer(&h->gc);
        h->gc.data = (unsigned long) set;
        h->gc.function = gc;
-       h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+       h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&h->gc);
        pr_debug("gc initialized, run in every %u\n",
-                IPSET_GC_PERIOD(h->timeout));
+                IPSET_GC_PERIOD(set->timeout));
 }
 
 static bool
@@ -428,37 +450,40 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b)
 
        /* Resizing changes htable_bits, so we ignore it */
        return x->maxelem == y->maxelem &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
 #ifdef IP_SET_HASH_WITH_NETMASK
               x->netmask == y->netmask &&
 #endif
               a->extensions == b->extensions;
 }
 
-/* Get the ith element from the array block n */
-#define ahash_data(n, i, dsize)        \
-       ((struct mtype_elem *)((n)->value + ((i) * (dsize))))
-
 /* Delete expired elements from the hashtable */
 static void
-mtype_expire(struct htype *h, u8 nets_length, size_t dsize)
+mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
 {
-       struct htable *t = h->table;
+       struct htable *t;
        struct hbucket *n;
        struct mtype_elem *data;
        u32 i;
        int j;
+#ifdef IP_SET_HASH_WITH_NETS
+       u8 k;
+#endif
 
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
        for (i = 0; i < jhash_size(t->htable_bits); i++) {
                n = hbucket(t, i);
                for (j = 0; j < n->pos; j++) {
                        data = ahash_data(n, j, dsize);
-                       if (ip_set_timeout_expired(ext_timeout(data, h))) {
+                       if (ip_set_timeout_expired(ext_timeout(data, set))) {
                                pr_debug("expired %u/%u\n", i, j);
 #ifdef IP_SET_HASH_WITH_NETS
-                               mtype_del_cidr(h, CIDR(data->cidr),
-                                              nets_length);
+                               for (k = 0; k < IPSET_NET_COUNT; k++)
+                                       mtype_del_cidr(h, CIDR(data->cidr, k),
+                                                      nets_length, k);
 #endif
+                               ip_set_ext_destroy(set, data);
                                if (j != n->pos - 1)
                                        /* Not last one */
                                        memcpy(data,
@@ -481,6 +506,7 @@ mtype_expire(struct htype *h, u8 nets_length, size_t dsize)
                        n->value = tmp;
                }
        }
+       rcu_read_unlock_bh();
 }
 
 static void
@@ -491,10 +517,10 @@ mtype_gc(unsigned long ul_set)
 
        pr_debug("called\n");
        write_lock_bh(&set->lock);
-       mtype_expire(h, NETS_LENGTH(set->family), h->dsize);
+       mtype_expire(set, h, NLEN(set->family), set->dsize);
        write_unlock_bh(&set->lock);
 
-       h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+       h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&h->gc);
 }
 
@@ -505,7 +531,7 @@ static int
 mtype_resize(struct ip_set *set, bool retried)
 {
        struct htype *h = set->data;
-       struct htable *t, *orig = h->table;
+       struct htable *t, *orig = rcu_dereference_bh_nfnl(h->table);
        u8 htable_bits = orig->htable_bits;
 #ifdef IP_SET_HASH_WITH_NETS
        u8 flags;
@@ -520,8 +546,7 @@ mtype_resize(struct ip_set *set, bool retried)
        if (SET_WITH_TIMEOUT(set) && !retried) {
                i = h->elements;
                write_lock_bh(&set->lock);
-               mtype_expire(set->data, NETS_LENGTH(set->family),
-                            h->dsize);
+               mtype_expire(set, set->data, NLEN(set->family), set->dsize);
                write_unlock_bh(&set->lock);
                if (h->elements < i)
                        return 0;
@@ -548,25 +573,25 @@ retry:
        for (i = 0; i < jhash_size(orig->htable_bits); i++) {
                n = hbucket(orig, i);
                for (j = 0; j < n->pos; j++) {
-                       data = ahash_data(n, j, h->dsize);
+                       data = ahash_data(n, j, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
                        flags = 0;
                        mtype_data_reset_flags(data, &flags);
 #endif
                        m = hbucket(t, HKEY(data, h->initval, htable_bits));
-                       ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize);
+                       ret = hbucket_elem_add(m, AHASH_MAX(h), set->dsize);
                        if (ret < 0) {
 #ifdef IP_SET_HASH_WITH_NETS
                                mtype_data_reset_flags(data, &flags);
 #endif
                                read_unlock_bh(&set->lock);
-                               ahash_destroy(t);
+                               mtype_ahash_destroy(set, t, false);
                                if (ret == -EAGAIN)
                                        goto retry;
                                return ret;
                        }
-                       d = ahash_data(m, m->pos++, h->dsize);
-                       memcpy(d, data, h->dsize);
+                       d = ahash_data(m, m->pos++, set->dsize);
+                       memcpy(d, data, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
                        mtype_data_reset_flags(d, &flags);
 #endif
@@ -581,7 +606,7 @@ retry:
 
        pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
                 orig->htable_bits, orig, t->htable_bits, t);
-       ahash_destroy(orig);
+       mtype_ahash_destroy(set, orig, false);
 
        return 0;
 }
@@ -604,7 +629,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 
        if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
                /* FIXME: when set is full, we slow down here */
-               mtype_expire(h, NETS_LENGTH(set->family), h->dsize);
+               mtype_expire(set, h, NLEN(set->family), set->dsize);
 
        if (h->elements >= h->maxelem) {
                if (net_ratelimit())
@@ -618,11 +643,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        key = HKEY(value, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
-               data = ahash_data(n, i, h->dsize);
+               data = ahash_data(n, i, set->dsize);
                if (mtype_data_equal(data, d, &multi)) {
                        if (flag_exist ||
                            (SET_WITH_TIMEOUT(set) &&
-                            ip_set_timeout_expired(ext_timeout(data, h)))) {
+                            ip_set_timeout_expired(ext_timeout(data, set)))) {
                                /* Just the extensions could be overwritten */
                                j = i;
                                goto reuse_slot;
@@ -633,30 +658,37 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                }
                /* Reuse first timed out entry */
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(data, h)) &&
+                   ip_set_timeout_expired(ext_timeout(data, set)) &&
                    j != AHASH_MAX(h) + 1)
                        j = i;
        }
 reuse_slot:
        if (j != AHASH_MAX(h) + 1) {
                /* Fill out reused slot */
-               data = ahash_data(n, j, h->dsize);
+               data = ahash_data(n, j, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
-               mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family));
-               mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+               for (i = 0; i < IPSET_NET_COUNT; i++) {
+                       mtype_del_cidr(h, CIDR(data->cidr, i),
+                                      NLEN(set->family), i);
+                       mtype_add_cidr(h, CIDR(d->cidr, i),
+                                      NLEN(set->family), i);
+               }
 #endif
+               ip_set_ext_destroy(set, data);
        } else {
                /* Use/create a new slot */
                TUNE_AHASH_MAX(h, multi);
-               ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize);
+               ret = hbucket_elem_add(n, AHASH_MAX(h), set->dsize);
                if (ret != 0) {
                        if (ret == -EAGAIN)
                                mtype_data_next(&h->next, d);
                        goto out;
                }
-               data = ahash_data(n, n->pos++, h->dsize);
+               data = ahash_data(n, n->pos++, set->dsize);
 #ifdef IP_SET_HASH_WITH_NETS
-               mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+               for (i = 0; i < IPSET_NET_COUNT; i++)
+                       mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family),
+                                      i);
 #endif
                h->elements++;
        }
@@ -665,9 +697,11 @@ reuse_slot:
        mtype_data_set_flags(data, flags);
 #endif
        if (SET_WITH_TIMEOUT(set))
-               ip_set_timeout_set(ext_timeout(data, h), ext->timeout);
+               ip_set_timeout_set(ext_timeout(data, set), ext->timeout);
        if (SET_WITH_COUNTER(set))
-               ip_set_init_counter(ext_counter(data, h), ext);
+               ip_set_init_counter(ext_counter(data, set), ext);
+       if (SET_WITH_COMMENT(set))
+               ip_set_init_comment(ext_comment(data, set), ext);
 
 out:
        rcu_read_unlock_bh();
@@ -682,47 +716,60 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
          struct ip_set_ext *mext, u32 flags)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t;
        const struct mtype_elem *d = value;
        struct mtype_elem *data;
        struct hbucket *n;
-       int i;
+       int i, ret = -IPSET_ERR_EXIST;
+#ifdef IP_SET_HASH_WITH_NETS
+       u8 j;
+#endif
        u32 key, multi = 0;
 
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
        key = HKEY(value, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
-               data = ahash_data(n, i, h->dsize);
+               data = ahash_data(n, i, set->dsize);
                if (!mtype_data_equal(data, d, &multi))
                        continue;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(data, h)))
-                       return -IPSET_ERR_EXIST;
+                   ip_set_timeout_expired(ext_timeout(data, set)))
+                       goto out;
                if (i != n->pos - 1)
                        /* Not last one */
-                       memcpy(data, ahash_data(n, n->pos - 1, h->dsize),
-                              h->dsize);
+                       memcpy(data, ahash_data(n, n->pos - 1, set->dsize),
+                              set->dsize);
 
                n->pos--;
                h->elements--;
 #ifdef IP_SET_HASH_WITH_NETS
-               mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+               for (j = 0; j < IPSET_NET_COUNT; j++)
+                       mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family),
+                                      j);
 #endif
+               ip_set_ext_destroy(set, data);
                if (n->pos + AHASH_INIT_SIZE < n->size) {
                        void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
-                                           * h->dsize,
+                                           * set->dsize,
                                            GFP_ATOMIC);
-                       if (!tmp)
-                               return 0;
+                       if (!tmp) {
+                               ret = 0;
+                               goto out;
+                       }
                        n->size -= AHASH_INIT_SIZE;
-                       memcpy(tmp, n->value, n->size * h->dsize);
+                       memcpy(tmp, n->value, n->size * set->dsize);
                        kfree(n->value);
                        n->value = tmp;
                }
-               return 0;
+               ret = 0;
+               goto out;
        }
 
-       return -IPSET_ERR_EXIST;
+out:
+       rcu_read_unlock_bh();
+       return ret;
 }
 
 static inline int
@@ -730,8 +777,7 @@ mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext,
                 struct ip_set_ext *mext, struct ip_set *set, u32 flags)
 {
        if (SET_WITH_COUNTER(set))
-               ip_set_update_counter(ext_counter(data,
-                                                 (struct htype *)(set->data)),
+               ip_set_update_counter(ext_counter(data, set),
                                      ext, mext, flags);
        return mtype_do_data_match(data);
 }
@@ -745,25 +791,38 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
                 struct ip_set_ext *mext, u32 flags)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t = rcu_dereference_bh(h->table);
        struct hbucket *n;
        struct mtype_elem *data;
+#if IPSET_NET_COUNT == 2
+       struct mtype_elem orig = *d;
+       int i, j = 0, k;
+#else
        int i, j = 0;
+#endif
        u32 key, multi = 0;
-       u8 nets_length = NETS_LENGTH(set->family);
+       u8 nets_length = NLEN(set->family);
 
        pr_debug("test by nets\n");
-       for (; j < nets_length && h->nets[j].nets && !multi; j++) {
-               mtype_data_netmask(d, h->nets[j].cidr);
+       for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) {
+#if IPSET_NET_COUNT == 2
+               mtype_data_reset_elem(d, &orig);
+               mtype_data_netmask(d, h->nets[j].cidr[0], false);
+               for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi;
+                    k++) {
+                       mtype_data_netmask(d, h->nets[k].cidr[1], true);
+#else
+               mtype_data_netmask(d, h->nets[j].cidr[0]);
+#endif
                key = HKEY(d, h->initval, t->htable_bits);
                n = hbucket(t, key);
                for (i = 0; i < n->pos; i++) {
-                       data = ahash_data(n, i, h->dsize);
+                       data = ahash_data(n, i, set->dsize);
                        if (!mtype_data_equal(data, d, &multi))
                                continue;
                        if (SET_WITH_TIMEOUT(set)) {
                                if (!ip_set_timeout_expired(
-                                                       ext_timeout(data, h)))
+                                               ext_timeout(data, set)))
                                        return mtype_data_match(data, ext,
                                                                mext, set,
                                                                flags);
@@ -774,6 +833,9 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
                                return mtype_data_match(data, ext,
                                                        mext, set, flags);
                }
+#if IPSET_NET_COUNT == 2
+               }
+#endif
        }
        return 0;
 }
@@ -785,30 +847,41 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
           struct ip_set_ext *mext, u32 flags)
 {
        struct htype *h = set->data;
-       struct htable *t = h->table;
+       struct htable *t;
        struct mtype_elem *d = value;
        struct hbucket *n;
        struct mtype_elem *data;
-       int i;
+       int i, ret = 0;
        u32 key, multi = 0;
 
+       rcu_read_lock_bh();
+       t = rcu_dereference_bh(h->table);
 #ifdef IP_SET_HASH_WITH_NETS
        /* If we test an IP address and not a network address,
         * try all possible network sizes */
-       if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
-               return mtype_test_cidrs(set, d, ext, mext, flags);
+       for (i = 0; i < IPSET_NET_COUNT; i++)
+               if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family))
+                       break;
+       if (i == IPSET_NET_COUNT) {
+               ret = mtype_test_cidrs(set, d, ext, mext, flags);
+               goto out;
+       }
 #endif
 
        key = HKEY(d, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
-               data = ahash_data(n, i, h->dsize);
+               data = ahash_data(n, i, set->dsize);
                if (mtype_data_equal(data, d, &multi) &&
                    !(SET_WITH_TIMEOUT(set) &&
-                     ip_set_timeout_expired(ext_timeout(data, h))))
-                       return mtype_data_match(data, ext, mext, set, flags);
+                     ip_set_timeout_expired(ext_timeout(data, set)))) {
+                       ret = mtype_data_match(data, ext, mext, set, flags);
+                       goto out;
+               }
        }
-       return 0;
+out:
+       rcu_read_unlock_bh();
+       return ret;
 }
 
 /* Reply a HEADER request: fill out the header part of the set */
@@ -816,18 +889,18 @@ static int
 mtype_head(struct ip_set *set, struct sk_buff *skb)
 {
        const struct htype *h = set->data;
+       const struct htable *t;
        struct nlattr *nested;
        size_t memsize;
 
-       read_lock_bh(&set->lock);
-       memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family));
-       read_unlock_bh(&set->lock);
+       t = rcu_dereference_bh_nfnl(h->table);
+       memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize);
 
        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
        if (!nested)
                goto nla_put_failure;
        if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE,
-                         htonl(jhash_size(h->table->htable_bits))) ||
+                         htonl(jhash_size(t->htable_bits))) ||
            nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
                goto nla_put_failure;
 #ifdef IP_SET_HASH_WITH_NETMASK
@@ -836,12 +909,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
                goto nla_put_failure;
 #endif
        if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
-           nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
-           ((set->extensions & IPSET_EXT_TIMEOUT) &&
-            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))) ||
-           ((set->extensions & IPSET_EXT_COUNTER) &&
-            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
-                          htonl(IPSET_FLAG_WITH_COUNTERS))))
+           nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)))
+               goto nla_put_failure;
+       if (unlikely(ip_set_put_flags(skb, set)))
                goto nla_put_failure;
        ipset_nest_end(skb, nested);
 
@@ -856,11 +926,11 @@ mtype_list(const struct ip_set *set,
           struct sk_buff *skb, struct netlink_callback *cb)
 {
        const struct htype *h = set->data;
-       const struct htable *t = h->table;
+       const struct htable *t = rcu_dereference_bh_nfnl(h->table);
        struct nlattr *atd, *nested;
        const struct hbucket *n;
        const struct mtype_elem *e;
-       u32 first = cb->args[2];
+       u32 first = cb->args[IPSET_CB_ARG0];
        /* We assume that one hash bucket fills into one page */
        void *incomplete;
        int i;
@@ -869,20 +939,22 @@ mtype_list(const struct ip_set *set,
        if (!atd)
                return -EMSGSIZE;
        pr_debug("list hash set %s\n", set->name);
-       for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
+       for (; cb->args[IPSET_CB_ARG0] < jhash_size(t->htable_bits);
+            cb->args[IPSET_CB_ARG0]++) {
                incomplete = skb_tail_pointer(skb);
-               n = hbucket(t, cb->args[2]);
-               pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n);
+               n = hbucket(t, cb->args[IPSET_CB_ARG0]);
+               pr_debug("cb->arg bucket: %lu, t %p n %p\n",
+                        cb->args[IPSET_CB_ARG0], t, n);
                for (i = 0; i < n->pos; i++) {
-                       e = ahash_data(n, i, h->dsize);
+                       e = ahash_data(n, i, set->dsize);
                        if (SET_WITH_TIMEOUT(set) &&
-                           ip_set_timeout_expired(ext_timeout(e, h)))
+                           ip_set_timeout_expired(ext_timeout(e, set)))
                                continue;
                        pr_debug("list hash %lu hbucket %p i %u, data %p\n",
-                                cb->args[2], n, i, e);
+                                cb->args[IPSET_CB_ARG0], n, i, e);
                        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
                        if (!nested) {
-                               if (cb->args[2] == first) {
+                               if (cb->args[IPSET_CB_ARG0] == first) {
                                        nla_nest_cancel(skb, atd);
                                        return -EMSGSIZE;
                                } else
@@ -890,43 +962,37 @@ mtype_list(const struct ip_set *set,
                        }
                        if (mtype_data_list(skb, e))
                                goto nla_put_failure;
-                       if (SET_WITH_TIMEOUT(set) &&
-                           nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                         htonl(ip_set_timeout_get(
-                                               ext_timeout(e, h)))))
-                               goto nla_put_failure;
-                       if (SET_WITH_COUNTER(set) &&
-                           ip_set_put_counter(skb, ext_counter(e, h)))
+                       if (ip_set_put_extensions(skb, set, e, true))
                                goto nla_put_failure;
                        ipset_nest_end(skb, nested);
                }
        }
        ipset_nest_end(skb, atd);
        /* Set listing finished */
-       cb->args[2] = 0;
+       cb->args[IPSET_CB_ARG0] = 0;
 
        return 0;
 
 nla_put_failure:
        nlmsg_trim(skb, incomplete);
-       ipset_nest_end(skb, atd);
-       if (unlikely(first == cb->args[2])) {
+       if (unlikely(first == cb->args[IPSET_CB_ARG0])) {
                pr_warning("Can't list set %s: one bucket does not fit into "
                           "a message. Please report it!\n", set->name);
-               cb->args[2] = 0;
+               cb->args[IPSET_CB_ARG0] = 0;
                return -EMSGSIZE;
        }
+       ipset_nest_end(skb, atd);
        return 0;
 }
 
 static int
-TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb,
-             const struct xt_action_param *par,
-             enum ipset_adt adt, struct ip_set_adt_opt *opt);
+IPSET_TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb,
+           const struct xt_action_param *par,
+           enum ipset_adt adt, struct ip_set_adt_opt *opt);
 
 static int
-TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[],
-             enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
+IPSET_TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[],
+           enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
 
 static const struct ip_set_type_variant mtype_variant = {
        .kadt   = mtype_kadt,
@@ -946,16 +1012,17 @@ static const struct ip_set_type_variant mtype_variant = {
 
 #ifdef IP_SET_EMIT_CREATE
 static int
-TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
+IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
+                           struct nlattr *tb[], u32 flags)
 {
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-       u32 cadt_flags = 0;
        u8 hbits;
 #ifdef IP_SET_HASH_WITH_NETMASK
        u8 netmask;
 #endif
        size_t hsize;
        struct HTYPE *h;
+       struct htable *t;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -1005,7 +1072,7 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->netmask = netmask;
 #endif
        get_random_bytes(&h->initval, sizeof(h->initval));
-       h->timeout = IPSET_NO_TIMEOUT;
+       set->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
        hsize = htable_size(hbits);
@@ -1013,91 +1080,37 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
                kfree(h);
                return -ENOMEM;
        }
-       h->table = ip_set_alloc(hsize);
-       if (!h->table) {
+       t = ip_set_alloc(hsize);
+       if (!t) {
                kfree(h);
                return -ENOMEM;
        }
-       h->table->htable_bits = hbits;
+       t->htable_bits = hbits;
+       rcu_assign_pointer(h->table, t);
 
        set->data = h;
-       if (set->family ==  NFPROTO_IPV4)
-               set->variant = &TOKEN(HTYPE, 4_variant);
-       else
-               set->variant = &TOKEN(HTYPE, 6_variant);
-
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       h->timeout =
-                               ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       if (set->family == NFPROTO_IPV4) {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 4ct_elem));
-                               h->offset[IPSET_OFFSET_TIMEOUT] =
-                                       offsetof(struct TOKEN(HTYPE, 4ct_elem),
-                                                timeout);
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 4ct_elem),
-                                                counter);
-                               TOKEN(HTYPE, 4_gc_init)(set,
-                                       TOKEN(HTYPE, 4_gc));
-                       } else {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 6ct_elem));
-                               h->offset[IPSET_OFFSET_TIMEOUT] =
-                                       offsetof(struct TOKEN(HTYPE, 6ct_elem),
-                                                timeout);
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 6ct_elem),
-                                                counter);
-                               TOKEN(HTYPE, 6_gc_init)(set,
-                                       TOKEN(HTYPE, 6_gc));
-                       }
-               } else {
-                       if (set->family == NFPROTO_IPV4) {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 4c_elem));
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 4c_elem),
-                                                counter);
-                       } else {
-                               h->dsize =
-                                       sizeof(struct TOKEN(HTYPE, 6c_elem));
-                               h->offset[IPSET_OFFSET_COUNTER] =
-                                       offsetof(struct TOKEN(HTYPE, 6c_elem),
-                                                counter);
-                       }
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-               set->extensions |= IPSET_EXT_TIMEOUT;
-               if (set->family == NFPROTO_IPV4) {
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem));
-                       h->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct TOKEN(HTYPE, 4t_elem),
-                                        timeout);
-                       TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc));
-               } else {
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem));
-                       h->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct TOKEN(HTYPE, 6t_elem),
-                                        timeout);
-                       TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc));
-               }
+       if (set->family == NFPROTO_IPV4) {
+               set->variant = &IPSET_TOKEN(HTYPE, 4_variant);
+               set->dsize = ip_set_elem_len(set, tb,
+                               sizeof(struct IPSET_TOKEN(HTYPE, 4_elem)));
        } else {
+               set->variant = &IPSET_TOKEN(HTYPE, 6_variant);
+               set->dsize = ip_set_elem_len(set, tb,
+                               sizeof(struct IPSET_TOKEN(HTYPE, 6_elem)));
+       }
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                if (set->family == NFPROTO_IPV4)
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem));
+                       IPSET_TOKEN(HTYPE, 4_gc_init)(set,
+                               IPSET_TOKEN(HTYPE, 4_gc));
                else
-                       h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem));
+                       IPSET_TOKEN(HTYPE, 6_gc_init)(set,
+                               IPSET_TOKEN(HTYPE, 6_gc));
        }
 
        pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-                set->name, jhash_size(h->table->htable_bits),
-                h->table->htable_bits, h->maxelem, set->data, h->table);
+                set->name, jhash_size(t->htable_bits),
+                t->htable_bits, h->maxelem, set->data, t);
 
        return 0;
 }
index c74e6e1..e65fc24 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1       /* Counters support */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1          Counters support */
+#define IPSET_TYPE_REV_MAX     2       /* Comments support */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ip
 #define IP_SET_HASH_WITH_NETMASK
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_ip4_elem {
@@ -43,22 +44,6 @@ struct hash_ip4_elem {
        __be32 ip;
 };
 
-struct hash_ip4t_elem {
-       __be32 ip;
-       unsigned long timeout;
-};
-
-struct hash_ip4c_elem {
-       __be32 ip;
-       struct ip_set_counter counter;
-};
-
-struct hash_ip4ct_elem {
-       __be32 ip;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -99,7 +84,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip4_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        __be32 ip;
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
@@ -118,8 +103,8 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip4_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, hosts;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, hosts;
        int ret = 0;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -178,29 +163,13 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 /* Member elements */
 struct hash_ip6_elem {
        union nf_inet_addr ip;
 };
 
-struct hash_ip6t_elem {
-       union nf_inet_addr ip;
-       unsigned long timeout;
-};
-
-struct hash_ip6c_elem {
-       union nf_inet_addr ip;
-       struct ip_set_counter counter;
-};
-
-struct hash_ip6ct_elem {
-       union nf_inet_addr ip;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -253,7 +222,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip6_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
        hash_ip6_netmask(&e.ip, h->netmask);
@@ -270,7 +239,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ip6_elem e = {};
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -304,8 +273,8 @@ static struct ip_set_type hash_ip_type __read_mostly = {
        .features       = IPSET_TYPE_IP,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ip_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -324,6 +293,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 7a2d2bd..525a595 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-#define REVISION_MAX   2 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Counters support added */
+#define IPSET_TYPE_REV_MAX     3 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ipport
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_ipport4_elem {
@@ -46,31 +47,6 @@ struct hash_ipport4_elem {
        u8 padding;
 };
 
-struct hash_ipport4t_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipport4c_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipport4ct_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -116,10 +92,9 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
                  const struct xt_action_param *par,
                  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -136,8 +111,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, p = 0, port, port_to;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip, ip_to = 0, p = 0, port, port_to;
        bool with_ports = false;
        int ret;
 
@@ -222,7 +197,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_ipport6_elem {
        union nf_inet_addr ip;
@@ -231,31 +206,6 @@ struct hash_ipport6_elem {
        u8 padding;
 };
 
-struct hash_ipport6t_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipport6c_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipport6ct_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -306,10 +256,9 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
                  const struct xt_action_param *par,
                  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -326,7 +275,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        int ret;
@@ -396,8 +345,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ipport_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -419,6 +368,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 34e8a1a..f563663 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-#define REVISION_MAX   2 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Counters support added */
+#define IPSET_TYPE_REV_MAX     3 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip,port,ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port,ip");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ipportip
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements  */
 struct hash_ipportip4_elem {
@@ -47,34 +48,6 @@ struct hash_ipportip4_elem {
        u8 padding;
 };
 
-struct hash_ipportip4t_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipportip4c_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportip4ct_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 static inline bool
 hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
                          const struct hash_ipportip4_elem *ip2,
@@ -120,10 +93,9 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
                    const struct xt_action_param *par,
                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -141,8 +113,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip4_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, p = 0, port, port_to;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip, ip_to = 0, p = 0, port, port_to;
        bool with_ports = false;
        int ret;
 
@@ -231,7 +203,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_ipportip6_elem {
        union nf_inet_addr ip;
@@ -241,34 +213,6 @@ struct hash_ipportip6_elem {
        u8 padding;
 };
 
-struct hash_ipportip6t_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       unsigned long timeout;
-};
-
-struct hash_ipportip6c_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportip6ct_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 proto;
-       u8 padding;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -319,10 +263,9 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
                    const struct xt_action_param *par,
                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
@@ -340,7 +283,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportip *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip6_elem e = { };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        int ret;
@@ -414,8 +357,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
        .dimension      = IPSET_DIM_THREE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ipportip_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -437,6 +380,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index f15f3e2..5d87fe8 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-/*                     2    Range as input support for IPv4 added */
-/*                     3    nomatch flag support added */
-#define REVISION_MAX   4 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Range as input support for IPv4 added */
+/*                             3    nomatch flag support added */
+/*                             4    Counters support added */
+#define IPSET_TYPE_REV_MAX     5 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port,net");
 
 /* Type specific function prefix */
@@ -46,7 +47,7 @@ MODULE_ALIAS("ip_set_hash:ip,port,net");
 #define IP_SET_HASH_WITH_PROTO
 #define IP_SET_HASH_WITH_NETS
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_ipportnet4_elem {
@@ -58,37 +59,6 @@ struct hash_ipportnet4_elem {
        u8 proto;
 };
 
-struct hash_ipportnet4t_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       unsigned long timeout;
-};
-
-struct hash_ipportnet4c_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportnet4ct_elem {
-       __be32 ip;
-       __be32 ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -170,9 +140,9 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -195,9 +165,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip, ip_to, p = 0, port, port_to;
-       u32 ip2_from, ip2_to, ip2_last, ip2;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, p = 0, port, port_to;
+       u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -272,7 +242,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                if (ip > ip_to)
                        swap(ip, ip_to);
        } else if (tb[IPSET_ATTR_CIDR]) {
-               u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
                if (!cidr || cidr > 32)
                        return -IPSET_ERR_INVALID_CIDR;
@@ -306,9 +276,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                                                       : port;
                for (; p <= port_to; p++) {
                        e.port = htons(p);
-                       ip2 = retried
-                             && ip == ntohl(h->next.ip)
-                             && p == ntohs(h->next.port)
+                       ip2 = retried &&
+                             ip == ntohl(h->next.ip) &&
+                             p == ntohs(h->next.port)
                                ? ntohl(h->next.ip2) : ip2_from;
                        while (!after(ip2, ip2_to)) {
                                e.ip2 = htonl(ip2);
@@ -328,7 +298,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_ipportnet6_elem {
        union nf_inet_addr ip;
@@ -339,37 +309,6 @@ struct hash_ipportnet6_elem {
        u8 proto;
 };
 
-struct hash_ipportnet6t_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       unsigned long timeout;
-};
-
-struct hash_ipportnet6c_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-};
-
-struct hash_ipportnet6ct_elem {
-       union nf_inet_addr ip;
-       union nf_inet_addr ip2;
-       __be16 port;
-       u8 cidr:7;
-       u8 nomatch:1;
-       u8 proto;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -454,9 +393,9 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -479,7 +418,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        u8 cidr;
@@ -574,8 +513,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
                          IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_THREE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_ipportnet_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -600,6 +539,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 223e9f5..8295cf4 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    Range as input support for IPv4 added */
-/*                     2    nomatch flag support added */
-#define REVISION_MAX   3 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    Range as input support for IPv4 added */
+/*                             2    nomatch flag support added */
+/*                             3    Counters support added */
+#define IPSET_TYPE_REV_MAX     4 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:net");
 
 /* Type specific function prefix */
 #define HTYPE          hash_net
 #define IP_SET_HASH_WITH_NETS
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements  */
 struct hash_net4_elem {
@@ -46,31 +47,6 @@ struct hash_net4_elem {
        u8 cidr;
 };
 
-struct hash_net4t_elem {
-       __be32 ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       unsigned long timeout;
-};
-
-struct hash_net4c_elem {
-       __be32 ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-};
-
-struct hash_net4ct_elem {
-       __be32 ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -143,9 +119,9 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (e.cidr == 0)
                return -EINVAL;
@@ -165,8 +141,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net4_elem e = { .cidr = HOST_MASK };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip = 0, ip_to, last;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, last;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -228,7 +204,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_net6_elem {
        union nf_inet_addr ip;
@@ -237,31 +213,6 @@ struct hash_net6_elem {
        u8 cidr;
 };
 
-struct hash_net6t_elem {
-       union nf_inet_addr ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       unsigned long timeout;
-};
-
-struct hash_net6c_elem {
-       union nf_inet_addr ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-};
-
-struct hash_net6ct_elem {
-       union nf_inet_addr ip;
-       u16 padding0;
-       u8 nomatch;
-       u8 cidr;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -338,9 +289,9 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (e.cidr == 0)
                return -EINVAL;
@@ -357,10 +308,9 @@ static int
 hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
               enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-       const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net6_elem e = { .cidr = HOST_MASK };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -406,8 +356,8 @@ static struct ip_set_type hash_net_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_net_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -425,6 +375,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 7d798d5..3f64a66 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    nomatch flag support added */
-/*                     2    /0 support added */
-#define REVISION_MAX   3 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    nomatch flag support added */
+/*                             2    /0 support added */
+/*                             3    Counters support added */
+#define IPSET_TYPE_REV_MAX     4 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:net,iface", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:net,iface", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:net,iface");
 
 /* Interface name rbtree */
@@ -134,7 +135,7 @@ iface_add(struct rb_root *root, const char **iface)
 
 #define STREQ(a, b)    (strcmp(a, b) == 0)
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 struct hash_netiface4_elem_hashed {
        __be32 ip;
@@ -144,7 +145,7 @@ struct hash_netiface4_elem_hashed {
        u8 elem;
 };
 
-/* Member elements without timeout */
+/* Member elements */
 struct hash_netiface4_elem {
        __be32 ip;
        u8 physdev;
@@ -154,37 +155,6 @@ struct hash_netiface4_elem {
        const char *iface;
 };
 
-struct hash_netiface4t_elem {
-       __be32 ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       unsigned long timeout;
-};
-
-struct hash_netiface4c_elem {
-       __be32 ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-};
-
-struct hash_netiface4ct_elem {
-       __be32 ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -265,10 +235,10 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
                .elem = 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        int ret;
 
        if (e.cidr == 0)
@@ -319,8 +289,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 ip = 0, ip_to, last;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, last;
        char iface[IFNAMSIZ];
        int ret;
 
@@ -399,7 +369,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_netiface6_elem_hashed {
        union nf_inet_addr ip;
@@ -418,37 +388,6 @@ struct hash_netiface6_elem {
        const char *iface;
 };
 
-struct hash_netiface6t_elem {
-       union nf_inet_addr ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       unsigned long timeout;
-};
-
-struct hash_netiface6c_elem {
-       union nf_inet_addr ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-};
-
-struct hash_netiface6ct_elem {
-       union nf_inet_addr ip;
-       u8 physdev;
-       u8 cidr;
-       u8 nomatch;
-       u8 elem;
-       const char *iface;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -534,10 +473,10 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
                .elem = 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
        int ret;
 
        if (e.cidr == 0)
@@ -584,7 +523,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        char iface[IFNAMSIZ];
        int ret;
 
@@ -645,8 +584,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
                          IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_netiface_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -668,6 +607,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c
new file mode 100644 (file)
index 0000000..2bc2dec
--- /dev/null
@@ -0,0 +1,481 @@
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
+ *
+ * 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.
+ */
+
+/* Kernel module implementing an IP set type: the hash:net type */
+
+#include <linux/jhash.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ipset/pfxlen.h>
+#include <linux/netfilter/ipset/ip_set.h>
+#include <linux/netfilter/ipset/ip_set_hash.h>
+
+#define IPSET_TYPE_REV_MIN     0
+#define IPSET_TYPE_REV_MAX     0
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
+IP_SET_MODULE_DESC("hash:net,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
+MODULE_ALIAS("ip_set_hash:net,net");
+
+/* Type specific function prefix */
+#define HTYPE          hash_netnet
+#define IP_SET_HASH_WITH_NETS
+#define IPSET_NET_COUNT 2
+
+/* IPv4 variants */
+
+/* Member elements  */
+struct hash_netnet4_elem {
+       union {
+               __be32 ip[2];
+               __be64 ipcmp;
+       };
+       u8 nomatch;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+};
+
+/* Common functions */
+
+static inline bool
+hash_netnet4_data_equal(const struct hash_netnet4_elem *ip1,
+                    const struct hash_netnet4_elem *ip2,
+                    u32 *multi)
+{
+       return ip1->ipcmp == ip2->ipcmp &&
+              ip2->ccmp == ip2->ccmp;
+}
+
+static inline int
+hash_netnet4_do_data_match(const struct hash_netnet4_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netnet4_data_set_flags(struct hash_netnet4_elem *elem, u32 flags)
+{
+       elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
+}
+
+static inline void
+hash_netnet4_data_reset_flags(struct hash_netnet4_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netnet4_data_reset_elem(struct hash_netnet4_elem *elem,
+                         struct hash_netnet4_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netnet4_data_netmask(struct hash_netnet4_elem *elem, u8 cidr, bool inner)
+{
+       if (inner) {
+               elem->ip[1] &= ip_set_netmask(cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               elem->ip[0] &= ip_set_netmask(cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netnet4_data_list(struct sk_buff *skb,
+                   const struct hash_netnet4_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) ||
+           nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netnet4_data_next(struct hash_netnet4_elem *next,
+                   const struct hash_netnet4_elem *d)
+{
+       next->ipcmp = d->ipcmp;
+}
+
+#define MTYPE          hash_netnet4
+#define PF             4
+#define HOST_MASK      32
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
+              const struct xt_action_param *par,
+              enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet4_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
+
+       ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
+       ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]);
+       e.ip[0] &= ip_set_netmask(e.cidr[0]);
+       e.ip[1] &= ip_set_netmask(e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
+              enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       const struct hash_netnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet4_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, last;
+       u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2;
+       u8 cidr, cidr2;
+       int ret;
+
+       e.cidr[0] = e.cidr[1] = HOST_MASK;
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+             ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[0] = cidr;
+       }
+
+       if (tb[IPSET_ATTR_CIDR2]) {
+               cidr2 = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+               if (!cidr2 || cidr2 > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[1] = cidr2;
+       }
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] &&
+                                  tb[IPSET_ATTR_IP2_TO])) {
+               e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
+               e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
+               ret = adtfn(set, &e, &ext, &ext, flags);
+               return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+                      ip_set_eexist(ret, flags) ? 0 : ret;
+       }
+
+       ip_to = ip;
+       if (tb[IPSET_ATTR_IP_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+               if (ret)
+                       return ret;
+               if (ip_to < ip)
+                       swap(ip, ip_to);
+               if (ip + UINT_MAX == ip_to)
+                       return -IPSET_ERR_HASH_RANGE;
+       }
+
+       ip2_to = ip2_from;
+       if (tb[IPSET_ATTR_IP2_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
+               if (ret)
+                       return ret;
+               if (ip2_to < ip2_from)
+                       swap(ip2_from, ip2_to);
+               if (ip2_from + UINT_MAX == ip2_to)
+                       return -IPSET_ERR_HASH_RANGE;
+
+       }
+
+       if (retried)
+               ip = ntohl(h->next.ip[0]);
+
+       while (!after(ip, ip_to)) {
+               e.ip[0] = htonl(ip);
+               last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+               e.cidr[0] = cidr;
+               ip2 = (retried &&
+                      ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1])
+                                                  : ip2_from;
+               while (!after(ip2, ip2_to)) {
+                       e.ip[1] = htonl(ip2);
+                       last2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr2);
+                       e.cidr[1] = cidr2;
+                       ret = adtfn(set, &e, &ext, &ext, flags);
+                       if (ret && !ip_set_eexist(ret, flags))
+                               return ret;
+                       else
+                               ret = 0;
+                       ip2 = last2 + 1;
+               }
+               ip = last + 1;
+       }
+       return ret;
+}
+
+/* IPv6 variants */
+
+struct hash_netnet6_elem {
+       union nf_inet_addr ip[2];
+       u8 nomatch;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+};
+
+/* Common functions */
+
+static inline bool
+hash_netnet6_data_equal(const struct hash_netnet6_elem *ip1,
+                    const struct hash_netnet6_elem *ip2,
+                    u32 *multi)
+{
+       return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) &&
+              ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) &&
+              ip1->ccmp == ip2->ccmp;
+}
+
+static inline int
+hash_netnet6_do_data_match(const struct hash_netnet6_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netnet6_data_set_flags(struct hash_netnet6_elem *elem, u32 flags)
+{
+       elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
+}
+
+static inline void
+hash_netnet6_data_reset_flags(struct hash_netnet6_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netnet6_data_reset_elem(struct hash_netnet6_elem *elem,
+                         struct hash_netnet6_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netnet6_data_netmask(struct hash_netnet6_elem *elem, u8 cidr, bool inner)
+{
+       if (inner) {
+               ip6_netmask(&elem->ip[1], cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               ip6_netmask(&elem->ip[0], cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netnet6_data_list(struct sk_buff *skb,
+                   const struct hash_netnet6_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) ||
+           nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netnet6_data_next(struct hash_netnet4_elem *next,
+                   const struct hash_netnet6_elem *d)
+{
+}
+
+#undef MTYPE
+#undef PF
+#undef HOST_MASK
+
+#define MTYPE          hash_netnet6
+#define PF             6
+#define HOST_MASK      128
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
+              const struct xt_action_param *par,
+              enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet6_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK;
+
+       ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6);
+       ip6addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1].in6);
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
+              enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netnet6_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       int ret;
+
+       e.cidr[0] = e.cidr[1] = HOST_MASK;
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+       if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO]))
+               return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) ||
+             ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR])
+               e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+       if (tb[IPSET_ATTR_CIDR2])
+               e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+
+       if (!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] ||
+           e.cidr[1] > HOST_MASK)
+               return -IPSET_ERR_INVALID_CIDR;
+
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       ret = adtfn(set, &e, &ext, &ext, flags);
+
+       return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+              ip_set_eexist(ret, flags) ? 0 : ret;
+}
+
+static struct ip_set_type hash_netnet_type __read_mostly = {
+       .name           = "hash:net,net",
+       .protocol       = IPSET_PROTOCOL,
+       .features       = IPSET_TYPE_IP | IPSET_TYPE_IP2 | IPSET_TYPE_NOMATCH,
+       .dimension      = IPSET_DIM_TWO,
+       .family         = NFPROTO_UNSPEC,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
+       .create         = hash_netnet_create,
+       .create_policy  = {
+               [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
+               [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
+               [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
+               [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+       },
+       .adt_policy     = {
+               [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2]        = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2_TO]     = { .type = NLA_NESTED },
+               [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
+               [IPSET_ATTR_CIDR2]      = { .type = NLA_U8 },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+               [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
+               [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init
+hash_netnet_init(void)
+{
+       return ip_set_type_register(&hash_netnet_type);
+}
+
+static void __exit
+hash_netnet_fini(void)
+{
+       ip_set_type_unregister(&hash_netnet_type);
+}
+
+module_init(hash_netnet_init);
+module_exit(hash_netnet_fini);
index 09d6690..7097fb0 100644 (file)
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
-#define REVISION_MIN   0
-/*                     1    SCTP and UDPLITE support added */
-/*                     2    Range as input support for IPv4 added */
-/*                     3    nomatch flag support added */
-#define REVISION_MAX   4 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    SCTP and UDPLITE support added */
+/*                             2    Range as input support for IPv4 added */
+/*                             3    nomatch flag support added */
+/*                             4    Counters support added */
+#define IPSET_TYPE_REV_MAX     5 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("hash:net,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_hash:net,port");
 
 /* Type specific function prefix */
@@ -45,7 +46,7 @@ MODULE_ALIAS("ip_set_hash:net,port");
  */
 #define IP_SET_HASH_WITH_NETS_PACKED
 
-/* IPv4 variants */
+/* IPv4 variant */
 
 /* Member elements */
 struct hash_netport4_elem {
@@ -56,34 +57,6 @@ struct hash_netport4_elem {
        u8 nomatch:1;
 };
 
-struct hash_netport4t_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       unsigned long timeout;
-};
-
-struct hash_netport4c_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-};
-
-struct hash_netport4ct_elem {
-       __be32 ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -162,9 +135,9 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -186,8 +159,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
-       u32 port, port_to, p = 0, ip = 0, ip_to, last;
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 port, port_to, p = 0, ip = 0, ip_to = 0, last;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -287,7 +260,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        return ret;
 }
 
-/* IPv6 variants */
+/* IPv6 variant */
 
 struct hash_netport6_elem {
        union nf_inet_addr ip;
@@ -297,34 +270,6 @@ struct hash_netport6_elem {
        u8 nomatch:1;
 };
 
-struct hash_netport6t_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       unsigned long timeout;
-};
-
-struct hash_netport6c_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-};
-
-struct hash_netport6ct_elem {
-       union nf_inet_addr ip;
-       __be16 port;
-       u8 proto;
-       u8 cidr:7;
-       u8 nomatch:1;
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 /* Common functions */
 
 static inline bool
@@ -407,9 +352,9 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport6_elem e = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
+               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
        };
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        if (adt == IPSET_TEST)
                e.cidr = HOST_MASK - 1;
@@ -431,7 +376,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport6_elem e = { .cidr = HOST_MASK  - 1 };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 port, port_to;
        bool with_ports = false;
        u8 cidr;
@@ -518,8 +463,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH,
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = hash_netport_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -542,6 +487,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
new file mode 100644 (file)
index 0000000..703d119
--- /dev/null
@@ -0,0 +1,586 @@
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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.
+ */
+
+/* Kernel module implementing an IP set type: the hash:ip,port,net type */
+
+#include <linux/jhash.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/netlink.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/ipset/pfxlen.h>
+#include <linux/netfilter/ipset/ip_set.h>
+#include <linux/netfilter/ipset/ip_set_getport.h>
+#include <linux/netfilter/ipset/ip_set_hash.h>
+
+#define IPSET_TYPE_REV_MIN     0
+#define IPSET_TYPE_REV_MAX     0 /* Comments support added */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
+IP_SET_MODULE_DESC("hash:net,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
+MODULE_ALIAS("ip_set_hash:net,port,net");
+
+/* Type specific function prefix */
+#define HTYPE          hash_netportnet
+#define IP_SET_HASH_WITH_PROTO
+#define IP_SET_HASH_WITH_NETS
+#define IPSET_NET_COUNT 2
+
+/* IPv4 variant */
+
+/* Member elements */
+struct hash_netportnet4_elem {
+       union {
+               __be32 ip[2];
+               __be64 ipcmp;
+       };
+       __be16 port;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+       u8 nomatch:1;
+       u8 proto;
+};
+
+/* Common functions */
+
+static inline bool
+hash_netportnet4_data_equal(const struct hash_netportnet4_elem *ip1,
+                          const struct hash_netportnet4_elem *ip2,
+                          u32 *multi)
+{
+       return ip1->ipcmp == ip2->ipcmp &&
+              ip1->ccmp == ip2->ccmp &&
+              ip1->port == ip2->port &&
+              ip1->proto == ip2->proto;
+}
+
+static inline int
+hash_netportnet4_do_data_match(const struct hash_netportnet4_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netportnet4_data_set_flags(struct hash_netportnet4_elem *elem, u32 flags)
+{
+       elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
+}
+
+static inline void
+hash_netportnet4_data_reset_flags(struct hash_netportnet4_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netportnet4_data_reset_elem(struct hash_netportnet4_elem *elem,
+                               struct hash_netportnet4_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netportnet4_data_netmask(struct hash_netportnet4_elem *elem,
+                             u8 cidr, bool inner)
+{
+       if (inner) {
+               elem->ip[1] &= ip_set_netmask(cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               elem->ip[0] &= ip_set_netmask(cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netportnet4_data_list(struct sk_buff *skb,
+                         const struct hash_netportnet4_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) ||
+           nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) ||
+           nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netportnet4_data_next(struct hash_netportnet4_elem *next,
+                         const struct hash_netportnet4_elem *d)
+{
+       next->ipcmp = d->ipcmp;
+       next->port = d->port;
+}
+
+#define MTYPE          hash_netportnet4
+#define PF             4
+#define HOST_MASK      32
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
+                    const struct xt_action_param *par,
+                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet4_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
+
+       if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
+                                &e.port, &e.proto))
+               return -EINVAL;
+
+       ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
+       ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1]);
+       e.ip[0] &= ip_set_netmask(e.cidr[0]);
+       e.ip[1] &= ip_set_netmask(e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
+                    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet4_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to;
+       u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
+       bool with_ports = false;
+       u8 cidr, cidr2;
+       int ret;
+
+       e.cidr[0] = e.cidr[1] = HOST_MASK;
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+             ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[0] = cidr;
+       }
+
+       if (tb[IPSET_ATTR_CIDR2]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               e.cidr[1] = cidr;
+       }
+
+       if (tb[IPSET_ATTR_PORT])
+               e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+       else
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_PROTO]) {
+               e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+               with_ports = ip_set_proto_with_ports(e.proto);
+
+               if (e.proto == 0)
+                       return -IPSET_ERR_INVALID_PROTO;
+       } else
+               return -IPSET_ERR_MISSING_PROTO;
+
+       if (!(with_ports || e.proto == IPPROTO_ICMP))
+               e.port = 0;
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
+       if (adt == IPSET_TEST ||
+           !(tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) {
+               e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
+               e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
+               ret = adtfn(set, &e, &ext, &ext, flags);
+               return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+                      ip_set_eexist(ret, flags) ? 0 : ret;
+       }
+
+       ip_to = ip;
+       if (tb[IPSET_ATTR_IP_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+               if (ret)
+                       return ret;
+               if (ip > ip_to)
+                       swap(ip, ip_to);
+               if (unlikely(ip + UINT_MAX == ip_to))
+                       return -IPSET_ERR_HASH_RANGE;
+       }
+
+       port_to = port = ntohs(e.port);
+       if (tb[IPSET_ATTR_PORT_TO]) {
+               port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
+               if (port > port_to)
+                       swap(port, port_to);
+       }
+
+       ip2_to = ip2_from;
+       if (tb[IPSET_ATTR_IP2_TO]) {
+               ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
+               if (ret)
+                       return ret;
+               if (ip2_from > ip2_to)
+                       swap(ip2_from, ip2_to);
+               if (unlikely(ip2_from + UINT_MAX == ip2_to))
+                       return -IPSET_ERR_HASH_RANGE;
+       }
+
+       if (retried)
+               ip = ntohl(h->next.ip[0]);
+
+       while (!after(ip, ip_to)) {
+               e.ip[0] = htonl(ip);
+               ip_last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+               e.cidr[0] = cidr;
+               p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port)
+                                                         : port;
+               for (; p <= port_to; p++) {
+                       e.port = htons(p);
+                       ip2 = (retried && ip == ntohl(h->next.ip[0]) &&
+                              p == ntohs(h->next.port)) ? ntohl(h->next.ip[1])
+                                                        : ip2_from;
+                       while (!after(ip2, ip2_to)) {
+                               e.ip[1] = htonl(ip2);
+                               ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
+                                                               &cidr2);
+                               e.cidr[1] = cidr2;
+                               ret = adtfn(set, &e, &ext, &ext, flags);
+                               if (ret && !ip_set_eexist(ret, flags))
+                                       return ret;
+                               else
+                                       ret = 0;
+                               ip2 = ip2_last + 1;
+                       }
+               }
+               ip = ip_last + 1;
+       }
+       return ret;
+}
+
+/* IPv6 variant */
+
+struct hash_netportnet6_elem {
+       union nf_inet_addr ip[2];
+       __be16 port;
+       union {
+               u8 cidr[2];
+               u16 ccmp;
+       };
+       u8 nomatch:1;
+       u8 proto;
+};
+
+/* Common functions */
+
+static inline bool
+hash_netportnet6_data_equal(const struct hash_netportnet6_elem *ip1,
+                          const struct hash_netportnet6_elem *ip2,
+                          u32 *multi)
+{
+       return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) &&
+              ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) &&
+              ip1->ccmp == ip2->ccmp &&
+              ip1->port == ip2->port &&
+              ip1->proto == ip2->proto;
+}
+
+static inline int
+hash_netportnet6_do_data_match(const struct hash_netportnet6_elem *elem)
+{
+       return elem->nomatch ? -ENOTEMPTY : 1;
+}
+
+static inline void
+hash_netportnet6_data_set_flags(struct hash_netportnet6_elem *elem, u32 flags)
+{
+       elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
+}
+
+static inline void
+hash_netportnet6_data_reset_flags(struct hash_netportnet6_elem *elem, u8 *flags)
+{
+       swap(*flags, elem->nomatch);
+}
+
+static inline void
+hash_netportnet6_data_reset_elem(struct hash_netportnet6_elem *elem,
+                               struct hash_netportnet6_elem *orig)
+{
+       elem->ip[1] = orig->ip[1];
+}
+
+static inline void
+hash_netportnet6_data_netmask(struct hash_netportnet6_elem *elem,
+                             u8 cidr, bool inner)
+{
+       if (inner) {
+               ip6_netmask(&elem->ip[1], cidr);
+               elem->cidr[1] = cidr;
+       } else {
+               ip6_netmask(&elem->ip[0], cidr);
+               elem->cidr[0] = cidr;
+       }
+}
+
+static bool
+hash_netportnet6_data_list(struct sk_buff *skb,
+                         const struct hash_netportnet6_elem *data)
+{
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
+       if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) ||
+           nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) ||
+           nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) ||
+           nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) ||
+           nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
+           (flags &&
+            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return 1;
+}
+
+static inline void
+hash_netportnet6_data_next(struct hash_netportnet4_elem *next,
+                         const struct hash_netportnet6_elem *d)
+{
+       next->port = d->port;
+}
+
+#undef MTYPE
+#undef PF
+#undef HOST_MASK
+
+#define MTYPE          hash_netportnet6
+#define PF             6
+#define HOST_MASK      128
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
+
+static int
+hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
+                    const struct xt_action_param *par,
+                    enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet6_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+
+       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       if (adt == IPSET_TEST)
+               e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK;
+
+       if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
+                                &e.port, &e.proto))
+               return -EINVAL;
+
+       ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6);
+       ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1].in6);
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
+}
+
+static int
+hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
+                    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
+{
+       const struct hash_netportnet *h = set->data;
+       ipset_adtfn adtfn = set->variant->adt[adt];
+       struct hash_netportnet6_elem e = { };
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       u32 port, port_to;
+       bool with_ports = false;
+       int ret;
+
+       e.cidr[0] = e.cidr[1] = HOST_MASK;
+       if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
+                    !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
+               return -IPSET_ERR_PROTOCOL;
+       if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO]))
+               return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
+
+       if (tb[IPSET_ATTR_LINENO])
+               *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
+       ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) ||
+             ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) ||
+             ip_set_get_extensions(set, tb, &ext);
+       if (ret)
+               return ret;
+
+       if (tb[IPSET_ATTR_CIDR])
+               e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+
+       if (tb[IPSET_ATTR_CIDR2])
+               e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+
+       if (unlikely(!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] ||
+                    e.cidr[1] > HOST_MASK))
+               return -IPSET_ERR_INVALID_CIDR;
+
+       ip6_netmask(&e.ip[0], e.cidr[0]);
+       ip6_netmask(&e.ip[1], e.cidr[1]);
+
+       if (tb[IPSET_ATTR_PORT])
+               e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+       else
+               return -IPSET_ERR_PROTOCOL;
+
+       if (tb[IPSET_ATTR_PROTO]) {
+               e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+               with_ports = ip_set_proto_with_ports(e.proto);
+
+               if (e.proto == 0)
+                       return -IPSET_ERR_INVALID_PROTO;
+       } else
+               return -IPSET_ERR_MISSING_PROTO;
+
+       if (!(with_ports || e.proto == IPPROTO_ICMPV6))
+               e.port = 0;
+
+       if (tb[IPSET_ATTR_CADT_FLAGS]) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (IPSET_FLAG_NOMATCH << 16);
+       }
+
+       if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
+               ret = adtfn(set, &e, &ext, &ext, flags);
+               return ip_set_enomatch(ret, flags, adt, set) ? -ret :
+                      ip_set_eexist(ret, flags) ? 0 : ret;
+       }
+
+       port = ntohs(e.port);
+       port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
+       if (port > port_to)
+               swap(port, port_to);
+
+       if (retried)
+               port = ntohs(h->next.port);
+       for (; port <= port_to; port++) {
+               e.port = htons(port);
+               ret = adtfn(set, &e, &ext, &ext, flags);
+
+               if (ret && !ip_set_eexist(ret, flags))
+                       return ret;
+               else
+                       ret = 0;
+       }
+       return ret;
+}
+
+static struct ip_set_type hash_netportnet_type __read_mostly = {
+       .name           = "hash:net,port,net",
+       .protocol       = IPSET_PROTOCOL,
+       .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 |
+                         IPSET_TYPE_NOMATCH,
+       .dimension      = IPSET_DIM_THREE,
+       .family         = NFPROTO_UNSPEC,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
+       .create         = hash_netportnet_create,
+       .create_policy  = {
+               [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
+               [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
+               [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
+               [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+       },
+       .adt_policy     = {
+               [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2]        = { .type = NLA_NESTED },
+               [IPSET_ATTR_IP2_TO]     = { .type = NLA_NESTED },
+               [IPSET_ATTR_PORT]       = { .type = NLA_U16 },
+               [IPSET_ATTR_PORT_TO]    = { .type = NLA_U16 },
+               [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
+               [IPSET_ATTR_CIDR2]      = { .type = NLA_U8 },
+               [IPSET_ATTR_PROTO]      = { .type = NLA_U8 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+               [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
+               [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
+               [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
+       },
+       .me             = THIS_MODULE,
+};
+
+static int __init
+hash_netportnet_init(void)
+{
+       return ip_set_type_register(&hash_netportnet_type);
+}
+
+static void __exit
+hash_netportnet_fini(void)
+{
+       ip_set_type_unregister(&hash_netportnet_type);
+}
+
+module_init(hash_netportnet_init);
+module_exit(hash_netportnet_fini);
index 979b8c9..3e2317f 100644 (file)
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_list.h>
 
-#define REVISION_MIN   0
-#define REVISION_MAX   1 /* Counters support added */
+#define IPSET_TYPE_REV_MIN     0
+/*                             1    Counters support added */
+#define IPSET_TYPE_REV_MAX     2 /* Comments support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
-IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
+IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
 MODULE_ALIAS("ip_set_list:set");
 
 /* Member elements  */
@@ -28,28 +29,6 @@ struct set_elem {
        ip_set_id_t id;
 };
 
-struct sett_elem {
-       struct {
-               ip_set_id_t id;
-       } __attribute__ ((aligned));
-       unsigned long timeout;
-};
-
-struct setc_elem {
-       struct {
-               ip_set_id_t id;
-       } __attribute__ ((aligned));
-       struct ip_set_counter counter;
-};
-
-struct setct_elem {
-       struct {
-               ip_set_id_t id;
-       } __attribute__ ((aligned));
-       struct ip_set_counter counter;
-       unsigned long timeout;
-};
-
 struct set_adt_elem {
        ip_set_id_t id;
        ip_set_id_t refid;
@@ -58,24 +37,14 @@ struct set_adt_elem {
 
 /* Type structure */
 struct list_set {
-       size_t dsize;           /* element size */
-       size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
        u32 size;               /* size of set list array */
-       u32 timeout;            /* timeout value */
        struct timer_list gc;   /* garbage collection */
+       struct net *net;        /* namespace */
        struct set_elem members[0]; /* the set members */
 };
 
-static inline struct set_elem *
-list_set_elem(const struct list_set *map, u32 id)
-{
-       return (struct set_elem *)((void *)map->members + id * map->dsize);
-}
-
-#define ext_timeout(e, m)      \
-(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
-#define ext_counter(e, m)      \
-(struct ip_set_counter *)((void *)(e) + (m)->offset[IPSET_OFFSET_COUNTER])
+#define list_set_elem(set, map, id)    \
+       (struct set_elem *)((void *)(map)->members + (id) * (set)->dsize)
 
 static int
 list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
@@ -92,16 +61,16 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
        if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
                opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                ret = ip_set_test(e->id, skb, par, opt);
                if (ret > 0) {
                        if (SET_WITH_COUNTER(set))
-                               ip_set_update_counter(ext_counter(e, map),
+                               ip_set_update_counter(ext_counter(e, set),
                                                      ext, &opt->ext,
                                                      cmdflags);
                        return ret;
@@ -121,11 +90,11 @@ list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
        int ret;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                ret = ip_set_add(e->id, skb, par, opt);
                if (ret == 0)
@@ -145,11 +114,11 @@ list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
        int ret;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                ret = ip_set_del(e->id, skb, par, opt);
                if (ret == 0)
@@ -163,8 +132,7 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
              const struct xt_action_param *par,
              enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-       struct list_set *map = set->data;
-       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+       struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        switch (adt) {
        case IPSET_TEST:
@@ -188,10 +156,10 @@ id_eq(const struct ip_set *set, u32 i, ip_set_id_t id)
        if (i >= map->size)
                return 0;
 
-       e = list_set_elem(map, i);
+       e = list_set_elem(set, map, i);
        return !!(e->id == id &&
                 !(SET_WITH_TIMEOUT(set) &&
-                  ip_set_timeout_expired(ext_timeout(e, map))));
+                  ip_set_timeout_expired(ext_timeout(e, set))));
 }
 
 static int
@@ -199,28 +167,36 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
             const struct ip_set_ext *ext)
 {
        struct list_set *map = set->data;
-       struct set_elem *e = list_set_elem(map, i);
+       struct set_elem *e = list_set_elem(set, map, i);
 
        if (e->id != IPSET_INVALID_ID) {
-               if (i == map->size - 1)
+               if (i == map->size - 1) {
                        /* Last element replaced: e.g. add new,before,last */
-                       ip_set_put_byindex(e->id);
-               else {
-                       struct set_elem *x = list_set_elem(map, map->size - 1);
+                       ip_set_put_byindex(map->net, e->id);
+                       ip_set_ext_destroy(set, e);
+               } else {
+                       struct set_elem *x = list_set_elem(set, map,
+                                                          map->size - 1);
 
                        /* Last element pushed off */
-                       if (x->id != IPSET_INVALID_ID)
-                               ip_set_put_byindex(x->id);
-                       memmove(list_set_elem(map, i + 1), e,
-                               map->dsize * (map->size - (i + 1)));
+                       if (x->id != IPSET_INVALID_ID) {
+                               ip_set_put_byindex(map->net, x->id);
+                               ip_set_ext_destroy(set, x);
+                       }
+                       memmove(list_set_elem(set, map, i + 1), e,
+                               set->dsize * (map->size - (i + 1)));
+                       /* Extensions must be initialized to zero */
+                       memset(e, 0, set->dsize);
                }
        }
 
        e->id = d->id;
        if (SET_WITH_TIMEOUT(set))
-               ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+               ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
        if (SET_WITH_COUNTER(set))
-               ip_set_init_counter(ext_counter(e, map), ext);
+               ip_set_init_counter(ext_counter(e, set), ext);
+       if (SET_WITH_COMMENT(set))
+               ip_set_init_comment(ext_comment(e, set), ext);
        return 0;
 }
 
@@ -228,16 +204,17 @@ static int
 list_set_del(struct ip_set *set, u32 i)
 {
        struct list_set *map = set->data;
-       struct set_elem *e = list_set_elem(map, i);
+       struct set_elem *e = list_set_elem(set, map, i);
 
-       ip_set_put_byindex(e->id);
+       ip_set_put_byindex(map->net, e->id);
+       ip_set_ext_destroy(set, e);
 
        if (i < map->size - 1)
-               memmove(e, list_set_elem(map, i + 1),
-                       map->dsize * (map->size - (i + 1)));
+               memmove(e, list_set_elem(set, map, i + 1),
+                       set->dsize * (map->size - (i + 1)));
 
        /* Last element */
-       e = list_set_elem(map, map->size - 1);
+       e = list_set_elem(set, map, map->size - 1);
        e->id = IPSET_INVALID_ID;
        return 0;
 }
@@ -247,13 +224,16 @@ set_cleanup_entries(struct ip_set *set)
 {
        struct list_set *map = set->data;
        struct set_elem *e;
-       u32 i;
+       u32 i = 0;
 
-       for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+       while (i < map->size) {
+               e = list_set_elem(set, map, i);
                if (e->id != IPSET_INVALID_ID &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        list_set_del(set, i);
+                       /* Check element moved to position i in next loop */
+               else
+                       i++;
        }
 }
 
@@ -268,11 +248,11 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        int ret;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return 0;
                else if (SET_WITH_TIMEOUT(set) &&
-                        ip_set_timeout_expired(ext_timeout(e, map)))
+                        ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                else if (e->id != d->id)
                        continue;
@@ -299,14 +279,14 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        bool flag_exist = flags & IPSET_FLAG_EXIST;
        u32 i, ret = 0;
 
+       if (SET_WITH_TIMEOUT(set))
+               set_cleanup_entries(set);
+
        /* Check already added element */
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        goto insert;
-               else if (SET_WITH_TIMEOUT(set) &&
-                        ip_set_timeout_expired(ext_timeout(e, map)))
-                       continue;
                else if (e->id != d->id)
                        continue;
 
@@ -319,18 +299,22 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                        /* Can't re-add */
                        return -IPSET_ERR_EXIST;
                /* Update extensions */
+               ip_set_ext_destroy(set, e);
+
                if (SET_WITH_TIMEOUT(set))
-                       ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+                       ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
                if (SET_WITH_COUNTER(set))
-                       ip_set_init_counter(ext_counter(e, map), ext);
+                       ip_set_init_counter(ext_counter(e, set), ext);
+               if (SET_WITH_COMMENT(set))
+                       ip_set_init_comment(ext_comment(e, set), ext);
                /* Set is already added to the list */
-               ip_set_put_byindex(d->id);
+               ip_set_put_byindex(map->net, d->id);
                return 0;
        }
 insert:
        ret = -IPSET_ERR_LIST_FULL;
        for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
                                : list_set_add(set, i, d, ext);
@@ -355,12 +339,12 @@ list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
        u32 i;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        return d->before != 0 ? -IPSET_ERR_REF_EXIST
                                              : -IPSET_ERR_EXIST;
                else if (SET_WITH_TIMEOUT(set) &&
-                        ip_set_timeout_expired(ext_timeout(e, map)))
+                        ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                else if (e->id != d->id)
                        continue;
@@ -386,7 +370,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
        struct list_set *map = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
-       struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+       struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        struct ip_set *s;
        int ret = 0;
 
@@ -403,7 +387,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
        ret = ip_set_get_extensions(set, tb, &ext);
        if (ret)
                return ret;
-       e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
+       e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
        if (e.id == IPSET_INVALID_ID)
                return -IPSET_ERR_NAME;
        /* "Loop detection" */
@@ -423,7 +407,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
        }
 
        if (tb[IPSET_ATTR_NAMEREF]) {
-               e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
+               e.refid = ip_set_get_byname(map->net,
+                                           nla_data(tb[IPSET_ATTR_NAMEREF]),
                                            &s);
                if (e.refid == IPSET_INVALID_ID) {
                        ret = -IPSET_ERR_NAMEREF;
@@ -439,9 +424,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 
 finish:
        if (e.refid != IPSET_INVALID_ID)
-               ip_set_put_byindex(e.refid);
+               ip_set_put_byindex(map->net, e.refid);
        if (adt != IPSET_ADD || ret)
-               ip_set_put_byindex(e.id);
+               ip_set_put_byindex(map->net, e.id);
 
        return ip_set_eexist(ret, flags) ? 0 : ret;
 }
@@ -454,9 +439,10 @@ list_set_flush(struct ip_set *set)
        u32 i;
 
        for (i = 0; i < map->size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                if (e->id != IPSET_INVALID_ID) {
-                       ip_set_put_byindex(e->id);
+                       ip_set_put_byindex(map->net, e->id);
+                       ip_set_ext_destroy(set, e);
                        e->id = IPSET_INVALID_ID;
                }
        }
@@ -485,14 +471,11 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
        if (!nested)
                goto nla_put_failure;
        if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
-           (SET_WITH_TIMEOUT(set) &&
-            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
-           (SET_WITH_COUNTER(set) &&
-            nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
-                          htonl(IPSET_FLAG_WITH_COUNTERS))) ||
            nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
            nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
-                         htonl(sizeof(*map) + map->size * map->dsize)))
+                         htonl(sizeof(*map) + map->size * set->dsize)))
+               goto nla_put_failure;
+       if (unlikely(ip_set_put_flags(skb, set)))
                goto nla_put_failure;
        ipset_nest_end(skb, nested);
 
@@ -507,19 +490,20 @@ list_set_list(const struct ip_set *set,
 {
        const struct list_set *map = set->data;
        struct nlattr *atd, *nested;
-       u32 i, first = cb->args[2];
+       u32 i, first = cb->args[IPSET_CB_ARG0];
        const struct set_elem *e;
 
        atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
        if (!atd)
                return -EMSGSIZE;
-       for (; cb->args[2] < map->size; cb->args[2]++) {
-               i = cb->args[2];
-               e = list_set_elem(map, i);
+       for (; cb->args[IPSET_CB_ARG0] < map->size;
+            cb->args[IPSET_CB_ARG0]++) {
+               i = cb->args[IPSET_CB_ARG0];
+               e = list_set_elem(set, map, i);
                if (e->id == IPSET_INVALID_ID)
                        goto finish;
                if (SET_WITH_TIMEOUT(set) &&
-                   ip_set_timeout_expired(ext_timeout(e, map)))
+                   ip_set_timeout_expired(ext_timeout(e, set)))
                        continue;
                nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
                if (!nested) {
@@ -530,31 +514,25 @@ list_set_list(const struct ip_set *set,
                                goto nla_put_failure;
                }
                if (nla_put_string(skb, IPSET_ATTR_NAME,
-                                  ip_set_name_byindex(e->id)))
-                       goto nla_put_failure;
-               if (SET_WITH_TIMEOUT(set) &&
-                   nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-                                 htonl(ip_set_timeout_get(
-                                               ext_timeout(e, map)))))
+                                  ip_set_name_byindex(map->net, e->id)))
                        goto nla_put_failure;
-               if (SET_WITH_COUNTER(set) &&
-                   ip_set_put_counter(skb, ext_counter(e, map)))
+               if (ip_set_put_extensions(skb, set, e, true))
                        goto nla_put_failure;
                ipset_nest_end(skb, nested);
        }
 finish:
        ipset_nest_end(skb, atd);
        /* Set listing finished */
-       cb->args[2] = 0;
+       cb->args[IPSET_CB_ARG0] = 0;
        return 0;
 
 nla_put_failure:
        nla_nest_cancel(skb, nested);
-       ipset_nest_end(skb, atd);
        if (unlikely(i == first)) {
-               cb->args[2] = 0;
+               cb->args[IPSET_CB_ARG0] = 0;
                return -EMSGSIZE;
        }
+       ipset_nest_end(skb, atd);
        return 0;
 }
 
@@ -565,7 +543,7 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
        const struct list_set *y = b->data;
 
        return x->size == y->size &&
-              x->timeout == y->timeout &&
+              a->timeout == b->timeout &&
               a->extensions == b->extensions;
 }
 
@@ -594,7 +572,7 @@ list_set_gc(unsigned long ul_set)
        set_cleanup_entries(set);
        write_unlock_bh(&set->lock);
 
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
@@ -606,43 +584,40 @@ list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
        init_timer(&map->gc);
        map->gc.data = (unsigned long) set;
        map->gc.function = gc;
-       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
        add_timer(&map->gc);
 }
 
 /* Create list:set type of sets */
 
-static struct list_set *
-init_list_set(struct ip_set *set, u32 size, size_t dsize,
-             unsigned long timeout)
+static bool
+init_list_set(struct net *net, struct ip_set *set, u32 size)
 {
        struct list_set *map;
        struct set_elem *e;
        u32 i;
 
-       map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
+       map = kzalloc(sizeof(*map) + size * set->dsize, GFP_KERNEL);
        if (!map)
-               return NULL;
+               return false;
 
        map->size = size;
-       map->dsize = dsize;
-       map->timeout = timeout;
+       map->net = net;
        set->data = map;
 
        for (i = 0; i < size; i++) {
-               e = list_set_elem(map, i);
+               e = list_set_elem(set, map, i);
                e->id = IPSET_INVALID_ID;
        }
 
-       return map;
+       return true;
 }
 
 static int
-list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
+list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
+               u32 flags)
 {
-       struct list_set *map;
-       u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0;
-       unsigned long timeout = 0;
+       u32 size = IP_SET_LIST_DEFAULT_SIZE;
 
        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
@@ -654,45 +629,13 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        if (size < IP_SET_LIST_MIN_SIZE)
                size = IP_SET_LIST_MIN_SIZE;
 
-       if (tb[IPSET_ATTR_CADT_FLAGS])
-               cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-       if (tb[IPSET_ATTR_TIMEOUT])
-               timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        set->variant = &set_variant;
-       if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
-               set->extensions |= IPSET_EXT_COUNTER;
-               if (tb[IPSET_ATTR_TIMEOUT]) {
-                       map = init_list_set(set, size,
-                                       sizeof(struct setct_elem), timeout);
-                       if (!map)
-                               return -ENOMEM;
-                       set->extensions |= IPSET_EXT_TIMEOUT;
-                       map->offset[IPSET_OFFSET_TIMEOUT] =
-                               offsetof(struct setct_elem, timeout);
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct setct_elem, counter);
-                       list_set_gc_init(set, list_set_gc);
-               } else {
-                       map = init_list_set(set, size,
-                                           sizeof(struct setc_elem), 0);
-                       if (!map)
-                               return -ENOMEM;
-                       map->offset[IPSET_OFFSET_COUNTER] =
-                               offsetof(struct setc_elem, counter);
-               }
-       } else if (tb[IPSET_ATTR_TIMEOUT]) {
-               map = init_list_set(set, size,
-                                   sizeof(struct sett_elem), timeout);
-               if (!map)
-                       return -ENOMEM;
-               set->extensions |= IPSET_EXT_TIMEOUT;
-               map->offset[IPSET_OFFSET_TIMEOUT] =
-                       offsetof(struct sett_elem, timeout);
+       set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem));
+       if (!init_list_set(net, set, size))
+               return -ENOMEM;
+       if (tb[IPSET_ATTR_TIMEOUT]) {
+               set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
                list_set_gc_init(set, list_set_gc);
-       } else {
-               map = init_list_set(set, size, sizeof(struct set_elem), 0);
-               if (!map)
-                       return -ENOMEM;
        }
        return 0;
 }
@@ -703,8 +646,8 @@ static struct ip_set_type list_set_type __read_mostly = {
        .features       = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
-       .revision_min   = REVISION_MIN,
-       .revision_max   = REVISION_MAX,
+       .revision_min   = IPSET_TYPE_REV_MIN,
+       .revision_max   = IPSET_TYPE_REV_MAX,
        .create         = list_set_create,
        .create_policy  = {
                [IPSET_ATTR_SIZE]       = { .type = NLA_U32 },
@@ -721,6 +664,7 @@ static struct ip_set_type list_set_type __read_mostly = {
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
+               [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING },
        },
        .me             = THIS_MODULE,
 };
index 4f69e83..4f26ee4 100644 (file)
@@ -116,6 +116,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 
        if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
                struct ip_vs_cpu_stats *s;
+               struct ip_vs_service *svc;
 
                s = this_cpu_ptr(dest->stats.cpustats);
                s->ustats.inpkts++;
@@ -123,11 +124,14 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
                s->ustats.inbytes += skb->len;
                u64_stats_update_end(&s->syncp);
 
-               s = this_cpu_ptr(dest->svc->stats.cpustats);
+               rcu_read_lock();
+               svc = rcu_dereference(dest->svc);
+               s = this_cpu_ptr(svc->stats.cpustats);
                s->ustats.inpkts++;
                u64_stats_update_begin(&s->syncp);
                s->ustats.inbytes += skb->len;
                u64_stats_update_end(&s->syncp);
+               rcu_read_unlock();
 
                s = this_cpu_ptr(ipvs->tot_stats.cpustats);
                s->ustats.inpkts++;
@@ -146,6 +150,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 
        if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
                struct ip_vs_cpu_stats *s;
+               struct ip_vs_service *svc;
 
                s = this_cpu_ptr(dest->stats.cpustats);
                s->ustats.outpkts++;
@@ -153,11 +158,14 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
                s->ustats.outbytes += skb->len;
                u64_stats_update_end(&s->syncp);
 
-               s = this_cpu_ptr(dest->svc->stats.cpustats);
+               rcu_read_lock();
+               svc = rcu_dereference(dest->svc);
+               s = this_cpu_ptr(svc->stats.cpustats);
                s->ustats.outpkts++;
                u64_stats_update_begin(&s->syncp);
                s->ustats.outbytes += skb->len;
                u64_stats_update_end(&s->syncp);
+               rcu_read_unlock();
 
                s = this_cpu_ptr(ipvs->tot_stats.cpustats);
                s->ustats.outpkts++;
@@ -1131,12 +1139,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
        ip_vs_fill_iph_skb(af, skb, &iph);
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6) {
-               if (!iph.fragoffs && skb_nfct_reasm(skb)) {
-                       struct sk_buff *reasm = skb_nfct_reasm(skb);
-                       /* Save fw mark for coming frags */
-                       reasm->ipvs_property = 1;
-                       reasm->mark = skb->mark;
-               }
                if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
                        int related;
                        int verdict = ip_vs_out_icmp_v6(skb, &related,
@@ -1231,11 +1233,11 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
             const struct net_device *in, const struct net_device *out,
             int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_out(hooknum, skb, AF_INET);
+       return ip_vs_out(ops->hooknum, skb, AF_INET);
 }
 
 /*
@@ -1243,11 +1245,11 @@ ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb,
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
                   const struct net_device *in, const struct net_device *out,
                   int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_out(hooknum, skb, AF_INET);
+       return ip_vs_out(ops->hooknum, skb, AF_INET);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1258,11 +1260,11 @@ ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb,
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
             const struct net_device *in, const struct net_device *out,
             int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_out(hooknum, skb, AF_INET6);
+       return ip_vs_out(ops->hooknum, skb, AF_INET6);
 }
 
 /*
@@ -1270,11 +1272,11 @@ ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb,
  *     Check if packet is reply for established ip_vs_conn.
  */
 static unsigned int
-ip_vs_local_reply6(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
                   const struct net_device *in, const struct net_device *out,
                   int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_out(hooknum, skb, AF_INET6);
+       return ip_vs_out(ops->hooknum, skb, AF_INET6);
 }
 
 #endif
@@ -1606,12 +1608,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
 
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6) {
-               if (!iph.fragoffs && skb_nfct_reasm(skb)) {
-                       struct sk_buff *reasm = skb_nfct_reasm(skb);
-                       /* Save fw mark for coming frags. */
-                       reasm->ipvs_property = 1;
-                       reasm->mark = skb->mark;
-               }
                if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
                        int related;
                        int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum,
@@ -1663,9 +1659,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
                /* sorry, all this trouble for a no-hit :) */
                IP_VS_DBG_PKT(12, af, pp, skb, 0,
                              "ip_vs_in: packet continues traversal as normal");
-               if (iph.fragoffs && !skb_nfct_reasm(skb)) {
+               if (iph.fragoffs) {
                        /* Fragment that couldn't be mapped to a conn entry
-                        * and don't have any pointer to a reasm skb
                         * is missing module nf_defrag_ipv6
                         */
                        IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n");
@@ -1725,12 +1720,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
  *     Schedule and forward packets from remote clients
  */
 static unsigned int
-ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
                      const struct net_device *in,
                      const struct net_device *out,
                      int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_in(hooknum, skb, AF_INET);
+       return ip_vs_in(ops->hooknum, skb, AF_INET);
 }
 
 /*
@@ -1738,58 +1733,26 @@ ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb,
  *     Schedule and forward packets from local clients
  */
 static unsigned int
-ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_in(hooknum, skb, AF_INET);
+       return ip_vs_in(ops->hooknum, skb, AF_INET);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
 
-/*
- * AF_INET6 fragment handling
- * Copy info from first fragment, to the rest of them.
- */
-static unsigned int
-ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb,
-                    const struct net_device *in,
-                    const struct net_device *out,
-                    int (*okfn)(struct sk_buff *))
-{
-       struct sk_buff *reasm = skb_nfct_reasm(skb);
-       struct net *net;
-
-       /* Skip if not a "replay" from nf_ct_frag6_output or first fragment.
-        * ipvs_property is set when checking first fragment
-        * in ip_vs_in() and ip_vs_out().
-        */
-       if (reasm)
-               IP_VS_DBG(2, "Fragment recv prop:%d\n", reasm->ipvs_property);
-       if (!reasm || !reasm->ipvs_property)
-               return NF_ACCEPT;
-
-       net = skb_net(skb);
-       if (!net_ipvs(net)->enable)
-               return NF_ACCEPT;
-
-       /* Copy stored fw mark, saved in ip_vs_{in,out} */
-       skb->mark = reasm->mark;
-
-       return NF_ACCEPT;
-}
-
 /*
  *     AF_INET6 handler in NF_INET_LOCAL_IN chain
  *     Schedule and forward packets from remote clients
  */
 static unsigned int
-ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
                      const struct net_device *in,
                      const struct net_device *out,
                      int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_in(hooknum, skb, AF_INET6);
+       return ip_vs_in(ops->hooknum, skb, AF_INET6);
 }
 
 /*
@@ -1797,11 +1760,11 @@ ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb,
  *     Schedule and forward packets from local clients
  */
 static unsigned int
-ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
                     const struct net_device *in, const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
 {
-       return ip_vs_in(hooknum, skb, AF_INET6);
+       return ip_vs_in(ops->hooknum, skb, AF_INET6);
 }
 
 #endif
@@ -1817,7 +1780,7 @@ ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb,
  *      and send them to ip_vs_in_icmp.
  */
 static unsigned int
-ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb,
                   const struct net_device *in, const struct net_device *out,
                   int (*okfn)(struct sk_buff *))
 {
@@ -1834,12 +1797,12 @@ ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb,
        if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
                return NF_ACCEPT;
 
-       return ip_vs_in_icmp(skb, &r, hooknum);
+       return ip_vs_in_icmp(skb, &r, ops->hooknum);
 }
 
 #ifdef CONFIG_IP_VS_IPV6
 static unsigned int
-ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
+ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb,
                      const struct net_device *in, const struct net_device *out,
                      int (*okfn)(struct sk_buff *))
 {
@@ -1858,7 +1821,7 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
        if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
                return NF_ACCEPT;
 
-       return ip_vs_in_icmp_v6(skb, &r, hooknum, &iphdr);
+       return ip_vs_in_icmp_v6(skb, &r, ops->hooknum, &iphdr);
 }
 #endif
 
@@ -1916,14 +1879,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
                .priority       = 100,
        },
 #ifdef CONFIG_IP_VS_IPV6
-       /* After mangle & nat fetch 2:nd fragment and following */
-       {
-               .hook           = ip_vs_preroute_frag6,
-               .owner          = THIS_MODULE,
-               .pf             = NFPROTO_IPV6,
-               .hooknum        = NF_INET_PRE_ROUTING,
-               .priority       = NF_IP6_PRI_NAT_DST + 1,
-       },
        /* After packet filtering, change source only for VS/NAT */
        {
                .hook           = ip_vs_reply6,
index c8148e4..62786a4 100644 (file)
@@ -460,7 +460,7 @@ static inline void
 __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc)
 {
        atomic_inc(&svc->refcnt);
-       dest->svc = svc;
+       rcu_assign_pointer(dest->svc, svc);
 }
 
 static void ip_vs_service_free(struct ip_vs_service *svc)
@@ -470,18 +470,25 @@ static void ip_vs_service_free(struct ip_vs_service *svc)
        kfree(svc);
 }
 
-static void
-__ip_vs_unbind_svc(struct ip_vs_dest *dest)
+static void ip_vs_service_rcu_free(struct rcu_head *head)
 {
-       struct ip_vs_service *svc = dest->svc;
+       struct ip_vs_service *svc;
 
-       dest->svc = NULL;
+       svc = container_of(head, struct ip_vs_service, rcu_head);
+       ip_vs_service_free(svc);
+}
+
+static void __ip_vs_svc_put(struct ip_vs_service *svc, bool do_delay)
+{
        if (atomic_dec_and_test(&svc->refcnt)) {
                IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n",
                              svc->fwmark,
                              IP_VS_DBG_ADDR(svc->af, &svc->addr),
                              ntohs(svc->port));
-               ip_vs_service_free(svc);
+               if (do_delay)
+                       call_rcu(&svc->rcu_head, ip_vs_service_rcu_free);
+               else
+                       ip_vs_service_free(svc);
        }
 }
 
@@ -667,11 +674,6 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
                              IP_VS_DBG_ADDR(svc->af, &dest->addr),
                              ntohs(dest->port),
                              atomic_read(&dest->refcnt));
-               /* We can not reuse dest while in grace period
-                * because conns still can use dest->svc
-                */
-               if (test_bit(IP_VS_DEST_STATE_REMOVING, &dest->state))
-                       continue;
                if (dest->af == svc->af &&
                    ip_vs_addr_equal(svc->af, &dest->addr, daddr) &&
                    dest->port == dport &&
@@ -697,10 +699,12 @@ out:
 
 static void ip_vs_dest_free(struct ip_vs_dest *dest)
 {
+       struct ip_vs_service *svc = rcu_dereference_protected(dest->svc, 1);
+
        __ip_vs_dst_cache_reset(dest);
-       __ip_vs_unbind_svc(dest);
+       __ip_vs_svc_put(svc, false);
        free_percpu(dest->stats.cpustats);
-       kfree(dest);
+       ip_vs_dest_put_and_free(dest);
 }
 
 /*
@@ -771,6 +775,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
                    struct ip_vs_dest_user_kern *udest, int add)
 {
        struct netns_ipvs *ipvs = net_ipvs(svc->net);
+       struct ip_vs_service *old_svc;
        struct ip_vs_scheduler *sched;
        int conn_flags;
 
@@ -792,13 +797,14 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
        atomic_set(&dest->conn_flags, conn_flags);
 
        /* bind the service */
-       if (!dest->svc) {
+       old_svc = rcu_dereference_protected(dest->svc, 1);
+       if (!old_svc) {
                __ip_vs_bind_svc(dest, svc);
        } else {
-               if (dest->svc != svc) {
-                       __ip_vs_unbind_svc(dest);
+               if (old_svc != svc) {
                        ip_vs_zero_stats(&dest->stats);
                        __ip_vs_bind_svc(dest, svc);
+                       __ip_vs_svc_put(old_svc, true);
                }
        }
 
@@ -998,16 +1004,6 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
        return 0;
 }
 
-static void ip_vs_dest_wait_readers(struct rcu_head *head)
-{
-       struct ip_vs_dest *dest = container_of(head, struct ip_vs_dest,
-                                              rcu_head);
-
-       /* End of grace period after unlinking */
-       clear_bit(IP_VS_DEST_STATE_REMOVING, &dest->state);
-}
-
-
 /*
  *     Delete a destination (must be already unlinked from the service)
  */
@@ -1023,20 +1019,16 @@ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest,
         */
        ip_vs_rs_unhash(dest);
 
-       if (!cleanup) {
-               set_bit(IP_VS_DEST_STATE_REMOVING, &dest->state);
-               call_rcu(&dest->rcu_head, ip_vs_dest_wait_readers);
-       }
-
        spin_lock_bh(&ipvs->dest_trash_lock);
        IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n",
                      IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port),
                      atomic_read(&dest->refcnt));
        if (list_empty(&ipvs->dest_trash) && !cleanup)
                mod_timer(&ipvs->dest_trash_timer,
-                         jiffies + IP_VS_DEST_TRASH_PERIOD);
+                         jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1));
        /* dest lives in trash without reference */
        list_add(&dest->t_list, &ipvs->dest_trash);
+       dest->idle_start = 0;
        spin_unlock_bh(&ipvs->dest_trash_lock);
        ip_vs_dest_put(dest);
 }
@@ -1108,24 +1100,30 @@ static void ip_vs_dest_trash_expire(unsigned long data)
        struct net *net = (struct net *) data;
        struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_dest *dest, *next;
+       unsigned long now = jiffies;
 
        spin_lock(&ipvs->dest_trash_lock);
        list_for_each_entry_safe(dest, next, &ipvs->dest_trash, t_list) {
-               /* Skip if dest is in grace period */
-               if (test_bit(IP_VS_DEST_STATE_REMOVING, &dest->state))
-                       continue;
                if (atomic_read(&dest->refcnt) > 0)
                        continue;
+               if (dest->idle_start) {
+                       if (time_before(now, dest->idle_start +
+                                            IP_VS_DEST_TRASH_PERIOD))
+                               continue;
+               } else {
+                       dest->idle_start = max(1UL, now);
+                       continue;
+               }
                IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u from trash\n",
                              dest->vfwmark,
-                             IP_VS_DBG_ADDR(dest->svc->af, &dest->addr),
+                             IP_VS_DBG_ADDR(dest->af, &dest->addr),
                              ntohs(dest->port));
                list_del(&dest->t_list);
                ip_vs_dest_free(dest);
        }
        if (!list_empty(&ipvs->dest_trash))
                mod_timer(&ipvs->dest_trash_timer,
-                         jiffies + IP_VS_DEST_TRASH_PERIOD);
+                         jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1));
        spin_unlock(&ipvs->dest_trash_lock);
 }
 
@@ -1320,14 +1318,6 @@ out:
        return ret;
 }
 
-static void ip_vs_service_rcu_free(struct rcu_head *head)
-{
-       struct ip_vs_service *svc;
-
-       svc = container_of(head, struct ip_vs_service, rcu_head);
-       ip_vs_service_free(svc);
-}
-
 /*
  *     Delete a service from the service list
  *     - The service must be unlinked, unlocked and not referenced!
@@ -1376,13 +1366,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
        /*
         *    Free the service if nobody refers to it
         */
-       if (atomic_dec_and_test(&svc->refcnt)) {
-               IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n",
-                             svc->fwmark,
-                             IP_VS_DBG_ADDR(svc->af, &svc->addr),
-                             ntohs(svc->port));
-               call_rcu(&svc->rcu_head, ip_vs_service_rcu_free);
-       }
+       __ip_vs_svc_put(svc, true);
 
        /* decrease the module use count */
        ip_vs_use_count_dec();
@@ -3836,10 +3820,6 @@ void __net_exit ip_vs_control_net_cleanup(struct net *net)
 {
        struct netns_ipvs *ipvs = net_ipvs(net);
 
-       /* Some dest can be in grace period even before cleanup, we have to
-        * defer ip_vs_trash_cleanup until ip_vs_dest_wait_readers is called.
-        */
-       rcu_barrier();
        ip_vs_trash_cleanup(net);
        ip_vs_stop_estimator(net, &ipvs->tot_stats);
        ip_vs_control_net_cleanup_sysctl(net);
index 6bee6d0..1425e9a 100644 (file)
@@ -59,12 +59,13 @@ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum,
                                 struct ip_vs_cpu_stats __percpu *stats)
 {
        int i;
+       bool add = false;
 
        for_each_possible_cpu(i) {
                struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i);
                unsigned int start;
                __u64 inbytes, outbytes;
-               if (i) {
+               if (add) {
                        sum->conns += s->ustats.conns;
                        sum->inpkts += s->ustats.inpkts;
                        sum->outpkts += s->ustats.outpkts;
@@ -76,6 +77,7 @@ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum,
                        sum->inbytes += inbytes;
                        sum->outbytes += outbytes;
                } else {
+                       add = true;
                        sum->conns = s->ustats.conns;
                        sum->inpkts = s->ustats.inpkts;
                        sum->outpkts = s->ustats.outpkts;
index 1383b0e..ca056a3 100644 (file)
@@ -93,7 +93,7 @@ struct ip_vs_lblc_entry {
        struct hlist_node       list;
        int                     af;             /* address family */
        union nf_inet_addr      addr;           /* destination IP address */
-       struct ip_vs_dest __rcu *dest;          /* real server (cache) */
+       struct ip_vs_dest       *dest;          /* real server (cache) */
        unsigned long           lastuse;        /* last used time */
        struct rcu_head         rcu_head;
 };
@@ -130,20 +130,21 @@ static struct ctl_table vs_vars_table[] = {
 };
 #endif
 
-static inline void ip_vs_lblc_free(struct ip_vs_lblc_entry *en)
+static void ip_vs_lblc_rcu_free(struct rcu_head *head)
 {
-       struct ip_vs_dest *dest;
+       struct ip_vs_lblc_entry *en = container_of(head,
+                                                  struct ip_vs_lblc_entry,
+                                                  rcu_head);
 
-       hlist_del_rcu(&en->list);
-       /*
-        * We don't kfree dest because it is referred either by its service
-        * or the trash dest list.
-        */
-       dest = rcu_dereference_protected(en->dest, 1);
-       ip_vs_dest_put(dest);
-       kfree_rcu(en, rcu_head);
+       ip_vs_dest_put_and_free(en->dest);
+       kfree(en);
 }
 
+static inline void ip_vs_lblc_del(struct ip_vs_lblc_entry *en)
+{
+       hlist_del_rcu(&en->list);
+       call_rcu(&en->rcu_head, ip_vs_lblc_rcu_free);
+}
 
 /*
  *     Returns hash value for IPVS LBLC entry
@@ -203,30 +204,23 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
        struct ip_vs_lblc_entry *en;
 
        en = ip_vs_lblc_get(dest->af, tbl, daddr);
-       if (!en) {
-               en = kmalloc(sizeof(*en), GFP_ATOMIC);
-               if (!en)
-                       return NULL;
-
-               en->af = dest->af;
-               ip_vs_addr_copy(dest->af, &en->addr, daddr);
-               en->lastuse = jiffies;
+       if (en) {
+               if (en->dest == dest)
+                       return en;
+               ip_vs_lblc_del(en);
+       }
+       en = kmalloc(sizeof(*en), GFP_ATOMIC);
+       if (!en)
+               return NULL;
 
-               ip_vs_dest_hold(dest);
-               RCU_INIT_POINTER(en->dest, dest);
+       en->af = dest->af;
+       ip_vs_addr_copy(dest->af, &en->addr, daddr);
+       en->lastuse = jiffies;
 
-               ip_vs_lblc_hash(tbl, en);
-       } else {
-               struct ip_vs_dest *old_dest;
+       ip_vs_dest_hold(dest);
+       en->dest = dest;
 
-               old_dest = rcu_dereference_protected(en->dest, 1);
-               if (old_dest != dest) {
-                       ip_vs_dest_put(old_dest);
-                       ip_vs_dest_hold(dest);
-                       /* No ordering constraints for refcnt */
-                       RCU_INIT_POINTER(en->dest, dest);
-               }
-       }
+       ip_vs_lblc_hash(tbl, en);
 
        return en;
 }
@@ -246,7 +240,7 @@ static void ip_vs_lblc_flush(struct ip_vs_service *svc)
        tbl->dead = 1;
        for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) {
                hlist_for_each_entry_safe(en, next, &tbl->bucket[i], list) {
-                       ip_vs_lblc_free(en);
+                       ip_vs_lblc_del(en);
                        atomic_dec(&tbl->entries);
                }
        }
@@ -281,7 +275,7 @@ static inline void ip_vs_lblc_full_check(struct ip_vs_service *svc)
                                        sysctl_lblc_expiration(svc)))
                                continue;
 
-                       ip_vs_lblc_free(en);
+                       ip_vs_lblc_del(en);
                        atomic_dec(&tbl->entries);
                }
                spin_unlock(&svc->sched_lock);
@@ -335,7 +329,7 @@ static void ip_vs_lblc_check_expire(unsigned long data)
                        if (time_before(now, en->lastuse + ENTRY_TIMEOUT))
                                continue;
 
-                       ip_vs_lblc_free(en);
+                       ip_vs_lblc_del(en);
                        atomic_dec(&tbl->entries);
                        goal--;
                }
@@ -443,8 +437,8 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
                        continue;
 
                doh = ip_vs_dest_conn_overhead(dest);
-               if (loh * atomic_read(&dest->weight) >
-                   doh * atomic_read(&least->weight)) {
+               if ((__s64)loh * atomic_read(&dest->weight) >
+                   (__s64)doh * atomic_read(&least->weight)) {
                        least = dest;
                        loh = doh;
                }
@@ -511,7 +505,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                 * free up entries from the trash at any time.
                 */
 
-               dest = rcu_dereference(en->dest);
+               dest = en->dest;
                if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
                    atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
                        goto out;
@@ -631,7 +625,7 @@ static void __exit ip_vs_lblc_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler);
        unregister_pernet_subsys(&ip_vs_lblc_ops);
-       synchronize_rcu();
+       rcu_barrier();
 }
 
 
index 5199448..3f21a2f 100644 (file)
@@ -89,7 +89,7 @@
  */
 struct ip_vs_dest_set_elem {
        struct list_head        list;          /* list link */
-       struct ip_vs_dest __rcu *dest;         /* destination server */
+       struct ip_vs_dest       *dest;          /* destination server */
        struct rcu_head         rcu_head;
 };
 
@@ -107,11 +107,7 @@ static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set,
 
        if (check) {
                list_for_each_entry(e, &set->list, list) {
-                       struct ip_vs_dest *d;
-
-                       d = rcu_dereference_protected(e->dest, 1);
-                       if (d == dest)
-                               /* already existed */
+                       if (e->dest == dest)
                                return;
                }
        }
@@ -121,7 +117,7 @@ static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set,
                return;
 
        ip_vs_dest_hold(dest);
-       RCU_INIT_POINTER(e->dest, dest);
+       e->dest = dest;
 
        list_add_rcu(&e->list, &set->list);
        atomic_inc(&set->size);
@@ -129,22 +125,27 @@ static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set,
        set->lastmod = jiffies;
 }
 
+static void ip_vs_lblcr_elem_rcu_free(struct rcu_head *head)
+{
+       struct ip_vs_dest_set_elem *e;
+
+       e = container_of(head, struct ip_vs_dest_set_elem, rcu_head);
+       ip_vs_dest_put_and_free(e->dest);
+       kfree(e);
+}
+
 static void
 ip_vs_dest_set_erase(struct ip_vs_dest_set *set, struct ip_vs_dest *dest)
 {
        struct ip_vs_dest_set_elem *e;
 
        list_for_each_entry(e, &set->list, list) {
-               struct ip_vs_dest *d;
-
-               d = rcu_dereference_protected(e->dest, 1);
-               if (d == dest) {
+               if (e->dest == dest) {
                        /* HIT */
                        atomic_dec(&set->size);
                        set->lastmod = jiffies;
-                       ip_vs_dest_put(dest);
                        list_del_rcu(&e->list);
-                       kfree_rcu(e, rcu_head);
+                       call_rcu(&e->rcu_head, ip_vs_lblcr_elem_rcu_free);
                        break;
                }
        }
@@ -155,16 +156,8 @@ static void ip_vs_dest_set_eraseall(struct ip_vs_dest_set *set)
        struct ip_vs_dest_set_elem *e, *ep;
 
        list_for_each_entry_safe(e, ep, &set->list, list) {
-               struct ip_vs_dest *d;
-
-               d = rcu_dereference_protected(e->dest, 1);
-               /*
-                * We don't kfree dest because it is referred either
-                * by its service or by the trash dest list.
-                */
-               ip_vs_dest_put(d);
                list_del_rcu(&e->list);
-               kfree_rcu(e, rcu_head);
+               call_rcu(&e->rcu_head, ip_vs_lblcr_elem_rcu_free);
        }
 }
 
@@ -175,12 +168,9 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
        struct ip_vs_dest *dest, *least;
        int loh, doh;
 
-       if (set == NULL)
-               return NULL;
-
        /* select the first destination server, whose weight > 0 */
        list_for_each_entry_rcu(e, &set->list, list) {
-               least = rcu_dereference(e->dest);
+               least = e->dest;
                if (least->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
@@ -195,13 +185,13 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
        /* find the destination with the weighted least load */
   nextstage:
        list_for_each_entry_continue_rcu(e, &set->list, list) {
-               dest = rcu_dereference(e->dest);
+               dest = e->dest;
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
 
                doh = ip_vs_dest_conn_overhead(dest);
-               if ((loh * atomic_read(&dest->weight) >
-                    doh * atomic_read(&least->weight))
+               if (((__s64)loh * atomic_read(&dest->weight) >
+                    (__s64)doh * atomic_read(&least->weight))
                    && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
                        least = dest;
                        loh = doh;
@@ -232,7 +222,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
 
        /* select the first destination server, whose weight > 0 */
        list_for_each_entry(e, &set->list, list) {
-               most = rcu_dereference_protected(e->dest, 1);
+               most = e->dest;
                if (atomic_read(&most->weight) > 0) {
                        moh = ip_vs_dest_conn_overhead(most);
                        goto nextstage;
@@ -243,11 +233,11 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
        /* find the destination with the weighted most load */
   nextstage:
        list_for_each_entry_continue(e, &set->list, list) {
-               dest = rcu_dereference_protected(e->dest, 1);
+               dest = e->dest;
                doh = ip_vs_dest_conn_overhead(dest);
                /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */
-               if ((moh * atomic_read(&dest->weight) <
-                    doh * atomic_read(&most->weight))
+               if (((__s64)moh * atomic_read(&dest->weight) <
+                    (__s64)doh * atomic_read(&most->weight))
                    && (atomic_read(&dest->weight) > 0)) {
                        most = dest;
                        moh = doh;
@@ -611,8 +601,8 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
                        continue;
 
                doh = ip_vs_dest_conn_overhead(dest);
-               if (loh * atomic_read(&dest->weight) >
-                   doh * atomic_read(&least->weight)) {
+               if ((__s64)loh * atomic_read(&dest->weight) >
+                   (__s64)doh * atomic_read(&least->weight)) {
                        least = dest;
                        loh = doh;
                }
@@ -819,7 +809,7 @@ static void __exit ip_vs_lblcr_cleanup(void)
 {
        unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler);
        unregister_pernet_subsys(&ip_vs_lblcr_ops);
-       synchronize_rcu();
+       rcu_barrier();
 }
 
 
index d8d9860..961a6de 100644 (file)
@@ -40,7 +40,7 @@
 #include <net/ip_vs.h>
 
 
-static inline unsigned int
+static inline int
 ip_vs_nq_dest_overhead(struct ip_vs_dest *dest)
 {
        /*
@@ -59,7 +59,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                  struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least = NULL;
-       unsigned int loh = 0, doh;
+       int loh = 0, doh;
 
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
@@ -92,8 +92,8 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                }
 
                if (!least ||
-                   (loh * atomic_read(&dest->weight) >
-                    doh * atomic_read(&least->weight))) {
+                   ((__s64)loh * atomic_read(&dest->weight) >
+                    (__s64)doh * atomic_read(&least->weight))) {
                        least = dest;
                        loh = doh;
                }
index 9ef22bd..bed5f70 100644 (file)
@@ -65,7 +65,6 @@ static int get_callid(const char *dptr, unsigned int dataoff,
 static int
 ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
 {
-       struct sk_buff *reasm = skb_nfct_reasm(skb);
        struct ip_vs_iphdr iph;
        unsigned int dataoff, datalen, matchoff, matchlen;
        const char *dptr;
@@ -79,15 +78,10 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
        /* todo: IPv6 fragments:
         *       I think this only should be done for the first fragment. /HS
         */
-       if (reasm) {
-               skb = reasm;
-               dataoff = iph.thoff_reasm + sizeof(struct udphdr);
-       } else
-               dataoff = iph.len + sizeof(struct udphdr);
+       dataoff = iph.len + sizeof(struct udphdr);
 
        if (dataoff >= skb->len)
                return -EINVAL;
-       /* todo: Check if this will mess-up the reasm skb !!! /HS */
        retc = skb_linearize(skb);
        if (retc < 0)
                return retc;
index 23e596e..2f7ea75 100644 (file)
@@ -20,13 +20,18 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
        sctp_sctphdr_t *sh, _sctph;
 
        sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph);
-       if (sh == NULL)
+       if (sh == NULL) {
+               *verdict = NF_DROP;
                return 0;
+       }
 
        sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t),
                                 sizeof(_schunkh), &_schunkh);
-       if (sch == NULL)
+       if (sch == NULL) {
+               *verdict = NF_DROP;
                return 0;
+       }
+
        net = skb_net(skb);
        ipvs = net_ipvs(net);
        rcu_read_lock();
@@ -76,6 +81,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
 {
        sctp_sctphdr_t *sctph;
        unsigned int sctphoff = iph->len;
+       bool payload_csum = false;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (cp->af == AF_INET6 && iph->fragoffs)
@@ -87,19 +93,31 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
                return 0;
 
        if (unlikely(cp->app != NULL)) {
+               int ret;
+
                /* Some checks before mangling */
                if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
                        return 0;
 
                /* Call application helper if needed */
-               if (!ip_vs_app_pkt_out(cp, skb))
+               ret = ip_vs_app_pkt_out(cp, skb);
+               if (ret == 0)
                        return 0;
+               /* ret=2: csum update is needed after payload mangling */
+               if (ret == 2)
+                       payload_csum = true;
        }
 
        sctph = (void *) skb_network_header(skb) + sctphoff;
-       sctph->source = cp->vport;
 
-       sctp_nat_csum(skb, sctph, sctphoff);
+       /* Only update csum if we really have to */
+       if (sctph->source != cp->vport || payload_csum ||
+           skb->ip_summed == CHECKSUM_PARTIAL) {
+               sctph->source = cp->vport;
+               sctp_nat_csum(skb, sctph, sctphoff);
+       } else {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 
        return 1;
 }
@@ -110,6 +128,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
 {
        sctp_sctphdr_t *sctph;
        unsigned int sctphoff = iph->len;
+       bool payload_csum = false;
 
 #ifdef CONFIG_IP_VS_IPV6
        if (cp->af == AF_INET6 && iph->fragoffs)
@@ -121,19 +140,32 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
                return 0;
 
        if (unlikely(cp->app != NULL)) {
+               int ret;
+
                /* Some checks before mangling */
                if (pp->csum_check && !pp->csum_check(cp->af, skb, pp))
                        return 0;
 
                /* Call application helper if needed */
-               if (!ip_vs_app_pkt_in(cp, skb))
+               ret = ip_vs_app_pkt_in(cp, skb);
+               if (ret == 0)
                        return 0;
+               /* ret=2: csum update is needed after payload mangling */
+               if (ret == 2)
+                       payload_csum = true;
        }
 
        sctph = (void *) skb_network_header(skb) + sctphoff;
-       sctph->dest = cp->dport;
 
-       sctp_nat_csum(skb, sctph, sctphoff);
+       /* Only update csum if we really have to */
+       if (sctph->dest != cp->dport || payload_csum ||
+           (skb->ip_summed == CHECKSUM_PARTIAL &&
+            !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) {
+               sctph->dest = cp->dport;
+               sctp_nat_csum(skb, sctph, sctphoff);
+       } else if (skb->ip_summed != CHECKSUM_PARTIAL) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 
        return 1;
 }
index a5284cc..e446b9f 100644 (file)
@@ -44,7 +44,7 @@
 #include <net/ip_vs.h>
 
 
-static inline unsigned int
+static inline int
 ip_vs_sed_dest_overhead(struct ip_vs_dest *dest)
 {
        /*
@@ -63,7 +63,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                   struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least;
-       unsigned int loh, doh;
+       int loh, doh;
 
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
@@ -99,8 +99,8 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
                doh = ip_vs_sed_dest_overhead(dest);
-               if (loh * atomic_read(&dest->weight) >
-                   doh * atomic_read(&least->weight)) {
+               if ((__s64)loh * atomic_read(&dest->weight) >
+                   (__s64)doh * atomic_read(&least->weight)) {
                        least = dest;
                        loh = doh;
                }
index 3588fae..cc65b2f 100644 (file)
@@ -115,27 +115,46 @@ ip_vs_sh_get(struct ip_vs_service *svc, struct ip_vs_sh_state *s,
 }
 
 
-/* As ip_vs_sh_get, but with fallback if selected server is unavailable */
+/* As ip_vs_sh_get, but with fallback if selected server is unavailable
+ *
+ * The fallback strategy loops around the table starting from a "random"
+ * point (in fact, it is chosen to be the original hash value to make the
+ * algorithm deterministic) to find a new server.
+ */
 static inline struct ip_vs_dest *
 ip_vs_sh_get_fallback(struct ip_vs_service *svc, struct ip_vs_sh_state *s,
                      const union nf_inet_addr *addr, __be16 port)
 {
-       unsigned int offset;
-       unsigned int hash;
+       unsigned int offset, roffset;
+       unsigned int hash, ihash;
        struct ip_vs_dest *dest;
 
+       /* first try the dest it's supposed to go to */
+       ihash = ip_vs_sh_hashkey(svc->af, addr, port, 0);
+       dest = rcu_dereference(s->buckets[ihash].dest);
+       if (!dest)
+               return NULL;
+       if (!is_unavailable(dest))
+               return dest;
+
+       IP_VS_DBG_BUF(6, "SH: selected unavailable server %s:%d, reselecting",
+                     IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port));
+
+       /* if the original dest is unavailable, loop around the table
+        * starting from ihash to find a new dest
+        */
        for (offset = 0; offset < IP_VS_SH_TAB_SIZE; offset++) {
-               hash = ip_vs_sh_hashkey(svc->af, addr, port, offset);
+               roffset = (offset + ihash) % IP_VS_SH_TAB_SIZE;
+               hash = ip_vs_sh_hashkey(svc->af, addr, port, roffset);
                dest = rcu_dereference(s->buckets[hash].dest);
                if (!dest)
                        break;
-               if (is_unavailable(dest))
-                       IP_VS_DBG_BUF(6, "SH: selected unavailable server "
-                                     "%s:%d (offset %d)",
-                                     IP_VS_DBG_ADDR(svc->af, &dest->addr),
-                                     ntohs(dest->port), offset);
-               else
+               if (!is_unavailable(dest))
                        return dest;
+               IP_VS_DBG_BUF(6, "SH: selected unavailable "
+                             "server %s:%d (offset %d), reselecting",
+                             IP_VS_DBG_ADDR(svc->af, &dest->addr),
+                             ntohs(dest->port), roffset);
        }
 
        return NULL;
index 6dc1fa1..b5b4650 100644 (file)
@@ -35,7 +35,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                   struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least;
-       unsigned int loh, doh;
+       int loh, doh;
 
        IP_VS_DBG(6, "ip_vs_wlc_schedule(): Scheduling...\n");
 
@@ -71,8 +71,8 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
                if (dest->flags & IP_VS_DEST_F_OVERLOAD)
                        continue;
                doh = ip_vs_dest_conn_overhead(dest);
-               if (loh * atomic_read(&dest->weight) >
-                   doh * atomic_read(&least->weight)) {
+               if ((__s64)loh * atomic_read(&dest->weight) >
+                   (__s64)doh * atomic_read(&least->weight)) {
                        least = dest;
                        loh = doh;
                }
index 2d3030a..a4b5e2a 100644 (file)
@@ -39,21 +39,23 @@ static struct ctl_table acct_sysctl_table[] = {
 unsigned int
 seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir)
 {
-       struct nf_conn_counter *acct;
+       struct nf_conn_acct *acct;
+       struct nf_conn_counter *counter;
 
        acct = nf_conn_acct_find(ct);
        if (!acct)
                return 0;
 
+       counter = acct->counter;
        return seq_printf(s, "packets=%llu bytes=%llu ",
-                         (unsigned long long)atomic64_read(&acct[dir].packets),
-                         (unsigned long long)atomic64_read(&acct[dir].bytes));
+                         (unsigned long long)atomic64_read(&counter[dir].packets),
+                         (unsigned long long)atomic64_read(&counter[dir].bytes));
 };
 EXPORT_SYMBOL_GPL(seq_print_acct);
 
 static struct nf_ct_ext_type acct_extend __read_mostly = {
-       .len    = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]),
-       .align  = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]),
+       .len    = sizeof(struct nf_conn_acct),
+       .align  = __alignof__(struct nf_conn_acct),
        .id     = NF_CT_EXT_ACCT,
 };
 
index 5d892fe..e22d950 100644 (file)
@@ -1109,12 +1109,14 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
 
 acct:
        if (do_acct) {
-               struct nf_conn_counter *acct;
+               struct nf_conn_acct *acct;
 
                acct = nf_conn_acct_find(ct);
                if (acct) {
-                       atomic64_inc(&acct[CTINFO2DIR(ctinfo)].packets);
-                       atomic64_add(skb->len, &acct[CTINFO2DIR(ctinfo)].bytes);
+                       struct nf_conn_counter *counter = acct->counter;
+
+                       atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets);
+                       atomic64_add(skb->len, &counter[CTINFO2DIR(ctinfo)].bytes);
                }
        }
 }
@@ -1126,13 +1128,15 @@ bool __nf_ct_kill_acct(struct nf_conn *ct,
                       int do_acct)
 {
        if (do_acct) {
-               struct nf_conn_counter *acct;
+               struct nf_conn_acct *acct;
 
                acct = nf_conn_acct_find(ct);
                if (acct) {
-                       atomic64_inc(&acct[CTINFO2DIR(ctinfo)].packets);
+                       struct nf_conn_counter *counter = acct->counter;
+
+                       atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets);
                        atomic64_add(skb->len - skb_network_offset(skb),
-                                    &acct[CTINFO2DIR(ctinfo)].bytes);
+                                    &counter[CTINFO2DIR(ctinfo)].bytes);
                }
        }
 
index bdebd03..70866d1 100644 (file)
@@ -778,8 +778,8 @@ static int callforward_do_filter(const union nf_inet_addr *src,
                                   flowi6_to_flowi(&fl1), false)) {
                        if (!afinfo->route(&init_net, (struct dst_entry **)&rt2,
                                           flowi6_to_flowi(&fl2), false)) {
-                               if (!memcmp(&rt1->rt6i_gateway, &rt2->rt6i_gateway,
-                                           sizeof(rt1->rt6i_gateway)) &&
+                               if (ipv6_addr_equal(rt6_nexthop(rt1),
+                                                   rt6_nexthop(rt2)) &&
                                    rt1->dst.dev == rt2->dst.dev)
                                        ret = 1;
                                dst_release(&rt2->dst);
index eea936b..08870b8 100644 (file)
@@ -211,13 +211,23 @@ nla_put_failure:
 }
 
 static int
-dump_counters(struct sk_buff *skb, u64 pkts, u64 bytes,
-             enum ip_conntrack_dir dir)
+dump_counters(struct sk_buff *skb, struct nf_conn_acct *acct,
+             enum ip_conntrack_dir dir, int type)
 {
-       enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
+       enum ctattr_type attr = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
+       struct nf_conn_counter *counter = acct->counter;
        struct nlattr *nest_count;
+       u64 pkts, bytes;
 
-       nest_count = nla_nest_start(skb, type | NLA_F_NESTED);
+       if (type == IPCTNL_MSG_CT_GET_CTRZERO) {
+               pkts = atomic64_xchg(&counter[dir].packets, 0);
+               bytes = atomic64_xchg(&counter[dir].bytes, 0);
+       } else {
+               pkts = atomic64_read(&counter[dir].packets);
+               bytes = atomic64_read(&counter[dir].bytes);
+       }
+
+       nest_count = nla_nest_start(skb, attr | NLA_F_NESTED);
        if (!nest_count)
                goto nla_put_failure;
 
@@ -234,24 +244,19 @@ nla_put_failure:
 }
 
 static int
-ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
-                       enum ip_conntrack_dir dir, int type)
+ctnetlink_dump_acct(struct sk_buff *skb, const struct nf_conn *ct, int type)
 {
-       struct nf_conn_counter *acct;
-       u64 pkts, bytes;
+       struct nf_conn_acct *acct = nf_conn_acct_find(ct);
 
-       acct = nf_conn_acct_find(ct);
        if (!acct)
                return 0;
 
-       if (type == IPCTNL_MSG_CT_GET_CTRZERO) {
-               pkts = atomic64_xchg(&acct[dir].packets, 0);
-               bytes = atomic64_xchg(&acct[dir].bytes, 0);
-       } else {
-               pkts = atomic64_read(&acct[dir].packets);
-               bytes = atomic64_read(&acct[dir].bytes);
-       }
-       return dump_counters(skb, pkts, bytes, dir);
+       if (dump_counters(skb, acct, IP_CT_DIR_ORIGINAL, type) < 0)
+               return -1;
+       if (dump_counters(skb, acct, IP_CT_DIR_REPLY, type) < 0)
+               return -1;
+
+       return 0;
 }
 
 static int
@@ -488,8 +493,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
 
        if (ctnetlink_dump_status(skb, ct) < 0 ||
            ctnetlink_dump_timeout(skb, ct) < 0 ||
-           ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL, type) < 0 ||
-           ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY, type) < 0 ||
+           ctnetlink_dump_acct(skb, ct, type) < 0 ||
            ctnetlink_dump_timestamp(skb, ct) < 0 ||
            ctnetlink_dump_protoinfo(skb, ct) < 0 ||
            ctnetlink_dump_helpinfo(skb, ct) < 0 ||
@@ -530,7 +534,7 @@ ctnetlink_proto_size(const struct nf_conn *ct)
 }
 
 static inline size_t
-ctnetlink_counters_size(const struct nf_conn *ct)
+ctnetlink_acct_size(const struct nf_conn *ct)
 {
        if (!nf_ct_ext_exist(ct, NF_CT_EXT_ACCT))
                return 0;
@@ -579,7 +583,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct)
               + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */
               + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */
               + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */
-              + ctnetlink_counters_size(ct)
+              + ctnetlink_acct_size(ct)
               + ctnetlink_timestamp_size(ct)
               + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */
               + nla_total_size(0) /* CTA_PROTOINFO */
@@ -673,10 +677,7 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
                goto nla_put_failure;
 
        if (events & (1 << IPCT_DESTROY)) {
-               if (ctnetlink_dump_counters(skb, ct,
-                                           IP_CT_DIR_ORIGINAL, type) < 0 ||
-                   ctnetlink_dump_counters(skb, ct,
-                                           IP_CT_DIR_REPLY, type) < 0 ||
+               if (ctnetlink_dump_acct(skb, ct, type) < 0 ||
                    ctnetlink_dump_timestamp(skb, ct) < 0)
                        goto nla_put_failure;
        } else {
index e0c4373..466410e 100644 (file)
@@ -52,66 +52,8 @@ module_param(sip_direct_media, int, 0600);
 MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling "
                                   "endpoints only (default 1)");
 
-unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff,
-                               unsigned int dataoff, const char **dptr,
-                               unsigned int *datalen) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
-
-void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff,
-                                  s16 off) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook);
-
-unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
-                                      unsigned int protoff,
-                                      unsigned int dataoff,
-                                      const char **dptr,
-                                      unsigned int *datalen,
-                                      struct nf_conntrack_expect *exp,
-                                      unsigned int matchoff,
-                                      unsigned int matchlen) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook);
-
-unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff,
-                                    unsigned int dataoff,
-                                    const char **dptr,
-                                    unsigned int *datalen,
-                                    unsigned int sdpoff,
-                                    enum sdp_header_types type,
-                                    enum sdp_header_types term,
-                                    const union nf_inet_addr *addr)
-                                    __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook);
-
-unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff,
-                                    unsigned int dataoff,
-                                    const char **dptr,
-                                    unsigned int *datalen,
-                                    unsigned int matchoff,
-                                    unsigned int matchlen,
-                                    u_int16_t port) __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook);
-
-unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
-                                       unsigned int protoff,
-                                       unsigned int dataoff,
-                                       const char **dptr,
-                                       unsigned int *datalen,
-                                       unsigned int sdpoff,
-                                       const union nf_inet_addr *addr)
-                                       __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook);
-
-unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff,
-                                     unsigned int dataoff,
-                                     const char **dptr,
-                                     unsigned int *datalen,
-                                     struct nf_conntrack_expect *rtp_exp,
-                                     struct nf_conntrack_expect *rtcp_exp,
-                                     unsigned int mediaoff,
-                                     unsigned int medialen,
-                                     union nf_inet_addr *rtp_addr)
-                                     __read_mostly;
-EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook);
+const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
+EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
 
 static int string_len(const struct nf_conn *ct, const char *dptr,
                      const char *limit, int *shift)
@@ -914,8 +856,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
        int direct_rtp = 0, skip_expect = 0, ret = NF_DROP;
        u_int16_t base_port;
        __be16 rtp_port, rtcp_port;
-       typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port;
-       typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media;
+       const struct nf_nat_sip_hooks *hooks;
 
        saddr = NULL;
        if (sip_direct_media) {
@@ -966,22 +907,23 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
 #endif
                        skip_expect = 1;
        } while (!skip_expect);
-       rcu_read_unlock();
 
        base_port = ntohs(tuple.dst.u.udp.port) & ~1;
        rtp_port = htons(base_port);
        rtcp_port = htons(base_port + 1);
 
        if (direct_rtp) {
-               nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook);
-               if (nf_nat_sdp_port &&
-                   !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen,
+               hooks = rcu_dereference(nf_nat_sip_hooks);
+               if (hooks &&
+                   !hooks->sdp_port(skb, protoff, dataoff, dptr, datalen,
                                     mediaoff, medialen, ntohs(rtp_port)))
                        goto err1;
        }
 
-       if (skip_expect)
+       if (skip_expect) {
+               rcu_read_unlock();
                return NF_ACCEPT;
+       }
 
        rtp_exp = nf_ct_expect_alloc(ct);
        if (rtp_exp == NULL)
@@ -995,10 +937,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
        nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr,
                          IPPROTO_UDP, NULL, &rtcp_port);
 
-       nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook);
-       if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp)
-               ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen,
-                                      rtp_exp, rtcp_exp,
+       hooks = rcu_dereference(nf_nat_sip_hooks);
+       if (hooks && ct->status & IPS_NAT_MASK && !direct_rtp)
+               ret = hooks->sdp_media(skb, protoff, dataoff, dptr,
+                                      datalen, rtp_exp, rtcp_exp,
                                       mediaoff, medialen, daddr);
        else {
                if (nf_ct_expect_related(rtp_exp) == 0) {
@@ -1012,6 +954,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
 err2:
        nf_ct_expect_put(rtp_exp);
 err1:
+       rcu_read_unlock();
        return ret;
 }
 
@@ -1051,13 +994,12 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
        unsigned int caddr_len, maddr_len;
        unsigned int i;
        union nf_inet_addr caddr, maddr, rtp_addr;
+       const struct nf_nat_sip_hooks *hooks;
        unsigned int port;
        const struct sdp_media_type *t;
        int ret = NF_ACCEPT;
-       typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr;
-       typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session;
 
-       nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook);
+       hooks = rcu_dereference(nf_nat_sip_hooks);
 
        /* Find beginning of session description */
        if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
@@ -1125,10 +1067,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
                }
 
                /* Update media connection address if present */
-               if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
-                       ret = nf_nat_sdp_addr(skb, protoff, dataoff,
+               if (maddr_len && hooks && ct->status & IPS_NAT_MASK) {
+                       ret = hooks->sdp_addr(skb, protoff, dataoff,
                                              dptr, datalen, mediaoff,
-                                             SDP_HDR_CONNECTION, SDP_HDR_MEDIA,
+                                             SDP_HDR_CONNECTION,
+                                             SDP_HDR_MEDIA,
                                              &rtp_addr);
                        if (ret != NF_ACCEPT) {
                                nf_ct_helper_log(skb, ct, "cannot mangle SDP");
@@ -1139,10 +1082,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
        }
 
        /* Update session connection and owner addresses */
-       nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook);
-       if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK)
-               ret = nf_nat_sdp_session(skb, protoff, dataoff,
-                                        dptr, datalen, sdpoff, &rtp_addr);
+       hooks = rcu_dereference(nf_nat_sip_hooks);
+       if (hooks && ct->status & IPS_NAT_MASK)
+               ret = hooks->sdp_session(skb, protoff, dataoff,
+                                        dptr, datalen, sdpoff,
+                                        &rtp_addr);
 
        return ret;
 }
@@ -1242,11 +1186,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
        unsigned int matchoff, matchlen;
        struct nf_conntrack_expect *exp;
        union nf_inet_addr *saddr, daddr;
+       const struct nf_nat_sip_hooks *hooks;
        __be16 port;
        u8 proto;
        unsigned int expires = 0;
        int ret;
-       typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect;
 
        /* Expected connections can not register again. */
        if (ct->status & IPS_EXPECTED)
@@ -1309,10 +1253,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
        exp->helper = nfct_help(ct)->helper;
        exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
 
-       nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook);
-       if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK)
-               ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen,
-                                       exp, matchoff, matchlen);
+       hooks = rcu_dereference(nf_nat_sip_hooks);
+       if (hooks && ct->status & IPS_NAT_MASK)
+               ret = hooks->expect(skb, protoff, dataoff, dptr, datalen,
+                                   exp, matchoff, matchlen);
        else {
                if (nf_ct_expect_related(exp) != 0) {
                        nf_ct_helper_log(skb, ct, "cannot add expectation");
@@ -1515,7 +1459,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
                           unsigned int protoff, unsigned int dataoff,
                           const char **dptr, unsigned int *datalen)
 {
-       typeof(nf_nat_sip_hook) nf_nat_sip;
+       const struct nf_nat_sip_hooks *hooks;
        int ret;
 
        if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
@@ -1524,9 +1468,9 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
                ret = process_sip_response(skb, protoff, dataoff, dptr, datalen);
 
        if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
-               nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
-               if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff,
-                                             dptr, datalen)) {
+               hooks = rcu_dereference(nf_nat_sip_hooks);
+               if (hooks && !hooks->msg(skb, protoff, dataoff,
+                                        dptr, datalen)) {
                        nf_ct_helper_log(skb, ct, "cannot NAT SIP message");
                        ret = NF_DROP;
                }
@@ -1546,7 +1490,6 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
        s16 diff, tdiff = 0;
        int ret = NF_ACCEPT;
        bool term;
-       typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust;
 
        if (ctinfo != IP_CT_ESTABLISHED &&
            ctinfo != IP_CT_ESTABLISHED_REPLY)
@@ -1610,9 +1553,11 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
        }
 
        if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
-               nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook);
-               if (nf_nat_sip_seq_adjust)
-                       nf_nat_sip_seq_adjust(skb, protoff, tdiff);
+               const struct nf_nat_sip_hooks *hooks;
+
+               hooks = rcu_dereference(nf_nat_sip_hooks);
+               if (hooks)
+                       hooks->seq_adjust(skb, protoff, tdiff);
        }
 
        return ret;
index 3deec99..61a3c92 100644 (file)
 
 
 /* core.c */
-extern unsigned int nf_iterate(struct list_head *head,
-                               struct sk_buff *skb,
-                               unsigned int hook,
-                               const struct net_device *indev,
-                               const struct net_device *outdev,
-                               struct nf_hook_ops **elemp,
-                               int (*okfn)(struct sk_buff *),
-                               int hook_thresh);
+unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb,
+                       unsigned int hook, const struct net_device *indev,
+                       const struct net_device *outdev,
+                       struct nf_hook_ops **elemp,
+                       int (*okfn)(struct sk_buff *), int hook_thresh);
 
 /* nf_queue.c */
-extern int nf_queue(struct sk_buff *skb,
-                   struct nf_hook_ops *elem,
-                   u_int8_t pf, unsigned int hook,
-                   struct net_device *indev,
-                   struct net_device *outdev,
-                   int (*okfn)(struct sk_buff *),
-                   unsigned int queuenum);
-extern int __init netfilter_queue_init(void);
+int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, u_int8_t pf,
+            unsigned int hook, struct net_device *indev,
+            struct net_device *outdev, int (*okfn)(struct sk_buff *),
+            unsigned int queuenum);
+int __init netfilter_queue_init(void);
 
 /* nf_log.c */
-extern int __init netfilter_log_init(void);
+int __init netfilter_log_init(void);
 
 #endif
index 6f0f4f7..63a8154 100644 (file)
@@ -432,6 +432,26 @@ nf_nat_setup_info(struct nf_conn *ct,
 }
 EXPORT_SYMBOL(nf_nat_setup_info);
 
+unsigned int
+nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
+{
+       /* Force range to this IP; let proto decide mapping for
+        * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
+        * Use reply in case it's already been mangled (eg local packet).
+        */
+       union nf_inet_addr ip =
+               (HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
+               ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3 :
+               ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3);
+       struct nf_nat_range range = {
+               .flags          = NF_NAT_RANGE_MAP_IPS,
+               .min_addr       = ip,
+               .max_addr       = ip,
+       };
+       return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
+}
+EXPORT_SYMBOL_GPL(nf_nat_alloc_null_binding);
+
 /* Do packet manipulations according to nf_nat_setup_info. */
 unsigned int nf_nat_packet(struct nf_conn *ct,
                           enum ip_conntrack_info ctinfo,
index f979040..b4d691d 100644 (file)
@@ -625,33 +625,26 @@ static struct nf_ct_helper_expectfn sip_nat = {
 
 static void __exit nf_nat_sip_fini(void)
 {
-       RCU_INIT_POINTER(nf_nat_sip_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
-       RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
+       RCU_INIT_POINTER(nf_nat_sip_hooks, NULL);
+
        nf_ct_helper_expectfn_unregister(&sip_nat);
        synchronize_rcu();
 }
 
+static const struct nf_nat_sip_hooks sip_hooks = {
+       .msg            = nf_nat_sip,
+       .seq_adjust     = nf_nat_sip_seq_adjust,
+       .expect         = nf_nat_sip_expect,
+       .sdp_addr       = nf_nat_sdp_addr,
+       .sdp_port       = nf_nat_sdp_port,
+       .sdp_session    = nf_nat_sdp_session,
+       .sdp_media      = nf_nat_sdp_media,
+};
+
 static int __init nf_nat_sip_init(void)
 {
-       BUG_ON(nf_nat_sip_hook != NULL);
-       BUG_ON(nf_nat_sip_seq_adjust_hook != NULL);
-       BUG_ON(nf_nat_sip_expect_hook != NULL);
-       BUG_ON(nf_nat_sdp_addr_hook != NULL);
-       BUG_ON(nf_nat_sdp_port_hook != NULL);
-       BUG_ON(nf_nat_sdp_session_hook != NULL);
-       BUG_ON(nf_nat_sdp_media_hook != NULL);
-       RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip);
-       RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust);
-       RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect);
-       RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr);
-       RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port);
-       RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session);
-       RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media);
+       BUG_ON(nf_nat_sip_hooks != NULL);
+       RCU_INIT_POINTER(nf_nat_sip_hooks, &sip_hooks);
        nf_ct_helper_expectfn_register(&sip_nat);
        return 0;
 }
index 6fd967c..cdf4567 100644 (file)
@@ -24,7 +24,7 @@
 int synproxy_net_id;
 EXPORT_SYMBOL_GPL(synproxy_net_id);
 
-void
+bool
 synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
                       const struct tcphdr *th, struct synproxy_options *opts)
 {
@@ -32,7 +32,8 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
        u8 buf[40], *ptr;
 
        ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
-       BUG_ON(ptr == NULL);
+       if (ptr == NULL)
+               return false;
 
        opts->options = 0;
        while (length > 0) {
@@ -41,16 +42,16 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
 
                switch (opcode) {
                case TCPOPT_EOL:
-                       return;
+                       return true;
                case TCPOPT_NOP:
                        length--;
                        continue;
                default:
                        opsize = *ptr++;
                        if (opsize < 2)
-                               return;
+                               return true;
                        if (opsize > length)
-                               return;
+                               return true;
 
                        switch (opcode) {
                        case TCPOPT_MSS:
@@ -84,6 +85,7 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
                        length -= opsize;
                }
        }
+       return true;
 }
 EXPORT_SYMBOL_GPL(synproxy_parse_options);
 
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
new file mode 100644 (file)
index 0000000..dcddc49
--- /dev/null
@@ -0,0 +1,3275 @@
+/*
+ * Copyright (c) 2007-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+static LIST_HEAD(nf_tables_expressions);
+
+/**
+ *     nft_register_afinfo - register nf_tables address family info
+ *
+ *     @afi: address family info to register
+ *
+ *     Register the address family for use with nf_tables. Returns zero on
+ *     success or a negative errno code otherwise.
+ */
+int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
+{
+       INIT_LIST_HEAD(&afi->tables);
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       list_add_tail(&afi->list, &net->nft.af_info);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nft_register_afinfo);
+
+/**
+ *     nft_unregister_afinfo - unregister nf_tables address family info
+ *
+ *     @afi: address family info to unregister
+ *
+ *     Unregister the address family for use with nf_tables.
+ */
+void nft_unregister_afinfo(struct nft_af_info *afi)
+{
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       list_del(&afi->list);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+}
+EXPORT_SYMBOL_GPL(nft_unregister_afinfo);
+
+static struct nft_af_info *nft_afinfo_lookup(struct net *net, int family)
+{
+       struct nft_af_info *afi;
+
+       list_for_each_entry(afi, &net->nft.af_info, list) {
+               if (afi->family == family)
+                       return afi;
+       }
+       return NULL;
+}
+
+static struct nft_af_info *
+nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
+{
+       struct nft_af_info *afi;
+
+       afi = nft_afinfo_lookup(net, family);
+       if (afi != NULL)
+               return afi;
+#ifdef CONFIG_MODULES
+       if (autoload) {
+               nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+               request_module("nft-afinfo-%u", family);
+               nfnl_lock(NFNL_SUBSYS_NFTABLES);
+               afi = nft_afinfo_lookup(net, family);
+               if (afi != NULL)
+                       return ERR_PTR(-EAGAIN);
+       }
+#endif
+       return ERR_PTR(-EAFNOSUPPORT);
+}
+
+/*
+ * Tables
+ */
+
+static struct nft_table *nft_table_lookup(const struct nft_af_info *afi,
+                                         const struct nlattr *nla)
+{
+       struct nft_table *table;
+
+       list_for_each_entry(table, &afi->tables, list) {
+               if (!nla_strcmp(nla, table->name))
+                       return table;
+       }
+       return NULL;
+}
+
+static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi,
+                                               const struct nlattr *nla)
+{
+       struct nft_table *table;
+
+       if (nla == NULL)
+               return ERR_PTR(-EINVAL);
+
+       table = nft_table_lookup(afi, nla);
+       if (table != NULL)
+               return table;
+
+       return ERR_PTR(-ENOENT);
+}
+
+static inline u64 nf_tables_alloc_handle(struct nft_table *table)
+{
+       return ++table->hgenerator;
+}
+
+static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
+
+static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
+{
+       int i;
+
+       for (i=0; i<NFT_CHAIN_T_MAX; i++) {
+               if (chain_type[family][i] != NULL &&
+                   !nla_strcmp(nla, chain_type[family][i]->name))
+                       return i;
+       }
+       return -1;
+}
+
+static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,
+                                      const struct nlattr *nla,
+                                      bool autoload)
+{
+       int type;
+
+       type = __nf_tables_chain_type_lookup(afi->family, nla);
+#ifdef CONFIG_MODULES
+       if (type < 0 && autoload) {
+               nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+               request_module("nft-chain-%u-%*.s", afi->family,
+                              nla_len(nla)-1, (const char *)nla_data(nla));
+               nfnl_lock(NFNL_SUBSYS_NFTABLES);
+               type = __nf_tables_chain_type_lookup(afi->family, nla);
+       }
+#endif
+       return type;
+}
+
+static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
+       [NFTA_TABLE_NAME]       = { .type = NLA_STRING },
+       [NFTA_TABLE_FLAGS]      = { .type = NLA_U32 },
+};
+
+static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
+                                    int event, u32 flags, int family,
+                                    const struct nft_table *table)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+
+       event |= NFNL_SUBSYS_NFTABLES << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family     = family;
+       nfmsg->version          = NFNETLINK_V0;
+       nfmsg->res_id           = 0;
+
+       if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
+           nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)))
+               goto nla_put_failure;
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_table_notify(const struct sk_buff *oskb,
+                                 const struct nlmsghdr *nlh,
+                                 const struct nft_table *table,
+                                 int event, int family)
+{
+       struct sk_buff *skb;
+       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
+       u32 seq = nlh ? nlh->nlmsg_seq : 0;
+       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
+       bool report;
+       int err;
+
+       report = nlh ? nlmsg_report(nlh) : false;
+       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb == NULL)
+               goto err;
+
+       err = nf_tables_fill_table_info(skb, portid, seq, event, 0,
+                                       family, table);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
+static int nf_tables_dump_tables(struct sk_buff *skb,
+                                struct netlink_callback *cb)
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       unsigned int idx = 0, s_idx = cb->args[0];
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+
+       list_for_each_entry(afi, &net->nft.af_info, list) {
+               if (family != NFPROTO_UNSPEC && family != afi->family)
+                       continue;
+
+               list_for_each_entry(table, &afi->tables, list) {
+                       if (idx < s_idx)
+                               goto cont;
+                       if (idx > s_idx)
+                               memset(&cb->args[1], 0,
+                                      sizeof(cb->args) - sizeof(cb->args[0]));
+                       if (nf_tables_fill_table_info(skb,
+                                                     NETLINK_CB(cb->skb).portid,
+                                                     cb->nlh->nlmsg_seq,
+                                                     NFT_MSG_NEWTABLE,
+                                                     NLM_F_MULTI,
+                                                     afi->family, table) < 0)
+                               goto done;
+cont:
+                       idx++;
+               }
+       }
+done:
+       cb->args[0] = idx;
+       return skb->len;
+}
+
+static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       struct sk_buff *skb2;
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+       int err;
+
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = nf_tables_dump_tables,
+               };
+               return netlink_dump_start(nlsk, skb, nlh, &c);
+       }
+
+       afi = nf_tables_afinfo_lookup(net, family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb2)
+               return -ENOMEM;
+
+       err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid,
+                                       nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
+                                       family, table);
+       if (err < 0)
+               goto err;
+
+       return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+
+err:
+       kfree_skb(skb2);
+       return err;
+}
+
+static int nf_tables_table_enable(struct nft_table *table)
+{
+       struct nft_chain *chain;
+       int err, i = 0;
+
+       list_for_each_entry(chain, &table->chains, list) {
+               err = nf_register_hook(&nft_base_chain(chain)->ops);
+               if (err < 0)
+                       goto err;
+
+               i++;
+       }
+       return 0;
+err:
+       list_for_each_entry(chain, &table->chains, list) {
+               if (i-- <= 0)
+                       break;
+
+               nf_unregister_hook(&nft_base_chain(chain)->ops);
+       }
+       return err;
+}
+
+static int nf_tables_table_disable(struct nft_table *table)
+{
+       struct nft_chain *chain;
+
+       list_for_each_entry(chain, &table->chains, list)
+               nf_unregister_hook(&nft_base_chain(chain)->ops);
+
+       return 0;
+}
+
+static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[],
+                             struct nft_af_info *afi, struct nft_table *table)
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       int family = nfmsg->nfgen_family, ret = 0;
+
+       if (nla[NFTA_TABLE_FLAGS]) {
+               __be32 flags;
+
+               flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
+               if (flags & ~NFT_TABLE_F_DORMANT)
+                       return -EINVAL;
+
+               if ((flags & NFT_TABLE_F_DORMANT) &&
+                   !(table->flags & NFT_TABLE_F_DORMANT)) {
+                       ret = nf_tables_table_disable(table);
+                       if (ret >= 0)
+                               table->flags |= NFT_TABLE_F_DORMANT;
+               } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+                          table->flags & NFT_TABLE_F_DORMANT) {
+                       ret = nf_tables_table_enable(table);
+                       if (ret >= 0)
+                               table->flags &= ~NFT_TABLE_F_DORMANT;
+               }
+               if (ret < 0)
+                       goto err;
+       }
+
+       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
+err:
+       return ret;
+}
+
+static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nlattr *name;
+       struct nft_af_info *afi;
+       struct nft_table *table;
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+
+       afi = nf_tables_afinfo_lookup(net, family, true);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       name = nla[NFTA_TABLE_NAME];
+       table = nf_tables_table_lookup(afi, name);
+       if (IS_ERR(table)) {
+               if (PTR_ERR(table) != -ENOENT)
+                       return PTR_ERR(table);
+               table = NULL;
+       }
+
+       if (table != NULL) {
+               if (nlh->nlmsg_flags & NLM_F_EXCL)
+                       return -EEXIST;
+               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+                       return -EOPNOTSUPP;
+               return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
+       }
+
+       table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
+       if (table == NULL)
+               return -ENOMEM;
+
+       nla_strlcpy(table->name, name, nla_len(name));
+       INIT_LIST_HEAD(&table->chains);
+       INIT_LIST_HEAD(&table->sets);
+
+       if (nla[NFTA_TABLE_FLAGS]) {
+               __be32 flags;
+
+               flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
+               if (flags & ~NFT_TABLE_F_DORMANT) {
+                       kfree(table);
+                       return -EINVAL;
+               }
+
+               table->flags |= flags;
+       }
+
+       list_add_tail(&table->list, &afi->tables);
+       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
+       return 0;
+}
+
+static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       struct nft_af_info *afi;
+       struct nft_table *table;
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+
+       afi = nf_tables_afinfo_lookup(net, family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       if (table->use)
+               return -EBUSY;
+
+       list_del(&table->list);
+       nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
+       kfree(table);
+       return 0;
+}
+
+int nft_register_chain_type(struct nf_chain_type *ctype)
+{
+       int err = 0;
+
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       if (chain_type[ctype->family][ctype->type] != NULL) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       if (!try_module_get(ctype->me))
+               goto out;
+
+       chain_type[ctype->family][ctype->type] = ctype;
+out:
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+       return err;
+}
+EXPORT_SYMBOL_GPL(nft_register_chain_type);
+
+void nft_unregister_chain_type(struct nf_chain_type *ctype)
+{
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       chain_type[ctype->family][ctype->type] = NULL;
+       module_put(ctype->me);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+}
+EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
+
+/*
+ * Chains
+ */
+
+static struct nft_chain *
+nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle)
+{
+       struct nft_chain *chain;
+
+       list_for_each_entry(chain, &table->chains, list) {
+               if (chain->handle == handle)
+                       return chain;
+       }
+
+       return ERR_PTR(-ENOENT);
+}
+
+static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table,
+                                               const struct nlattr *nla)
+{
+       struct nft_chain *chain;
+
+       if (nla == NULL)
+               return ERR_PTR(-EINVAL);
+
+       list_for_each_entry(chain, &table->chains, list) {
+               if (!nla_strcmp(nla, chain->name))
+                       return chain;
+       }
+
+       return ERR_PTR(-ENOENT);
+}
+
+static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
+       [NFTA_CHAIN_TABLE]      = { .type = NLA_STRING },
+       [NFTA_CHAIN_HANDLE]     = { .type = NLA_U64 },
+       [NFTA_CHAIN_NAME]       = { .type = NLA_STRING,
+                                   .len = NFT_CHAIN_MAXNAMELEN - 1 },
+       [NFTA_CHAIN_HOOK]       = { .type = NLA_NESTED },
+       [NFTA_CHAIN_POLICY]     = { .type = NLA_U32 },
+       [NFTA_CHAIN_TYPE]       = { .type = NLA_NUL_STRING },
+       [NFTA_CHAIN_COUNTERS]   = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
+       [NFTA_HOOK_HOOKNUM]     = { .type = NLA_U32 },
+       [NFTA_HOOK_PRIORITY]    = { .type = NLA_U32 },
+};
+
+static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
+{
+       struct nft_stats *cpu_stats, total;
+       struct nlattr *nest;
+       int cpu;
+
+       memset(&total, 0, sizeof(total));
+       for_each_possible_cpu(cpu) {
+               cpu_stats = per_cpu_ptr(stats, cpu);
+               total.pkts += cpu_stats->pkts;
+               total.bytes += cpu_stats->bytes;
+       }
+       nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) ||
+           nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes)))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       return -ENOSPC;
+}
+
+static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
+                                    int event, u32 flags, int family,
+                                    const struct nft_table *table,
+                                    const struct nft_chain *chain)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+
+       event |= NFNL_SUBSYS_NFTABLES << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family     = family;
+       nfmsg->version          = NFNETLINK_V0;
+       nfmsg->res_id           = 0;
+
+       if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
+               goto nla_put_failure;
+       if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle)))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name))
+               goto nla_put_failure;
+
+       if (chain->flags & NFT_BASE_CHAIN) {
+               const struct nft_base_chain *basechain = nft_base_chain(chain);
+               const struct nf_hook_ops *ops = &basechain->ops;
+               struct nlattr *nest;
+
+               nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
+               if (nest == NULL)
+                       goto nla_put_failure;
+               if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum)))
+                       goto nla_put_failure;
+               if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
+                       goto nla_put_failure;
+               nla_nest_end(skb, nest);
+
+               if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
+                                htonl(basechain->policy)))
+                       goto nla_put_failure;
+
+               if (nla_put_string(skb, NFTA_CHAIN_TYPE,
+                       chain_type[ops->pf][nft_base_chain(chain)->type]->name))
+                               goto nla_put_failure;
+
+               if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
+               goto nla_put_failure;
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_chain_notify(const struct sk_buff *oskb,
+                                 const struct nlmsghdr *nlh,
+                                 const struct nft_table *table,
+                                 const struct nft_chain *chain,
+                                 int event, int family)
+{
+       struct sk_buff *skb;
+       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
+       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
+       u32 seq = nlh ? nlh->nlmsg_seq : 0;
+       bool report;
+       int err;
+
+       report = nlh ? nlmsg_report(nlh) : false;
+       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb == NULL)
+               goto err;
+
+       err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family,
+                                       table, chain);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
+static int nf_tables_dump_chains(struct sk_buff *skb,
+                                struct netlink_callback *cb)
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       const struct nft_chain *chain;
+       unsigned int idx = 0, s_idx = cb->args[0];
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+
+       list_for_each_entry(afi, &net->nft.af_info, list) {
+               if (family != NFPROTO_UNSPEC && family != afi->family)
+                       continue;
+
+               list_for_each_entry(table, &afi->tables, list) {
+                       list_for_each_entry(chain, &table->chains, list) {
+                               if (idx < s_idx)
+                                       goto cont;
+                               if (idx > s_idx)
+                                       memset(&cb->args[1], 0,
+                                              sizeof(cb->args) - sizeof(cb->args[0]));
+                               if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid,
+                                                             cb->nlh->nlmsg_seq,
+                                                             NFT_MSG_NEWCHAIN,
+                                                             NLM_F_MULTI,
+                                                             afi->family, table, chain) < 0)
+                                       goto done;
+cont:
+                               idx++;
+                       }
+               }
+       }
+done:
+       cb->args[0] = idx;
+       return skb->len;
+}
+
+
+static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       const struct nft_chain *chain;
+       struct sk_buff *skb2;
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+       int err;
+
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = nf_tables_dump_chains,
+               };
+               return netlink_dump_start(nlsk, skb, nlh, &c);
+       }
+
+       afi = nf_tables_afinfo_lookup(net, family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
+       if (IS_ERR(chain))
+               return PTR_ERR(chain);
+
+       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb2)
+               return -ENOMEM;
+
+       err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid,
+                                       nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
+                                       family, table, chain);
+       if (err < 0)
+               goto err;
+
+       return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+
+err:
+       kfree_skb(skb2);
+       return err;
+}
+
+static int
+nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr)
+{
+       switch (ntohl(nla_get_be32(attr))) {
+       case NF_DROP:
+               chain->policy = NF_DROP;
+               break;
+       case NF_ACCEPT:
+               chain->policy = NF_ACCEPT;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
+       [NFTA_COUNTER_PACKETS]  = { .type = NLA_U64 },
+       [NFTA_COUNTER_BYTES]    = { .type = NLA_U64 },
+};
+
+static int
+nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
+{
+       struct nlattr *tb[NFTA_COUNTER_MAX+1];
+       struct nft_stats __percpu *newstats;
+       struct nft_stats *stats;
+       int err;
+
+       err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
+       if (err < 0)
+               return err;
+
+       if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
+               return -EINVAL;
+
+       newstats = alloc_percpu(struct nft_stats);
+       if (newstats == NULL)
+               return -ENOMEM;
+
+       /* Restore old counters on this cpu, no problem. Per-cpu statistics
+        * are not exposed to userspace.
+        */
+       stats = this_cpu_ptr(newstats);
+       stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
+       stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
+
+       if (chain->stats) {
+               /* nfnl_lock is held, add some nfnl function for this, later */
+               struct nft_stats __percpu *oldstats =
+                       rcu_dereference_protected(chain->stats, 1);
+
+               rcu_assign_pointer(chain->stats, newstats);
+               synchronize_rcu();
+               free_percpu(oldstats);
+       } else
+               rcu_assign_pointer(chain->stats, newstats);
+
+       return 0;
+}
+
+static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nlattr * uninitialized_var(name);
+       const struct nft_af_info *afi;
+       struct nft_table *table;
+       struct nft_chain *chain;
+       struct nft_base_chain *basechain = NULL;
+       struct nlattr *ha[NFTA_HOOK_MAX + 1];
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+       u64 handle = 0;
+       int err;
+       bool create;
+
+       create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
+
+       afi = nf_tables_afinfo_lookup(net, family, true);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       if (table->use == UINT_MAX)
+               return -EOVERFLOW;
+
+       chain = NULL;
+       name = nla[NFTA_CHAIN_NAME];
+
+       if (nla[NFTA_CHAIN_HANDLE]) {
+               handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
+               chain = nf_tables_chain_lookup_byhandle(table, handle);
+               if (IS_ERR(chain))
+                       return PTR_ERR(chain);
+       } else {
+               chain = nf_tables_chain_lookup(table, name);
+               if (IS_ERR(chain)) {
+                       if (PTR_ERR(chain) != -ENOENT)
+                               return PTR_ERR(chain);
+                       chain = NULL;
+               }
+       }
+
+       if (chain != NULL) {
+               if (nlh->nlmsg_flags & NLM_F_EXCL)
+                       return -EEXIST;
+               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+                       return -EOPNOTSUPP;
+
+               if (nla[NFTA_CHAIN_HANDLE] && name &&
+                   !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
+                       return -EEXIST;
+
+               if (nla[NFTA_CHAIN_POLICY]) {
+                       if (!(chain->flags & NFT_BASE_CHAIN))
+                               return -EOPNOTSUPP;
+
+                       err = nf_tables_chain_policy(nft_base_chain(chain),
+                                                    nla[NFTA_CHAIN_POLICY]);
+                       if (err < 0)
+                               return err;
+               }
+
+               if (nla[NFTA_CHAIN_COUNTERS]) {
+                       if (!(chain->flags & NFT_BASE_CHAIN))
+                               return -EOPNOTSUPP;
+
+                       err = nf_tables_counters(nft_base_chain(chain),
+                                                nla[NFTA_CHAIN_COUNTERS]);
+                       if (err < 0)
+                               return err;
+               }
+
+               if (nla[NFTA_CHAIN_HANDLE] && name)
+                       nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
+
+               goto notify;
+       }
+
+       if (nla[NFTA_CHAIN_HOOK]) {
+               struct nf_hook_ops *ops;
+               nf_hookfn *hookfn;
+               u32 hooknum;
+               int type = NFT_CHAIN_T_DEFAULT;
+
+               if (nla[NFTA_CHAIN_TYPE]) {
+                       type = nf_tables_chain_type_lookup(afi,
+                                                          nla[NFTA_CHAIN_TYPE],
+                                                          create);
+                       if (type < 0)
+                               return -ENOENT;
+               }
+
+               err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
+                                      nft_hook_policy);
+               if (err < 0)
+                       return err;
+               if (ha[NFTA_HOOK_HOOKNUM] == NULL ||
+                   ha[NFTA_HOOK_PRIORITY] == NULL)
+                       return -EINVAL;
+
+               hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
+               if (hooknum >= afi->nhooks)
+                       return -EINVAL;
+
+               hookfn = chain_type[family][type]->fn[hooknum];
+               if (hookfn == NULL)
+                       return -EOPNOTSUPP;
+
+               basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
+               if (basechain == NULL)
+                       return -ENOMEM;
+
+               basechain->type = type;
+               chain = &basechain->chain;
+
+               ops = &basechain->ops;
+               ops->pf         = family;
+               ops->owner      = afi->owner;
+               ops->hooknum    = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
+               ops->priority   = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
+               ops->priv       = chain;
+               ops->hook       = hookfn;
+               if (afi->hooks[ops->hooknum])
+                       ops->hook = afi->hooks[ops->hooknum];
+
+               chain->flags |= NFT_BASE_CHAIN;
+
+               if (nla[NFTA_CHAIN_POLICY]) {
+                       err = nf_tables_chain_policy(basechain,
+                                                    nla[NFTA_CHAIN_POLICY]);
+                       if (err < 0) {
+                               free_percpu(basechain->stats);
+                               kfree(basechain);
+                               return err;
+                       }
+               } else
+                       basechain->policy = NF_ACCEPT;
+
+               if (nla[NFTA_CHAIN_COUNTERS]) {
+                       err = nf_tables_counters(basechain,
+                                                nla[NFTA_CHAIN_COUNTERS]);
+                       if (err < 0) {
+                               free_percpu(basechain->stats);
+                               kfree(basechain);
+                               return err;
+                       }
+               } else {
+                       struct nft_stats __percpu *newstats;
+
+                       newstats = alloc_percpu(struct nft_stats);
+                       if (newstats == NULL)
+                               return -ENOMEM;
+
+                       rcu_assign_pointer(nft_base_chain(chain)->stats,
+                                          newstats);
+               }
+       } else {
+               chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+               if (chain == NULL)
+                       return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&chain->rules);
+       chain->handle = nf_tables_alloc_handle(table);
+       chain->net = net;
+       chain->table = table;
+       nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
+
+       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+           chain->flags & NFT_BASE_CHAIN) {
+               err = nf_register_hook(&nft_base_chain(chain)->ops);
+               if (err < 0) {
+                       free_percpu(basechain->stats);
+                       kfree(basechain);
+                       return err;
+               }
+       }
+       list_add_tail(&chain->list, &table->chains);
+       table->use++;
+notify:
+       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
+                              family);
+       return 0;
+}
+
+static void nf_tables_rcu_chain_destroy(struct rcu_head *head)
+{
+       struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head);
+
+       BUG_ON(chain->use > 0);
+
+       if (chain->flags & NFT_BASE_CHAIN) {
+               free_percpu(nft_base_chain(chain)->stats);
+               kfree(nft_base_chain(chain));
+       } else
+               kfree(chain);
+}
+
+static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
+                             const struct nlmsghdr *nlh,
+                             const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       struct nft_table *table;
+       struct nft_chain *chain;
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+
+       afi = nf_tables_afinfo_lookup(net, family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
+       if (IS_ERR(chain))
+               return PTR_ERR(chain);
+
+       if (!list_empty(&chain->rules))
+               return -EBUSY;
+
+       list_del(&chain->list);
+       table->use--;
+
+       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+           chain->flags & NFT_BASE_CHAIN)
+               nf_unregister_hook(&nft_base_chain(chain)->ops);
+
+       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
+                              family);
+
+       /* Make sure all rule references are gone before this is released */
+       call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy);
+       return 0;
+}
+
+static void nft_ctx_init(struct nft_ctx *ctx,
+                        const struct sk_buff *skb,
+                        const struct nlmsghdr *nlh,
+                        const struct nft_af_info *afi,
+                        const struct nft_table *table,
+                        const struct nft_chain *chain,
+                        const struct nlattr * const *nla)
+{
+       ctx->net   = sock_net(skb->sk);
+       ctx->skb   = skb;
+       ctx->nlh   = nlh;
+       ctx->afi   = afi;
+       ctx->table = table;
+       ctx->chain = chain;
+       ctx->nla   = nla;
+}
+
+/*
+ * Expressions
+ */
+
+/**
+ *     nft_register_expr - register nf_tables expr type
+ *     @ops: expr type
+ *
+ *     Registers the expr type for use with nf_tables. Returns zero on
+ *     success or a negative errno code otherwise.
+ */
+int nft_register_expr(struct nft_expr_type *type)
+{
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       list_add_tail(&type->list, &nf_tables_expressions);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nft_register_expr);
+
+/**
+ *     nft_unregister_expr - unregister nf_tables expr type
+ *     @ops: expr type
+ *
+ *     Unregisters the expr typefor use with nf_tables.
+ */
+void nft_unregister_expr(struct nft_expr_type *type)
+{
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       list_del(&type->list);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+}
+EXPORT_SYMBOL_GPL(nft_unregister_expr);
+
+static const struct nft_expr_type *__nft_expr_type_get(struct nlattr *nla)
+{
+       const struct nft_expr_type *type;
+
+       list_for_each_entry(type, &nf_tables_expressions, list) {
+               if (!nla_strcmp(nla, type->name))
+                       return type;
+       }
+       return NULL;
+}
+
+static const struct nft_expr_type *nft_expr_type_get(struct nlattr *nla)
+{
+       const struct nft_expr_type *type;
+
+       if (nla == NULL)
+               return ERR_PTR(-EINVAL);
+
+       type = __nft_expr_type_get(nla);
+       if (type != NULL && try_module_get(type->owner))
+               return type;
+
+#ifdef CONFIG_MODULES
+       if (type == NULL) {
+               nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+               request_module("nft-expr-%.*s",
+                              nla_len(nla), (char *)nla_data(nla));
+               nfnl_lock(NFNL_SUBSYS_NFTABLES);
+               if (__nft_expr_type_get(nla))
+                       return ERR_PTR(-EAGAIN);
+       }
+#endif
+       return ERR_PTR(-ENOENT);
+}
+
+static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = {
+       [NFTA_EXPR_NAME]        = { .type = NLA_STRING },
+       [NFTA_EXPR_DATA]        = { .type = NLA_NESTED },
+};
+
+static int nf_tables_fill_expr_info(struct sk_buff *skb,
+                                   const struct nft_expr *expr)
+{
+       if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name))
+               goto nla_put_failure;
+
+       if (expr->ops->dump) {
+               struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA);
+               if (data == NULL)
+                       goto nla_put_failure;
+               if (expr->ops->dump(skb, expr) < 0)
+                       goto nla_put_failure;
+               nla_nest_end(skb, data);
+       }
+
+       return skb->len;
+
+nla_put_failure:
+       return -1;
+};
+
+struct nft_expr_info {
+       const struct nft_expr_ops       *ops;
+       struct nlattr                   *tb[NFT_EXPR_MAXATTR + 1];
+};
+
+static int nf_tables_expr_parse(const struct nft_ctx *ctx,
+                               const struct nlattr *nla,
+                               struct nft_expr_info *info)
+{
+       const struct nft_expr_type *type;
+       const struct nft_expr_ops *ops;
+       struct nlattr *tb[NFTA_EXPR_MAX + 1];
+       int err;
+
+       err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy);
+       if (err < 0)
+               return err;
+
+       type = nft_expr_type_get(tb[NFTA_EXPR_NAME]);
+       if (IS_ERR(type))
+               return PTR_ERR(type);
+
+       if (tb[NFTA_EXPR_DATA]) {
+               err = nla_parse_nested(info->tb, type->maxattr,
+                                      tb[NFTA_EXPR_DATA], type->policy);
+               if (err < 0)
+                       goto err1;
+       } else
+               memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1));
+
+       if (type->select_ops != NULL) {
+               ops = type->select_ops(ctx,
+                                      (const struct nlattr * const *)info->tb);
+               if (IS_ERR(ops)) {
+                       err = PTR_ERR(ops);
+                       goto err1;
+               }
+       } else
+               ops = type->ops;
+
+       info->ops = ops;
+       return 0;
+
+err1:
+       module_put(type->owner);
+       return err;
+}
+
+static int nf_tables_newexpr(const struct nft_ctx *ctx,
+                            const struct nft_expr_info *info,
+                            struct nft_expr *expr)
+{
+       const struct nft_expr_ops *ops = info->ops;
+       int err;
+
+       expr->ops = ops;
+       if (ops->init) {
+               err = ops->init(ctx, expr, (const struct nlattr **)info->tb);
+               if (err < 0)
+                       goto err1;
+       }
+
+       return 0;
+
+err1:
+       expr->ops = NULL;
+       return err;
+}
+
+static void nf_tables_expr_destroy(struct nft_expr *expr)
+{
+       if (expr->ops->destroy)
+               expr->ops->destroy(expr);
+       module_put(expr->ops->type->owner);
+}
+
+/*
+ * Rules
+ */
+
+static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain,
+                                               u64 handle)
+{
+       struct nft_rule *rule;
+
+       // FIXME: this sucks
+       list_for_each_entry(rule, &chain->rules, list) {
+               if (handle == rule->handle)
+                       return rule;
+       }
+
+       return ERR_PTR(-ENOENT);
+}
+
+static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain,
+                                             const struct nlattr *nla)
+{
+       if (nla == NULL)
+               return ERR_PTR(-EINVAL);
+
+       return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla)));
+}
+
+static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
+       [NFTA_RULE_TABLE]       = { .type = NLA_STRING },
+       [NFTA_RULE_CHAIN]       = { .type = NLA_STRING,
+                                   .len = NFT_CHAIN_MAXNAMELEN - 1 },
+       [NFTA_RULE_HANDLE]      = { .type = NLA_U64 },
+       [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED },
+       [NFTA_RULE_COMPAT]      = { .type = NLA_NESTED },
+       [NFTA_RULE_POSITION]    = { .type = NLA_U64 },
+};
+
+static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
+                                   int event, u32 flags, int family,
+                                   const struct nft_table *table,
+                                   const struct nft_chain *chain,
+                                   const struct nft_rule *rule)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+       const struct nft_expr *expr, *next;
+       struct nlattr *list;
+       const struct nft_rule *prule;
+       int type = event | NFNL_SUBSYS_NFTABLES << 8;
+
+       nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg),
+                       flags);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family     = family;
+       nfmsg->version          = NFNETLINK_V0;
+       nfmsg->res_id           = 0;
+
+       if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name))
+               goto nla_put_failure;
+       if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle)))
+               goto nla_put_failure;
+
+       if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) {
+               prule = list_entry(rule->list.prev, struct nft_rule, list);
+               if (nla_put_be64(skb, NFTA_RULE_POSITION,
+                                cpu_to_be64(prule->handle)))
+                       goto nla_put_failure;
+       }
+
+       list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS);
+       if (list == NULL)
+               goto nla_put_failure;
+       nft_rule_for_each_expr(expr, next, rule) {
+               struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM);
+               if (elem == NULL)
+                       goto nla_put_failure;
+               if (nf_tables_fill_expr_info(skb, expr) < 0)
+                       goto nla_put_failure;
+               nla_nest_end(skb, elem);
+       }
+       nla_nest_end(skb, list);
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_rule_notify(const struct sk_buff *oskb,
+                                const struct nlmsghdr *nlh,
+                                const struct nft_table *table,
+                                const struct nft_chain *chain,
+                                const struct nft_rule *rule,
+                                int event, u32 flags, int family)
+{
+       struct sk_buff *skb;
+       u32 portid = NETLINK_CB(oskb).portid;
+       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
+       u32 seq = nlh->nlmsg_seq;
+       bool report;
+       int err;
+
+       report = nlmsg_report(nlh);
+       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb == NULL)
+               goto err;
+
+       err = nf_tables_fill_rule_info(skb, portid, seq, event, flags,
+                                      family, table, chain, rule);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
+static inline bool
+nft_rule_is_active(struct net *net, const struct nft_rule *rule)
+{
+       return (rule->genmask & (1 << net->nft.gencursor)) == 0;
+}
+
+static inline int gencursor_next(struct net *net)
+{
+       return net->nft.gencursor+1 == 1 ? 1 : 0;
+}
+
+static inline int
+nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
+{
+       return (rule->genmask & (1 << gencursor_next(net))) == 0;
+}
+
+static inline void
+nft_rule_activate_next(struct net *net, struct nft_rule *rule)
+{
+       /* Now inactive, will be active in the future */
+       rule->genmask = (1 << net->nft.gencursor);
+}
+
+static inline void
+nft_rule_disactivate_next(struct net *net, struct nft_rule *rule)
+{
+       rule->genmask = (1 << gencursor_next(net));
+}
+
+static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
+{
+       rule->genmask = 0;
+}
+
+static int nf_tables_dump_rules(struct sk_buff *skb,
+                               struct netlink_callback *cb)
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       const struct nft_chain *chain;
+       const struct nft_rule *rule;
+       unsigned int idx = 0, s_idx = cb->args[0];
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+       u8 genctr = ACCESS_ONCE(net->nft.genctr);
+       u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
+
+       list_for_each_entry(afi, &net->nft.af_info, list) {
+               if (family != NFPROTO_UNSPEC && family != afi->family)
+                       continue;
+
+               list_for_each_entry(table, &afi->tables, list) {
+                       list_for_each_entry(chain, &table->chains, list) {
+                               list_for_each_entry(rule, &chain->rules, list) {
+                                       if (!nft_rule_is_active(net, rule))
+                                               goto cont;
+                                       if (idx < s_idx)
+                                               goto cont;
+                                       if (idx > s_idx)
+                                               memset(&cb->args[1], 0,
+                                                      sizeof(cb->args) - sizeof(cb->args[0]));
+                                       if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid,
+                                                                     cb->nlh->nlmsg_seq,
+                                                                     NFT_MSG_NEWRULE,
+                                                                     NLM_F_MULTI | NLM_F_APPEND,
+                                                                     afi->family, table, chain, rule) < 0)
+                                               goto done;
+cont:
+                                       idx++;
+                               }
+                       }
+               }
+       }
+done:
+       /* Invalidate this dump, a transition to the new generation happened */
+       if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
+               return -EBUSY;
+
+       cb->args[0] = idx;
+       return skb->len;
+}
+
+static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
+                            const struct nlmsghdr *nlh,
+                            const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       const struct nft_chain *chain;
+       const struct nft_rule *rule;
+       struct sk_buff *skb2;
+       struct net *net = sock_net(skb->sk);
+       int family = nfmsg->nfgen_family;
+       int err;
+
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = nf_tables_dump_rules,
+               };
+               return netlink_dump_start(nlsk, skb, nlh, &c);
+       }
+
+       afi = nf_tables_afinfo_lookup(net, family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
+       if (IS_ERR(chain))
+               return PTR_ERR(chain);
+
+       rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
+       if (IS_ERR(rule))
+               return PTR_ERR(rule);
+
+       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb2)
+               return -ENOMEM;
+
+       err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid,
+                                      nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
+                                      family, table, chain, rule);
+       if (err < 0)
+               goto err;
+
+       return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+
+err:
+       kfree_skb(skb2);
+       return err;
+}
+
+static void nf_tables_rcu_rule_destroy(struct rcu_head *head)
+{
+       struct nft_rule *rule = container_of(head, struct nft_rule, rcu_head);
+       struct nft_expr *expr;
+
+       /*
+        * Careful: some expressions might not be initialized in case this
+        * is called on error from nf_tables_newrule().
+        */
+       expr = nft_expr_first(rule);
+       while (expr->ops && expr != nft_expr_last(rule)) {
+               nf_tables_expr_destroy(expr);
+               expr = nft_expr_next(expr);
+       }
+       kfree(rule);
+}
+
+static void nf_tables_rule_destroy(struct nft_rule *rule)
+{
+       call_rcu(&rule->rcu_head, nf_tables_rcu_rule_destroy);
+}
+
+#define NFT_RULE_MAXEXPRS      128
+
+static struct nft_expr_info *info;
+
+static struct nft_rule_trans *
+nf_tables_trans_add(struct nft_rule *rule, const struct nft_ctx *ctx)
+{
+       struct nft_rule_trans *rupd;
+
+       rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
+       if (rupd == NULL)
+              return NULL;
+
+       rupd->chain = ctx->chain;
+       rupd->table = ctx->table;
+       rupd->rule = rule;
+       rupd->family = ctx->afi->family;
+       rupd->nlh = ctx->nlh;
+       list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
+
+       return rupd;
+}
+
+static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
+                            const struct nlmsghdr *nlh,
+                            const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       struct net *net = sock_net(skb->sk);
+       struct nft_table *table;
+       struct nft_chain *chain;
+       struct nft_rule *rule, *old_rule = NULL;
+       struct nft_rule_trans *repl = NULL;
+       struct nft_expr *expr;
+       struct nft_ctx ctx;
+       struct nlattr *tmp;
+       unsigned int size, i, n;
+       int err, rem;
+       bool create;
+       u64 handle, pos_handle;
+
+       create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
+
+       afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
+       if (IS_ERR(chain))
+               return PTR_ERR(chain);
+
+       if (nla[NFTA_RULE_HANDLE]) {
+               handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE]));
+               rule = __nf_tables_rule_lookup(chain, handle);
+               if (IS_ERR(rule))
+                       return PTR_ERR(rule);
+
+               if (nlh->nlmsg_flags & NLM_F_EXCL)
+                       return -EEXIST;
+               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+                       old_rule = rule;
+               else
+                       return -EOPNOTSUPP;
+       } else {
+               if (!create || nlh->nlmsg_flags & NLM_F_REPLACE)
+                       return -EINVAL;
+               handle = nf_tables_alloc_handle(table);
+       }
+
+       if (nla[NFTA_RULE_POSITION]) {
+               if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+                       return -EOPNOTSUPP;
+
+               pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
+               old_rule = __nf_tables_rule_lookup(chain, pos_handle);
+               if (IS_ERR(old_rule))
+                       return PTR_ERR(old_rule);
+       }
+
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+
+       n = 0;
+       size = 0;
+       if (nla[NFTA_RULE_EXPRESSIONS]) {
+               nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) {
+                       err = -EINVAL;
+                       if (nla_type(tmp) != NFTA_LIST_ELEM)
+                               goto err1;
+                       if (n == NFT_RULE_MAXEXPRS)
+                               goto err1;
+                       err = nf_tables_expr_parse(&ctx, tmp, &info[n]);
+                       if (err < 0)
+                               goto err1;
+                       size += info[n].ops->size;
+                       n++;
+               }
+       }
+
+       err = -ENOMEM;
+       rule = kzalloc(sizeof(*rule) + size, GFP_KERNEL);
+       if (rule == NULL)
+               goto err1;
+
+       nft_rule_activate_next(net, rule);
+
+       rule->handle = handle;
+       rule->dlen   = size;
+
+       expr = nft_expr_first(rule);
+       for (i = 0; i < n; i++) {
+               err = nf_tables_newexpr(&ctx, &info[i], expr);
+               if (err < 0)
+                       goto err2;
+               info[i].ops = NULL;
+               expr = nft_expr_next(expr);
+       }
+
+       if (nlh->nlmsg_flags & NLM_F_REPLACE) {
+               if (nft_rule_is_active_next(net, old_rule)) {
+                       repl = nf_tables_trans_add(old_rule, &ctx);
+                       if (repl == NULL) {
+                               err = -ENOMEM;
+                               goto err2;
+                       }
+                       nft_rule_disactivate_next(net, old_rule);
+                       list_add_tail(&rule->list, &old_rule->list);
+               } else {
+                       err = -ENOENT;
+                       goto err2;
+               }
+       } else if (nlh->nlmsg_flags & NLM_F_APPEND)
+               if (old_rule)
+                       list_add_rcu(&rule->list, &old_rule->list);
+               else
+                       list_add_tail_rcu(&rule->list, &chain->rules);
+       else {
+               if (old_rule)
+                       list_add_tail_rcu(&rule->list, &old_rule->list);
+               else
+                       list_add_rcu(&rule->list, &chain->rules);
+       }
+
+       if (nf_tables_trans_add(rule, &ctx) == NULL) {
+               err = -ENOMEM;
+               goto err3;
+       }
+       return 0;
+
+err3:
+       list_del_rcu(&rule->list);
+       if (repl) {
+               list_del_rcu(&repl->rule->list);
+               list_del(&repl->list);
+               nft_rule_clear(net, repl->rule);
+               kfree(repl);
+       }
+err2:
+       nf_tables_rule_destroy(rule);
+err1:
+       for (i = 0; i < n; i++) {
+               if (info[i].ops != NULL)
+                       module_put(info[i].ops->type->owner);
+       }
+       return err;
+}
+
+static int
+nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
+{
+       /* You cannot delete the same rule twice */
+       if (nft_rule_is_active_next(ctx->net, rule)) {
+               if (nf_tables_trans_add(rule, ctx) == NULL)
+                       return -ENOMEM;
+               nft_rule_disactivate_next(ctx->net, rule);
+               return 0;
+       }
+       return -ENOENT;
+}
+
+static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
+                            const struct nlmsghdr *nlh,
+                            const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       struct net *net = sock_net(skb->sk);
+       const struct nft_table *table;
+       struct nft_chain *chain;
+       struct nft_rule *rule, *tmp;
+       int family = nfmsg->nfgen_family, err = 0;
+       struct nft_ctx ctx;
+
+       afi = nf_tables_afinfo_lookup(net, family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
+       if (IS_ERR(chain))
+               return PTR_ERR(chain);
+
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+
+       if (nla[NFTA_RULE_HANDLE]) {
+               rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
+               if (IS_ERR(rule))
+                       return PTR_ERR(rule);
+
+               err = nf_tables_delrule_one(&ctx, rule);
+       } else {
+               /* Remove all rules in this chain */
+               list_for_each_entry_safe(rule, tmp, &chain->rules, list) {
+                       err = nf_tables_delrule_one(&ctx, rule);
+                       if (err < 0)
+                               break;
+               }
+       }
+
+       return err;
+}
+
+static int nf_tables_commit(struct sk_buff *skb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nft_rule_trans *rupd, *tmp;
+
+       /* Bump generation counter, invalidate any dump in progress */
+       net->nft.genctr++;
+
+       /* A new generation has just started */
+       net->nft.gencursor = gencursor_next(net);
+
+       /* Make sure all packets have left the previous generation before
+        * purging old rules.
+        */
+       synchronize_rcu();
+
+       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
+               /* Delete this rule from the dirty list */
+               list_del(&rupd->list);
+
+               /* This rule was inactive in the past and just became active.
+                * Clear the next bit of the genmask since its meaning has
+                * changed, now it is the future.
+                */
+               if (nft_rule_is_active(net, rupd->rule)) {
+                       nft_rule_clear(net, rupd->rule);
+                       nf_tables_rule_notify(skb, rupd->nlh, rupd->table,
+                                             rupd->chain, rupd->rule,
+                                             NFT_MSG_NEWRULE, 0,
+                                             rupd->family);
+                       kfree(rupd);
+                       continue;
+               }
+
+               /* This rule is in the past, get rid of it */
+               list_del_rcu(&rupd->rule->list);
+               nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain,
+                                     rupd->rule, NFT_MSG_DELRULE, 0,
+                                     rupd->family);
+               nf_tables_rule_destroy(rupd->rule);
+               kfree(rupd);
+       }
+
+       return 0;
+}
+
+static int nf_tables_abort(struct sk_buff *skb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nft_rule_trans *rupd, *tmp;
+
+       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
+               /* Delete all rules from the dirty list */
+               list_del(&rupd->list);
+
+               if (!nft_rule_is_active_next(net, rupd->rule)) {
+                       nft_rule_clear(net, rupd->rule);
+                       kfree(rupd);
+                       continue;
+               }
+
+               /* This rule is inactive, get rid of it */
+               list_del_rcu(&rupd->rule->list);
+               nf_tables_rule_destroy(rupd->rule);
+               kfree(rupd);
+       }
+       return 0;
+}
+
+/*
+ * Sets
+ */
+
+static LIST_HEAD(nf_tables_set_ops);
+
+int nft_register_set(struct nft_set_ops *ops)
+{
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       list_add_tail(&ops->list, &nf_tables_set_ops);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nft_register_set);
+
+void nft_unregister_set(struct nft_set_ops *ops)
+{
+       nfnl_lock(NFNL_SUBSYS_NFTABLES);
+       list_del(&ops->list);
+       nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+}
+EXPORT_SYMBOL_GPL(nft_unregister_set);
+
+static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[])
+{
+       const struct nft_set_ops *ops;
+       u32 features;
+
+#ifdef CONFIG_MODULES
+       if (list_empty(&nf_tables_set_ops)) {
+               nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+               request_module("nft-set");
+               nfnl_lock(NFNL_SUBSYS_NFTABLES);
+               if (!list_empty(&nf_tables_set_ops))
+                       return ERR_PTR(-EAGAIN);
+       }
+#endif
+       features = 0;
+       if (nla[NFTA_SET_FLAGS] != NULL) {
+               features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
+               features &= NFT_SET_INTERVAL | NFT_SET_MAP;
+       }
+
+       // FIXME: implement selection properly
+       list_for_each_entry(ops, &nf_tables_set_ops, list) {
+               if ((ops->features & features) != features)
+                       continue;
+               if (!try_module_get(ops->owner))
+                       continue;
+               return ops;
+       }
+
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
+       [NFTA_SET_TABLE]                = { .type = NLA_STRING },
+       [NFTA_SET_NAME]                 = { .type = NLA_STRING },
+       [NFTA_SET_FLAGS]                = { .type = NLA_U32 },
+       [NFTA_SET_KEY_TYPE]             = { .type = NLA_U32 },
+       [NFTA_SET_KEY_LEN]              = { .type = NLA_U32 },
+       [NFTA_SET_DATA_TYPE]            = { .type = NLA_U32 },
+       [NFTA_SET_DATA_LEN]             = { .type = NLA_U32 },
+};
+
+static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
+                                    const struct sk_buff *skb,
+                                    const struct nlmsghdr *nlh,
+                                    const struct nlattr * const nla[])
+{
+       struct net *net = sock_net(skb->sk);
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table = NULL;
+
+       afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       if (nla[NFTA_SET_TABLE] != NULL) {
+               table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
+               if (IS_ERR(table))
+                       return PTR_ERR(table);
+       }
+
+       nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
+       return 0;
+}
+
+struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
+                                    const struct nlattr *nla)
+{
+       struct nft_set *set;
+
+       if (nla == NULL)
+               return ERR_PTR(-EINVAL);
+
+       list_for_each_entry(set, &table->sets, list) {
+               if (!nla_strcmp(nla, set->name))
+                       return set;
+       }
+       return ERR_PTR(-ENOENT);
+}
+
+static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
+                                   const char *name)
+{
+       const struct nft_set *i;
+       const char *p;
+       unsigned long *inuse;
+       unsigned int n = 0;
+
+       p = strnchr(name, IFNAMSIZ, '%');
+       if (p != NULL) {
+               if (p[1] != 'd' || strchr(p + 2, '%'))
+                       return -EINVAL;
+
+               inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
+               if (inuse == NULL)
+                       return -ENOMEM;
+
+               list_for_each_entry(i, &ctx->table->sets, list) {
+                       if (!sscanf(i->name, name, &n))
+                               continue;
+                       if (n < 0 || n > BITS_PER_LONG * PAGE_SIZE)
+                               continue;
+                       set_bit(n, inuse);
+               }
+
+               n = find_first_zero_bit(inuse, BITS_PER_LONG * PAGE_SIZE);
+               free_page((unsigned long)inuse);
+       }
+
+       snprintf(set->name, sizeof(set->name), name, n);
+       list_for_each_entry(i, &ctx->table->sets, list) {
+               if (!strcmp(set->name, i->name))
+                       return -ENFILE;
+       }
+       return 0;
+}
+
+static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
+                             const struct nft_set *set, u16 event, u16 flags)
+{
+       struct nfgenmsg *nfmsg;
+       struct nlmsghdr *nlh;
+       u32 portid = NETLINK_CB(ctx->skb).portid;
+       u32 seq = ctx->nlh->nlmsg_seq;
+
+       event |= NFNL_SUBSYS_NFTABLES << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
+                       flags);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family     = ctx->afi->family;
+       nfmsg->version          = NFNETLINK_V0;
+       nfmsg->res_id           = 0;
+
+       if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_SET_NAME, set->name))
+               goto nla_put_failure;
+       if (set->flags != 0)
+               if (nla_put_be32(skb, NFTA_SET_FLAGS, htonl(set->flags)))
+                       goto nla_put_failure;
+
+       if (nla_put_be32(skb, NFTA_SET_KEY_TYPE, htonl(set->ktype)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_SET_KEY_LEN, htonl(set->klen)))
+               goto nla_put_failure;
+       if (set->flags & NFT_SET_MAP) {
+               if (nla_put_be32(skb, NFTA_SET_DATA_TYPE, htonl(set->dtype)))
+                       goto nla_put_failure;
+               if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen)))
+                       goto nla_put_failure;
+       }
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_set_notify(const struct nft_ctx *ctx,
+                               const struct nft_set *set,
+                               int event)
+{
+       struct sk_buff *skb;
+       u32 portid = NETLINK_CB(ctx->skb).portid;
+       bool report;
+       int err;
+
+       report = nlmsg_report(ctx->nlh);
+       if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb == NULL)
+               goto err;
+
+       err = nf_tables_fill_set(skb, ctx, set, event, 0);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
+static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
+                                    struct netlink_callback *cb)
+{
+       const struct nft_set *set;
+       unsigned int idx = 0, s_idx = cb->args[0];
+
+       if (cb->args[1])
+               return skb->len;
+
+       list_for_each_entry(set, &ctx->table->sets, list) {
+               if (idx < s_idx)
+                       goto cont;
+               if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
+                                      NLM_F_MULTI) < 0) {
+                       cb->args[0] = idx;
+                       goto done;
+               }
+cont:
+               idx++;
+       }
+       cb->args[1] = 1;
+done:
+       return skb->len;
+}
+
+static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
+                                  struct netlink_callback *cb)
+{
+       const struct nft_set *set;
+       unsigned int idx = 0, s_idx = cb->args[0];
+       struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
+
+       if (cb->args[1])
+               return skb->len;
+
+       list_for_each_entry(table, &ctx->afi->tables, list) {
+               if (cur_table && cur_table != table)
+                       continue;
+
+               ctx->table = table;
+               list_for_each_entry(set, &ctx->table->sets, list) {
+                       if (idx < s_idx)
+                               goto cont;
+                       if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
+                                              NLM_F_MULTI) < 0) {
+                               cb->args[0] = idx;
+                               cb->args[2] = (unsigned long) table;
+                               goto done;
+                       }
+cont:
+                       idx++;
+               }
+       }
+       cb->args[1] = 1;
+done:
+       return skb->len;
+}
+
+static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+       struct nlattr *nla[NFTA_SET_MAX + 1];
+       struct nft_ctx ctx;
+       int err, ret;
+
+       err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_MAX,
+                         nft_set_policy);
+       if (err < 0)
+               return err;
+
+       err = nft_ctx_init_from_setattr(&ctx, cb->skb, cb->nlh, (void *)nla);
+       if (err < 0)
+               return err;
+
+       if (ctx.table == NULL)
+               ret = nf_tables_dump_sets_all(&ctx, skb, cb);
+       else
+               ret = nf_tables_dump_sets_table(&ctx, skb, cb);
+
+       return ret;
+}
+
+static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
+                           const struct nlmsghdr *nlh,
+                           const struct nlattr * const nla[])
+{
+       const struct nft_set *set;
+       struct nft_ctx ctx;
+       struct sk_buff *skb2;
+       int err;
+
+       /* Verify existance before starting dump */
+       err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
+       if (err < 0)
+               return err;
+
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = nf_tables_dump_sets,
+               };
+               return netlink_dump_start(nlsk, skb, nlh, &c);
+       }
+
+       set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+
+       skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb2 == NULL)
+               return -ENOMEM;
+
+       err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0);
+       if (err < 0)
+               goto err;
+
+       return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+
+err:
+       kfree_skb(skb2);
+       return err;
+}
+
+static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
+                           const struct nlmsghdr *nlh,
+                           const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_set_ops *ops;
+       const struct nft_af_info *afi;
+       struct net *net = sock_net(skb->sk);
+       struct nft_table *table;
+       struct nft_set *set;
+       struct nft_ctx ctx;
+       char name[IFNAMSIZ];
+       unsigned int size;
+       bool create;
+       u32 ktype, klen, dlen, dtype, flags;
+       int err;
+
+       if (nla[NFTA_SET_TABLE] == NULL ||
+           nla[NFTA_SET_NAME] == NULL ||
+           nla[NFTA_SET_KEY_LEN] == NULL)
+               return -EINVAL;
+
+       ktype = NFT_DATA_VALUE;
+       if (nla[NFTA_SET_KEY_TYPE] != NULL) {
+               ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
+               if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK)
+                       return -EINVAL;
+       }
+
+       klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
+       if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data))
+               return -EINVAL;
+
+       flags = 0;
+       if (nla[NFTA_SET_FLAGS] != NULL) {
+               flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
+               if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
+                             NFT_SET_INTERVAL | NFT_SET_MAP))
+                       return -EINVAL;
+       }
+
+       dtype = 0;
+       dlen  = 0;
+       if (nla[NFTA_SET_DATA_TYPE] != NULL) {
+               if (!(flags & NFT_SET_MAP))
+                       return -EINVAL;
+
+               dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE]));
+               if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK &&
+                   dtype != NFT_DATA_VERDICT)
+                       return -EINVAL;
+
+               if (dtype != NFT_DATA_VERDICT) {
+                       if (nla[NFTA_SET_DATA_LEN] == NULL)
+                               return -EINVAL;
+                       dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
+                       if (dlen == 0 ||
+                           dlen > FIELD_SIZEOF(struct nft_data, data))
+                               return -EINVAL;
+               } else
+                       dlen = sizeof(struct nft_data);
+       } else if (flags & NFT_SET_MAP)
+               return -EINVAL;
+
+       create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
+
+       afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+
+       set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
+       if (IS_ERR(set)) {
+               if (PTR_ERR(set) != -ENOENT)
+                       return PTR_ERR(set);
+               set = NULL;
+       }
+
+       if (set != NULL) {
+               if (nlh->nlmsg_flags & NLM_F_EXCL)
+                       return -EEXIST;
+               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+                       return -EOPNOTSUPP;
+               return 0;
+       }
+
+       if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+               return -ENOENT;
+
+       ops = nft_select_set_ops(nla);
+       if (IS_ERR(ops))
+               return PTR_ERR(ops);
+
+       size = 0;
+       if (ops->privsize != NULL)
+               size = ops->privsize(nla);
+
+       err = -ENOMEM;
+       set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
+       if (set == NULL)
+               goto err1;
+
+       nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name));
+       err = nf_tables_set_alloc_name(&ctx, set, name);
+       if (err < 0)
+               goto err2;
+
+       INIT_LIST_HEAD(&set->bindings);
+       set->ops   = ops;
+       set->ktype = ktype;
+       set->klen  = klen;
+       set->dtype = dtype;
+       set->dlen  = dlen;
+       set->flags = flags;
+
+       err = ops->init(set, nla);
+       if (err < 0)
+               goto err2;
+
+       list_add_tail(&set->list, &table->sets);
+       nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET);
+       return 0;
+
+err2:
+       kfree(set);
+err1:
+       module_put(ops->owner);
+       return err;
+}
+
+static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       list_del(&set->list);
+       if (!(set->flags & NFT_SET_ANONYMOUS))
+               nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
+
+       set->ops->destroy(set);
+       module_put(set->ops->owner);
+       kfree(set);
+}
+
+static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
+                           const struct nlmsghdr *nlh,
+                           const struct nlattr * const nla[])
+{
+       struct nft_set *set;
+       struct nft_ctx ctx;
+       int err;
+
+       if (nla[NFTA_SET_TABLE] == NULL)
+               return -EINVAL;
+
+       err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
+       if (err < 0)
+               return err;
+
+       set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+       if (!list_empty(&set->bindings))
+               return -EBUSY;
+
+       nf_tables_set_destroy(&ctx, set);
+       return 0;
+}
+
+static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
+                                       const struct nft_set *set,
+                                       const struct nft_set_iter *iter,
+                                       const struct nft_set_elem *elem)
+{
+       enum nft_registers dreg;
+
+       dreg = nft_type_to_reg(set->dtype);
+       return nft_validate_data_load(ctx, dreg, &elem->data, set->dtype);
+}
+
+int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+                      struct nft_set_binding *binding)
+{
+       struct nft_set_binding *i;
+       struct nft_set_iter iter;
+
+       if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
+               return -EBUSY;
+
+       if (set->flags & NFT_SET_MAP) {
+               /* If the set is already bound to the same chain all
+                * jumps are already validated for that chain.
+                */
+               list_for_each_entry(i, &set->bindings, list) {
+                       if (i->chain == binding->chain)
+                               goto bind;
+               }
+
+               iter.skip       = 0;
+               iter.count      = 0;
+               iter.err        = 0;
+               iter.fn         = nf_tables_bind_check_setelem;
+
+               set->ops->walk(ctx, set, &iter);
+               if (iter.err < 0) {
+                       /* Destroy anonymous sets if binding fails */
+                       if (set->flags & NFT_SET_ANONYMOUS)
+                               nf_tables_set_destroy(ctx, set);
+
+                       return iter.err;
+               }
+       }
+bind:
+       binding->chain = ctx->chain;
+       list_add_tail(&binding->list, &set->bindings);
+       return 0;
+}
+
+void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+                         struct nft_set_binding *binding)
+{
+       list_del(&binding->list);
+
+       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
+               nf_tables_set_destroy(ctx, set);
+}
+
+/*
+ * Set elements
+ */
+
+static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
+       [NFTA_SET_ELEM_KEY]             = { .type = NLA_NESTED },
+       [NFTA_SET_ELEM_DATA]            = { .type = NLA_NESTED },
+       [NFTA_SET_ELEM_FLAGS]           = { .type = NLA_U32 },
+};
+
+static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
+       [NFTA_SET_ELEM_LIST_TABLE]      = { .type = NLA_STRING },
+       [NFTA_SET_ELEM_LIST_SET]        = { .type = NLA_STRING },
+       [NFTA_SET_ELEM_LIST_ELEMENTS]   = { .type = NLA_NESTED },
+};
+
+static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
+                                     const struct sk_buff *skb,
+                                     const struct nlmsghdr *nlh,
+                                     const struct nlattr * const nla[])
+{
+       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+       const struct nft_af_info *afi;
+       const struct nft_table *table;
+       struct net *net = sock_net(skb->sk);
+
+       afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
+       if (IS_ERR(afi))
+               return PTR_ERR(afi);
+
+       table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
+       if (IS_ERR(table))
+               return PTR_ERR(table);
+
+       nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
+       return 0;
+}
+
+static int nf_tables_fill_setelem(struct sk_buff *skb,
+                                 const struct nft_set *set,
+                                 const struct nft_set_elem *elem)
+{
+       unsigned char *b = skb_tail_pointer(skb);
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, NFTA_LIST_ELEM);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
+                         set->klen) < 0)
+               goto nla_put_failure;
+
+       if (set->flags & NFT_SET_MAP &&
+           !(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
+           nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
+                         set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
+                         set->dlen) < 0)
+               goto nla_put_failure;
+
+       if (elem->flags != 0)
+               if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
+                       goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       nlmsg_trim(skb, b);
+       return -EMSGSIZE;
+}
+
+struct nft_set_dump_args {
+       const struct netlink_callback   *cb;
+       struct nft_set_iter             iter;
+       struct sk_buff                  *skb;
+};
+
+static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
+                                 const struct nft_set *set,
+                                 const struct nft_set_iter *iter,
+                                 const struct nft_set_elem *elem)
+{
+       struct nft_set_dump_args *args;
+
+       args = container_of(iter, struct nft_set_dump_args, iter);
+       return nf_tables_fill_setelem(args->skb, set, elem);
+}
+
+static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       const struct nft_set *set;
+       struct nft_set_dump_args args;
+       struct nft_ctx ctx;
+       struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
+       struct nfgenmsg *nfmsg;
+       struct nlmsghdr *nlh;
+       struct nlattr *nest;
+       u32 portid, seq;
+       int event, err;
+
+       nfmsg = nlmsg_data(cb->nlh);
+       err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_ELEM_LIST_MAX,
+                         nft_set_elem_list_policy);
+       if (err < 0)
+               return err;
+
+       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla);
+       if (err < 0)
+               return err;
+
+       set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+
+       event  = NFT_MSG_NEWSETELEM;
+       event |= NFNL_SUBSYS_NFTABLES << 8;
+       portid = NETLINK_CB(cb->skb).portid;
+       seq    = cb->nlh->nlmsg_seq;
+
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
+                       NLM_F_MULTI);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family = NFPROTO_UNSPEC;
+       nfmsg->version      = NFNETLINK_V0;
+       nfmsg->res_id       = 0;
+
+       if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
+               goto nla_put_failure;
+
+       nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       args.cb         = cb;
+       args.skb        = skb;
+       args.iter.skip  = cb->args[0];
+       args.iter.count = 0;
+       args.iter.err   = 0;
+       args.iter.fn    = nf_tables_dump_setelem;
+       set->ops->walk(&ctx, set, &args.iter);
+
+       nla_nest_end(skb, nest);
+       nlmsg_end(skb, nlh);
+
+       if (args.iter.err && args.iter.err != -EMSGSIZE)
+               return args.iter.err;
+       if (args.iter.count == cb->args[0])
+               return 0;
+
+       cb->args[0] = args.iter.count;
+       return skb->len;
+
+nla_put_failure:
+       return -ENOSPC;
+}
+
+static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
+                               const struct nlmsghdr *nlh,
+                               const struct nlattr * const nla[])
+{
+       const struct nft_set *set;
+       struct nft_ctx ctx;
+       int err;
+
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       if (err < 0)
+               return err;
+
+       set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+
+       if (nlh->nlmsg_flags & NLM_F_DUMP) {
+               struct netlink_dump_control c = {
+                       .dump = nf_tables_dump_set,
+               };
+               return netlink_dump_start(nlsk, skb, nlh, &c);
+       }
+       return -EOPNOTSUPP;
+}
+
+static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
+                           const struct nlattr *attr)
+{
+       struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
+       struct nft_data_desc d1, d2;
+       struct nft_set_elem elem;
+       struct nft_set_binding *binding;
+       enum nft_registers dreg;
+       int err;
+
+       err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
+                              nft_set_elem_policy);
+       if (err < 0)
+               return err;
+
+       if (nla[NFTA_SET_ELEM_KEY] == NULL)
+               return -EINVAL;
+
+       elem.flags = 0;
+       if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
+               elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
+               if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
+                       return -EINVAL;
+       }
+
+       if (set->flags & NFT_SET_MAP) {
+               if (nla[NFTA_SET_ELEM_DATA] == NULL &&
+                   !(elem.flags & NFT_SET_ELEM_INTERVAL_END))
+                       return -EINVAL;
+       } else {
+               if (nla[NFTA_SET_ELEM_DATA] != NULL)
+                       return -EINVAL;
+       }
+
+       err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
+       if (err < 0)
+               goto err1;
+       err = -EINVAL;
+       if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
+               goto err2;
+
+       err = -EEXIST;
+       if (set->ops->get(set, &elem) == 0)
+               goto err2;
+
+       if (nla[NFTA_SET_ELEM_DATA] != NULL) {
+               err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]);
+               if (err < 0)
+                       goto err2;
+
+               err = -EINVAL;
+               if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen)
+                       goto err3;
+
+               dreg = nft_type_to_reg(set->dtype);
+               list_for_each_entry(binding, &set->bindings, list) {
+                       struct nft_ctx bind_ctx = {
+                               .afi    = ctx->afi,
+                               .table  = ctx->table,
+                               .chain  = binding->chain,
+                       };
+
+                       err = nft_validate_data_load(&bind_ctx, dreg,
+                                                    &elem.data, d2.type);
+                       if (err < 0)
+                               goto err3;
+               }
+       }
+
+       err = set->ops->insert(set, &elem);
+       if (err < 0)
+               goto err3;
+
+       return 0;
+
+err3:
+       if (nla[NFTA_SET_ELEM_DATA] != NULL)
+               nft_data_uninit(&elem.data, d2.type);
+err2:
+       nft_data_uninit(&elem.key, d1.type);
+err1:
+       return err;
+}
+
+static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
+                               const struct nlmsghdr *nlh,
+                               const struct nlattr * const nla[])
+{
+       const struct nlattr *attr;
+       struct nft_set *set;
+       struct nft_ctx ctx;
+       int rem, err;
+
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       if (err < 0)
+               return err;
+
+       set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+       if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
+               return -EBUSY;
+
+       nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
+               err = nft_add_set_elem(&ctx, set, attr);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
+                          const struct nlattr *attr)
+{
+       struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
+       struct nft_data_desc desc;
+       struct nft_set_elem elem;
+       int err;
+
+       err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
+                              nft_set_elem_policy);
+       if (err < 0)
+               goto err1;
+
+       err = -EINVAL;
+       if (nla[NFTA_SET_ELEM_KEY] == NULL)
+               goto err1;
+
+       err = nft_data_init(ctx, &elem.key, &desc, nla[NFTA_SET_ELEM_KEY]);
+       if (err < 0)
+               goto err1;
+
+       err = -EINVAL;
+       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
+               goto err2;
+
+       err = set->ops->get(set, &elem);
+       if (err < 0)
+               goto err2;
+
+       set->ops->remove(set, &elem);
+
+       nft_data_uninit(&elem.key, NFT_DATA_VALUE);
+       if (set->flags & NFT_SET_MAP)
+               nft_data_uninit(&elem.data, set->dtype);
+
+err2:
+       nft_data_uninit(&elem.key, desc.type);
+err1:
+       return err;
+}
+
+static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
+                               const struct nlmsghdr *nlh,
+                               const struct nlattr * const nla[])
+{
+       const struct nlattr *attr;
+       struct nft_set *set;
+       struct nft_ctx ctx;
+       int rem, err;
+
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       if (err < 0)
+               return err;
+
+       set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+       if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
+               return -EBUSY;
+
+       nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
+               err = nft_del_setelem(&ctx, set, attr);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
+       [NFT_MSG_NEWTABLE] = {
+               .call           = nf_tables_newtable,
+               .attr_count     = NFTA_TABLE_MAX,
+               .policy         = nft_table_policy,
+       },
+       [NFT_MSG_GETTABLE] = {
+               .call           = nf_tables_gettable,
+               .attr_count     = NFTA_TABLE_MAX,
+               .policy         = nft_table_policy,
+       },
+       [NFT_MSG_DELTABLE] = {
+               .call           = nf_tables_deltable,
+               .attr_count     = NFTA_TABLE_MAX,
+               .policy         = nft_table_policy,
+       },
+       [NFT_MSG_NEWCHAIN] = {
+               .call           = nf_tables_newchain,
+               .attr_count     = NFTA_CHAIN_MAX,
+               .policy         = nft_chain_policy,
+       },
+       [NFT_MSG_GETCHAIN] = {
+               .call           = nf_tables_getchain,
+               .attr_count     = NFTA_CHAIN_MAX,
+               .policy         = nft_chain_policy,
+       },
+       [NFT_MSG_DELCHAIN] = {
+               .call           = nf_tables_delchain,
+               .attr_count     = NFTA_CHAIN_MAX,
+               .policy         = nft_chain_policy,
+       },
+       [NFT_MSG_NEWRULE] = {
+               .call_batch     = nf_tables_newrule,
+               .attr_count     = NFTA_RULE_MAX,
+               .policy         = nft_rule_policy,
+       },
+       [NFT_MSG_GETRULE] = {
+               .call           = nf_tables_getrule,
+               .attr_count     = NFTA_RULE_MAX,
+               .policy         = nft_rule_policy,
+       },
+       [NFT_MSG_DELRULE] = {
+               .call_batch     = nf_tables_delrule,
+               .attr_count     = NFTA_RULE_MAX,
+               .policy         = nft_rule_policy,
+       },
+       [NFT_MSG_NEWSET] = {
+               .call           = nf_tables_newset,
+               .attr_count     = NFTA_SET_MAX,
+               .policy         = nft_set_policy,
+       },
+       [NFT_MSG_GETSET] = {
+               .call           = nf_tables_getset,
+               .attr_count     = NFTA_SET_MAX,
+               .policy         = nft_set_policy,
+       },
+       [NFT_MSG_DELSET] = {
+               .call           = nf_tables_delset,
+               .attr_count     = NFTA_SET_MAX,
+               .policy         = nft_set_policy,
+       },
+       [NFT_MSG_NEWSETELEM] = {
+               .call           = nf_tables_newsetelem,
+               .attr_count     = NFTA_SET_ELEM_LIST_MAX,
+               .policy         = nft_set_elem_list_policy,
+       },
+       [NFT_MSG_GETSETELEM] = {
+               .call           = nf_tables_getsetelem,
+               .attr_count     = NFTA_SET_ELEM_LIST_MAX,
+               .policy         = nft_set_elem_list_policy,
+       },
+       [NFT_MSG_DELSETELEM] = {
+               .call           = nf_tables_delsetelem,
+               .attr_count     = NFTA_SET_ELEM_LIST_MAX,
+               .policy         = nft_set_elem_list_policy,
+       },
+};
+
+static const struct nfnetlink_subsystem nf_tables_subsys = {
+       .name           = "nf_tables",
+       .subsys_id      = NFNL_SUBSYS_NFTABLES,
+       .cb_count       = NFT_MSG_MAX,
+       .cb             = nf_tables_cb,
+       .commit         = nf_tables_commit,
+       .abort          = nf_tables_abort,
+};
+
+/*
+ * Loop detection - walk through the ruleset beginning at the destination chain
+ * of a new jump until either the source chain is reached (loop) or all
+ * reachable chains have been traversed.
+ *
+ * The loop check is performed whenever a new jump verdict is added to an
+ * expression or verdict map or a verdict map is bound to a new chain.
+ */
+
+static int nf_tables_check_loops(const struct nft_ctx *ctx,
+                                const struct nft_chain *chain);
+
+static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
+                                       const struct nft_set *set,
+                                       const struct nft_set_iter *iter,
+                                       const struct nft_set_elem *elem)
+{
+       switch (elem->data.verdict) {
+       case NFT_JUMP:
+       case NFT_GOTO:
+               return nf_tables_check_loops(ctx, elem->data.chain);
+       default:
+               return 0;
+       }
+}
+
+static int nf_tables_check_loops(const struct nft_ctx *ctx,
+                                const struct nft_chain *chain)
+{
+       const struct nft_rule *rule;
+       const struct nft_expr *expr, *last;
+       const struct nft_set *set;
+       struct nft_set_binding *binding;
+       struct nft_set_iter iter;
+
+       if (ctx->chain == chain)
+               return -ELOOP;
+
+       list_for_each_entry(rule, &chain->rules, list) {
+               nft_rule_for_each_expr(expr, last, rule) {
+                       const struct nft_data *data = NULL;
+                       int err;
+
+                       if (!expr->ops->validate)
+                               continue;
+
+                       err = expr->ops->validate(ctx, expr, &data);
+                       if (err < 0)
+                               return err;
+
+                       if (data == NULL)
+                               continue;
+
+                       switch (data->verdict) {
+                       case NFT_JUMP:
+                       case NFT_GOTO:
+                               err = nf_tables_check_loops(ctx, data->chain);
+                               if (err < 0)
+                                       return err;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       list_for_each_entry(set, &ctx->table->sets, list) {
+               if (!(set->flags & NFT_SET_MAP) ||
+                   set->dtype != NFT_DATA_VERDICT)
+                       continue;
+
+               list_for_each_entry(binding, &set->bindings, list) {
+                       if (binding->chain != chain)
+                               continue;
+
+                       iter.skip       = 0;
+                       iter.count      = 0;
+                       iter.err        = 0;
+                       iter.fn         = nf_tables_loop_check_setelem;
+
+                       set->ops->walk(ctx, set, &iter);
+                       if (iter.err < 0)
+                               return iter.err;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ *     nft_validate_input_register - validate an expressions' input register
+ *
+ *     @reg: the register number
+ *
+ *     Validate that the input register is one of the general purpose
+ *     registers.
+ */
+int nft_validate_input_register(enum nft_registers reg)
+{
+       if (reg <= NFT_REG_VERDICT)
+               return -EINVAL;
+       if (reg > NFT_REG_MAX)
+               return -ERANGE;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nft_validate_input_register);
+
+/**
+ *     nft_validate_output_register - validate an expressions' output register
+ *
+ *     @reg: the register number
+ *
+ *     Validate that the output register is one of the general purpose
+ *     registers or the verdict register.
+ */
+int nft_validate_output_register(enum nft_registers reg)
+{
+       if (reg < NFT_REG_VERDICT)
+               return -EINVAL;
+       if (reg > NFT_REG_MAX)
+               return -ERANGE;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nft_validate_output_register);
+
+/**
+ *     nft_validate_data_load - validate an expressions' data load
+ *
+ *     @ctx: context of the expression performing the load
+ *     @reg: the destination register number
+ *     @data: the data to load
+ *     @type: the data type
+ *
+ *     Validate that a data load uses the appropriate data type for
+ *     the destination register. A value of NULL for the data means
+ *     that its runtime gathered data, which is always of type
+ *     NFT_DATA_VALUE.
+ */
+int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
+                          const struct nft_data *data,
+                          enum nft_data_types type)
+{
+       int err;
+
+       switch (reg) {
+       case NFT_REG_VERDICT:
+               if (data == NULL || type != NFT_DATA_VERDICT)
+                       return -EINVAL;
+
+               if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) {
+                       err = nf_tables_check_loops(ctx, data->chain);
+                       if (err < 0)
+                               return err;
+
+                       if (ctx->chain->level + 1 > data->chain->level) {
+                               if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE)
+                                       return -EMLINK;
+                               data->chain->level = ctx->chain->level + 1;
+                       }
+               }
+
+               return 0;
+       default:
+               if (data != NULL && type != NFT_DATA_VALUE)
+                       return -EINVAL;
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(nft_validate_data_load);
+
+static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = {
+       [NFTA_VERDICT_CODE]     = { .type = NLA_U32 },
+       [NFTA_VERDICT_CHAIN]    = { .type = NLA_STRING,
+                                   .len = NFT_CHAIN_MAXNAMELEN - 1 },
+};
+
+static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
+                           struct nft_data_desc *desc, const struct nlattr *nla)
+{
+       struct nlattr *tb[NFTA_VERDICT_MAX + 1];
+       struct nft_chain *chain;
+       int err;
+
+       err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy);
+       if (err < 0)
+               return err;
+
+       if (!tb[NFTA_VERDICT_CODE])
+               return -EINVAL;
+       data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
+
+       switch (data->verdict) {
+       case NF_ACCEPT:
+       case NF_DROP:
+       case NF_QUEUE:
+       case NFT_CONTINUE:
+       case NFT_BREAK:
+       case NFT_RETURN:
+               desc->len = sizeof(data->verdict);
+               break;
+       case NFT_JUMP:
+       case NFT_GOTO:
+               if (!tb[NFTA_VERDICT_CHAIN])
+                       return -EINVAL;
+               chain = nf_tables_chain_lookup(ctx->table,
+                                              tb[NFTA_VERDICT_CHAIN]);
+               if (IS_ERR(chain))
+                       return PTR_ERR(chain);
+               if (chain->flags & NFT_BASE_CHAIN)
+                       return -EOPNOTSUPP;
+
+               chain->use++;
+               data->chain = chain;
+               desc->len = sizeof(data);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       desc->type = NFT_DATA_VERDICT;
+       return 0;
+}
+
+static void nft_verdict_uninit(const struct nft_data *data)
+{
+       switch (data->verdict) {
+       case NFT_JUMP:
+       case NFT_GOTO:
+               data->chain->use--;
+               break;
+       }
+}
+
+static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
+{
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
+       if (!nest)
+               goto nla_put_failure;
+
+       if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict)))
+               goto nla_put_failure;
+
+       switch (data->verdict) {
+       case NFT_JUMP:
+       case NFT_GOTO:
+               if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name))
+                       goto nla_put_failure;
+       }
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data,
+                         struct nft_data_desc *desc, const struct nlattr *nla)
+{
+       unsigned int len;
+
+       len = nla_len(nla);
+       if (len == 0)
+               return -EINVAL;
+       if (len > sizeof(data->data))
+               return -EOVERFLOW;
+
+       nla_memcpy(data->data, nla, sizeof(data->data));
+       desc->type = NFT_DATA_VALUE;
+       desc->len  = len;
+       return 0;
+}
+
+static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data,
+                         unsigned int len)
+{
+       return nla_put(skb, NFTA_DATA_VALUE, len, data->data);
+}
+
+static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
+       [NFTA_DATA_VALUE]       = { .type = NLA_BINARY,
+                                   .len  = FIELD_SIZEOF(struct nft_data, data) },
+       [NFTA_DATA_VERDICT]     = { .type = NLA_NESTED },
+};
+
+/**
+ *     nft_data_init - parse nf_tables data netlink attributes
+ *
+ *     @ctx: context of the expression using the data
+ *     @data: destination struct nft_data
+ *     @desc: data description
+ *     @nla: netlink attribute containing data
+ *
+ *     Parse the netlink data attributes and initialize a struct nft_data.
+ *     The type and length of data are returned in the data description.
+ *
+ *     The caller can indicate that it only wants to accept data of type
+ *     NFT_DATA_VALUE by passing NULL for the ctx argument.
+ */
+int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
+                 struct nft_data_desc *desc, const struct nlattr *nla)
+{
+       struct nlattr *tb[NFTA_DATA_MAX + 1];
+       int err;
+
+       err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy);
+       if (err < 0)
+               return err;
+
+       if (tb[NFTA_DATA_VALUE])
+               return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
+       if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
+               return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(nft_data_init);
+
+/**
+ *     nft_data_uninit - release a nft_data item
+ *
+ *     @data: struct nft_data to release
+ *     @type: type of data
+ *
+ *     Release a nft_data item. NFT_DATA_VALUE types can be silently discarded,
+ *     all others need to be released by calling this function.
+ */
+void nft_data_uninit(const struct nft_data *data, enum nft_data_types type)
+{
+       switch (type) {
+       case NFT_DATA_VALUE:
+               return;
+       case NFT_DATA_VERDICT:
+               return nft_verdict_uninit(data);
+       default:
+               WARN_ON(1);
+       }
+}
+EXPORT_SYMBOL_GPL(nft_data_uninit);
+
+int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
+                 enum nft_data_types type, unsigned int len)
+{
+       struct nlattr *nest;
+       int err;
+
+       nest = nla_nest_start(skb, attr);
+       if (nest == NULL)
+               return -1;
+
+       switch (type) {
+       case NFT_DATA_VALUE:
+               err = nft_value_dump(skb, data, len);
+               break;
+       case NFT_DATA_VERDICT:
+               err = nft_verdict_dump(skb, data);
+               break;
+       default:
+               err = -EINVAL;
+               WARN_ON(1);
+       }
+
+       nla_nest_end(skb, nest);
+       return err;
+}
+EXPORT_SYMBOL_GPL(nft_data_dump);
+
+static int nf_tables_init_net(struct net *net)
+{
+       INIT_LIST_HEAD(&net->nft.af_info);
+       INIT_LIST_HEAD(&net->nft.commit_list);
+       return 0;
+}
+
+static struct pernet_operations nf_tables_net_ops = {
+       .init   = nf_tables_init_net,
+};
+
+static int __init nf_tables_module_init(void)
+{
+       int err;
+
+       info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS,
+                      GFP_KERNEL);
+       if (info == NULL) {
+               err = -ENOMEM;
+               goto err1;
+       }
+
+       err = nf_tables_core_module_init();
+       if (err < 0)
+               goto err2;
+
+       err = nfnetlink_subsys_register(&nf_tables_subsys);
+       if (err < 0)
+               goto err3;
+
+       pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
+       return register_pernet_subsys(&nf_tables_net_ops);
+err3:
+       nf_tables_core_module_exit();
+err2:
+       kfree(info);
+err1:
+       return err;
+}
+
+static void __exit nf_tables_module_exit(void)
+{
+       unregister_pernet_subsys(&nf_tables_net_ops);
+       nfnetlink_subsys_unregister(&nf_tables_subsys);
+       nf_tables_core_module_exit();
+       kfree(info);
+}
+
+module_init(nf_tables_module_init);
+module_exit(nf_tables_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES);
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
new file mode 100644 (file)
index 0000000..cb9e685
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/rculist.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_log.h>
+
+static void nft_cmp_fast_eval(const struct nft_expr *expr,
+                             struct nft_data data[NFT_REG_MAX + 1])
+{
+       const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
+       u32 mask;
+
+       mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - priv->len);
+       if ((data[priv->sreg].data[0] & mask) == priv->data)
+               return;
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static bool nft_payload_fast_eval(const struct nft_expr *expr,
+                                 struct nft_data data[NFT_REG_MAX + 1],
+                                 const struct nft_pktinfo *pkt)
+{
+       const struct nft_payload *priv = nft_expr_priv(expr);
+       const struct sk_buff *skb = pkt->skb;
+       struct nft_data *dest = &data[priv->dreg];
+       unsigned char *ptr;
+
+       if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
+               ptr = skb_network_header(skb);
+       else
+               ptr = skb_network_header(skb) + pkt->xt.thoff;
+
+       ptr += priv->offset;
+
+       if (unlikely(ptr + priv->len >= skb_tail_pointer(skb)))
+               return false;
+
+       if (priv->len == 2)
+               *(u16 *)dest->data = *(u16 *)ptr;
+       else if (priv->len == 4)
+               *(u32 *)dest->data = *(u32 *)ptr;
+       else
+               *(u8 *)dest->data = *(u8 *)ptr;
+       return true;
+}
+
+struct nft_jumpstack {
+       const struct nft_chain  *chain;
+       const struct nft_rule   *rule;
+       int                     rulenum;
+};
+
+static inline void
+nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt,
+               struct nft_jumpstack *jumpstack, unsigned int stackptr)
+{
+       struct nft_stats __percpu *stats;
+       const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this;
+
+       rcu_read_lock_bh();
+       stats = rcu_dereference(nft_base_chain(chain)->stats);
+       __this_cpu_inc(stats->pkts);
+       __this_cpu_add(stats->bytes, pkt->skb->len);
+       rcu_read_unlock_bh();
+}
+
+enum nft_trace {
+       NFT_TRACE_RULE,
+       NFT_TRACE_RETURN,
+       NFT_TRACE_POLICY,
+};
+
+static const char *const comments[] = {
+       [NFT_TRACE_RULE]        = "rule",
+       [NFT_TRACE_RETURN]      = "return",
+       [NFT_TRACE_POLICY]      = "policy",
+};
+
+static struct nf_loginfo trace_loginfo = {
+       .type = NF_LOG_TYPE_LOG,
+       .u = {
+               .log = {
+                       .level = 4,
+                       .logflags = NF_LOG_MASK,
+               },
+       },
+};
+
+static inline void nft_trace_packet(const struct nft_pktinfo *pkt,
+                                   const struct nft_chain *chain,
+                                   int rulenum, enum nft_trace type)
+{
+       struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
+
+       nf_log_packet(net, pkt->xt.family, pkt->hooknum, pkt->skb, pkt->in,
+                     pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
+                     chain->table->name, chain->name, comments[type],
+                     rulenum);
+}
+
+unsigned int
+nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
+{
+       const struct nft_chain *chain = ops->priv;
+       const struct nft_rule *rule;
+       const struct nft_expr *expr, *last;
+       struct nft_data data[NFT_REG_MAX + 1];
+       unsigned int stackptr = 0;
+       struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
+       int rulenum = 0;
+       /*
+        * Cache cursor to avoid problems in case that the cursor is updated
+        * while traversing the ruleset.
+        */
+       unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor);
+
+do_chain:
+       rule = list_entry(&chain->rules, struct nft_rule, list);
+next_rule:
+       data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
+       list_for_each_entry_continue_rcu(rule, &chain->rules, list) {
+
+               /* This rule is not active, skip. */
+               if (unlikely(rule->genmask & (1 << gencursor)))
+                       continue;
+
+               rulenum++;
+
+               nft_rule_for_each_expr(expr, last, rule) {
+                       if (expr->ops == &nft_cmp_fast_ops)
+                               nft_cmp_fast_eval(expr, data);
+                       else if (expr->ops != &nft_payload_fast_ops ||
+                                !nft_payload_fast_eval(expr, data, pkt))
+                               expr->ops->eval(expr, data, pkt);
+
+                       if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)
+                               break;
+               }
+
+               switch (data[NFT_REG_VERDICT].verdict) {
+               case NFT_BREAK:
+                       data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
+                       /* fall through */
+               case NFT_CONTINUE:
+                       continue;
+               }
+               break;
+       }
+
+       switch (data[NFT_REG_VERDICT].verdict) {
+       case NF_ACCEPT:
+       case NF_DROP:
+       case NF_QUEUE:
+               if (unlikely(pkt->skb->nf_trace))
+                       nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+
+               return data[NFT_REG_VERDICT].verdict;
+       case NFT_JUMP:
+               if (unlikely(pkt->skb->nf_trace))
+                       nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+
+               BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);
+               jumpstack[stackptr].chain = chain;
+               jumpstack[stackptr].rule  = rule;
+               jumpstack[stackptr].rulenum = rulenum;
+               stackptr++;
+               /* fall through */
+       case NFT_GOTO:
+               chain = data[NFT_REG_VERDICT].chain;
+               goto do_chain;
+       case NFT_RETURN:
+               if (unlikely(pkt->skb->nf_trace))
+                       nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);
+
+               /* fall through */
+       case NFT_CONTINUE:
+               break;
+       default:
+               WARN_ON(1);
+       }
+
+       if (stackptr > 0) {
+               if (unlikely(pkt->skb->nf_trace))
+                       nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN);
+
+               stackptr--;
+               chain = jumpstack[stackptr].chain;
+               rule  = jumpstack[stackptr].rule;
+               rulenum = jumpstack[stackptr].rulenum;
+               goto next_rule;
+       }
+       nft_chain_stats(chain, pkt, jumpstack, stackptr);
+
+       if (unlikely(pkt->skb->nf_trace))
+               nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY);
+
+       return nft_base_chain(chain)->policy;
+}
+EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo);
+
+int __init nf_tables_core_module_init(void)
+{
+       int err;
+
+       err = nft_immediate_module_init();
+       if (err < 0)
+               goto err1;
+
+       err = nft_cmp_module_init();
+       if (err < 0)
+               goto err2;
+
+       err = nft_lookup_module_init();
+       if (err < 0)
+               goto err3;
+
+       err = nft_bitwise_module_init();
+       if (err < 0)
+               goto err4;
+
+       err = nft_byteorder_module_init();
+       if (err < 0)
+               goto err5;
+
+       err = nft_payload_module_init();
+       if (err < 0)
+               goto err6;
+
+       return 0;
+
+err6:
+       nft_byteorder_module_exit();
+err5:
+       nft_bitwise_module_exit();
+err4:
+       nft_lookup_module_exit();
+err3:
+       nft_cmp_module_exit();
+err2:
+       nft_immediate_module_exit();
+err1:
+       return err;
+}
+
+void nf_tables_core_module_exit(void)
+{
+       nft_payload_module_exit();
+       nft_byteorder_module_exit();
+       nft_bitwise_module_exit();
+       nft_lookup_module_exit();
+       nft_cmp_module_exit();
+       nft_immediate_module_exit();
+}
index 572d87d..046aa13 100644 (file)
@@ -147,9 +147,6 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        const struct nfnetlink_subsystem *ss;
        int type, err;
 
-       if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
-               return -EPERM;
-
        /* All the messages must at least contain nfgenmsg */
        if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
                return 0;
@@ -217,9 +214,181 @@ replay:
        }
 }
 
+static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
+                               u_int16_t subsys_id)
+{
+       struct sk_buff *nskb, *oskb = skb;
+       struct net *net = sock_net(skb->sk);
+       const struct nfnetlink_subsystem *ss;
+       const struct nfnl_callback *nc;
+       bool success = true, done = false;
+       int err;
+
+       if (subsys_id >= NFNL_SUBSYS_COUNT)
+               return netlink_ack(skb, nlh, -EINVAL);
+replay:
+       nskb = netlink_skb_clone(oskb, GFP_KERNEL);
+       if (!nskb)
+               return netlink_ack(oskb, nlh, -ENOMEM);
+
+       nskb->sk = oskb->sk;
+       skb = nskb;
+
+       nfnl_lock(subsys_id);
+       ss = rcu_dereference_protected(table[subsys_id].subsys,
+                                      lockdep_is_held(&table[subsys_id].mutex));
+       if (!ss) {
+#ifdef CONFIG_MODULES
+               nfnl_unlock(subsys_id);
+               request_module("nfnetlink-subsys-%d", subsys_id);
+               nfnl_lock(subsys_id);
+               ss = rcu_dereference_protected(table[subsys_id].subsys,
+                                              lockdep_is_held(&table[subsys_id].mutex));
+               if (!ss)
+#endif
+               {
+                       nfnl_unlock(subsys_id);
+                       kfree_skb(nskb);
+                       return netlink_ack(skb, nlh, -EOPNOTSUPP);
+               }
+       }
+
+       if (!ss->commit || !ss->abort) {
+               nfnl_unlock(subsys_id);
+               kfree_skb(nskb);
+               return netlink_ack(skb, nlh, -EOPNOTSUPP);
+       }
+
+       while (skb->len >= nlmsg_total_size(0)) {
+               int msglen, type;
+
+               nlh = nlmsg_hdr(skb);
+               err = 0;
+
+               if (nlh->nlmsg_len < NLMSG_HDRLEN) {
+                       err = -EINVAL;
+                       goto ack;
+               }
+
+               /* Only requests are handled by the kernel */
+               if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
+                       err = -EINVAL;
+                       goto ack;
+               }
+
+               type = nlh->nlmsg_type;
+               if (type == NFNL_MSG_BATCH_BEGIN) {
+                       /* Malformed: Batch begin twice */
+                       success = false;
+                       goto done;
+               } else if (type == NFNL_MSG_BATCH_END) {
+                       done = true;
+                       goto done;
+               } else if (type < NLMSG_MIN_TYPE) {
+                       err = -EINVAL;
+                       goto ack;
+               }
+
+               /* We only accept a batch with messages for the same
+                * subsystem.
+                */
+               if (NFNL_SUBSYS_ID(type) != subsys_id) {
+                       err = -EINVAL;
+                       goto ack;
+               }
+
+               nc = nfnetlink_find_client(type, ss);
+               if (!nc) {
+                       err = -EINVAL;
+                       goto ack;
+               }
+
+               {
+                       int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
+                       u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
+                       struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
+                       struct nlattr *attr = (void *)nlh + min_len;
+                       int attrlen = nlh->nlmsg_len - min_len;
+
+                       err = nla_parse(cda, ss->cb[cb_id].attr_count,
+                                       attr, attrlen, ss->cb[cb_id].policy);
+                       if (err < 0)
+                               goto ack;
+
+                       if (nc->call_batch) {
+                               err = nc->call_batch(net->nfnl, skb, nlh,
+                                                    (const struct nlattr **)cda);
+                       }
+
+                       /* The lock was released to autoload some module, we
+                        * have to abort and start from scratch using the
+                        * original skb.
+                        */
+                       if (err == -EAGAIN) {
+                               ss->abort(skb);
+                               nfnl_unlock(subsys_id);
+                               kfree_skb(nskb);
+                               goto replay;
+                       }
+               }
+ack:
+               if (nlh->nlmsg_flags & NLM_F_ACK || err) {
+                       /* We don't stop processing the batch on errors, thus,
+                        * userspace gets all the errors that the batch
+                        * triggers.
+                        */
+                       netlink_ack(skb, nlh, err);
+                       if (err)
+                               success = false;
+               }
+
+               msglen = NLMSG_ALIGN(nlh->nlmsg_len);
+               if (msglen > skb->len)
+                       msglen = skb->len;
+               skb_pull(skb, msglen);
+       }
+done:
+       if (success && done)
+               ss->commit(skb);
+       else
+               ss->abort(skb);
+
+       nfnl_unlock(subsys_id);
+       kfree_skb(nskb);
+}
+
 static void nfnetlink_rcv(struct sk_buff *skb)
 {
-       netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
+       struct nlmsghdr *nlh = nlmsg_hdr(skb);
+       struct net *net = sock_net(skb->sk);
+       int msglen;
+
+       if (nlh->nlmsg_len < NLMSG_HDRLEN ||
+           skb->len < nlh->nlmsg_len)
+               return;
+
+       if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+               netlink_ack(skb, nlh, -EPERM);
+               return;
+       }
+
+       if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) {
+               struct nfgenmsg *nfgenmsg;
+
+               msglen = NLMSG_ALIGN(nlh->nlmsg_len);
+               if (msglen > skb->len)
+                       msglen = skb->len;
+
+               if (nlh->nlmsg_len < NLMSG_HDRLEN ||
+                   skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
+                       return;
+
+               nfgenmsg = nlmsg_data(nlh);
+               skb_pull(skb, msglen);
+               nfnetlink_rcv_batch(skb, nlh, nfgenmsg->res_id);
+       } else {
+               netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
+       }
 }
 
 #ifdef CONFIG_MODULES
index 5058049..476accd 100644 (file)
@@ -49,10 +49,8 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
 };
 
 static int
-ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
-                         struct nf_conntrack_l4proto *l4proto,
-                         struct net *net,
-                         const struct nlattr *attr)
+ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto,
+                         struct net *net, const struct nlattr *attr)
 {
        int ret = 0;
 
@@ -64,8 +62,7 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
                if (ret < 0)
                        return ret;
 
-               ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net,
-                                                         &timeout->data);
+               ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts);
        }
        return ret;
 }
@@ -123,7 +120,8 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
                                goto err_proto_put;
                        }
 
-                       ret = ctnl_timeout_parse_policy(matching, l4proto, net,
+                       ret = ctnl_timeout_parse_policy(&matching->data,
+                                                       l4proto, net,
                                                        cda[CTA_TIMEOUT_DATA]);
                        return ret;
                }
@@ -138,7 +136,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
                goto err_proto_put;
        }
 
-       ret = ctnl_timeout_parse_policy(timeout, l4proto, net,
+       ret = ctnl_timeout_parse_policy(&timeout->data, l4proto, net,
                                        cda[CTA_TIMEOUT_DATA]);
        if (ret < 0)
                goto err;
@@ -342,6 +340,147 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
        return ret;
 }
 
+static int
+cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb,
+                     const struct nlmsghdr *nlh,
+                     const struct nlattr * const cda[])
+{
+       __u16 l3num;
+       __u8 l4num;
+       struct nf_conntrack_l4proto *l4proto;
+       struct net *net = sock_net(skb->sk);
+       unsigned int *timeouts;
+       int ret;
+
+       if (!cda[CTA_TIMEOUT_L3PROTO] ||
+           !cda[CTA_TIMEOUT_L4PROTO] ||
+           !cda[CTA_TIMEOUT_DATA])
+               return -EINVAL;
+
+       l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
+       l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
+       l4proto = nf_ct_l4proto_find_get(l3num, l4num);
+
+       /* This protocol is not supported, skip. */
+       if (l4proto->l4proto != l4num) {
+               ret = -EOPNOTSUPP;
+               goto err;
+       }
+
+       timeouts = l4proto->get_timeouts(net);
+
+       ret = ctnl_timeout_parse_policy(timeouts, l4proto, net,
+                                       cda[CTA_TIMEOUT_DATA]);
+       if (ret < 0)
+               goto err;
+
+       nf_ct_l4proto_put(l4proto);
+       return 0;
+err:
+       nf_ct_l4proto_put(l4proto);
+       return ret;
+}
+
+static int
+cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid,
+                           u32 seq, u32 type, int event,
+                           struct nf_conntrack_l4proto *l4proto)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+       unsigned int flags = portid ? NLM_F_MULTI : 0;
+
+       event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
+       if (nlh == NULL)
+               goto nlmsg_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family = AF_UNSPEC;
+       nfmsg->version = NFNETLINK_V0;
+       nfmsg->res_id = 0;
+
+       if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l4proto->l3proto)) ||
+           nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto))
+               goto nla_put_failure;
+
+       if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
+               struct nlattr *nest_parms;
+               unsigned int *timeouts = l4proto->get_timeouts(net);
+               int ret;
+
+               nest_parms = nla_nest_start(skb,
+                                           CTA_TIMEOUT_DATA | NLA_F_NESTED);
+               if (!nest_parms)
+                       goto nla_put_failure;
+
+               ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts);
+               if (ret < 0)
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, nest_parms);
+       }
+
+       nlmsg_end(skb, nlh);
+       return skb->len;
+
+nlmsg_failure:
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -1;
+}
+
+static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb,
+                                const struct nlmsghdr *nlh,
+                                const struct nlattr * const cda[])
+{
+       __u16 l3num;
+       __u8 l4num;
+       struct nf_conntrack_l4proto *l4proto;
+       struct net *net = sock_net(skb->sk);
+       struct sk_buff *skb2;
+       int ret, err;
+
+       if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO])
+               return -EINVAL;
+
+       l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
+       l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
+       l4proto = nf_ct_l4proto_find_get(l3num, l4num);
+
+       /* This protocol is not supported, skip. */
+       if (l4proto->l4proto != l4num) {
+               err = -EOPNOTSUPP;
+               goto err;
+       }
+
+       skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (skb2 == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       ret = cttimeout_default_fill_info(net, skb2, NETLINK_CB(skb).portid,
+                                         nlh->nlmsg_seq,
+                                         NFNL_MSG_TYPE(nlh->nlmsg_type),
+                                         IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
+                                         l4proto);
+       if (ret <= 0) {
+               kfree_skb(skb2);
+               err = -ENOMEM;
+               goto err;
+       }
+       ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
+       if (ret > 0)
+               ret = 0;
+
+       /* this avoids a loop in nfnetlink. */
+       return ret == -EAGAIN ? -ENOBUFS : ret;
+err:
+       nf_ct_l4proto_put(l4proto);
+       return err;
+}
+
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
 {
@@ -384,6 +523,12 @@ static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
        [IPCTNL_MSG_TIMEOUT_DELETE]     = { .call = cttimeout_del_timeout,
                                            .attr_count = CTA_TIMEOUT_MAX,
                                            .policy = cttimeout_nla_policy },
+       [IPCTNL_MSG_TIMEOUT_DEFAULT_SET]= { .call = cttimeout_default_set,
+                                           .attr_count = CTA_TIMEOUT_MAX,
+                                           .policy = cttimeout_nla_policy },
+       [IPCTNL_MSG_TIMEOUT_DEFAULT_GET]= { .call = cttimeout_default_get,
+                                           .attr_count = CTA_TIMEOUT_MAX,
+                                           .policy = cttimeout_nla_policy },
 };
 
 static const struct nfnetlink_subsystem cttimeout_subsys = {
index d92cc31..3c4b69e 100644 (file)
@@ -319,7 +319,8 @@ nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags)
 }
 
 static struct sk_buff *
-nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size)
+nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
+                unsigned int pkt_size)
 {
        struct sk_buff *skb;
        unsigned int n;
@@ -328,13 +329,13 @@ nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size)
         * message.  WARNING: has to be <= 128k due to slab restrictions */
 
        n = max(inst_size, pkt_size);
-       skb = nfnetlink_alloc_skb(&init_net, n, peer_portid, GFP_ATOMIC);
+       skb = nfnetlink_alloc_skb(net, n, peer_portid, GFP_ATOMIC);
        if (!skb) {
                if (n > pkt_size) {
                        /* try to allocate only as much as we need for current
                         * packet */
 
-                       skb = nfnetlink_alloc_skb(&init_net, pkt_size,
+                       skb = nfnetlink_alloc_skb(net, pkt_size,
                                                  peer_portid, GFP_ATOMIC);
                        if (!skb)
                                pr_err("nfnetlink_log: can't even alloc %u bytes\n",
@@ -702,8 +703,8 @@ nfulnl_log_packet(struct net *net,
        }
 
        if (!inst->skb) {
-               inst->skb = nfulnl_alloc_skb(inst->peer_portid, inst->nlbufsiz,
-                                            size);
+               inst->skb = nfulnl_alloc_skb(net, inst->peer_portid,
+                                            inst->nlbufsiz, size);
                if (!inst->skb)
                        goto alloc_failure;
        }
index ae2e5c1..21258cf 100644 (file)
@@ -298,7 +298,7 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
 }
 
 static struct sk_buff *
-nfqnl_build_packet_message(struct nfqnl_instance *queue,
+nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                           struct nf_queue_entry *entry,
                           __be32 **packet_id_ptr)
 {
@@ -372,7 +372,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        if (queue->flags & NFQA_CFG_F_CONNTRACK)
                ct = nfqnl_ct_get(entskb, &size, &ctinfo);
 
-       skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid,
+       skb = nfnetlink_alloc_skb(net, size, queue->peer_portid,
                                  GFP_ATOMIC);
        if (!skb)
                return NULL;
@@ -525,7 +525,7 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue,
        __be32 *packet_id_ptr;
        int failopen = 0;
 
-       nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr);
+       nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr);
        if (nskb == NULL) {
                err = -ENOMEM;
                goto err_out;
diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c
new file mode 100644 (file)
index 0000000..4fb6ee2
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_bitwise {
+       enum nft_registers      sreg:8;
+       enum nft_registers      dreg:8;
+       u8                      len;
+       struct nft_data         mask;
+       struct nft_data         xor;
+};
+
+static void nft_bitwise_eval(const struct nft_expr *expr,
+                            struct nft_data data[NFT_REG_MAX + 1],
+                            const struct nft_pktinfo *pkt)
+{
+       const struct nft_bitwise *priv = nft_expr_priv(expr);
+       const struct nft_data *src = &data[priv->sreg];
+       struct nft_data *dst = &data[priv->dreg];
+       unsigned int i;
+
+       for (i = 0; i < DIV_ROUND_UP(priv->len, 4); i++) {
+               dst->data[i] = (src->data[i] & priv->mask.data[i]) ^
+                              priv->xor.data[i];
+       }
+}
+
+static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
+       [NFTA_BITWISE_SREG]     = { .type = NLA_U32 },
+       [NFTA_BITWISE_DREG]     = { .type = NLA_U32 },
+       [NFTA_BITWISE_LEN]      = { .type = NLA_U32 },
+       [NFTA_BITWISE_MASK]     = { .type = NLA_NESTED },
+       [NFTA_BITWISE_XOR]      = { .type = NLA_NESTED },
+};
+
+static int nft_bitwise_init(const struct nft_ctx *ctx,
+                           const struct nft_expr *expr,
+                           const struct nlattr * const tb[])
+{
+       struct nft_bitwise *priv = nft_expr_priv(expr);
+       struct nft_data_desc d1, d2;
+       int err;
+
+       if (tb[NFTA_BITWISE_SREG] == NULL ||
+           tb[NFTA_BITWISE_DREG] == NULL ||
+           tb[NFTA_BITWISE_LEN] == NULL ||
+           tb[NFTA_BITWISE_MASK] == NULL ||
+           tb[NFTA_BITWISE_XOR] == NULL)
+               return -EINVAL;
+
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_SREG]));
+       err = nft_validate_input_register(priv->sreg);
+       if (err < 0)
+               return err;
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               return err;
+
+       priv->len = ntohl(nla_get_be32(tb[NFTA_BITWISE_LEN]));
+
+       err = nft_data_init(NULL, &priv->mask, &d1, tb[NFTA_BITWISE_MASK]);
+       if (err < 0)
+               return err;
+       if (d1.len != priv->len)
+               return -EINVAL;
+
+       err = nft_data_init(NULL, &priv->xor, &d2, tb[NFTA_BITWISE_XOR]);
+       if (err < 0)
+               return err;
+       if (d2.len != priv->len)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int nft_bitwise_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_bitwise *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_BITWISE_SREG, htonl(priv->sreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_BITWISE_DREG, htonl(priv->dreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_BITWISE_LEN, htonl(priv->len)))
+               goto nla_put_failure;
+
+       if (nft_data_dump(skb, NFTA_BITWISE_MASK, &priv->mask,
+                         NFT_DATA_VALUE, priv->len) < 0)
+               goto nla_put_failure;
+
+       if (nft_data_dump(skb, NFTA_BITWISE_XOR, &priv->xor,
+                         NFT_DATA_VALUE, priv->len) < 0)
+               goto nla_put_failure;
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_bitwise_type;
+static const struct nft_expr_ops nft_bitwise_ops = {
+       .type           = &nft_bitwise_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)),
+       .eval           = nft_bitwise_eval,
+       .init           = nft_bitwise_init,
+       .dump           = nft_bitwise_dump,
+};
+
+static struct nft_expr_type nft_bitwise_type __read_mostly = {
+       .name           = "bitwise",
+       .ops            = &nft_bitwise_ops,
+       .policy         = nft_bitwise_policy,
+       .maxattr        = NFTA_BITWISE_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_bitwise_module_init(void)
+{
+       return nft_register_expr(&nft_bitwise_type);
+}
+
+void nft_bitwise_module_exit(void)
+{
+       nft_unregister_expr(&nft_bitwise_type);
+}
diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c
new file mode 100644 (file)
index 0000000..c39ed8d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_byteorder {
+       enum nft_registers      sreg:8;
+       enum nft_registers      dreg:8;
+       enum nft_byteorder_ops  op:8;
+       u8                      len;
+       u8                      size;
+};
+
+static void nft_byteorder_eval(const struct nft_expr *expr,
+                              struct nft_data data[NFT_REG_MAX + 1],
+                              const struct nft_pktinfo *pkt)
+{
+       const struct nft_byteorder *priv = nft_expr_priv(expr);
+       struct nft_data *src = &data[priv->sreg], *dst = &data[priv->dreg];
+       union { u32 u32; u16 u16; } *s, *d;
+       unsigned int i;
+
+       s = (void *)src->data;
+       d = (void *)dst->data;
+
+       switch (priv->size) {
+       case 4:
+               switch (priv->op) {
+               case NFT_BYTEORDER_NTOH:
+                       for (i = 0; i < priv->len / 4; i++)
+                               d[i].u32 = ntohl((__force __be32)s[i].u32);
+                       break;
+               case NFT_BYTEORDER_HTON:
+                       for (i = 0; i < priv->len / 4; i++)
+                               d[i].u32 = (__force __u32)htonl(s[i].u32);
+                       break;
+               }
+               break;
+       case 2:
+               switch (priv->op) {
+               case NFT_BYTEORDER_NTOH:
+                       for (i = 0; i < priv->len / 2; i++)
+                               d[i].u16 = ntohs((__force __be16)s[i].u16);
+                       break;
+               case NFT_BYTEORDER_HTON:
+                       for (i = 0; i < priv->len / 2; i++)
+                               d[i].u16 = (__force __u16)htons(s[i].u16);
+                       break;
+               }
+               break;
+       }
+}
+
+static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = {
+       [NFTA_BYTEORDER_SREG]   = { .type = NLA_U32 },
+       [NFTA_BYTEORDER_DREG]   = { .type = NLA_U32 },
+       [NFTA_BYTEORDER_OP]     = { .type = NLA_U32 },
+       [NFTA_BYTEORDER_LEN]    = { .type = NLA_U32 },
+       [NFTA_BYTEORDER_SIZE]   = { .type = NLA_U32 },
+};
+
+static int nft_byteorder_init(const struct nft_ctx *ctx,
+                             const struct nft_expr *expr,
+                             const struct nlattr * const tb[])
+{
+       struct nft_byteorder *priv = nft_expr_priv(expr);
+       int err;
+
+       if (tb[NFTA_BYTEORDER_SREG] == NULL ||
+           tb[NFTA_BYTEORDER_DREG] == NULL ||
+           tb[NFTA_BYTEORDER_LEN] == NULL ||
+           tb[NFTA_BYTEORDER_SIZE] == NULL ||
+           tb[NFTA_BYTEORDER_OP] == NULL)
+               return -EINVAL;
+
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SREG]));
+       err = nft_validate_input_register(priv->sreg);
+       if (err < 0)
+               return err;
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               return err;
+
+       priv->op = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_OP]));
+       switch (priv->op) {
+       case NFT_BYTEORDER_NTOH:
+       case NFT_BYTEORDER_HTON:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       priv->len = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_LEN]));
+       if (priv->len == 0 || priv->len > FIELD_SIZEOF(struct nft_data, data))
+               return -EINVAL;
+
+       priv->size = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SIZE]));
+       switch (priv->size) {
+       case 2:
+       case 4:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nft_byteorder_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_byteorder *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_BYTEORDER_SREG, htonl(priv->sreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_BYTEORDER_DREG, htonl(priv->dreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_byteorder_type;
+static const struct nft_expr_ops nft_byteorder_ops = {
+       .type           = &nft_byteorder_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)),
+       .eval           = nft_byteorder_eval,
+       .init           = nft_byteorder_init,
+       .dump           = nft_byteorder_dump,
+};
+
+static struct nft_expr_type nft_byteorder_type __read_mostly = {
+       .name           = "byteorder",
+       .ops            = &nft_byteorder_ops,
+       .policy         = nft_byteorder_policy,
+       .maxattr        = NFTA_BYTEORDER_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_byteorder_module_init(void)
+{
+       return nft_register_expr(&nft_byteorder_type);
+}
+
+void nft_byteorder_module_exit(void)
+{
+       nft_unregister_expr(&nft_byteorder_type);
+}
diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c
new file mode 100644 (file)
index 0000000..954925d
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_cmp_expr {
+       struct nft_data         data;
+       enum nft_registers      sreg:8;
+       u8                      len;
+       enum nft_cmp_ops        op:8;
+};
+
+static void nft_cmp_eval(const struct nft_expr *expr,
+                        struct nft_data data[NFT_REG_MAX + 1],
+                        const struct nft_pktinfo *pkt)
+{
+       const struct nft_cmp_expr *priv = nft_expr_priv(expr);
+       int d;
+
+       d = nft_data_cmp(&data[priv->sreg], &priv->data, priv->len);
+       switch (priv->op) {
+       case NFT_CMP_EQ:
+               if (d != 0)
+                       goto mismatch;
+               break;
+       case NFT_CMP_NEQ:
+               if (d == 0)
+                       goto mismatch;
+               break;
+       case NFT_CMP_LT:
+               if (d == 0)
+                       goto mismatch;
+       case NFT_CMP_LTE:
+               if (d > 0)
+                       goto mismatch;
+               break;
+       case NFT_CMP_GT:
+               if (d == 0)
+                       goto mismatch;
+       case NFT_CMP_GTE:
+               if (d < 0)
+                       goto mismatch;
+               break;
+       }
+       return;
+
+mismatch:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = {
+       [NFTA_CMP_SREG]         = { .type = NLA_U32 },
+       [NFTA_CMP_OP]           = { .type = NLA_U32 },
+       [NFTA_CMP_DATA]         = { .type = NLA_NESTED },
+};
+
+static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+                       const struct nlattr * const tb[])
+{
+       struct nft_cmp_expr *priv = nft_expr_priv(expr);
+       struct nft_data_desc desc;
+       int err;
+
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
+       priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
+
+       err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
+       BUG_ON(err < 0);
+
+       priv->len = desc.len;
+       return 0;
+}
+
+static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_cmp_expr *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op)))
+               goto nla_put_failure;
+
+       if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data,
+                         NFT_DATA_VALUE, priv->len) < 0)
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_cmp_type;
+static const struct nft_expr_ops nft_cmp_ops = {
+       .type           = &nft_cmp_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
+       .eval           = nft_cmp_eval,
+       .init           = nft_cmp_init,
+       .dump           = nft_cmp_dump,
+};
+
+static int nft_cmp_fast_init(const struct nft_ctx *ctx,
+                            const struct nft_expr *expr,
+                            const struct nlattr * const tb[])
+{
+       struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
+       struct nft_data_desc desc;
+       struct nft_data data;
+       u32 mask;
+       int err;
+
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
+
+       err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
+       BUG_ON(err < 0);
+       desc.len *= BITS_PER_BYTE;
+
+       mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - desc.len);
+       priv->data = data.data[0] & mask;
+       priv->len  = desc.len;
+       return 0;
+}
+
+static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
+       struct nft_data data;
+
+       if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ)))
+               goto nla_put_failure;
+
+       data.data[0] = priv->data;
+       if (nft_data_dump(skb, NFTA_CMP_DATA, &data,
+                         NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0)
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+const struct nft_expr_ops nft_cmp_fast_ops = {
+       .type           = &nft_cmp_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
+       .eval           = NULL, /* inlined */
+       .init           = nft_cmp_fast_init,
+       .dump           = nft_cmp_fast_dump,
+};
+
+static const struct nft_expr_ops *
+nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
+{
+       struct nft_data_desc desc;
+       struct nft_data data;
+       enum nft_registers sreg;
+       enum nft_cmp_ops op;
+       int err;
+
+       if (tb[NFTA_CMP_SREG] == NULL ||
+           tb[NFTA_CMP_OP] == NULL ||
+           tb[NFTA_CMP_DATA] == NULL)
+               return ERR_PTR(-EINVAL);
+
+       sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
+       err = nft_validate_input_register(sreg);
+       if (err < 0)
+               return ERR_PTR(err);
+
+       op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
+       switch (op) {
+       case NFT_CMP_EQ:
+       case NFT_CMP_NEQ:
+       case NFT_CMP_LT:
+       case NFT_CMP_LTE:
+       case NFT_CMP_GT:
+       case NFT_CMP_GTE:
+               break;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+
+       err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
+       if (err < 0)
+               return ERR_PTR(err);
+
+       if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ)
+               return &nft_cmp_fast_ops;
+       else
+               return &nft_cmp_ops;
+}
+
+static struct nft_expr_type nft_cmp_type __read_mostly = {
+       .name           = "cmp",
+       .select_ops     = nft_cmp_select_ops,
+       .policy         = nft_cmp_policy,
+       .maxattr        = NFTA_CMP_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_cmp_module_init(void)
+{
+       return nft_register_expr(&nft_cmp_type);
+}
+
+void nft_cmp_module_exit(void)
+{
+       nft_unregister_expr(&nft_cmp_type);
+}
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
new file mode 100644 (file)
index 0000000..a82667c
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <asm/uaccess.h> /* for set_fs */
+#include <net/netfilter/nf_tables.h>
+
+union nft_entry {
+       struct ipt_entry e4;
+       struct ip6t_entry e6;
+};
+
+static inline void
+nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info)
+{
+       par->target     = xt;
+       par->targinfo   = xt_info;
+       par->hotdrop    = false;
+}
+
+static void nft_target_eval(const struct nft_expr *expr,
+                           struct nft_data data[NFT_REG_MAX + 1],
+                           const struct nft_pktinfo *pkt)
+{
+       void *info = nft_expr_priv(expr);
+       struct xt_target *target = expr->ops->data;
+       struct sk_buff *skb = pkt->skb;
+       int ret;
+
+       nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info);
+
+       ret = target->target(skb, &pkt->xt);
+
+       if (pkt->xt.hotdrop)
+               ret = NF_DROP;
+
+       switch(ret) {
+       case XT_CONTINUE:
+               data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
+               break;
+       default:
+               data[NFT_REG_VERDICT].verdict = ret;
+               break;
+       }
+       return;
+}
+
+static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = {
+       [NFTA_TARGET_NAME]      = { .type = NLA_NUL_STRING },
+       [NFTA_TARGET_REV]       = { .type = NLA_U32 },
+       [NFTA_TARGET_INFO]      = { .type = NLA_BINARY },
+};
+
+static void
+nft_target_set_tgchk_param(struct xt_tgchk_param *par,
+                          const struct nft_ctx *ctx,
+                          struct xt_target *target, void *info,
+                          union nft_entry *entry, u8 proto, bool inv)
+{
+       par->net        = &init_net;
+       par->table      = ctx->table->name;
+       switch (ctx->afi->family) {
+       case AF_INET:
+               entry->e4.ip.proto = proto;
+               entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
+               break;
+       case AF_INET6:
+               entry->e6.ipv6.proto = proto;
+               entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
+               break;
+       }
+       par->entryinfo  = entry;
+       par->target     = target;
+       par->targinfo   = info;
+       if (ctx->chain->flags & NFT_BASE_CHAIN) {
+               const struct nft_base_chain *basechain =
+                                               nft_base_chain(ctx->chain);
+               const struct nf_hook_ops *ops = &basechain->ops;
+
+               par->hook_mask = 1 << ops->hooknum;
+       }
+       par->family     = ctx->afi->family;
+}
+
+static void target_compat_from_user(struct xt_target *t, void *in, void *out)
+{
+#ifdef CONFIG_COMPAT
+       if (t->compat_from_user) {
+               int pad;
+
+               t->compat_from_user(out, in);
+               pad = XT_ALIGN(t->targetsize) - t->targetsize;
+               if (pad > 0)
+                       memset(out + t->targetsize, 0, pad);
+       } else
+#endif
+               memcpy(out, in, XT_ALIGN(t->targetsize));
+}
+
+static inline int nft_compat_target_offset(struct xt_target *target)
+{
+#ifdef CONFIG_COMPAT
+       return xt_compat_target_offset(target);
+#else
+       return 0;
+#endif
+}
+
+static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = {
+       [NFTA_RULE_COMPAT_PROTO]        = { .type = NLA_U32 },
+       [NFTA_RULE_COMPAT_FLAGS]        = { .type = NLA_U32 },
+};
+
+static u8 nft_parse_compat(const struct nlattr *attr, bool *inv)
+{
+       struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
+       u32 flags;
+       int err;
+
+       err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr,
+                              nft_rule_compat_policy);
+       if (err < 0)
+               return err;
+
+       if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS])
+               return -EINVAL;
+
+       flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS]));
+       if (flags & ~NFT_RULE_COMPAT_F_MASK)
+               return -EINVAL;
+       if (flags & NFT_RULE_COMPAT_F_INV)
+               *inv = true;
+
+       return ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
+}
+
+static int
+nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+               const struct nlattr * const tb[])
+{
+       void *info = nft_expr_priv(expr);
+       struct xt_target *target = expr->ops->data;
+       struct xt_tgchk_param par;
+       size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
+       u8 proto = 0;
+       bool inv = false;
+       union nft_entry e = {};
+       int ret;
+
+       target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);
+
+       if (ctx->nla[NFTA_RULE_COMPAT])
+               proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+
+       nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
+
+       ret = xt_check_target(&par, size, proto, inv);
+       if (ret < 0)
+               goto err;
+
+       /* The standard target cannot be used */
+       if (target->target == NULL) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+err:
+       module_put(target->me);
+       return ret;
+}
+
+static void
+nft_target_destroy(const struct nft_expr *expr)
+{
+       struct xt_target *target = expr->ops->data;
+
+       module_put(target->me);
+}
+
+static int
+target_dump_info(struct sk_buff *skb, const struct xt_target *t, const void *in)
+{
+       int ret;
+
+#ifdef CONFIG_COMPAT
+       if (t->compat_to_user) {
+               mm_segment_t old_fs;
+               void *out;
+
+               out = kmalloc(XT_ALIGN(t->targetsize), GFP_ATOMIC);
+               if (out == NULL)
+                       return -ENOMEM;
+
+               /* We want to reuse existing compat_to_user */
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               t->compat_to_user(out, in);
+               set_fs(old_fs);
+               ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), out);
+               kfree(out);
+       } else
+#endif
+               ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), in);
+
+       return ret;
+}
+
+static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct xt_target *target = expr->ops->data;
+       void *info = nft_expr_priv(expr);
+
+       if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) ||
+           nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) ||
+           target_dump_info(skb, target, info))
+               goto nla_put_failure;
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static int nft_target_validate(const struct nft_ctx *ctx,
+                              const struct nft_expr *expr,
+                              const struct nft_data **data)
+{
+       struct xt_target *target = expr->ops->data;
+       unsigned int hook_mask = 0;
+
+       if (ctx->chain->flags & NFT_BASE_CHAIN) {
+               const struct nft_base_chain *basechain =
+                                               nft_base_chain(ctx->chain);
+               const struct nf_hook_ops *ops = &basechain->ops;
+
+               hook_mask = 1 << ops->hooknum;
+               if (hook_mask & target->hooks)
+                       return 0;
+
+               /* This target is being called from an invalid chain */
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void nft_match_eval(const struct nft_expr *expr,
+                          struct nft_data data[NFT_REG_MAX + 1],
+                          const struct nft_pktinfo *pkt)
+{
+       void *info = nft_expr_priv(expr);
+       struct xt_match *match = expr->ops->data;
+       struct sk_buff *skb = pkt->skb;
+       bool ret;
+
+       nft_compat_set_par((struct xt_action_param *)&pkt->xt, match, info);
+
+       ret = match->match(skb, (struct xt_action_param *)&pkt->xt);
+
+       if (pkt->xt.hotdrop) {
+               data[NFT_REG_VERDICT].verdict = NF_DROP;
+               return;
+       }
+
+       switch(ret) {
+       case true:
+               data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
+               break;
+       case false:
+               data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+               break;
+       }
+}
+
+static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = {
+       [NFTA_MATCH_NAME]       = { .type = NLA_NUL_STRING },
+       [NFTA_MATCH_REV]        = { .type = NLA_U32 },
+       [NFTA_MATCH_INFO]       = { .type = NLA_BINARY },
+};
+
+/* struct xt_mtchk_param and xt_tgchk_param look very similar */
+static void
+nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
+                         struct xt_match *match, void *info,
+                         union nft_entry *entry, u8 proto, bool inv)
+{
+       par->net        = &init_net;
+       par->table      = ctx->table->name;
+       switch (ctx->afi->family) {
+       case AF_INET:
+               entry->e4.ip.proto = proto;
+               entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
+               break;
+       case AF_INET6:
+               entry->e6.ipv6.proto = proto;
+               entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
+               break;
+       }
+       par->entryinfo  = entry;
+       par->match      = match;
+       par->matchinfo  = info;
+       if (ctx->chain->flags & NFT_BASE_CHAIN) {
+               const struct nft_base_chain *basechain =
+                                               nft_base_chain(ctx->chain);
+               const struct nf_hook_ops *ops = &basechain->ops;
+
+               par->hook_mask = 1 << ops->hooknum;
+       }
+       par->family     = ctx->afi->family;
+}
+
+static void match_compat_from_user(struct xt_match *m, void *in, void *out)
+{
+#ifdef CONFIG_COMPAT
+       if (m->compat_from_user) {
+               int pad;
+
+               m->compat_from_user(out, in);
+               pad = XT_ALIGN(m->matchsize) - m->matchsize;
+               if (pad > 0)
+                       memset(out + m->matchsize, 0, pad);
+       } else
+#endif
+               memcpy(out, in, XT_ALIGN(m->matchsize));
+}
+
+static int
+nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+               const struct nlattr * const tb[])
+{
+       void *info = nft_expr_priv(expr);
+       struct xt_match *match = expr->ops->data;
+       struct xt_mtchk_param par;
+       size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
+       u8 proto = 0;
+       bool inv = false;
+       union nft_entry e = {};
+       int ret;
+
+       match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);
+
+       if (ctx->nla[NFTA_RULE_COMPAT])
+               proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+
+       nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
+
+       ret = xt_check_match(&par, size, proto, inv);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+err:
+       module_put(match->me);
+       return ret;
+}
+
+static void
+nft_match_destroy(const struct nft_expr *expr)
+{
+       struct xt_match *match = expr->ops->data;
+
+       module_put(match->me);
+}
+
+static int
+match_dump_info(struct sk_buff *skb, const struct xt_match *m, const void *in)
+{
+       int ret;
+
+#ifdef CONFIG_COMPAT
+       if (m->compat_to_user) {
+               mm_segment_t old_fs;
+               void *out;
+
+               out = kmalloc(XT_ALIGN(m->matchsize), GFP_ATOMIC);
+               if (out == NULL)
+                       return -ENOMEM;
+
+               /* We want to reuse existing compat_to_user */
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               m->compat_to_user(out, in);
+               set_fs(old_fs);
+               ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), out);
+               kfree(out);
+       } else
+#endif
+               ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), in);
+
+       return ret;
+}
+
+static inline int nft_compat_match_offset(struct xt_match *match)
+{
+#ifdef CONFIG_COMPAT
+       return xt_compat_match_offset(match);
+#else
+       return 0;
+#endif
+}
+
+static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       void *info = nft_expr_priv(expr);
+       struct xt_match *match = expr->ops->data;
+
+       if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) ||
+           nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) ||
+           match_dump_info(skb, match, info))
+               goto nla_put_failure;
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static int nft_match_validate(const struct nft_ctx *ctx,
+                             const struct nft_expr *expr,
+                             const struct nft_data **data)
+{
+       struct xt_match *match = expr->ops->data;
+       unsigned int hook_mask = 0;
+
+       if (ctx->chain->flags & NFT_BASE_CHAIN) {
+               const struct nft_base_chain *basechain =
+                                               nft_base_chain(ctx->chain);
+               const struct nf_hook_ops *ops = &basechain->ops;
+
+               hook_mask = 1 << ops->hooknum;
+               if (hook_mask & match->hooks)
+                       return 0;
+
+               /* This match is being called from an invalid chain */
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
+                     int event, u16 family, const char *name,
+                     int rev, int target)
+{
+       struct nlmsghdr *nlh;
+       struct nfgenmsg *nfmsg;
+       unsigned int flags = portid ? NLM_F_MULTI : 0;
+
+       event |= NFNL_SUBSYS_NFT_COMPAT << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
+       if (nlh == NULL)
+               goto nlmsg_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family = family;
+       nfmsg->version = NFNETLINK_V0;
+       nfmsg->res_id = 0;
+
+       if (nla_put_string(skb, NFTA_COMPAT_NAME, name) ||
+           nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) ||
+           nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target)))
+               goto nla_put_failure;
+
+       nlmsg_end(skb, nlh);
+       return skb->len;
+
+nlmsg_failure:
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -1;
+}
+
+static int
+nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb,
+               const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+{
+       int ret = 0, target;
+       struct nfgenmsg *nfmsg;
+       const char *fmt;
+       const char *name;
+       u32 rev;
+       struct sk_buff *skb2;
+
+       if (tb[NFTA_COMPAT_NAME] == NULL ||
+           tb[NFTA_COMPAT_REV] == NULL ||
+           tb[NFTA_COMPAT_TYPE] == NULL)
+               return -EINVAL;
+
+       name = nla_data(tb[NFTA_COMPAT_NAME]);
+       rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV]));
+       target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE]));
+
+       nfmsg = nlmsg_data(nlh);
+
+       switch(nfmsg->nfgen_family) {
+       case AF_INET:
+               fmt = "ipt_%s";
+               break;
+       case AF_INET6:
+               fmt = "ip6t_%s";
+               break;
+       default:
+               pr_err("nft_compat: unsupported protocol %d\n",
+                       nfmsg->nfgen_family);
+               return -EINVAL;
+       }
+
+       try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name,
+                                                rev, target, &ret),
+                                                fmt, name);
+
+       if (ret < 0)
+               return ret;
+
+       skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (skb2 == NULL)
+               return -ENOMEM;
+
+       /* include the best revision for this extension in the message */
+       if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid,
+                                 nlh->nlmsg_seq,
+                                 NFNL_MSG_TYPE(nlh->nlmsg_type),
+                                 NFNL_MSG_COMPAT_GET,
+                                 nfmsg->nfgen_family,
+                                 name, ret, target) <= 0) {
+               kfree_skb(skb2);
+               return -ENOSPC;
+       }
+
+       ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid,
+                               MSG_DONTWAIT);
+       if (ret > 0)
+               ret = 0;
+
+       return ret == -EAGAIN ? -ENOBUFS : ret;
+}
+
+static const struct nla_policy nfnl_compat_policy_get[NFTA_COMPAT_MAX+1] = {
+       [NFTA_COMPAT_NAME]      = { .type = NLA_NUL_STRING,
+                                   .len = NFT_COMPAT_NAME_MAX-1 },
+       [NFTA_COMPAT_REV]       = { .type = NLA_U32 },
+       [NFTA_COMPAT_TYPE]      = { .type = NLA_U32 },
+};
+
+static const struct nfnl_callback nfnl_nft_compat_cb[NFNL_MSG_COMPAT_MAX] = {
+       [NFNL_MSG_COMPAT_GET]           = { .call = nfnl_compat_get,
+                                           .attr_count = NFTA_COMPAT_MAX,
+                                           .policy = nfnl_compat_policy_get },
+};
+
+static const struct nfnetlink_subsystem nfnl_compat_subsys = {
+       .name           = "nft-compat",
+       .subsys_id      = NFNL_SUBSYS_NFT_COMPAT,
+       .cb_count       = NFNL_MSG_COMPAT_MAX,
+       .cb             = nfnl_nft_compat_cb,
+};
+
+static LIST_HEAD(nft_match_list);
+
+struct nft_xt {
+       struct list_head        head;
+       struct nft_expr_ops     ops;
+};
+
+static struct nft_expr_type nft_match_type;
+
+static const struct nft_expr_ops *
+nft_match_select_ops(const struct nft_ctx *ctx,
+                    const struct nlattr * const tb[])
+{
+       struct nft_xt *nft_match;
+       struct xt_match *match;
+       char *mt_name;
+       __u32 rev, family;
+
+       if (tb[NFTA_MATCH_NAME] == NULL ||
+           tb[NFTA_MATCH_REV] == NULL ||
+           tb[NFTA_MATCH_INFO] == NULL)
+               return ERR_PTR(-EINVAL);
+
+       mt_name = nla_data(tb[NFTA_MATCH_NAME]);
+       rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV]));
+       family = ctx->afi->family;
+
+       /* Re-use the existing match if it's already loaded. */
+       list_for_each_entry(nft_match, &nft_match_list, head) {
+               struct xt_match *match = nft_match->ops.data;
+
+               if (strcmp(match->name, mt_name) == 0 &&
+                   match->revision == rev && match->family == family)
+                       return &nft_match->ops;
+       }
+
+       match = xt_request_find_match(family, mt_name, rev);
+       if (IS_ERR(match))
+               return ERR_PTR(-ENOENT);
+
+       /* This is the first time we use this match, allocate operations */
+       nft_match = kzalloc(sizeof(struct nft_xt), GFP_KERNEL);
+       if (nft_match == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       nft_match->ops.type = &nft_match_type;
+       nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize) +
+                                           nft_compat_match_offset(match));
+       nft_match->ops.eval = nft_match_eval;
+       nft_match->ops.init = nft_match_init;
+       nft_match->ops.destroy = nft_match_destroy;
+       nft_match->ops.dump = nft_match_dump;
+       nft_match->ops.validate = nft_match_validate;
+       nft_match->ops.data = match;
+
+       list_add(&nft_match->head, &nft_match_list);
+
+       return &nft_match->ops;
+}
+
+static void nft_match_release(void)
+{
+       struct nft_xt *nft_match, *tmp;
+
+       list_for_each_entry_safe(nft_match, tmp, &nft_match_list, head)
+               kfree(nft_match);
+}
+
+static struct nft_expr_type nft_match_type __read_mostly = {
+       .name           = "match",
+       .select_ops     = nft_match_select_ops,
+       .policy         = nft_match_policy,
+       .maxattr        = NFTA_MATCH_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static LIST_HEAD(nft_target_list);
+
+static struct nft_expr_type nft_target_type;
+
+static const struct nft_expr_ops *
+nft_target_select_ops(const struct nft_ctx *ctx,
+                     const struct nlattr * const tb[])
+{
+       struct nft_xt *nft_target;
+       struct xt_target *target;
+       char *tg_name;
+       __u32 rev, family;
+
+       if (tb[NFTA_TARGET_NAME] == NULL ||
+           tb[NFTA_TARGET_REV] == NULL ||
+           tb[NFTA_TARGET_INFO] == NULL)
+               return ERR_PTR(-EINVAL);
+
+       tg_name = nla_data(tb[NFTA_TARGET_NAME]);
+       rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV]));
+       family = ctx->afi->family;
+
+       /* Re-use the existing target if it's already loaded. */
+       list_for_each_entry(nft_target, &nft_match_list, head) {
+               struct xt_target *target = nft_target->ops.data;
+
+               if (strcmp(target->name, tg_name) == 0 &&
+                   target->revision == rev && target->family == family)
+                       return &nft_target->ops;
+       }
+
+       target = xt_request_find_target(family, tg_name, rev);
+       if (IS_ERR(target))
+               return ERR_PTR(-ENOENT);
+
+       /* This is the first time we use this target, allocate operations */
+       nft_target = kzalloc(sizeof(struct nft_xt), GFP_KERNEL);
+       if (nft_target == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       nft_target->ops.type = &nft_target_type;
+       nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize) +
+                                            nft_compat_target_offset(target));
+       nft_target->ops.eval = nft_target_eval;
+       nft_target->ops.init = nft_target_init;
+       nft_target->ops.destroy = nft_target_destroy;
+       nft_target->ops.dump = nft_target_dump;
+       nft_target->ops.validate = nft_target_validate;
+       nft_target->ops.data = target;
+
+       list_add(&nft_target->head, &nft_target_list);
+
+       return &nft_target->ops;
+}
+
+static void nft_target_release(void)
+{
+       struct nft_xt *nft_target, *tmp;
+
+       list_for_each_entry_safe(nft_target, tmp, &nft_target_list, head)
+               kfree(nft_target);
+}
+
+static struct nft_expr_type nft_target_type __read_mostly = {
+       .name           = "target",
+       .select_ops     = nft_target_select_ops,
+       .policy         = nft_target_policy,
+       .maxattr        = NFTA_TARGET_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_compat_module_init(void)
+{
+       int ret;
+
+       ret = nft_register_expr(&nft_match_type);
+       if (ret < 0)
+               return ret;
+
+       ret = nft_register_expr(&nft_target_type);
+       if (ret < 0)
+               goto err_match;
+
+       ret = nfnetlink_subsys_register(&nfnl_compat_subsys);
+       if (ret < 0) {
+               pr_err("nft_compat: cannot register with nfnetlink.\n");
+               goto err_target;
+       }
+
+       pr_info("nf_tables_compat: (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>\n");
+
+       return ret;
+
+err_target:
+       nft_unregister_expr(&nft_target_type);
+err_match:
+       nft_unregister_expr(&nft_match_type);
+       return ret;
+}
+
+static void __exit nft_compat_module_exit(void)
+{
+       nfnetlink_subsys_unregister(&nfnl_compat_subsys);
+       nft_unregister_expr(&nft_target_type);
+       nft_unregister_expr(&nft_match_type);
+       nft_match_release();
+       nft_target_release();
+}
+
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);
+
+module_init(nft_compat_module_init);
+module_exit(nft_compat_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_ALIAS_NFT_EXPR("match");
+MODULE_ALIAS_NFT_EXPR("target");
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
new file mode 100644 (file)
index 0000000..c89ee48
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/seqlock.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_counter {
+       seqlock_t       lock;
+       u64             bytes;
+       u64             packets;
+};
+
+static void nft_counter_eval(const struct nft_expr *expr,
+                            struct nft_data data[NFT_REG_MAX + 1],
+                            const struct nft_pktinfo *pkt)
+{
+       struct nft_counter *priv = nft_expr_priv(expr);
+
+       write_seqlock_bh(&priv->lock);
+       priv->bytes += pkt->skb->len;
+       priv->packets++;
+       write_sequnlock_bh(&priv->lock);
+}
+
+static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       struct nft_counter *priv = nft_expr_priv(expr);
+       unsigned int seq;
+       u64 bytes;
+       u64 packets;
+
+       do {
+               seq = read_seqbegin(&priv->lock);
+               bytes   = priv->bytes;
+               packets = priv->packets;
+       } while (read_seqretry(&priv->lock, seq));
+
+       if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(bytes)))
+               goto nla_put_failure;
+       if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(packets)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
+       [NFTA_COUNTER_PACKETS]  = { .type = NLA_U64 },
+       [NFTA_COUNTER_BYTES]    = { .type = NLA_U64 },
+};
+
+static int nft_counter_init(const struct nft_ctx *ctx,
+                           const struct nft_expr *expr,
+                           const struct nlattr * const tb[])
+{
+       struct nft_counter *priv = nft_expr_priv(expr);
+
+       if (tb[NFTA_COUNTER_PACKETS])
+               priv->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
+       if (tb[NFTA_COUNTER_BYTES])
+               priv->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
+
+       seqlock_init(&priv->lock);
+       return 0;
+}
+
+static struct nft_expr_type nft_counter_type;
+static const struct nft_expr_ops nft_counter_ops = {
+       .type           = &nft_counter_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_counter)),
+       .eval           = nft_counter_eval,
+       .init           = nft_counter_init,
+       .dump           = nft_counter_dump,
+};
+
+static struct nft_expr_type nft_counter_type __read_mostly = {
+       .name           = "counter",
+       .ops            = &nft_counter_ops,
+       .policy         = nft_counter_policy,
+       .maxattr        = NFTA_COUNTER_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_counter_module_init(void)
+{
+       return nft_register_expr(&nft_counter_type);
+}
+
+static void __exit nft_counter_module_exit(void)
+{
+       nft_unregister_expr(&nft_counter_type);
+}
+
+module_init(nft_counter_module_init);
+module_exit(nft_counter_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("counter");
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
new file mode 100644 (file)
index 0000000..955f4e6
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+
+struct nft_ct {
+       enum nft_ct_keys        key:8;
+       enum ip_conntrack_dir   dir:8;
+       enum nft_registers      dreg:8;
+       uint8_t                 family;
+};
+
+static void nft_ct_eval(const struct nft_expr *expr,
+                       struct nft_data data[NFT_REG_MAX + 1],
+                       const struct nft_pktinfo *pkt)
+{
+       const struct nft_ct *priv = nft_expr_priv(expr);
+       struct nft_data *dest = &data[priv->dreg];
+       enum ip_conntrack_info ctinfo;
+       const struct nf_conn *ct;
+       const struct nf_conn_help *help;
+       const struct nf_conntrack_tuple *tuple;
+       const struct nf_conntrack_helper *helper;
+       long diff;
+       unsigned int state;
+
+       ct = nf_ct_get(pkt->skb, &ctinfo);
+
+       switch (priv->key) {
+       case NFT_CT_STATE:
+               if (ct == NULL)
+                       state = NF_CT_STATE_INVALID_BIT;
+               else if (nf_ct_is_untracked(ct))
+                       state = NF_CT_STATE_UNTRACKED_BIT;
+               else
+                       state = NF_CT_STATE_BIT(ctinfo);
+               dest->data[0] = state;
+               return;
+       }
+
+       if (ct == NULL)
+               goto err;
+
+       switch (priv->key) {
+       case NFT_CT_DIRECTION:
+               dest->data[0] = CTINFO2DIR(ctinfo);
+               return;
+       case NFT_CT_STATUS:
+               dest->data[0] = ct->status;
+               return;
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       case NFT_CT_MARK:
+               dest->data[0] = ct->mark;
+               return;
+#endif
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+       case NFT_CT_SECMARK:
+               dest->data[0] = ct->secmark;
+               return;
+#endif
+       case NFT_CT_EXPIRATION:
+               diff = (long)jiffies - (long)ct->timeout.expires;
+               if (diff < 0)
+                       diff = 0;
+               dest->data[0] = jiffies_to_msecs(diff);
+               return;
+       case NFT_CT_HELPER:
+               if (ct->master == NULL)
+                       goto err;
+               help = nfct_help(ct->master);
+               if (help == NULL)
+                       goto err;
+               helper = rcu_dereference(help->helper);
+               if (helper == NULL)
+                       goto err;
+               if (strlen(helper->name) >= sizeof(dest->data))
+                       goto err;
+               strncpy((char *)dest->data, helper->name, sizeof(dest->data));
+               return;
+       }
+
+       tuple = &ct->tuplehash[priv->dir].tuple;
+       switch (priv->key) {
+       case NFT_CT_L3PROTOCOL:
+               dest->data[0] = nf_ct_l3num(ct);
+               return;
+       case NFT_CT_SRC:
+               memcpy(dest->data, tuple->src.u3.all,
+                      nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
+               return;
+       case NFT_CT_DST:
+               memcpy(dest->data, tuple->dst.u3.all,
+                      nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
+               return;
+       case NFT_CT_PROTOCOL:
+               dest->data[0] = nf_ct_protonum(ct);
+               return;
+       case NFT_CT_PROTO_SRC:
+               dest->data[0] = (__force __u16)tuple->src.u.all;
+               return;
+       case NFT_CT_PROTO_DST:
+               dest->data[0] = (__force __u16)tuple->dst.u.all;
+               return;
+       }
+       return;
+err:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
+       [NFTA_CT_DREG]          = { .type = NLA_U32 },
+       [NFTA_CT_KEY]           = { .type = NLA_U32 },
+       [NFTA_CT_DIRECTION]     = { .type = NLA_U8 },
+};
+
+static int nft_ct_init(const struct nft_ctx *ctx,
+                      const struct nft_expr *expr,
+                      const struct nlattr * const tb[])
+{
+       struct nft_ct *priv = nft_expr_priv(expr);
+       int err;
+
+       if (tb[NFTA_CT_DREG] == NULL ||
+           tb[NFTA_CT_KEY] == NULL)
+               return -EINVAL;
+
+       priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
+       if (tb[NFTA_CT_DIRECTION] != NULL) {
+               priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
+               switch (priv->dir) {
+               case IP_CT_DIR_ORIGINAL:
+               case IP_CT_DIR_REPLY:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       switch (priv->key) {
+       case NFT_CT_STATE:
+       case NFT_CT_DIRECTION:
+       case NFT_CT_STATUS:
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       case NFT_CT_MARK:
+#endif
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+       case NFT_CT_SECMARK:
+#endif
+       case NFT_CT_EXPIRATION:
+       case NFT_CT_HELPER:
+               if (tb[NFTA_CT_DIRECTION] != NULL)
+                       return -EINVAL;
+               break;
+       case NFT_CT_PROTOCOL:
+       case NFT_CT_SRC:
+       case NFT_CT_DST:
+       case NFT_CT_PROTO_SRC:
+       case NFT_CT_PROTO_DST:
+               if (tb[NFTA_CT_DIRECTION] == NULL)
+                       return -EINVAL;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       err = nf_ct_l3proto_try_module_get(ctx->afi->family);
+       if (err < 0)
+               return err;
+       priv->family = ctx->afi->family;
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               goto err1;
+
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               goto err1;
+       return 0;
+
+err1:
+       nf_ct_l3proto_module_put(ctx->afi->family);
+       return err;
+}
+
+static void nft_ct_destroy(const struct nft_expr *expr)
+{
+       struct nft_ct *priv = nft_expr_priv(expr);
+
+       nf_ct_l3proto_module_put(priv->family);
+}
+
+static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_ct *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
+               goto nla_put_failure;
+       if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_ct_type;
+static const struct nft_expr_ops nft_ct_ops = {
+       .type           = &nft_ct_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
+       .eval           = nft_ct_eval,
+       .init           = nft_ct_init,
+       .destroy        = nft_ct_destroy,
+       .dump           = nft_ct_dump,
+};
+
+static struct nft_expr_type nft_ct_type __read_mostly = {
+       .name           = "ct",
+       .ops            = &nft_ct_ops,
+       .policy         = nft_ct_policy,
+       .maxattr        = NFTA_CT_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_ct_module_init(void)
+{
+       return nft_register_expr(&nft_ct_type);
+}
+
+static void __exit nft_ct_module_exit(void)
+{
+       nft_unregister_expr(&nft_ct_type);
+}
+
+module_init(nft_ct_module_init);
+module_exit(nft_ct_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("ct");
diff --git a/net/netfilter/nft_expr_template.c b/net/netfilter/nft_expr_template.c
new file mode 100644 (file)
index 0000000..b6eed4d
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_template {
+
+};
+
+static void nft_template_eval(const struct nft_expr *expr,
+                             struct nft_data data[NFT_REG_MAX + 1],
+                             const struct nft_pktinfo *pkt)
+{
+       struct nft_template *priv = nft_expr_priv(expr);
+
+}
+
+static const struct nla_policy nft_template_policy[NFTA_TEMPLATE_MAX + 1] = {
+       [NFTA_TEMPLATE_ATTR]            = { .type = NLA_U32 },
+};
+
+static int nft_template_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
+{
+       struct nft_template *priv = nft_expr_priv(expr);
+
+       return 0;
+}
+
+static void nft_template_destroy(const struct nft_ctx *ctx,
+                              const struct nft_expr *expr)
+{
+       struct nft_template *priv = nft_expr_priv(expr);
+
+}
+
+static int nft_template_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_template *priv = nft_expr_priv(expr);
+
+       NLA_PUT_BE32(skb, NFTA_TEMPLATE_ATTR, priv->field);
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_template_type;
+static const struct nft_expr_ops nft_template_ops = {
+       .type           = &nft_template_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_template)),
+       .eval           = nft_template_eval,
+       .init           = nft_template_init,
+       .destroy        = nft_template_destroy,
+       .dump           = nft_template_dump,
+};
+
+static struct nft_expr_type nft_template_type __read_mostly = {
+       .name           = "template",
+       .ops            = &nft_template_ops,
+       .policy         = nft_template_policy,
+       .maxattr        = NFTA_TEMPLATE_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_template_module_init(void)
+{
+       return nft_register_expr(&nft_template_type);
+}
+
+static void __exit nft_template_module_exit(void)
+{
+       nft_unregister_expr(&nft_template_type);
+}
+
+module_init(nft_template_module_init);
+module_exit(nft_template_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("template");
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
new file mode 100644 (file)
index 0000000..8e0bb75
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+// FIXME:
+#include <net/ipv6.h>
+
+struct nft_exthdr {
+       u8                      type;
+       u8                      offset;
+       u8                      len;
+       enum nft_registers      dreg:8;
+};
+
+static void nft_exthdr_eval(const struct nft_expr *expr,
+                           struct nft_data data[NFT_REG_MAX + 1],
+                           const struct nft_pktinfo *pkt)
+{
+       struct nft_exthdr *priv = nft_expr_priv(expr);
+       struct nft_data *dest = &data[priv->dreg];
+       unsigned int offset;
+       int err;
+
+       err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
+       if (err < 0)
+               goto err;
+       offset += priv->offset;
+
+       if (skb_copy_bits(pkt->skb, offset, dest->data, priv->len) < 0)
+               goto err;
+       return;
+err:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
+       [NFTA_EXTHDR_DREG]              = { .type = NLA_U32 },
+       [NFTA_EXTHDR_TYPE]              = { .type = NLA_U8 },
+       [NFTA_EXTHDR_OFFSET]            = { .type = NLA_U32 },
+       [NFTA_EXTHDR_LEN]               = { .type = NLA_U32 },
+};
+
+static int nft_exthdr_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
+{
+       struct nft_exthdr *priv = nft_expr_priv(expr);
+       int err;
+
+       if (tb[NFTA_EXTHDR_DREG] == NULL ||
+           tb[NFTA_EXTHDR_TYPE] == NULL ||
+           tb[NFTA_EXTHDR_OFFSET] == NULL ||
+           tb[NFTA_EXTHDR_LEN] == NULL)
+               return -EINVAL;
+
+       priv->type   = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
+       priv->offset = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OFFSET]));
+       priv->len    = ntohl(nla_get_be32(tb[NFTA_EXTHDR_LEN]));
+       if (priv->len == 0 ||
+           priv->len > FIELD_SIZEOF(struct nft_data, data))
+               return -EINVAL;
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_EXTHDR_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+       return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+}
+
+static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_exthdr *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_EXTHDR_DREG, htonl(priv->dreg)))
+               goto nla_put_failure;
+       if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_exthdr_type;
+static const struct nft_expr_ops nft_exthdr_ops = {
+       .type           = &nft_exthdr_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
+       .eval           = nft_exthdr_eval,
+       .init           = nft_exthdr_init,
+       .dump           = nft_exthdr_dump,
+};
+
+static struct nft_expr_type nft_exthdr_type __read_mostly = {
+       .name           = "exthdr",
+       .ops            = &nft_exthdr_ops,
+       .policy         = nft_exthdr_policy,
+       .maxattr        = NFTA_EXTHDR_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_exthdr_module_init(void)
+{
+       return nft_register_expr(&nft_exthdr_type);
+}
+
+static void __exit nft_exthdr_module_exit(void)
+{
+       nft_unregister_expr(&nft_exthdr_type);
+}
+
+module_init(nft_exthdr_module_init);
+module_exit(nft_exthdr_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("exthdr");
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
new file mode 100644 (file)
index 0000000..3d3f8fc
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_hash {
+       struct hlist_head       *hash;
+       unsigned int            hsize;
+};
+
+struct nft_hash_elem {
+       struct hlist_node       hnode;
+       struct nft_data         key;
+       struct nft_data         data[];
+};
+
+static u32 nft_hash_rnd __read_mostly;
+static bool nft_hash_rnd_initted __read_mostly;
+
+static unsigned int nft_hash_data(const struct nft_data *data,
+                                 unsigned int hsize, unsigned int len)
+{
+       unsigned int h;
+
+       h = jhash(data->data, len, nft_hash_rnd);
+       return ((u64)h * hsize) >> 32;
+}
+
+static bool nft_hash_lookup(const struct nft_set *set,
+                           const struct nft_data *key,
+                           struct nft_data *data)
+{
+       const struct nft_hash *priv = nft_set_priv(set);
+       const struct nft_hash_elem *he;
+       unsigned int h;
+
+       h = nft_hash_data(key, priv->hsize, set->klen);
+       hlist_for_each_entry(he, &priv->hash[h], hnode) {
+               if (nft_data_cmp(&he->key, key, set->klen))
+                       continue;
+               if (set->flags & NFT_SET_MAP)
+                       nft_data_copy(data, he->data);
+               return true;
+       }
+       return false;
+}
+
+static void nft_hash_elem_destroy(const struct nft_set *set,
+                                 struct nft_hash_elem *he)
+{
+       nft_data_uninit(&he->key, NFT_DATA_VALUE);
+       if (set->flags & NFT_SET_MAP)
+               nft_data_uninit(he->data, set->dtype);
+       kfree(he);
+}
+
+static int nft_hash_insert(const struct nft_set *set,
+                          const struct nft_set_elem *elem)
+{
+       struct nft_hash *priv = nft_set_priv(set);
+       struct nft_hash_elem *he;
+       unsigned int size, h;
+
+       if (elem->flags != 0)
+               return -EINVAL;
+
+       size = sizeof(*he);
+       if (set->flags & NFT_SET_MAP)
+               size += sizeof(he->data[0]);
+
+       he = kzalloc(size, GFP_KERNEL);
+       if (he == NULL)
+               return -ENOMEM;
+
+       nft_data_copy(&he->key, &elem->key);
+       if (set->flags & NFT_SET_MAP)
+               nft_data_copy(he->data, &elem->data);
+
+       h = nft_hash_data(&he->key, priv->hsize, set->klen);
+       hlist_add_head_rcu(&he->hnode, &priv->hash[h]);
+       return 0;
+}
+
+static void nft_hash_remove(const struct nft_set *set,
+                           const struct nft_set_elem *elem)
+{
+       struct nft_hash_elem *he = elem->cookie;
+
+       hlist_del_rcu(&he->hnode);
+       kfree(he);
+}
+
+static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem)
+{
+       const struct nft_hash *priv = nft_set_priv(set);
+       struct nft_hash_elem *he;
+       unsigned int h;
+
+       h = nft_hash_data(&elem->key, priv->hsize, set->klen);
+       hlist_for_each_entry(he, &priv->hash[h], hnode) {
+               if (nft_data_cmp(&he->key, &elem->key, set->klen))
+                       continue;
+
+               elem->cookie = he;
+               elem->flags  = 0;
+               if (set->flags & NFT_SET_MAP)
+                       nft_data_copy(&elem->data, he->data);
+               return 0;
+       }
+       return -ENOENT;
+}
+
+static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
+                         struct nft_set_iter *iter)
+{
+       const struct nft_hash *priv = nft_set_priv(set);
+       const struct nft_hash_elem *he;
+       struct nft_set_elem elem;
+       unsigned int i;
+
+       for (i = 0; i < priv->hsize; i++) {
+               hlist_for_each_entry(he, &priv->hash[i], hnode) {
+                       if (iter->count < iter->skip)
+                               goto cont;
+
+                       memcpy(&elem.key, &he->key, sizeof(elem.key));
+                       if (set->flags & NFT_SET_MAP)
+                               memcpy(&elem.data, he->data, sizeof(elem.data));
+                       elem.flags = 0;
+
+                       iter->err = iter->fn(ctx, set, iter, &elem);
+                       if (iter->err < 0)
+                               return;
+cont:
+                       iter->count++;
+               }
+       }
+}
+
+static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
+{
+       return sizeof(struct nft_hash);
+}
+
+static int nft_hash_init(const struct nft_set *set,
+                        const struct nlattr * const tb[])
+{
+       struct nft_hash *priv = nft_set_priv(set);
+       unsigned int cnt, i;
+
+       if (unlikely(!nft_hash_rnd_initted)) {
+               get_random_bytes(&nft_hash_rnd, 4);
+               nft_hash_rnd_initted = true;
+       }
+
+       /* Aim for a load factor of 0.75 */
+       // FIXME: temporarily broken until we have set descriptions
+       cnt = 100;
+       cnt = cnt * 4 / 3;
+
+       priv->hash = kcalloc(cnt, sizeof(struct hlist_head), GFP_KERNEL);
+       if (priv->hash == NULL)
+               return -ENOMEM;
+       priv->hsize = cnt;
+
+       for (i = 0; i < cnt; i++)
+               INIT_HLIST_HEAD(&priv->hash[i]);
+
+       return 0;
+}
+
+static void nft_hash_destroy(const struct nft_set *set)
+{
+       const struct nft_hash *priv = nft_set_priv(set);
+       const struct hlist_node *next;
+       struct nft_hash_elem *elem;
+       unsigned int i;
+
+       for (i = 0; i < priv->hsize; i++) {
+               hlist_for_each_entry_safe(elem, next, &priv->hash[i], hnode) {
+                       hlist_del(&elem->hnode);
+                       nft_hash_elem_destroy(set, elem);
+               }
+       }
+       kfree(priv->hash);
+}
+
+static struct nft_set_ops nft_hash_ops __read_mostly = {
+       .privsize       = nft_hash_privsize,
+       .init           = nft_hash_init,
+       .destroy        = nft_hash_destroy,
+       .get            = nft_hash_get,
+       .insert         = nft_hash_insert,
+       .remove         = nft_hash_remove,
+       .lookup         = nft_hash_lookup,
+       .walk           = nft_hash_walk,
+       .features       = NFT_SET_MAP,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_hash_module_init(void)
+{
+       return nft_register_set(&nft_hash_ops);
+}
+
+static void __exit nft_hash_module_exit(void)
+{
+       nft_unregister_set(&nft_hash_ops);
+}
+
+module_init(nft_hash_module_init);
+module_exit(nft_hash_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_SET();
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
new file mode 100644 (file)
index 0000000..f169501
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_immediate_expr {
+       struct nft_data         data;
+       enum nft_registers      dreg:8;
+       u8                      dlen;
+};
+
+static void nft_immediate_eval(const struct nft_expr *expr,
+                              struct nft_data data[NFT_REG_MAX + 1],
+                              const struct nft_pktinfo *pkt)
+{
+       const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+
+       nft_data_copy(&data[priv->dreg], &priv->data);
+}
+
+static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = {
+       [NFTA_IMMEDIATE_DREG]   = { .type = NLA_U32 },
+       [NFTA_IMMEDIATE_DATA]   = { .type = NLA_NESTED },
+};
+
+static int nft_immediate_init(const struct nft_ctx *ctx,
+                             const struct nft_expr *expr,
+                             const struct nlattr * const tb[])
+{
+       struct nft_immediate_expr *priv = nft_expr_priv(expr);
+       struct nft_data_desc desc;
+       int err;
+
+       if (tb[NFTA_IMMEDIATE_DREG] == NULL ||
+           tb[NFTA_IMMEDIATE_DATA] == NULL)
+               return -EINVAL;
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_IMMEDIATE_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+
+       err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]);
+       if (err < 0)
+               return err;
+       priv->dlen = desc.len;
+
+       err = nft_validate_data_load(ctx, priv->dreg, &priv->data, desc.type);
+       if (err < 0)
+               goto err1;
+
+       return 0;
+
+err1:
+       nft_data_uninit(&priv->data, desc.type);
+       return err;
+}
+
+static void nft_immediate_destroy(const struct nft_expr *expr)
+{
+       const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+       return nft_data_uninit(&priv->data, nft_dreg_to_type(priv->dreg));
+}
+
+static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_IMMEDIATE_DREG, htonl(priv->dreg)))
+               goto nla_put_failure;
+
+       return nft_data_dump(skb, NFTA_IMMEDIATE_DATA, &priv->data,
+                            nft_dreg_to_type(priv->dreg), priv->dlen);
+
+nla_put_failure:
+       return -1;
+}
+
+static int nft_immediate_validate(const struct nft_ctx *ctx,
+                                 const struct nft_expr *expr,
+                                 const struct nft_data **data)
+{
+       const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+
+       if (priv->dreg == NFT_REG_VERDICT)
+               *data = &priv->data;
+
+       return 0;
+}
+
+static struct nft_expr_type nft_imm_type;
+static const struct nft_expr_ops nft_imm_ops = {
+       .type           = &nft_imm_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
+       .eval           = nft_immediate_eval,
+       .init           = nft_immediate_init,
+       .destroy        = nft_immediate_destroy,
+       .dump           = nft_immediate_dump,
+       .validate       = nft_immediate_validate,
+};
+
+static struct nft_expr_type nft_imm_type __read_mostly = {
+       .name           = "immediate",
+       .ops            = &nft_imm_ops,
+       .policy         = nft_immediate_policy,
+       .maxattr        = NFTA_IMMEDIATE_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_immediate_module_init(void)
+{
+       return nft_register_expr(&nft_imm_type);
+}
+
+void nft_immediate_module_exit(void)
+{
+       nft_unregister_expr(&nft_imm_type);
+}
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c
new file mode 100644 (file)
index 0000000..85da5bd
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+static DEFINE_SPINLOCK(limit_lock);
+
+struct nft_limit {
+       u64             tokens;
+       u64             rate;
+       u64             unit;
+       unsigned long   stamp;
+};
+
+static void nft_limit_eval(const struct nft_expr *expr,
+                          struct nft_data data[NFT_REG_MAX + 1],
+                          const struct nft_pktinfo *pkt)
+{
+       struct nft_limit *priv = nft_expr_priv(expr);
+
+       spin_lock_bh(&limit_lock);
+       if (time_after_eq(jiffies, priv->stamp)) {
+               priv->tokens = priv->rate;
+               priv->stamp = jiffies + priv->unit * HZ;
+       }
+
+       if (priv->tokens >= 1) {
+               priv->tokens--;
+               spin_unlock_bh(&limit_lock);
+               return;
+       }
+       spin_unlock_bh(&limit_lock);
+
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
+       [NFTA_LIMIT_RATE]       = { .type = NLA_U64 },
+       [NFTA_LIMIT_UNIT]       = { .type = NLA_U64 },
+};
+
+static int nft_limit_init(const struct nft_ctx *ctx,
+                         const struct nft_expr *expr,
+                         const struct nlattr * const tb[])
+{
+       struct nft_limit *priv = nft_expr_priv(expr);
+
+       if (tb[NFTA_LIMIT_RATE] == NULL ||
+           tb[NFTA_LIMIT_UNIT] == NULL)
+               return -EINVAL;
+
+       priv->rate   = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
+       priv->unit   = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
+       priv->stamp  = jiffies + priv->unit * HZ;
+       priv->tokens = priv->rate;
+       return 0;
+}
+
+static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_limit *priv = nft_expr_priv(expr);
+
+       if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate)))
+               goto nla_put_failure;
+       if (nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(priv->unit)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_limit_type;
+static const struct nft_expr_ops nft_limit_ops = {
+       .type           = &nft_limit_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_limit)),
+       .eval           = nft_limit_eval,
+       .init           = nft_limit_init,
+       .dump           = nft_limit_dump,
+};
+
+static struct nft_expr_type nft_limit_type __read_mostly = {
+       .name           = "limit",
+       .ops            = &nft_limit_ops,
+       .policy         = nft_limit_policy,
+       .maxattr        = NFTA_LIMIT_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_limit_module_init(void)
+{
+       return nft_register_expr(&nft_limit_type);
+}
+
+static void __exit nft_limit_module_exit(void)
+{
+       nft_unregister_expr(&nft_limit_type);
+}
+
+module_init(nft_limit_module_init);
+module_exit(nft_limit_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("limit");
diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c
new file mode 100644 (file)
index 0000000..57cad07
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_log.h>
+#include <linux/netdevice.h>
+
+static const char *nft_log_null_prefix = "";
+
+struct nft_log {
+       struct nf_loginfo       loginfo;
+       char                    *prefix;
+       int                     family;
+};
+
+static void nft_log_eval(const struct nft_expr *expr,
+                        struct nft_data data[NFT_REG_MAX + 1],
+                        const struct nft_pktinfo *pkt)
+{
+       const struct nft_log *priv = nft_expr_priv(expr);
+       struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
+
+       nf_log_packet(net, priv->family, pkt->hooknum, pkt->skb, pkt->in,
+                     pkt->out, &priv->loginfo, "%s", priv->prefix);
+}
+
+static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = {
+       [NFTA_LOG_GROUP]        = { .type = NLA_U16 },
+       [NFTA_LOG_PREFIX]       = { .type = NLA_STRING },
+       [NFTA_LOG_SNAPLEN]      = { .type = NLA_U32 },
+       [NFTA_LOG_QTHRESHOLD]   = { .type = NLA_U16 },
+};
+
+static int nft_log_init(const struct nft_ctx *ctx,
+                       const struct nft_expr *expr,
+                       const struct nlattr * const tb[])
+{
+       struct nft_log *priv = nft_expr_priv(expr);
+       struct nf_loginfo *li = &priv->loginfo;
+       const struct nlattr *nla;
+
+       priv->family = ctx->afi->family;
+
+       nla = tb[NFTA_LOG_PREFIX];
+       if (nla != NULL) {
+               priv->prefix = kmalloc(nla_len(nla) + 1, GFP_KERNEL);
+               if (priv->prefix == NULL)
+                       return -ENOMEM;
+               nla_strlcpy(priv->prefix, nla, nla_len(nla) + 1);
+       } else
+               priv->prefix = (char *)nft_log_null_prefix;
+
+       li->type = NF_LOG_TYPE_ULOG;
+       if (tb[NFTA_LOG_GROUP] != NULL)
+               li->u.ulog.group = ntohs(nla_get_be16(tb[NFTA_LOG_GROUP]));
+
+       if (tb[NFTA_LOG_SNAPLEN] != NULL)
+               li->u.ulog.copy_len = ntohl(nla_get_be32(tb[NFTA_LOG_SNAPLEN]));
+       if (tb[NFTA_LOG_QTHRESHOLD] != NULL) {
+               li->u.ulog.qthreshold =
+                       ntohs(nla_get_be16(tb[NFTA_LOG_QTHRESHOLD]));
+       }
+
+       return 0;
+}
+
+static void nft_log_destroy(const struct nft_expr *expr)
+{
+       struct nft_log *priv = nft_expr_priv(expr);
+
+       if (priv->prefix != nft_log_null_prefix)
+               kfree(priv->prefix);
+}
+
+static int nft_log_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_log *priv = nft_expr_priv(expr);
+       const struct nf_loginfo *li = &priv->loginfo;
+
+       if (priv->prefix != nft_log_null_prefix)
+               if (nla_put_string(skb, NFTA_LOG_PREFIX, priv->prefix))
+                       goto nla_put_failure;
+       if (li->u.ulog.group)
+               if (nla_put_be16(skb, NFTA_LOG_GROUP, htons(li->u.ulog.group)))
+                       goto nla_put_failure;
+       if (li->u.ulog.copy_len)
+               if (nla_put_be32(skb, NFTA_LOG_SNAPLEN,
+                                htonl(li->u.ulog.copy_len)))
+                       goto nla_put_failure;
+       if (li->u.ulog.qthreshold)
+               if (nla_put_be16(skb, NFTA_LOG_QTHRESHOLD,
+                                htons(li->u.ulog.qthreshold)))
+                       goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_log_type;
+static const struct nft_expr_ops nft_log_ops = {
+       .type           = &nft_log_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_log)),
+       .eval           = nft_log_eval,
+       .init           = nft_log_init,
+       .destroy        = nft_log_destroy,
+       .dump           = nft_log_dump,
+};
+
+static struct nft_expr_type nft_log_type __read_mostly = {
+       .name           = "log",
+       .ops            = &nft_log_ops,
+       .policy         = nft_log_policy,
+       .maxattr        = NFTA_LOG_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_log_module_init(void)
+{
+       return nft_register_expr(&nft_log_type);
+}
+
+static void __exit nft_log_module_exit(void)
+{
+       nft_unregister_expr(&nft_log_type);
+}
+
+module_init(nft_log_module_init);
+module_exit(nft_log_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("log");
diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
new file mode 100644 (file)
index 0000000..8a6116b
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_lookup {
+       struct nft_set                  *set;
+       enum nft_registers              sreg:8;
+       enum nft_registers              dreg:8;
+       struct nft_set_binding          binding;
+};
+
+static void nft_lookup_eval(const struct nft_expr *expr,
+                           struct nft_data data[NFT_REG_MAX + 1],
+                           const struct nft_pktinfo *pkt)
+{
+       const struct nft_lookup *priv = nft_expr_priv(expr);
+       const struct nft_set *set = priv->set;
+
+       if (set->ops->lookup(set, &data[priv->sreg], &data[priv->dreg]))
+               return;
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = {
+       [NFTA_LOOKUP_SET]       = { .type = NLA_STRING },
+       [NFTA_LOOKUP_SREG]      = { .type = NLA_U32 },
+       [NFTA_LOOKUP_DREG]      = { .type = NLA_U32 },
+};
+
+static int nft_lookup_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
+{
+       struct nft_lookup *priv = nft_expr_priv(expr);
+       struct nft_set *set;
+       int err;
+
+       if (tb[NFTA_LOOKUP_SET] == NULL ||
+           tb[NFTA_LOOKUP_SREG] == NULL)
+               return -EINVAL;
+
+       set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]);
+       if (IS_ERR(set))
+               return PTR_ERR(set);
+
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG]));
+       err = nft_validate_input_register(priv->sreg);
+       if (err < 0)
+               return err;
+
+       if (tb[NFTA_LOOKUP_DREG] != NULL) {
+               if (!(set->flags & NFT_SET_MAP))
+                       return -EINVAL;
+
+               priv->dreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_DREG]));
+               err = nft_validate_output_register(priv->dreg);
+               if (err < 0)
+                       return err;
+
+               if (priv->dreg == NFT_REG_VERDICT) {
+                       if (set->dtype != NFT_DATA_VERDICT)
+                               return -EINVAL;
+               } else if (set->dtype == NFT_DATA_VERDICT)
+                       return -EINVAL;
+       } else if (set->flags & NFT_SET_MAP)
+               return -EINVAL;
+
+       err = nf_tables_bind_set(ctx, set, &priv->binding);
+       if (err < 0)
+               return err;
+
+       priv->set = set;
+       return 0;
+}
+
+static void nft_lookup_destroy(const struct nft_expr *expr)
+{
+       struct nft_lookup *priv = nft_expr_priv(expr);
+
+       nf_tables_unbind_set(NULL, priv->set, &priv->binding);
+}
+
+static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_lookup *priv = nft_expr_priv(expr);
+
+       if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_LOOKUP_SREG, htonl(priv->sreg)))
+               goto nla_put_failure;
+       if (priv->set->flags & NFT_SET_MAP)
+               if (nla_put_be32(skb, NFTA_LOOKUP_DREG, htonl(priv->dreg)))
+                       goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_lookup_type;
+static const struct nft_expr_ops nft_lookup_ops = {
+       .type           = &nft_lookup_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
+       .eval           = nft_lookup_eval,
+       .init           = nft_lookup_init,
+       .destroy        = nft_lookup_destroy,
+       .dump           = nft_lookup_dump,
+};
+
+static struct nft_expr_type nft_lookup_type __read_mostly = {
+       .name           = "lookup",
+       .ops            = &nft_lookup_ops,
+       .policy         = nft_lookup_policy,
+       .maxattr        = NFTA_LOOKUP_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_lookup_module_init(void)
+{
+       return nft_register_expr(&nft_lookup_type);
+}
+
+void nft_lookup_module_exit(void)
+{
+       nft_unregister_expr(&nft_lookup_type);
+}
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
new file mode 100644 (file)
index 0000000..8c28220
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/dst.h>
+#include <net/sock.h>
+#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
+#include <net/netfilter/nf_tables.h>
+
+struct nft_meta {
+       enum nft_meta_keys      key:8;
+       enum nft_registers      dreg:8;
+};
+
+static void nft_meta_eval(const struct nft_expr *expr,
+                         struct nft_data data[NFT_REG_MAX + 1],
+                         const struct nft_pktinfo *pkt)
+{
+       const struct nft_meta *priv = nft_expr_priv(expr);
+       const struct sk_buff *skb = pkt->skb;
+       const struct net_device *in = pkt->in, *out = pkt->out;
+       struct nft_data *dest = &data[priv->dreg];
+
+       switch (priv->key) {
+       case NFT_META_LEN:
+               dest->data[0] = skb->len;
+               break;
+       case NFT_META_PROTOCOL:
+               *(__be16 *)dest->data = skb->protocol;
+               break;
+       case NFT_META_PRIORITY:
+               dest->data[0] = skb->priority;
+               break;
+       case NFT_META_MARK:
+               dest->data[0] = skb->mark;
+               break;
+       case NFT_META_IIF:
+               if (in == NULL)
+                       goto err;
+               dest->data[0] = in->ifindex;
+               break;
+       case NFT_META_OIF:
+               if (out == NULL)
+                       goto err;
+               dest->data[0] = out->ifindex;
+               break;
+       case NFT_META_IIFNAME:
+               if (in == NULL)
+                       goto err;
+               strncpy((char *)dest->data, in->name, sizeof(dest->data));
+               break;
+       case NFT_META_OIFNAME:
+               if (out == NULL)
+                       goto err;
+               strncpy((char *)dest->data, out->name, sizeof(dest->data));
+               break;
+       case NFT_META_IIFTYPE:
+               if (in == NULL)
+                       goto err;
+               *(u16 *)dest->data = in->type;
+               break;
+       case NFT_META_OIFTYPE:
+               if (out == NULL)
+                       goto err;
+               *(u16 *)dest->data = out->type;
+               break;
+       case NFT_META_SKUID:
+               if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT)
+                       goto err;
+
+               read_lock_bh(&skb->sk->sk_callback_lock);
+               if (skb->sk->sk_socket == NULL ||
+                   skb->sk->sk_socket->file == NULL) {
+                       read_unlock_bh(&skb->sk->sk_callback_lock);
+                       goto err;
+               }
+
+               dest->data[0] =
+                       from_kuid_munged(&init_user_ns,
+                               skb->sk->sk_socket->file->f_cred->fsuid);
+               read_unlock_bh(&skb->sk->sk_callback_lock);
+               break;
+       case NFT_META_SKGID:
+               if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT)
+                       goto err;
+
+               read_lock_bh(&skb->sk->sk_callback_lock);
+               if (skb->sk->sk_socket == NULL ||
+                   skb->sk->sk_socket->file == NULL) {
+                       read_unlock_bh(&skb->sk->sk_callback_lock);
+                       goto err;
+               }
+               dest->data[0] =
+                       from_kgid_munged(&init_user_ns,
+                                skb->sk->sk_socket->file->f_cred->fsgid);
+               read_unlock_bh(&skb->sk->sk_callback_lock);
+               break;
+#ifdef CONFIG_NET_CLS_ROUTE
+       case NFT_META_RTCLASSID: {
+               const struct dst_entry *dst = skb_dst(skb);
+
+               if (dst == NULL)
+                       goto err;
+               dest->data[0] = dst->tclassid;
+               break;
+       }
+#endif
+#ifdef CONFIG_NETWORK_SECMARK
+       case NFT_META_SECMARK:
+               dest->data[0] = skb->secmark;
+               break;
+#endif
+       default:
+               WARN_ON(1);
+               goto err;
+       }
+       return;
+
+err:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
+       [NFTA_META_DREG]        = { .type = NLA_U32 },
+       [NFTA_META_KEY]         = { .type = NLA_U32 },
+};
+
+static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+                        const struct nlattr * const tb[])
+{
+       struct nft_meta *priv = nft_expr_priv(expr);
+       int err;
+
+       if (tb[NFTA_META_DREG] == NULL ||
+           tb[NFTA_META_KEY] == NULL)
+               return -EINVAL;
+
+       priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (priv->key) {
+       case NFT_META_LEN:
+       case NFT_META_PROTOCOL:
+       case NFT_META_PRIORITY:
+       case NFT_META_MARK:
+       case NFT_META_IIF:
+       case NFT_META_OIF:
+       case NFT_META_IIFNAME:
+       case NFT_META_OIFNAME:
+       case NFT_META_IIFTYPE:
+       case NFT_META_OIFTYPE:
+       case NFT_META_SKUID:
+       case NFT_META_SKGID:
+#ifdef CONFIG_NET_CLS_ROUTE
+       case NFT_META_RTCLASSID:
+#endif
+#ifdef CONFIG_NETWORK_SECMARK
+       case NFT_META_SECMARK:
+#endif
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+       return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+}
+
+static int nft_meta_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_meta *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_META_DREG, htonl(priv->dreg)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_meta_type;
+static const struct nft_expr_ops nft_meta_ops = {
+       .type           = &nft_meta_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .eval           = nft_meta_eval,
+       .init           = nft_meta_init,
+       .dump           = nft_meta_dump,
+};
+
+static struct nft_expr_type nft_meta_type __read_mostly = {
+       .name           = "meta",
+       .ops            = &nft_meta_ops,
+       .policy         = nft_meta_policy,
+       .maxattr        = NFTA_META_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_meta_module_init(void)
+{
+       return nft_register_expr(&nft_meta_type);
+}
+
+static void __exit nft_meta_module_exit(void)
+{
+       nft_unregister_expr(&nft_meta_type);
+}
+
+module_init(nft_meta_module_init);
+module_exit(nft_meta_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("meta");
diff --git a/net/netfilter/nft_meta_target.c b/net/netfilter/nft_meta_target.c
new file mode 100644 (file)
index 0000000..71177df
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_meta {
+       enum nft_meta_keys      key;
+};
+
+static void nft_meta_eval(const struct nft_expr *expr,
+                         struct nft_data *nfres,
+                         struct nft_data *data,
+                         const struct nft_pktinfo *pkt)
+{
+       const struct nft_meta *meta = nft_expr_priv(expr);
+       struct sk_buff *skb = pkt->skb;
+       u32 val = data->data[0];
+
+       switch (meta->key) {
+       case NFT_META_MARK:
+               skb->mark = val;
+               break;
+       case NFT_META_PRIORITY:
+               skb->priority = val;
+               break;
+       case NFT_META_NFTRACE:
+               skb->nf_trace = val;
+               break;
+#ifdef CONFIG_NETWORK_SECMARK
+       case NFT_META_SECMARK:
+               skb->secmark = val;
+               break;
+#endif
+       default:
+               WARN_ON(1);
+       }
+}
+
+static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
+       [NFTA_META_KEY]         = { .type = NLA_U32 },
+};
+
+static int nft_meta_init(const struct nft_expr *expr, struct nlattr *tb[])
+{
+       struct nft_meta *meta = nft_expr_priv(expr);
+
+       if (tb[NFTA_META_KEY] == NULL)
+               return -EINVAL;
+
+       meta->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (meta->key) {
+       case NFT_META_MARK:
+       case NFT_META_PRIORITY:
+       case NFT_META_NFTRACE:
+#ifdef CONFIG_NETWORK_SECMARK
+       case NFT_META_SECMARK:
+#endif
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nft_meta_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       struct nft_meta *meta = nft_expr_priv(expr);
+
+       NLA_PUT_BE32(skb, NFTA_META_KEY, htonl(meta->key));
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_ops meta_target __read_mostly = {
+       .name           = "meta",
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .owner          = THIS_MODULE,
+       .eval           = nft_meta_eval,
+       .init           = nft_meta_init,
+       .dump           = nft_meta_dump,
+       .policy         = nft_meta_policy,
+       .maxattr        = NFTA_META_MAX,
+};
+
+static int __init nft_meta_target_init(void)
+{
+       return nft_register_expr(&meta_target);
+}
+
+static void __exit nft_meta_target_exit(void)
+{
+       nft_unregister_expr(&meta_target);
+}
+
+module_init(nft_meta_target_init);
+module_exit(nft_meta_target_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_EXPR("meta");
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c
new file mode 100644 (file)
index 0000000..d3b1ffe
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
+ * Copyright (c) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/string.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_nat_l3proto.h>
+#include <net/ip.h>
+
+struct nft_nat {
+       enum nft_registers      sreg_addr_min:8;
+       enum nft_registers      sreg_addr_max:8;
+       enum nft_registers      sreg_proto_min:8;
+       enum nft_registers      sreg_proto_max:8;
+       int                     family;
+       enum nf_nat_manip_type  type;
+};
+
+static void nft_nat_eval(const struct nft_expr *expr,
+                        struct nft_data data[NFT_REG_MAX + 1],
+                        const struct nft_pktinfo *pkt)
+{
+       const struct nft_nat *priv = nft_expr_priv(expr);
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo);
+       struct nf_nat_range range;
+
+       memset(&range, 0, sizeof(range));
+       if (priv->sreg_addr_min) {
+               if (priv->family == AF_INET) {
+                       range.min_addr.ip = (__force __be32)
+                                       data[priv->sreg_addr_min].data[0];
+                       range.max_addr.ip = (__force __be32)
+                                       data[priv->sreg_addr_max].data[0];
+
+               } else {
+                       memcpy(range.min_addr.ip6,
+                              data[priv->sreg_addr_min].data,
+                              sizeof(struct nft_data));
+                       memcpy(range.max_addr.ip6,
+                              data[priv->sreg_addr_max].data,
+                              sizeof(struct nft_data));
+               }
+               range.flags |= NF_NAT_RANGE_MAP_IPS;
+       }
+
+       if (priv->sreg_proto_min) {
+               range.min_proto.all = (__force __be16)
+                                       data[priv->sreg_proto_min].data[0];
+               range.max_proto.all = (__force __be16)
+                                       data[priv->sreg_proto_max].data[0];
+               range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+       }
+
+       data[NFT_REG_VERDICT].verdict =
+               nf_nat_setup_info(ct, &range, priv->type);
+}
+
+static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = {
+       [NFTA_NAT_TYPE]          = { .type = NLA_U32 },
+       [NFTA_NAT_FAMILY]        = { .type = NLA_U32 },
+       [NFTA_NAT_REG_ADDR_MIN]  = { .type = NLA_U32 },
+       [NFTA_NAT_REG_ADDR_MAX]  = { .type = NLA_U32 },
+       [NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 },
+       [NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 },
+};
+
+static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+                       const struct nlattr * const tb[])
+{
+       struct nft_nat *priv = nft_expr_priv(expr);
+       int err;
+
+       if (tb[NFTA_NAT_TYPE] == NULL)
+               return -EINVAL;
+
+       switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) {
+       case NFT_NAT_SNAT:
+               priv->type = NF_NAT_MANIP_SRC;
+               break;
+       case NFT_NAT_DNAT:
+               priv->type = NF_NAT_MANIP_DST;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (tb[NFTA_NAT_FAMILY] == NULL)
+               return -EINVAL;
+
+       priv->family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY]));
+       if (priv->family != AF_INET && priv->family != AF_INET6)
+               return -EINVAL;
+
+       if (tb[NFTA_NAT_REG_ADDR_MIN]) {
+               priv->sreg_addr_min = ntohl(nla_get_be32(
+                                               tb[NFTA_NAT_REG_ADDR_MIN]));
+               err = nft_validate_input_register(priv->sreg_addr_min);
+               if (err < 0)
+                       return err;
+       }
+
+       if (tb[NFTA_NAT_REG_ADDR_MAX]) {
+               priv->sreg_addr_max = ntohl(nla_get_be32(
+                                               tb[NFTA_NAT_REG_ADDR_MAX]));
+               err = nft_validate_input_register(priv->sreg_addr_max);
+               if (err < 0)
+                       return err;
+       } else
+               priv->sreg_addr_max = priv->sreg_addr_min;
+
+       if (tb[NFTA_NAT_REG_PROTO_MIN]) {
+               priv->sreg_proto_min = ntohl(nla_get_be32(
+                                               tb[NFTA_NAT_REG_PROTO_MIN]));
+               err = nft_validate_input_register(priv->sreg_proto_min);
+               if (err < 0)
+                       return err;
+       }
+
+       if (tb[NFTA_NAT_REG_PROTO_MAX]) {
+               priv->sreg_proto_max = ntohl(nla_get_be32(
+                                               tb[NFTA_NAT_REG_PROTO_MAX]));
+               err = nft_validate_input_register(priv->sreg_proto_max);
+               if (err < 0)
+                       return err;
+       } else
+               priv->sreg_proto_max = priv->sreg_proto_min;
+
+       return 0;
+}
+
+static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_nat *priv = nft_expr_priv(expr);
+
+       switch (priv->type) {
+       case NF_NAT_MANIP_SRC:
+               if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT)))
+                       goto nla_put_failure;
+               break;
+       case NF_NAT_MANIP_DST:
+               if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT)))
+                       goto nla_put_failure;
+               break;
+       }
+
+       if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb,
+                        NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb,
+                        NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb,
+                        NFTA_NAT_REG_PROTO_MIN, htonl(priv->sreg_proto_min)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb,
+                        NFTA_NAT_REG_PROTO_MAX, htonl(priv->sreg_proto_max)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_nat_type;
+static const struct nft_expr_ops nft_nat_ops = {
+       .type           = &nft_nat_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
+       .eval           = nft_nat_eval,
+       .init           = nft_nat_init,
+       .dump           = nft_nat_dump,
+};
+
+static struct nft_expr_type nft_nat_type __read_mostly = {
+       .name           = "nat",
+       .ops            = &nft_nat_ops,
+       .policy         = nft_nat_policy,
+       .maxattr        = NFTA_NAT_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_nat_module_init(void)
+{
+       int err;
+
+       err = nft_register_expr(&nft_nat_type);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static void __exit nft_nat_module_exit(void)
+{
+       nft_unregister_expr(&nft_nat_type);
+}
+
+module_init(nft_nat_module_init);
+module_exit(nft_nat_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
+MODULE_ALIAS_NFT_EXPR("nat");
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
new file mode 100644 (file)
index 0000000..a2aeb31
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+static void nft_payload_eval(const struct nft_expr *expr,
+                            struct nft_data data[NFT_REG_MAX + 1],
+                            const struct nft_pktinfo *pkt)
+{
+       const struct nft_payload *priv = nft_expr_priv(expr);
+       const struct sk_buff *skb = pkt->skb;
+       struct nft_data *dest = &data[priv->dreg];
+       int offset;
+
+       switch (priv->base) {
+       case NFT_PAYLOAD_LL_HEADER:
+               if (!skb_mac_header_was_set(skb))
+                       goto err;
+               offset = skb_mac_header(skb) - skb->data;
+               break;
+       case NFT_PAYLOAD_NETWORK_HEADER:
+               offset = skb_network_offset(skb);
+               break;
+       case NFT_PAYLOAD_TRANSPORT_HEADER:
+               offset = pkt->xt.thoff;
+               break;
+       default:
+               BUG();
+       }
+       offset += priv->offset;
+
+       if (skb_copy_bits(skb, offset, dest->data, priv->len) < 0)
+               goto err;
+       return;
+err:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
+       [NFTA_PAYLOAD_DREG]     = { .type = NLA_U32 },
+       [NFTA_PAYLOAD_BASE]     = { .type = NLA_U32 },
+       [NFTA_PAYLOAD_OFFSET]   = { .type = NLA_U32 },
+       [NFTA_PAYLOAD_LEN]      = { .type = NLA_U32 },
+};
+
+static int nft_payload_init(const struct nft_ctx *ctx,
+                           const struct nft_expr *expr,
+                           const struct nlattr * const tb[])
+{
+       struct nft_payload *priv = nft_expr_priv(expr);
+       int err;
+
+       priv->base   = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
+       priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
+       priv->len    = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+       return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+}
+
+static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_payload *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_PAYLOAD_DREG, htonl(priv->dreg)) ||
+           nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) ||
+           nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) ||
+           nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_payload_type;
+static const struct nft_expr_ops nft_payload_ops = {
+       .type           = &nft_payload_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
+       .eval           = nft_payload_eval,
+       .init           = nft_payload_init,
+       .dump           = nft_payload_dump,
+};
+
+const struct nft_expr_ops nft_payload_fast_ops = {
+       .type           = &nft_payload_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
+       .eval           = nft_payload_eval,
+       .init           = nft_payload_init,
+       .dump           = nft_payload_dump,
+};
+
+static const struct nft_expr_ops *
+nft_payload_select_ops(const struct nft_ctx *ctx,
+                      const struct nlattr * const tb[])
+{
+       enum nft_payload_bases base;
+       unsigned int offset, len;
+
+       if (tb[NFTA_PAYLOAD_DREG] == NULL ||
+           tb[NFTA_PAYLOAD_BASE] == NULL ||
+           tb[NFTA_PAYLOAD_OFFSET] == NULL ||
+           tb[NFTA_PAYLOAD_LEN] == NULL)
+               return ERR_PTR(-EINVAL);
+
+       base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
+       switch (base) {
+       case NFT_PAYLOAD_LL_HEADER:
+       case NFT_PAYLOAD_NETWORK_HEADER:
+       case NFT_PAYLOAD_TRANSPORT_HEADER:
+               break;
+       default:
+               return ERR_PTR(-EOPNOTSUPP);
+       }
+
+       offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
+       len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
+       if (len == 0 || len > FIELD_SIZEOF(struct nft_data, data))
+               return ERR_PTR(-EINVAL);
+
+       if (len <= 4 && IS_ALIGNED(offset, len) && base != NFT_PAYLOAD_LL_HEADER)
+               return &nft_payload_fast_ops;
+       else
+               return &nft_payload_ops;
+}
+
+static struct nft_expr_type nft_payload_type __read_mostly = {
+       .name           = "payload",
+       .select_ops     = nft_payload_select_ops,
+       .policy         = nft_payload_policy,
+       .maxattr        = NFTA_PAYLOAD_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_payload_module_init(void)
+{
+       return nft_register_expr(&nft_payload_type);
+}
+
+void nft_payload_module_exit(void)
+{
+       nft_unregister_expr(&nft_payload_type);
+}
diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c
new file mode 100644 (file)
index 0000000..ca0c1b2
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.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.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_rbtree {
+       struct rb_root          root;
+};
+
+struct nft_rbtree_elem {
+       struct rb_node          node;
+       u16                     flags;
+       struct nft_data         key;
+       struct nft_data         data[];
+};
+
+static bool nft_rbtree_lookup(const struct nft_set *set,
+                             const struct nft_data *key,
+                             struct nft_data *data)
+{
+       const struct nft_rbtree *priv = nft_set_priv(set);
+       const struct nft_rbtree_elem *rbe, *interval = NULL;
+       const struct rb_node *parent = priv->root.rb_node;
+       int d;
+
+       while (parent != NULL) {
+               rbe = rb_entry(parent, struct nft_rbtree_elem, node);
+
+               d = nft_data_cmp(&rbe->key, key, set->klen);
+               if (d < 0) {
+                       parent = parent->rb_left;
+                       interval = rbe;
+               } else if (d > 0)
+                       parent = parent->rb_right;
+               else {
+found:
+                       if (rbe->flags & NFT_SET_ELEM_INTERVAL_END)
+                               goto out;
+                       if (set->flags & NFT_SET_MAP)
+                               nft_data_copy(data, rbe->data);
+                       return true;
+               }
+       }
+
+       if (set->flags & NFT_SET_INTERVAL && interval != NULL) {
+               rbe = interval;
+               goto found;
+       }
+out:
+       return false;
+}
+
+static void nft_rbtree_elem_destroy(const struct nft_set *set,
+                                   struct nft_rbtree_elem *rbe)
+{
+       nft_data_uninit(&rbe->key, NFT_DATA_VALUE);
+       if (set->flags & NFT_SET_MAP)
+               nft_data_uninit(rbe->data, set->dtype);
+       kfree(rbe);
+}
+
+static int __nft_rbtree_insert(const struct nft_set *set,
+                              struct nft_rbtree_elem *new)
+{
+       struct nft_rbtree *priv = nft_set_priv(set);
+       struct nft_rbtree_elem *rbe;
+       struct rb_node *parent, **p;
+       int d;
+
+       parent = NULL;
+       p = &priv->root.rb_node;
+       while (*p != NULL) {
+               parent = *p;
+               rbe = rb_entry(parent, struct nft_rbtree_elem, node);
+               d = nft_data_cmp(&rbe->key, &new->key, set->klen);
+               if (d < 0)
+                       p = &parent->rb_left;
+               else if (d > 0)
+                       p = &parent->rb_right;
+               else
+                       return -EEXIST;
+       }
+       rb_link_node(&new->node, parent, p);
+       rb_insert_color(&new->node, &priv->root);
+       return 0;
+}
+
+static int nft_rbtree_insert(const struct nft_set *set,
+                            const struct nft_set_elem *elem)
+{
+       struct nft_rbtree_elem *rbe;
+       unsigned int size;
+       int err;
+
+       size = sizeof(*rbe);
+       if (set->flags & NFT_SET_MAP)
+               size += sizeof(rbe->data[0]);
+
+       rbe = kzalloc(size, GFP_KERNEL);
+       if (rbe == NULL)
+               return -ENOMEM;
+
+       rbe->flags = elem->flags;
+       nft_data_copy(&rbe->key, &elem->key);
+       if (set->flags & NFT_SET_MAP)
+               nft_data_copy(rbe->data, &elem->data);
+
+       err = __nft_rbtree_insert(set, rbe);
+       if (err < 0)
+               kfree(rbe);
+       return err;
+}
+
+static void nft_rbtree_remove(const struct nft_set *set,
+                             const struct nft_set_elem *elem)
+{
+       struct nft_rbtree *priv = nft_set_priv(set);
+       struct nft_rbtree_elem *rbe = elem->cookie;
+
+       rb_erase(&rbe->node, &priv->root);
+       kfree(rbe);
+}
+
+static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
+{
+       const struct nft_rbtree *priv = nft_set_priv(set);
+       const struct rb_node *parent = priv->root.rb_node;
+       struct nft_rbtree_elem *rbe;
+       int d;
+
+       while (parent != NULL) {
+               rbe = rb_entry(parent, struct nft_rbtree_elem, node);
+
+               d = nft_data_cmp(&rbe->key, &elem->key, set->klen);
+               if (d < 0)
+                       parent = parent->rb_left;
+               else if (d > 0)
+                       parent = parent->rb_right;
+               else {
+                       elem->cookie = rbe;
+                       if (set->flags & NFT_SET_MAP)
+                               nft_data_copy(&elem->data, rbe->data);
+                       elem->flags = rbe->flags;
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+static void nft_rbtree_walk(const struct nft_ctx *ctx,
+                           const struct nft_set *set,
+                           struct nft_set_iter *iter)
+{
+       const struct nft_rbtree *priv = nft_set_priv(set);
+       const struct nft_rbtree_elem *rbe;
+       struct nft_set_elem elem;
+       struct rb_node *node;
+
+       for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
+               if (iter->count < iter->skip)
+                       goto cont;
+
+               rbe = rb_entry(node, struct nft_rbtree_elem, node);
+               nft_data_copy(&elem.key, &rbe->key);
+               if (set->flags & NFT_SET_MAP)
+                       nft_data_copy(&elem.data, rbe->data);
+               elem.flags = rbe->flags;
+
+               iter->err = iter->fn(ctx, set, iter, &elem);
+               if (iter->err < 0)
+                       return;
+cont:
+               iter->count++;
+       }
+}
+
+static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
+{
+       return sizeof(struct nft_rbtree);
+}
+
+static int nft_rbtree_init(const struct nft_set *set,
+                          const struct nlattr * const nla[])
+{
+       struct nft_rbtree *priv = nft_set_priv(set);
+
+       priv->root = RB_ROOT;
+       return 0;
+}
+
+static void nft_rbtree_destroy(const struct nft_set *set)
+{
+       struct nft_rbtree *priv = nft_set_priv(set);
+       struct nft_rbtree_elem *rbe;
+       struct rb_node *node;
+
+       while ((node = priv->root.rb_node) != NULL) {
+               rb_erase(node, &priv->root);
+               rbe = rb_entry(node, struct nft_rbtree_elem, node);
+               nft_rbtree_elem_destroy(set, rbe);
+       }
+}
+
+static struct nft_set_ops nft_rbtree_ops __read_mostly = {
+       .privsize       = nft_rbtree_privsize,
+       .init           = nft_rbtree_init,
+       .destroy        = nft_rbtree_destroy,
+       .insert         = nft_rbtree_insert,
+       .remove         = nft_rbtree_remove,
+       .get            = nft_rbtree_get,
+       .lookup         = nft_rbtree_lookup,
+       .walk           = nft_rbtree_walk,
+       .features       = NFT_SET_INTERVAL | NFT_SET_MAP,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_rbtree_module_init(void)
+{
+       return nft_register_set(&nft_rbtree_ops);
+}
+
+static void __exit nft_rbtree_module_exit(void)
+{
+       nft_unregister_set(&nft_rbtree_ops);
+}
+
+module_init(nft_rbtree_module_init);
+module_exit(nft_rbtree_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_ALIAS_NFT_SET();
index 8b03028..227aa11 100644 (file)
@@ -845,8 +845,13 @@ xt_replace_table(struct xt_table *table,
                return NULL;
        }
 
-       table->private = newinfo;
        newinfo->initial_entries = private->initial_entries;
+       /*
+        * Ensure contents of newinfo are visible before assigning to
+        * private.
+        */
+       smp_wmb();
+       table->private = newinfo;
 
        /*
         * Even though table entries have now been swapped, other CPU's
index 1e2fae3..ed00fef 100644 (file)
@@ -147,6 +147,7 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
 {
        const struct xt_NFQ_info_v3 *info = par->targinfo;
        u32 queue = info->queuenum;
+       int ret;
 
        if (info->queues_total > 1) {
                if (info->flags & NFQ_FLAG_CPU_FANOUT) {
@@ -157,7 +158,11 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
                        queue = nfqueue_hash(skb, par);
        }
 
-       return NF_QUEUE_NR(queue);
+       ret = NF_QUEUE_NR(queue);
+       if (info->flags & NFQ_FLAG_BYPASS)
+               ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
+
+       return ret;
 }
 
 static struct xt_target nfqueue_tg_reg[] __read_mostly = {
index cd24290..e762de5 100644 (file)
@@ -43,10 +43,42 @@ optlen(const u_int8_t *opt, unsigned int offset)
                return opt[offset+1];
 }
 
+static u_int32_t tcpmss_reverse_mtu(struct net *net,
+                                   const struct sk_buff *skb,
+                                   unsigned int family)
+{
+       struct flowi fl;
+       const struct nf_afinfo *ai;
+       struct rtable *rt = NULL;
+       u_int32_t mtu     = ~0U;
+
+       if (family == PF_INET) {
+               struct flowi4 *fl4 = &fl.u.ip4;
+               memset(fl4, 0, sizeof(*fl4));
+               fl4->daddr = ip_hdr(skb)->saddr;
+       } else {
+               struct flowi6 *fl6 = &fl.u.ip6;
+
+               memset(fl6, 0, sizeof(*fl6));
+               fl6->daddr = ipv6_hdr(skb)->saddr;
+       }
+       rcu_read_lock();
+       ai = nf_get_afinfo(family);
+       if (ai != NULL)
+               ai->route(net, (struct dst_entry **)&rt, &fl, false);
+       rcu_read_unlock();
+
+       if (rt != NULL) {
+               mtu = dst_mtu(&rt->dst);
+               dst_release(&rt->dst);
+       }
+       return mtu;
+}
+
 static int
 tcpmss_mangle_packet(struct sk_buff *skb,
                     const struct xt_action_param *par,
-                    unsigned int in_mtu,
+                    unsigned int family,
                     unsigned int tcphoff,
                     unsigned int minlen)
 {
@@ -76,6 +108,9 @@ tcpmss_mangle_packet(struct sk_buff *skb,
                return -1;
 
        if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
+               struct net *net = dev_net(par->in ? par->in : par->out);
+               unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family);
+
                if (dst_mtu(skb_dst(skb)) <= minlen) {
                        net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
                                            dst_mtu(skb_dst(skb)));
@@ -165,37 +200,6 @@ tcpmss_mangle_packet(struct sk_buff *skb,
        return TCPOLEN_MSS;
 }
 
-static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
-                                   unsigned int family)
-{
-       struct flowi fl;
-       const struct nf_afinfo *ai;
-       struct rtable *rt = NULL;
-       u_int32_t mtu     = ~0U;
-
-       if (family == PF_INET) {
-               struct flowi4 *fl4 = &fl.u.ip4;
-               memset(fl4, 0, sizeof(*fl4));
-               fl4->daddr = ip_hdr(skb)->saddr;
-       } else {
-               struct flowi6 *fl6 = &fl.u.ip6;
-
-               memset(fl6, 0, sizeof(*fl6));
-               fl6->daddr = ipv6_hdr(skb)->saddr;
-       }
-       rcu_read_lock();
-       ai = nf_get_afinfo(family);
-       if (ai != NULL)
-               ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
-       rcu_read_unlock();
-
-       if (rt != NULL) {
-               mtu = dst_mtu(&rt->dst);
-               dst_release(&rt->dst);
-       }
-       return mtu;
-}
-
 static unsigned int
 tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
 {
@@ -204,7 +208,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
        int ret;
 
        ret = tcpmss_mangle_packet(skb, par,
-                                  tcpmss_reverse_mtu(skb, PF_INET),
+                                  PF_INET,
                                   iph->ihl * 4,
                                   sizeof(*iph) + sizeof(struct tcphdr));
        if (ret < 0)
@@ -233,7 +237,7 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
        if (tcphoff < 0)
                return NF_DROP;
        ret = tcpmss_mangle_packet(skb, par,
-                                  tcpmss_reverse_mtu(skb, PF_INET6),
+                                  PF_INET6,
                                   tcphoff,
                                   sizeof(*ipv6h) + sizeof(struct tcphdr));
        if (ret < 0)
index 5d8a3a3..ef8a926 100644 (file)
@@ -200,7 +200,7 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol,
                                     in->ifindex);
                if (sk) {
                        int connected = (sk->sk_state == TCP_ESTABLISHED);
-                       int wildcard = ipv6_addr_any(&inet6_sk(sk)->rcv_saddr);
+                       int wildcard = ipv6_addr_any(&sk->sk_v6_rcv_saddr);
 
                        /* NOTE: we return listeners even if bound to
                         * 0.0.0.0, those are filtered out in
index e595e07..1e63461 100644 (file)
@@ -26,16 +26,18 @@ connbytes_mt(const struct sk_buff *skb, struct xt_action_param *par)
        u_int64_t what = 0;     /* initialize to make gcc happy */
        u_int64_t bytes = 0;
        u_int64_t pkts = 0;
+       const struct nf_conn_acct *acct;
        const struct nf_conn_counter *counters;
 
        ct = nf_ct_get(skb, &ctinfo);
        if (!ct)
                return false;
 
-       counters = nf_conn_acct_find(ct);
-       if (!counters)
+       acct = nf_conn_acct_find(ct);
+       if (!acct)
                return false;
 
+       counters = acct->counter;
        switch (sinfo->what) {
        case XT_CONNBYTES_PKTS:
                switch (sinfo->direction) {
index 31790e7..e7c4e0e 100644 (file)
@@ -81,7 +81,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
        struct xt_set_info_match_v0 *info = par->matchinfo;
        ip_set_id_t index;
 
-       index = ip_set_nfnl_get_byindex(info->match_set.index);
+       index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
 
        if (index == IPSET_INVALID_ID) {
                pr_warning("Cannot find set indentified by id %u to match\n",
@@ -91,7 +91,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
        if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
                pr_warning("Protocol error: set match dimension "
                           "is over the limit!\n");
-               ip_set_nfnl_put(info->match_set.index);
+               ip_set_nfnl_put(par->net, info->match_set.index);
                return -ERANGE;
        }
 
@@ -106,9 +106,104 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par)
 {
        struct xt_set_info_match_v0 *info = par->matchinfo;
 
-       ip_set_nfnl_put(info->match_set.index);
+       ip_set_nfnl_put(par->net, info->match_set.index);
 }
 
+/* Revision 1 match */
+
+static bool
+set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_set_info_match_v1 *info = par->matchinfo;
+       ADT_OPT(opt, par->family, info->match_set.dim,
+               info->match_set.flags, 0, UINT_MAX);
+
+       if (opt.flags & IPSET_RETURN_NOMATCH)
+               opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
+
+       return match_set(info->match_set.index, skb, par, &opt,
+                        info->match_set.flags & IPSET_INV_MATCH);
+}
+
+static int
+set_match_v1_checkentry(const struct xt_mtchk_param *par)
+{
+       struct xt_set_info_match_v1 *info = par->matchinfo;
+       ip_set_id_t index;
+
+       index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
+
+       if (index == IPSET_INVALID_ID) {
+               pr_warning("Cannot find set indentified by id %u to match\n",
+                          info->match_set.index);
+               return -ENOENT;
+       }
+       if (info->match_set.dim > IPSET_DIM_MAX) {
+               pr_warning("Protocol error: set match dimension "
+                          "is over the limit!\n");
+               ip_set_nfnl_put(par->net, info->match_set.index);
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+static void
+set_match_v1_destroy(const struct xt_mtdtor_param *par)
+{
+       struct xt_set_info_match_v1 *info = par->matchinfo;
+
+       ip_set_nfnl_put(par->net, info->match_set.index);
+}
+
+/* Revision 3 match */
+
+static bool
+match_counter(u64 counter, const struct ip_set_counter_match *info)
+{
+       switch (info->op) {
+       case IPSET_COUNTER_NONE:
+               return true;
+       case IPSET_COUNTER_EQ:
+               return counter == info->value;
+       case IPSET_COUNTER_NE:
+               return counter != info->value;
+       case IPSET_COUNTER_LT:
+               return counter < info->value;
+       case IPSET_COUNTER_GT:
+               return counter > info->value;
+       }
+       return false;
+}
+
+static bool
+set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_set_info_match_v3 *info = par->matchinfo;
+       ADT_OPT(opt, par->family, info->match_set.dim,
+               info->match_set.flags, info->flags, UINT_MAX);
+       int ret;
+
+       if (info->packets.op != IPSET_COUNTER_NONE ||
+           info->bytes.op != IPSET_COUNTER_NONE)
+               opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
+
+       ret = match_set(info->match_set.index, skb, par, &opt,
+                       info->match_set.flags & IPSET_INV_MATCH);
+
+       if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
+               return ret;
+
+       if (!match_counter(opt.ext.packets, &info->packets))
+               return 0;
+       return match_counter(opt.ext.bytes, &info->bytes);
+}
+
+#define set_match_v3_checkentry        set_match_v1_checkentry
+#define set_match_v3_destroy   set_match_v1_destroy
+
+/* Revision 0 interface: backward compatible with netfilter/iptables */
+
 static unsigned int
 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
 {
@@ -133,7 +228,7 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
        ip_set_id_t index;
 
        if (info->add_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->add_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find add_set index %u as target\n",
                                   info->add_set.index);
@@ -142,12 +237,12 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
        }
 
        if (info->del_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->del_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find del_set index %u as target\n",
                                   info->del_set.index);
                        if (info->add_set.index != IPSET_INVALID_ID)
-                               ip_set_nfnl_put(info->add_set.index);
+                               ip_set_nfnl_put(par->net, info->add_set.index);
                        return -ENOENT;
                }
        }
@@ -156,9 +251,9 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
                pr_warning("Protocol error: SET target dimension "
                           "is over the limit!\n");
                if (info->add_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->add_set.index);
+                       ip_set_nfnl_put(par->net, info->add_set.index);
                if (info->del_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->del_set.index);
+                       ip_set_nfnl_put(par->net, info->del_set.index);
                return -ERANGE;
        }
 
@@ -175,57 +270,12 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par)
        const struct xt_set_info_target_v0 *info = par->targinfo;
 
        if (info->add_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->add_set.index);
+               ip_set_nfnl_put(par->net, info->add_set.index);
        if (info->del_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->del_set.index);
+               ip_set_nfnl_put(par->net, info->del_set.index);
 }
 
-/* Revision 1 match and target */
-
-static bool
-set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
-{
-       const struct xt_set_info_match_v1 *info = par->matchinfo;
-       ADT_OPT(opt, par->family, info->match_set.dim,
-               info->match_set.flags, 0, UINT_MAX);
-
-       if (opt.flags & IPSET_RETURN_NOMATCH)
-               opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
-
-       return match_set(info->match_set.index, skb, par, &opt,
-                        info->match_set.flags & IPSET_INV_MATCH);
-}
-
-static int
-set_match_v1_checkentry(const struct xt_mtchk_param *par)
-{
-       struct xt_set_info_match_v1 *info = par->matchinfo;
-       ip_set_id_t index;
-
-       index = ip_set_nfnl_get_byindex(info->match_set.index);
-
-       if (index == IPSET_INVALID_ID) {
-               pr_warning("Cannot find set indentified by id %u to match\n",
-                          info->match_set.index);
-               return -ENOENT;
-       }
-       if (info->match_set.dim > IPSET_DIM_MAX) {
-               pr_warning("Protocol error: set match dimension "
-                          "is over the limit!\n");
-               ip_set_nfnl_put(info->match_set.index);
-               return -ERANGE;
-       }
-
-       return 0;
-}
-
-static void
-set_match_v1_destroy(const struct xt_mtdtor_param *par)
-{
-       struct xt_set_info_match_v1 *info = par->matchinfo;
-
-       ip_set_nfnl_put(info->match_set.index);
-}
+/* Revision 1 target */
 
 static unsigned int
 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
@@ -251,7 +301,7 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par)
        ip_set_id_t index;
 
        if (info->add_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->add_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find add_set index %u as target\n",
                                   info->add_set.index);
@@ -260,12 +310,12 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par)
        }
 
        if (info->del_set.index != IPSET_INVALID_ID) {
-               index = ip_set_nfnl_get_byindex(info->del_set.index);
+               index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
                if (index == IPSET_INVALID_ID) {
                        pr_warning("Cannot find del_set index %u as target\n",
                                   info->del_set.index);
                        if (info->add_set.index != IPSET_INVALID_ID)
-                               ip_set_nfnl_put(info->add_set.index);
+                               ip_set_nfnl_put(par->net, info->add_set.index);
                        return -ENOENT;
                }
        }
@@ -274,9 +324,9 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par)
                pr_warning("Protocol error: SET target dimension "
                           "is over the limit!\n");
                if (info->add_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->add_set.index);
+                       ip_set_nfnl_put(par->net, info->add_set.index);
                if (info->del_set.index != IPSET_INVALID_ID)
-                       ip_set_nfnl_put(info->del_set.index);
+                       ip_set_nfnl_put(par->net, info->del_set.index);
                return -ERANGE;
        }
 
@@ -289,9 +339,9 @@ set_target_v1_destroy(const struct xt_tgdtor_param *par)
        const struct xt_set_info_target_v1 *info = par->targinfo;
 
        if (info->add_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->add_set.index);
+               ip_set_nfnl_put(par->net, info->add_set.index);
        if (info->del_set.index != IPSET_INVALID_ID)
-               ip_set_nfnl_put(info->del_set.index);
+               ip_set_nfnl_put(par->net, info->del_set.index);
 }
 
 /* Revision 2 target */
@@ -320,52 +370,6 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
 #define set_target_v2_checkentry       set_target_v1_checkentry
 #define set_target_v2_destroy          set_target_v1_destroy
 
-/* Revision 3 match */
-
-static bool
-match_counter(u64 counter, const struct ip_set_counter_match *info)
-{
-       switch (info->op) {
-       case IPSET_COUNTER_NONE:
-               return true;
-       case IPSET_COUNTER_EQ:
-               return counter == info->value;
-       case IPSET_COUNTER_NE:
-               return counter != info->value;
-       case IPSET_COUNTER_LT:
-               return counter < info->value;
-       case IPSET_COUNTER_GT:
-               return counter > info->value;
-       }
-       return false;
-}
-
-static bool
-set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
-{
-       const struct xt_set_info_match_v3 *info = par->matchinfo;
-       ADT_OPT(opt, par->family, info->match_set.dim,
-               info->match_set.flags, info->flags, UINT_MAX);
-       int ret;
-
-       if (info->packets.op != IPSET_COUNTER_NONE ||
-           info->bytes.op != IPSET_COUNTER_NONE)
-               opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
-
-       ret = match_set(info->match_set.index, skb, par, &opt,
-                       info->match_set.flags & IPSET_INV_MATCH);
-
-       if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
-               return ret;
-
-       if (!match_counter(opt.ext.packets, &info->packets))
-               return 0;
-       return match_counter(opt.ext.bytes, &info->bytes);
-}
-
-#define set_match_v3_checkentry        set_match_v1_checkentry
-#define set_match_v3_destroy   set_match_v1_destroy
-
 static struct xt_match set_matches[] __read_mostly = {
        {
                .name           = "set",
index 06df2b9..1ba6793 100644 (file)
 #include <net/netfilter/nf_conntrack.h>
 #endif
 
-static void
-xt_socket_put_sk(struct sock *sk)
-{
-       if (sk->sk_state == TCP_TIME_WAIT)
-               inet_twsk_put(inet_twsk(sk));
-       else
-               sock_put(sk);
-}
-
 static int
 extract_icmp4_fields(const struct sk_buff *skb,
                    u8 *protocol,
@@ -216,7 +207,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
                                        inet_twsk(sk)->tw_transparent));
 
                if (sk != skb->sk)
-                       xt_socket_put_sk(sk);
+                       sock_gen_put(sk);
 
                if (wildcard || !transparent)
                        sk = NULL;
@@ -370,7 +361,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
                 */
                wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
                            sk->sk_state != TCP_TIME_WAIT &&
-                           ipv6_addr_any(&inet6_sk(sk)->rcv_saddr));
+                           ipv6_addr_any(&sk->sk_v6_rcv_saddr));
 
                /* Ignore non-transparent sockets,
                   if XT_SOCKET_TRANSPARENT is used */
@@ -381,7 +372,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
                                        inet_twsk(sk)->tw_transparent));
 
                if (sk != skb->sk)
-                       xt_socket_put_sk(sk);
+                       sock_gen_put(sk);
 
                if (wildcard || !transparent)
                        sk = NULL;
index 96a458e..dce1beb 100644 (file)
@@ -817,7 +817,7 @@ int netlbl_req_setattr(struct request_sock *req,
        switch (req->rsk_ops->family) {
        case AF_INET:
                entry = netlbl_domhsh_getentry_af4(secattr->domain,
-                                                  inet_rsk(req)->rmt_addr);
+                                                  inet_rsk(req)->ir_rmt_addr);
                if (entry == NULL) {
                        ret_val = -ENOENT;
                        goto req_setattr_return;
index 5948b2f..6e0fa0c 100644 (file)
@@ -14,6 +14,20 @@ menuconfig NFC
          To compile this support as a module, choose M here: the module will
          be called nfc.
 
+config NFC_DIGITAL
+       depends on NFC
+       select CRC_CCITT
+       select CRC_ITU_T
+       tristate "NFC Digital Protocol stack support"
+       default n
+       help
+         Say Y if you want to build NFC digital protocol stack support.
+         This is needed by NFC chipsets whose firmware only implement
+         the NFC analog layer.
+
+         To compile this support as a module, choose M here: the module will
+         be called nfc_digital.
+
 source "net/nfc/nci/Kconfig"
 source "net/nfc/hci/Kconfig"
 
index a76f453..2555ff8 100644 (file)
@@ -5,7 +5,9 @@
 obj-$(CONFIG_NFC) += nfc.o
 obj-$(CONFIG_NFC_NCI) += nci/
 obj-$(CONFIG_NFC_HCI) += hci/
+obj-$(CONFIG_NFC_DIGITAL) += nfc_digital.o
 
 nfc-objs := core.o netlink.o af_nfc.o rawsock.o llcp_core.o llcp_commands.o \
                llcp_sock.o
 
+nfc_digital-objs := digital_core.o digital_technology.o digital_dep.o
index e92923c..8725291 100644 (file)
@@ -384,6 +384,19 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
 {
        dev->dep_link_up = true;
 
+       if (!dev->active_target) {
+               struct nfc_target *target;
+
+               target = nfc_find_target(dev, target_idx);
+               if (target == NULL)
+                       return -ENOTCONN;
+
+               dev->active_target = target;
+       }
+
+       dev->polling = false;
+       dev->rf_mode = rf_mode;
+
        nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode);
 
        return nfc_genl_dep_link_up_event(dev, target_idx, comm_mode, rf_mode);
@@ -536,7 +549,7 @@ error:
        return rc;
 }
 
-static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
+struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx)
 {
        struct nfc_se *se, *n;
 
@@ -546,6 +559,7 @@ static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
 
        return NULL;
 }
+EXPORT_SYMBOL(nfc_find_se);
 
 int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
 {
@@ -577,7 +591,7 @@ int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
                goto error;
        }
 
-       se = find_se(dev, se_idx);
+       se = nfc_find_se(dev, se_idx);
        if (!se) {
                rc = -EINVAL;
                goto error;
@@ -622,7 +636,7 @@ int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
                goto error;
        }
 
-       se = find_se(dev, se_idx);
+       se = nfc_find_se(dev, se_idx);
        if (!se) {
                rc = -EINVAL;
                goto error;
@@ -881,7 +895,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
 
        pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
 
-       se = find_se(dev, se_idx);
+       se = nfc_find_se(dev, se_idx);
        if (se)
                return -EALREADY;
 
diff --git a/net/nfc/digital.h b/net/nfc/digital.h
new file mode 100644 (file)
index 0000000..08b29b5
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#ifndef __DIGITAL_H
+#define __DIGITAL_H
+
+#include <net/nfc/nfc.h>
+#include <net/nfc/digital.h>
+
+#include <linux/crc-ccitt.h>
+#include <linux/crc-itu-t.h>
+
+#define PROTOCOL_ERR(req) pr_err("%d: NFC Digital Protocol error: %s\n", \
+                                __LINE__, req)
+
+#define DIGITAL_CMD_IN_SEND        0
+#define DIGITAL_CMD_TG_SEND        1
+#define DIGITAL_CMD_TG_LISTEN      2
+#define DIGITAL_CMD_TG_LISTEN_MDAA 3
+
+#define DIGITAL_MAX_HEADER_LEN 7
+#define DIGITAL_CRC_LEN        2
+
+#define DIGITAL_SENSF_NFCID2_NFC_DEP_B1 0x01
+#define DIGITAL_SENSF_NFCID2_NFC_DEP_B2 0xFE
+
+#define DIGITAL_SENS_RES_NFC_DEP 0x0100
+#define DIGITAL_SEL_RES_NFC_DEP  0x40
+#define DIGITAL_SENSF_FELICA_SC  0xFFFF
+
+#define DIGITAL_DRV_CAPS_IN_CRC(ddev) \
+       ((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_IN_CRC)
+#define DIGITAL_DRV_CAPS_TG_CRC(ddev) \
+       ((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_TG_CRC)
+
+struct digital_data_exch {
+       data_exchange_cb_t cb;
+       void *cb_context;
+};
+
+struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev,
+                                 unsigned int len);
+
+int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
+                    struct sk_buff *skb, struct digital_tg_mdaa_params *params,
+                    u16 timeout, nfc_digital_cmd_complete_t cmd_cb,
+                    void *cb_context);
+
+int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param);
+static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev,
+                                     struct sk_buff *skb, u16 timeout,
+                                     nfc_digital_cmd_complete_t cmd_cb,
+                                     void *cb_context)
+{
+       return digital_send_cmd(ddev, DIGITAL_CMD_IN_SEND, skb, NULL, timeout,
+                               cmd_cb, cb_context);
+}
+
+void digital_poll_next_tech(struct nfc_digital_dev *ddev);
+
+int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+int digital_target_found(struct nfc_digital_dev *ddev,
+                        struct nfc_target *target, u8 protocol);
+
+int digital_in_recv_mifare_res(struct sk_buff *resp);
+
+int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, __u8 comm_mode, __u8 *gb,
+                           size_t gb_len);
+int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, struct sk_buff *skb,
+                           struct digital_data_exch *data_exch);
+
+int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param);
+static inline int digital_tg_send_cmd(struct nfc_digital_dev *ddev,
+                       struct sk_buff *skb, u16 timeout,
+                       nfc_digital_cmd_complete_t cmd_cb, void *cb_context)
+{
+       return digital_send_cmd(ddev, DIGITAL_CMD_TG_SEND, skb, NULL, timeout,
+                               cmd_cb, cb_context);
+}
+
+void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg,
+                             struct sk_buff *resp);
+
+void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg,
+                              struct sk_buff *resp);
+
+static inline int digital_tg_listen(struct nfc_digital_dev *ddev, u16 timeout,
+                                   nfc_digital_cmd_complete_t cb, void *arg)
+{
+       return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN, NULL, NULL,
+                               timeout, cb, arg);
+}
+
+void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
+                            struct sk_buff *resp);
+
+int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb);
+
+int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech);
+int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech);
+
+typedef u16 (*crc_func_t)(u16, const u8 *, size_t);
+
+#define CRC_A_INIT 0x6363
+#define CRC_B_INIT 0xFFFF
+#define CRC_F_INIT 0x0000
+
+void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init,
+                        u8 bitwise_inv, u8 msb_first);
+
+static inline void digital_skb_add_crc_a(struct sk_buff *skb)
+{
+       digital_skb_add_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0);
+}
+
+static inline void digital_skb_add_crc_b(struct sk_buff *skb)
+{
+       digital_skb_add_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0);
+}
+
+static inline void digital_skb_add_crc_f(struct sk_buff *skb)
+{
+       digital_skb_add_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1);
+}
+
+static inline void digital_skb_add_crc_none(struct sk_buff *skb)
+{
+       return;
+}
+
+int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func,
+                         u16 crc_init, u8 bitwise_inv, u8 msb_first);
+
+static inline int digital_skb_check_crc_a(struct sk_buff *skb)
+{
+       return digital_skb_check_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0);
+}
+
+static inline int digital_skb_check_crc_b(struct sk_buff *skb)
+{
+       return digital_skb_check_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0);
+}
+
+static inline int digital_skb_check_crc_f(struct sk_buff *skb)
+{
+       return digital_skb_check_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1);
+}
+
+static inline int digital_skb_check_crc_none(struct sk_buff *skb)
+{
+       return 0;
+}
+
+#endif /* __DIGITAL_H */
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
new file mode 100644 (file)
index 0000000..09fc954
--- /dev/null
@@ -0,0 +1,737 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) "digital: %s: " fmt, __func__
+
+#include <linux/module.h>
+
+#include "digital.h"
+
+#define DIGITAL_PROTO_NFCA_RF_TECH \
+       (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK)
+
+#define DIGITAL_PROTO_NFCF_RF_TECH \
+       (NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK)
+
+struct digital_cmd {
+       struct list_head queue;
+
+       u8 type;
+       u8 pending;
+
+       u16 timeout;
+       struct sk_buff *req;
+       struct sk_buff *resp;
+       struct digital_tg_mdaa_params *mdaa_params;
+
+       nfc_digital_cmd_complete_t cmd_cb;
+       void *cb_context;
+};
+
+struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev,
+                                 unsigned int len)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(len + ddev->tx_headroom + ddev->tx_tailroom,
+                       GFP_KERNEL);
+       if (skb)
+               skb_reserve(skb, ddev->tx_headroom);
+
+       return skb;
+}
+
+void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init,
+                        u8 bitwise_inv, u8 msb_first)
+{
+       u16 crc;
+
+       crc = crc_func(init, skb->data, skb->len);
+
+       if (bitwise_inv)
+               crc = ~crc;
+
+       if (msb_first)
+               crc = __fswab16(crc);
+
+       *skb_put(skb, 1) = crc & 0xFF;
+       *skb_put(skb, 1) = (crc >> 8) & 0xFF;
+}
+
+int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func,
+                         u16 crc_init, u8 bitwise_inv, u8 msb_first)
+{
+       int rc;
+       u16 crc;
+
+       if (skb->len <= 2)
+               return -EIO;
+
+       crc = crc_func(crc_init, skb->data, skb->len - 2);
+
+       if (bitwise_inv)
+               crc = ~crc;
+
+       if (msb_first)
+               crc = __swab16(crc);
+
+       rc = (skb->data[skb->len - 2] - (crc & 0xFF)) +
+            (skb->data[skb->len - 1] - ((crc >> 8) & 0xFF));
+
+       if (rc)
+               return -EIO;
+
+       skb_trim(skb, skb->len - 2);
+
+       return 0;
+}
+
+static inline void digital_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+       ddev->ops->switch_rf(ddev, on);
+}
+
+static inline void digital_abort_cmd(struct nfc_digital_dev *ddev)
+{
+       ddev->ops->abort_cmd(ddev);
+}
+
+static void digital_wq_cmd_complete(struct work_struct *work)
+{
+       struct digital_cmd *cmd;
+       struct nfc_digital_dev *ddev = container_of(work,
+                                                   struct nfc_digital_dev,
+                                                   cmd_complete_work);
+
+       mutex_lock(&ddev->cmd_lock);
+
+       cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd,
+                                      queue);
+       if (!cmd) {
+               mutex_unlock(&ddev->cmd_lock);
+               return;
+       }
+
+       list_del(&cmd->queue);
+
+       mutex_unlock(&ddev->cmd_lock);
+
+       if (!IS_ERR(cmd->resp))
+               print_hex_dump_debug("DIGITAL RX: ", DUMP_PREFIX_NONE, 16, 1,
+                                    cmd->resp->data, cmd->resp->len, false);
+
+       cmd->cmd_cb(ddev, cmd->cb_context, cmd->resp);
+
+       kfree(cmd->mdaa_params);
+       kfree(cmd);
+
+       schedule_work(&ddev->cmd_work);
+}
+
+static void digital_send_cmd_complete(struct nfc_digital_dev *ddev,
+                                     void *arg, struct sk_buff *resp)
+{
+       struct digital_cmd *cmd = arg;
+
+       cmd->resp = resp;
+
+       schedule_work(&ddev->cmd_complete_work);
+}
+
+static void digital_wq_cmd(struct work_struct *work)
+{
+       int rc;
+       struct digital_cmd *cmd;
+       struct digital_tg_mdaa_params *params;
+       struct nfc_digital_dev *ddev = container_of(work,
+                                                   struct nfc_digital_dev,
+                                                   cmd_work);
+
+       mutex_lock(&ddev->cmd_lock);
+
+       cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd,
+                                      queue);
+       if (!cmd || cmd->pending) {
+               mutex_unlock(&ddev->cmd_lock);
+               return;
+       }
+
+       mutex_unlock(&ddev->cmd_lock);
+
+       if (cmd->req)
+               print_hex_dump_debug("DIGITAL TX: ", DUMP_PREFIX_NONE, 16, 1,
+                                    cmd->req->data, cmd->req->len, false);
+
+       switch (cmd->type) {
+       case DIGITAL_CMD_IN_SEND:
+               rc = ddev->ops->in_send_cmd(ddev, cmd->req, cmd->timeout,
+                                           digital_send_cmd_complete, cmd);
+               break;
+
+       case DIGITAL_CMD_TG_SEND:
+               rc = ddev->ops->tg_send_cmd(ddev, cmd->req, cmd->timeout,
+                                           digital_send_cmd_complete, cmd);
+               break;
+
+       case DIGITAL_CMD_TG_LISTEN:
+               rc = ddev->ops->tg_listen(ddev, cmd->timeout,
+                                         digital_send_cmd_complete, cmd);
+               break;
+
+       case DIGITAL_CMD_TG_LISTEN_MDAA:
+               params = cmd->mdaa_params;
+
+               rc = ddev->ops->tg_listen_mdaa(ddev, params, cmd->timeout,
+                                              digital_send_cmd_complete, cmd);
+               break;
+
+       default:
+               pr_err("Unknown cmd type %d\n", cmd->type);
+               return;
+       }
+
+       if (!rc)
+               return;
+
+       pr_err("in_send_command returned err %d\n", rc);
+
+       mutex_lock(&ddev->cmd_lock);
+       list_del(&cmd->queue);
+       mutex_unlock(&ddev->cmd_lock);
+
+       kfree_skb(cmd->req);
+       kfree(cmd->mdaa_params);
+       kfree(cmd);
+
+       schedule_work(&ddev->cmd_work);
+}
+
+int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
+                    struct sk_buff *skb, struct digital_tg_mdaa_params *params,
+                    u16 timeout, nfc_digital_cmd_complete_t cmd_cb,
+                    void *cb_context)
+{
+       struct digital_cmd *cmd;
+
+       cmd = kzalloc(sizeof(struct digital_cmd), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       cmd->type = cmd_type;
+       cmd->timeout = timeout;
+       cmd->req = skb;
+       cmd->mdaa_params = params;
+       cmd->cmd_cb = cmd_cb;
+       cmd->cb_context = cb_context;
+       INIT_LIST_HEAD(&cmd->queue);
+
+       mutex_lock(&ddev->cmd_lock);
+       list_add_tail(&cmd->queue, &ddev->cmd_queue);
+       mutex_unlock(&ddev->cmd_lock);
+
+       schedule_work(&ddev->cmd_work);
+
+       return 0;
+}
+
+int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
+{
+       int rc;
+
+       rc = ddev->ops->in_configure_hw(ddev, type, param);
+       if (rc)
+               pr_err("in_configure_hw failed: %d\n", rc);
+
+       return rc;
+}
+
+int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
+{
+       int rc;
+
+       rc = ddev->ops->tg_configure_hw(ddev, type, param);
+       if (rc)
+               pr_err("tg_configure_hw failed: %d\n", rc);
+
+       return rc;
+}
+
+static int digital_tg_listen_mdaa(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct digital_tg_mdaa_params *params;
+
+       params = kzalloc(sizeof(struct digital_tg_mdaa_params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
+
+       params->sens_res = DIGITAL_SENS_RES_NFC_DEP;
+       get_random_bytes(params->nfcid1, sizeof(params->nfcid1));
+       params->sel_res = DIGITAL_SEL_RES_NFC_DEP;
+
+       params->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+       params->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+       get_random_bytes(params->nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2);
+       params->sc = DIGITAL_SENSF_FELICA_SC;
+
+       return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN_MDAA, NULL, params,
+                               500, digital_tg_recv_atr_req, NULL);
+}
+
+int digital_target_found(struct nfc_digital_dev *ddev,
+                        struct nfc_target *target, u8 protocol)
+{
+       int rc;
+       u8 framing;
+       u8 rf_tech;
+       int (*check_crc)(struct sk_buff *skb);
+       void (*add_crc)(struct sk_buff *skb);
+
+       rf_tech = ddev->poll_techs[ddev->poll_tech_index].rf_tech;
+
+       switch (protocol) {
+       case NFC_PROTO_JEWEL:
+               framing = NFC_DIGITAL_FRAMING_NFCA_T1T;
+               check_crc = digital_skb_check_crc_b;
+               add_crc = digital_skb_add_crc_b;
+               break;
+
+       case NFC_PROTO_MIFARE:
+               framing = NFC_DIGITAL_FRAMING_NFCA_T2T;
+               check_crc = digital_skb_check_crc_a;
+               add_crc = digital_skb_add_crc_a;
+               break;
+
+       case NFC_PROTO_FELICA:
+               framing = NFC_DIGITAL_FRAMING_NFCF_T3T;
+               check_crc = digital_skb_check_crc_f;
+               add_crc = digital_skb_add_crc_f;
+               break;
+
+       case NFC_PROTO_NFC_DEP:
+               if (rf_tech == NFC_DIGITAL_RF_TECH_106A) {
+                       framing = NFC_DIGITAL_FRAMING_NFCA_NFC_DEP;
+                       check_crc = digital_skb_check_crc_a;
+                       add_crc = digital_skb_add_crc_a;
+               } else {
+                       framing = NFC_DIGITAL_FRAMING_NFCF_NFC_DEP;
+                       check_crc = digital_skb_check_crc_f;
+                       add_crc = digital_skb_add_crc_f;
+               }
+               break;
+
+       default:
+               pr_err("Invalid protocol %d\n", protocol);
+               return -EINVAL;
+       }
+
+       pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol);
+
+       ddev->curr_rf_tech = rf_tech;
+       ddev->curr_protocol = protocol;
+
+       if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               ddev->skb_add_crc = digital_skb_add_crc_none;
+               ddev->skb_check_crc = digital_skb_check_crc_none;
+       } else {
+               ddev->skb_add_crc = add_crc;
+               ddev->skb_check_crc = check_crc;
+       }
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, framing);
+       if (rc)
+               return rc;
+
+       target->supported_protocols = (1 << protocol);
+       rc = nfc_targets_found(ddev->nfc_dev, target, 1);
+       if (rc)
+               return rc;
+
+       ddev->poll_tech_count = 0;
+
+       return 0;
+}
+
+void digital_poll_next_tech(struct nfc_digital_dev *ddev)
+{
+       digital_switch_rf(ddev, 0);
+
+       mutex_lock(&ddev->poll_lock);
+
+       if (!ddev->poll_tech_count) {
+               mutex_unlock(&ddev->poll_lock);
+               return;
+       }
+
+       ddev->poll_tech_index = (ddev->poll_tech_index + 1) %
+                               ddev->poll_tech_count;
+
+       mutex_unlock(&ddev->poll_lock);
+
+       schedule_work(&ddev->poll_work);
+}
+
+static void digital_wq_poll(struct work_struct *work)
+{
+       int rc;
+       struct digital_poll_tech *poll_tech;
+       struct nfc_digital_dev *ddev = container_of(work,
+                                                   struct nfc_digital_dev,
+                                                   poll_work);
+       mutex_lock(&ddev->poll_lock);
+
+       if (!ddev->poll_tech_count) {
+               mutex_unlock(&ddev->poll_lock);
+               return;
+       }
+
+       poll_tech = &ddev->poll_techs[ddev->poll_tech_index];
+
+       mutex_unlock(&ddev->poll_lock);
+
+       rc = poll_tech->poll_func(ddev, poll_tech->rf_tech);
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+static void digital_add_poll_tech(struct nfc_digital_dev *ddev, u8 rf_tech,
+                                 digital_poll_t poll_func)
+{
+       struct digital_poll_tech *poll_tech;
+
+       if (ddev->poll_tech_count >= NFC_DIGITAL_POLL_MODE_COUNT_MAX)
+               return;
+
+       poll_tech = &ddev->poll_techs[ddev->poll_tech_count++];
+
+       poll_tech->rf_tech = rf_tech;
+       poll_tech->poll_func = poll_func;
+}
+
+/**
+ * start_poll operation
+ *
+ * For every supported protocol, the corresponding polling function is added
+ * to the table of polling technologies (ddev->poll_techs[]) using
+ * digital_add_poll_tech().
+ * When a polling function fails (by timeout or protocol error) the next one is
+ * schedule by digital_poll_next_tech() on the poll workqueue (ddev->poll_work).
+ */
+static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
+                             __u32 tm_protocols)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+       u32 matching_im_protocols, matching_tm_protocols;
+
+       pr_debug("protocols: im 0x%x, tm 0x%x, supported 0x%x\n", im_protocols,
+                tm_protocols, ddev->protocols);
+
+       matching_im_protocols = ddev->protocols & im_protocols;
+       matching_tm_protocols = ddev->protocols & tm_protocols;
+
+       if (!matching_im_protocols && !matching_tm_protocols) {
+               pr_err("Unknown protocol\n");
+               return -EINVAL;
+       }
+
+       if (ddev->poll_tech_count) {
+               pr_err("Already polling\n");
+               return -EBUSY;
+       }
+
+       if (ddev->curr_protocol) {
+               pr_err("A target is already active\n");
+               return -EBUSY;
+       }
+
+       ddev->poll_tech_count = 0;
+       ddev->poll_tech_index = 0;
+
+       if (matching_im_protocols & DIGITAL_PROTO_NFCA_RF_TECH)
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
+                                     digital_in_send_sens_req);
+
+       if (im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) {
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
+                                     digital_in_send_sensf_req);
+
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F,
+                                     digital_in_send_sensf_req);
+       }
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               if (ddev->ops->tg_listen_mdaa) {
+                       digital_add_poll_tech(ddev, 0,
+                                             digital_tg_listen_mdaa);
+               } else {
+                       digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
+                                             digital_tg_listen_nfca);
+
+                       digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
+                                             digital_tg_listen_nfcf);
+
+                       digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F,
+                                             digital_tg_listen_nfcf);
+               }
+       }
+
+       if (!ddev->poll_tech_count) {
+               pr_err("Unsupported protocols: im=0x%x, tm=0x%x\n",
+                      matching_im_protocols, matching_tm_protocols);
+               return -EINVAL;
+       }
+
+       schedule_work(&ddev->poll_work);
+
+       return 0;
+}
+
+static void digital_stop_poll(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       mutex_lock(&ddev->poll_lock);
+
+       if (!ddev->poll_tech_count) {
+               pr_err("Polling operation was not running\n");
+               mutex_unlock(&ddev->poll_lock);
+               return;
+       }
+
+       ddev->poll_tech_count = 0;
+
+       mutex_unlock(&ddev->poll_lock);
+
+       cancel_work_sync(&ddev->poll_work);
+
+       digital_abort_cmd(ddev);
+}
+
+static int digital_dev_up(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       digital_switch_rf(ddev, 1);
+
+       return 0;
+}
+
+static int digital_dev_down(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       digital_switch_rf(ddev, 0);
+
+       return 0;
+}
+
+static int digital_dep_link_up(struct nfc_dev *nfc_dev,
+                              struct nfc_target *target,
+                              __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       return digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len);
+}
+
+static int digital_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       ddev->curr_protocol = 0;
+
+       return 0;
+}
+
+static int digital_activate_target(struct nfc_dev *nfc_dev,
+                                  struct nfc_target *target, __u32 protocol)
+{
+       return 0;
+}
+
+static void digital_deactivate_target(struct nfc_dev *nfc_dev,
+                                     struct nfc_target *target)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       ddev->curr_protocol = 0;
+}
+
+static int digital_tg_send(struct nfc_dev *dev, struct sk_buff *skb)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(dev);
+
+       return digital_tg_send_dep_res(ddev, skb);
+}
+
+static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg,
+                                    struct sk_buff *resp)
+{
+       struct digital_data_exch *data_exch = arg;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               goto done;
+       }
+
+       if (ddev->curr_protocol == NFC_PROTO_MIFARE)
+               rc = digital_in_recv_mifare_res(resp);
+       else
+               rc = ddev->skb_check_crc(resp);
+
+       if (rc) {
+               kfree_skb(resp);
+               resp = NULL;
+       }
+
+done:
+       data_exch->cb(data_exch->cb_context, resp, rc);
+
+       kfree(data_exch);
+}
+
+static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                          struct sk_buff *skb, data_exchange_cb_t cb,
+                          void *cb_context)
+{
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+       struct digital_data_exch *data_exch;
+
+       data_exch = kzalloc(sizeof(struct digital_data_exch), GFP_KERNEL);
+       if (!data_exch) {
+               pr_err("Failed to allocate data_exch struct\n");
+               return -ENOMEM;
+       }
+
+       data_exch->cb = cb;
+       data_exch->cb_context = cb_context;
+
+       if (ddev->curr_protocol == NFC_PROTO_NFC_DEP)
+               return digital_in_send_dep_req(ddev, target, skb, data_exch);
+
+       ddev->skb_add_crc(skb);
+
+       return digital_in_send_cmd(ddev, skb, 500, digital_in_send_complete,
+                                  data_exch);
+}
+
+static struct nfc_ops digital_nfc_ops = {
+       .dev_up = digital_dev_up,
+       .dev_down = digital_dev_down,
+       .start_poll = digital_start_poll,
+       .stop_poll = digital_stop_poll,
+       .dep_link_up = digital_dep_link_up,
+       .dep_link_down = digital_dep_link_down,
+       .activate_target = digital_activate_target,
+       .deactivate_target = digital_deactivate_target,
+       .tm_send = digital_tg_send,
+       .im_transceive = digital_in_send,
+};
+
+struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
+                                           __u32 supported_protocols,
+                                           __u32 driver_capabilities,
+                                           int tx_headroom, int tx_tailroom)
+{
+       struct nfc_digital_dev *ddev;
+
+       if (!ops->in_configure_hw || !ops->in_send_cmd || !ops->tg_listen ||
+           !ops->tg_configure_hw || !ops->tg_send_cmd || !ops->abort_cmd ||
+           !ops->switch_rf)
+               return NULL;
+
+       ddev = kzalloc(sizeof(struct nfc_digital_dev), GFP_KERNEL);
+       if (!ddev)
+               return NULL;
+
+       ddev->driver_capabilities = driver_capabilities;
+       ddev->ops = ops;
+
+       mutex_init(&ddev->cmd_lock);
+       INIT_LIST_HEAD(&ddev->cmd_queue);
+
+       INIT_WORK(&ddev->cmd_work, digital_wq_cmd);
+       INIT_WORK(&ddev->cmd_complete_work, digital_wq_cmd_complete);
+
+       mutex_init(&ddev->poll_lock);
+       INIT_WORK(&ddev->poll_work, digital_wq_poll);
+
+       if (supported_protocols & NFC_PROTO_JEWEL_MASK)
+               ddev->protocols |= NFC_PROTO_JEWEL_MASK;
+       if (supported_protocols & NFC_PROTO_MIFARE_MASK)
+               ddev->protocols |= NFC_PROTO_MIFARE_MASK;
+       if (supported_protocols & NFC_PROTO_FELICA_MASK)
+               ddev->protocols |= NFC_PROTO_FELICA_MASK;
+       if (supported_protocols & NFC_PROTO_NFC_DEP_MASK)
+               ddev->protocols |= NFC_PROTO_NFC_DEP_MASK;
+
+       ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN;
+       ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN;
+
+       ddev->nfc_dev = nfc_allocate_device(&digital_nfc_ops, ddev->protocols,
+                                           ddev->tx_headroom,
+                                           ddev->tx_tailroom);
+       if (!ddev->nfc_dev) {
+               pr_err("nfc_allocate_device failed\n");
+               goto free_dev;
+       }
+
+       nfc_set_drvdata(ddev->nfc_dev, ddev);
+
+       return ddev;
+
+free_dev:
+       kfree(ddev);
+
+       return NULL;
+}
+EXPORT_SYMBOL(nfc_digital_allocate_device);
+
+void nfc_digital_free_device(struct nfc_digital_dev *ddev)
+{
+       nfc_free_device(ddev->nfc_dev);
+       kfree(ddev);
+}
+EXPORT_SYMBOL(nfc_digital_free_device);
+
+int nfc_digital_register_device(struct nfc_digital_dev *ddev)
+{
+       return nfc_register_device(ddev->nfc_dev);
+}
+EXPORT_SYMBOL(nfc_digital_register_device);
+
+void nfc_digital_unregister_device(struct nfc_digital_dev *ddev)
+{
+       struct digital_cmd *cmd, *n;
+
+       nfc_unregister_device(ddev->nfc_dev);
+
+       mutex_lock(&ddev->poll_lock);
+       ddev->poll_tech_count = 0;
+       mutex_unlock(&ddev->poll_lock);
+
+       cancel_work_sync(&ddev->poll_work);
+       cancel_work_sync(&ddev->cmd_work);
+       cancel_work_sync(&ddev->cmd_complete_work);
+
+       list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) {
+               list_del(&cmd->queue);
+               kfree(cmd->mdaa_params);
+               kfree(cmd);
+       }
+}
+EXPORT_SYMBOL(nfc_digital_unregister_device);
+
+MODULE_LICENSE("GPL");
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
new file mode 100644 (file)
index 0000000..07bbc24
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) "digital: %s: " fmt, __func__
+
+#include "digital.h"
+
+#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4
+#define DIGITAL_NFC_DEP_FRAME_DIR_IN  0xD5
+
+#define DIGITAL_NFC_DEP_NFCA_SOD_SB   0xF0
+
+#define DIGITAL_CMD_ATR_REQ 0x00
+#define DIGITAL_CMD_ATR_RES 0x01
+#define DIGITAL_CMD_PSL_REQ 0x04
+#define DIGITAL_CMD_PSL_RES 0x05
+#define DIGITAL_CMD_DEP_REQ 0x06
+#define DIGITAL_CMD_DEP_RES 0x07
+
+#define DIGITAL_ATR_REQ_MIN_SIZE 16
+#define DIGITAL_ATR_REQ_MAX_SIZE 64
+
+#define DIGITAL_NFCID3_LEN ((u8)8)
+#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
+#define DIGITAL_GB_BIT 0x02
+
+#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
+
+#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+
+#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
+                               ((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT)
+#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & 0x10)
+#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
+#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define DIGITAL_NFC_DEP_PFB_PNI(pfb)     ((pfb) & 0x03)
+
+#define DIGITAL_NFC_DEP_PFB_I_PDU          0x00
+#define DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU   0x40
+#define DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU 0x80
+
+struct digital_atr_req {
+       u8 dir;
+       u8 cmd;
+       u8 nfcid3[10];
+       u8 did;
+       u8 bs;
+       u8 br;
+       u8 pp;
+       u8 gb[0];
+} __packed;
+
+struct digital_atr_res {
+       u8 dir;
+       u8 cmd;
+       u8 nfcid3[10];
+       u8 did;
+       u8 bs;
+       u8 br;
+       u8 to;
+       u8 pp;
+       u8 gb[0];
+} __packed;
+
+struct digital_psl_req {
+       u8 dir;
+       u8 cmd;
+       u8 did;
+       u8 brs;
+       u8 fsl;
+} __packed;
+
+struct digital_psl_res {
+       u8 dir;
+       u8 cmd;
+       u8 did;
+} __packed;
+
+struct digital_dep_req_res {
+       u8 dir;
+       u8 cmd;
+       u8 pfb;
+} __packed;
+
+static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp);
+
+static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev,
+                                    struct sk_buff *skb)
+{
+       skb_push(skb, sizeof(u8));
+
+       skb->data[0] = skb->len;
+
+       if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A)
+               *skb_push(skb, sizeof(u8)) = DIGITAL_NFC_DEP_NFCA_SOD_SB;
+}
+
+static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev,
+                                   struct sk_buff *skb)
+{
+       u8 size;
+
+       if (skb->len < 2)
+               return -EIO;
+
+       if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A)
+               skb_pull(skb, sizeof(u8));
+
+       size = skb->data[0];
+       if (size != skb->len)
+               return -EIO;
+
+       skb_pull(skb, sizeof(u8));
+
+       return 0;
+}
+
+static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
+                                struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       struct digital_atr_res *atr_res;
+       u8 gb_len;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       if (resp->len < sizeof(struct digital_atr_res)) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       gb_len = resp->len - sizeof(struct digital_atr_res);
+
+       atr_res = (struct digital_atr_res *)resp->data;
+
+       rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len);
+       if (rc)
+               goto exit;
+
+       rc = nfc_dep_link_is_up(ddev->nfc_dev, target->idx, NFC_COMM_ACTIVE,
+                               NFC_RF_INITIATOR);
+
+       ddev->curr_nfc_dep_pni = 0;
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc)
+               ddev->curr_protocol = 0;
+}
+
+int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, __u8 comm_mode, __u8 *gb,
+                           size_t gb_len)
+{
+       struct sk_buff *skb;
+       struct digital_atr_req *atr_req;
+       uint size;
+
+       size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len;
+
+       if (size > DIGITAL_ATR_REQ_MAX_SIZE) {
+               PROTOCOL_ERR("14.6.1.1");
+               return -EINVAL;
+       }
+
+       skb = digital_skb_alloc(ddev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_atr_req));
+
+       atr_req = (struct digital_atr_req *)skb->data;
+       memset(atr_req, 0, sizeof(struct digital_atr_req));
+
+       atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       atr_req->cmd = DIGITAL_CMD_ATR_REQ;
+       if (target->nfcid2_len)
+               memcpy(atr_req->nfcid3, target->nfcid2,
+                      max(target->nfcid2_len, DIGITAL_NFCID3_LEN));
+       else
+               get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN);
+
+       atr_req->did = 0;
+       atr_req->bs = 0;
+       atr_req->br = 0;
+
+       atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+
+       if (gb_len) {
+               atr_req->pp |= DIGITAL_GB_BIT;
+               memcpy(skb_put(skb, gb_len), gb, gb_len);
+       }
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target);
+
+       return 0;
+}
+
+static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
+                               struct digital_data_exch *data_exch, u8 rtox)
+{
+       struct digital_dep_req_res *dep_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = rtox;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU |
+                      DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                data_exch);
+
+       return rc;
+}
+
+static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       struct digital_data_exch *data_exch = arg;
+       struct digital_dep_req_res *dep_res;
+       u8 pfb;
+       uint size;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto error;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       dep_res = (struct digital_dep_req_res *)resp->data;
+
+       if (resp->len < sizeof(struct digital_dep_req_res) ||
+           dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
+           dep_res->cmd != DIGITAL_CMD_DEP_RES) {
+               rc = -EIO;
+               goto error;
+       }
+
+       pfb = dep_res->pfb;
+
+       switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
+       case DIGITAL_NFC_DEP_PFB_I_PDU:
+               if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
+                       PROTOCOL_ERR("14.12.3.3");
+                       rc = -EIO;
+                       goto error;
+               }
+
+               ddev->curr_nfc_dep_pni =
+                       DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+               rc = 0;
+               break;
+
+       case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+               pr_err("Received a ACK/NACK PDU\n");
+               rc = -EIO;
+               goto error;
+
+       case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+               if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
+                       rc = -EINVAL;
+                       goto error;
+               }
+
+               rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]);
+               if (rc)
+                       goto error;
+
+               kfree_skb(resp);
+               return;
+       }
+
+       if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
+               pr_err("MI bit set. Chained PDU not supported\n");
+               rc = -EIO;
+               goto error;
+       }
+
+       size = sizeof(struct digital_dep_req_res);
+
+       if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb))
+               size++;
+
+       if (size > resp->len) {
+               rc = -EIO;
+               goto error;
+       }
+
+       skb_pull(resp, size);
+
+exit:
+       data_exch->cb(data_exch->cb_context, resp, rc);
+
+error:
+       kfree(data_exch);
+
+       if (rc)
+               kfree_skb(resp);
+}
+
+int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
+                           struct nfc_target *target, struct sk_buff *skb,
+                           struct digital_data_exch *data_exch)
+{
+       struct digital_dep_req_res *dep_req;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+
+       dep_req = (struct digital_dep_req_res *)skb->data;
+       dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+       dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+       dep_req->pfb = ddev->curr_nfc_dep_pni;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+                                  data_exch);
+}
+
+static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       int rc;
+       struct digital_dep_req_res *dep_req;
+       size_t size;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       size = sizeof(struct digital_dep_req_res);
+       dep_req = (struct digital_dep_req_res *)resp->data;
+
+       if (resp->len < size || dep_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
+           dep_req->cmd != DIGITAL_CMD_DEP_REQ) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb))
+               size++;
+
+       if (resp->len < size) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
+       case DIGITAL_NFC_DEP_PFB_I_PDU:
+               pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
+               ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb);
+               break;
+       case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+               pr_err("Received a ACK/NACK PDU\n");
+               rc = -EINVAL;
+               goto exit;
+               break;
+       case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+               pr_err("Received a SUPERVISOR PDU\n");
+               rc = -EINVAL;
+               goto exit;
+               break;
+       }
+
+       skb_pull(resp, size);
+
+       rc = nfc_tm_data_received(ddev->nfc_dev, resp);
+
+exit:
+       if (rc)
+               kfree_skb(resp);
+}
+
+int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
+{
+       struct digital_dep_req_res *dep_res;
+
+       skb_push(skb, sizeof(struct digital_dep_req_res));
+       dep_res = (struct digital_dep_req_res *)skb->data;
+
+       dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       dep_res->cmd = DIGITAL_CMD_DEP_RES;
+       dep_res->pfb = ddev->curr_nfc_dep_pni;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+                                  NULL);
+}
+
+static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
+                                            void *arg, struct sk_buff *resp)
+{
+       u8 rf_tech = PTR_ERR(arg);
+
+       if (IS_ERR(resp))
+               return;
+
+       digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+
+       digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did,
+                                  u8 rf_tech)
+{
+       struct digital_psl_res *psl_res;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_psl_res));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_psl_res));
+
+       psl_res = (struct digital_psl_res *)skb->data;
+
+       psl_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       psl_res->cmd = DIGITAL_CMD_PSL_RES;
+       psl_res->did = did;
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
+                                ERR_PTR(rf_tech));
+
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       int rc;
+       struct digital_psl_req *psl_req;
+       u8 rf_tech;
+       u8 dsi;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       psl_req = (struct digital_psl_req *)resp->data;
+
+       if (resp->len != sizeof(struct digital_psl_req) ||
+           psl_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
+           psl_req->cmd != DIGITAL_CMD_PSL_REQ) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       dsi = (psl_req->brs >> 3) & 0x07;
+       switch (dsi) {
+       case 0:
+               rf_tech = NFC_DIGITAL_RF_TECH_106A;
+               break;
+       case 1:
+               rf_tech = NFC_DIGITAL_RF_TECH_212F;
+               break;
+       case 2:
+               rf_tech = NFC_DIGITAL_RF_TECH_424F;
+               break;
+       default:
+               pr_err("Unsuported dsi value %d\n", dsi);
+               goto exit;
+       }
+
+       rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech);
+
+exit:
+       kfree_skb(resp);
+}
+
+static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev,
+                                            void *arg, struct sk_buff *resp)
+{
+       int offset;
+
+       if (IS_ERR(resp)) {
+               digital_poll_next_tech(ddev);
+               return;
+       }
+
+       offset = 2;
+       if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB)
+               offset++;
+
+       if (resp->data[offset] == DIGITAL_CMD_PSL_REQ)
+               digital_tg_recv_psl_req(ddev, arg, resp);
+       else
+               digital_tg_recv_dep_req(ddev, arg, resp);
+}
+
+static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
+                                  struct digital_atr_req *atr_req)
+{
+       struct digital_atr_res *atr_res;
+       struct sk_buff *skb;
+       u8 *gb;
+       size_t gb_len;
+       int rc;
+
+       gb = nfc_get_local_general_bytes(ddev->nfc_dev, &gb_len);
+       if (!gb)
+               gb_len = 0;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_atr_res) + gb_len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_atr_res));
+       atr_res = (struct digital_atr_res *)skb->data;
+
+       memset(atr_res, 0, sizeof(struct digital_atr_res));
+
+       atr_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+       atr_res->cmd = DIGITAL_CMD_ATR_RES;
+       memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3));
+       atr_res->to = 8;
+       atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+       if (gb_len) {
+               skb_put(skb, gb_len);
+
+               atr_res->pp |= DIGITAL_GB_BIT;
+               memcpy(atr_res->gb, gb, gb_len);
+       }
+
+       digital_skb_push_dep_sod(ddev, skb);
+
+       ddev->skb_add_crc(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 999,
+                                digital_tg_send_atr_res_complete, NULL);
+       if (rc) {
+               kfree_skb(skb);
+               return rc;
+       }
+
+       return rc;
+}
+
+void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
+                            struct sk_buff *resp)
+{
+       int rc;
+       struct digital_atr_req *atr_req;
+       size_t gb_len, min_size;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!resp->len) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) {
+               min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2;
+
+               ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_106A;
+               ddev->skb_add_crc = digital_skb_add_crc_a;
+               ddev->skb_check_crc = digital_skb_check_crc_a;
+       } else {
+               min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1;
+
+               ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_212F;
+               ddev->skb_add_crc = digital_skb_add_crc_f;
+               ddev->skb_check_crc = digital_skb_check_crc_f;
+       }
+
+       if (resp->len < min_size) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
+               ddev->skb_add_crc = digital_skb_add_crc_none;
+               ddev->skb_check_crc = digital_skb_check_crc_none;
+       }
+
+       rc = ddev->skb_check_crc(resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.6");
+               goto exit;
+       }
+
+       rc = digital_skb_pull_dep_sod(ddev, resp);
+       if (rc) {
+               PROTOCOL_ERR("14.4.1.2");
+               goto exit;
+       }
+
+       atr_req = (struct digital_atr_req *)resp->data;
+
+       if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
+           atr_req->cmd != DIGITAL_CMD_ATR_REQ) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED);
+       if (rc)
+               goto exit;
+
+       rc = digital_tg_send_atr_res(ddev, atr_req);
+       if (rc)
+               goto exit;
+
+       gb_len = resp->len - sizeof(struct digital_atr_req);
+       rc = nfc_tm_activated(ddev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+                             NFC_COMM_PASSIVE, atr_req->gb, gb_len);
+       if (rc)
+               goto exit;
+
+       ddev->poll_tech_count = 0;
+
+       rc = 0;
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c
new file mode 100644 (file)
index 0000000..251c8c7
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+ * NFC Digital Protocol stack
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) "digital: %s: " fmt, __func__
+
+#include "digital.h"
+
+#define DIGITAL_CMD_SENS_REQ    0x26
+#define DIGITAL_CMD_ALL_REQ     0x52
+#define DIGITAL_CMD_SEL_REQ_CL1 0x93
+#define DIGITAL_CMD_SEL_REQ_CL2 0x95
+#define DIGITAL_CMD_SEL_REQ_CL3 0x97
+
+#define DIGITAL_SDD_REQ_SEL_PAR 0x20
+
+#define DIGITAL_SDD_RES_CT  0x88
+#define DIGITAL_SDD_RES_LEN 5
+
+#define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04))
+#define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60))
+#define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40)
+
+#define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00)
+#define DIGITAL_SENS_RES_IS_VALID(sens_res) \
+       ((!((sens_res) & 0x001F) && (((sens_res) & 0x0C00) == 0x0C00)) || \
+       (((sens_res) & 0x001F) && ((sens_res) & 0x0C00) != 0x0C00))
+
+#define DIGITAL_MIFARE_READ_RES_LEN 16
+#define DIGITAL_MIFARE_ACK_RES 0x0A
+
+#define DIGITAL_CMD_SENSF_REQ  0x00
+#define DIGITAL_CMD_SENSF_RES  0x01
+
+#define DIGITAL_SENSF_RES_MIN_LENGTH 17
+#define DIGITAL_SENSF_RES_RD_AP_B1   0x00
+#define DIGITAL_SENSF_RES_RD_AP_B2   0x8F
+
+#define DIGITAL_SENSF_REQ_RC_NONE 0
+#define DIGITAL_SENSF_REQ_RC_SC   1
+#define DIGITAL_SENSF_REQ_RC_AP   2
+
+struct digital_sdd_res {
+       u8 nfcid1[4];
+       u8 bcc;
+} __packed;
+
+struct digital_sel_req {
+       u8 sel_cmd;
+       u8 b2;
+       u8 nfcid1[4];
+       u8 bcc;
+} __packed;
+
+struct digital_sensf_req {
+       u8 cmd;
+       u8 sc1;
+       u8 sc2;
+       u8 rc;
+       u8 tsn;
+} __packed;
+
+struct digital_sensf_res {
+       u8 cmd;
+       u8 nfcid2[8];
+       u8 pad0[2];
+       u8 pad1[3];
+       u8 mrti_check;
+       u8 mrti_update;
+       u8 pad2;
+       u8 rd[2];
+} __packed;
+
+static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,
+                                  struct nfc_target *target);
+
+static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       int rc;
+       u8 sel_res;
+       u8 nfc_proto;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               rc = digital_skb_check_crc_a(resp);
+               if (rc) {
+                       PROTOCOL_ERR("4.4.1.3");
+                       goto exit;
+               }
+       }
+
+       if (!resp->len) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       sel_res = resp->data[0];
+
+       if (!DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res)) {
+               rc = digital_in_send_sdd_req(ddev, target);
+               if (rc)
+                       goto exit;
+
+               goto exit_free_skb;
+       }
+
+       if (DIGITAL_SEL_RES_IS_T2T(sel_res)) {
+               nfc_proto = NFC_PROTO_MIFARE;
+       } else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) {
+               nfc_proto = NFC_PROTO_NFC_DEP;
+       } else {
+               rc = -EOPNOTSUPP;
+               goto exit;
+       }
+
+       target->sel_res = sel_res;
+
+       rc = digital_target_found(ddev, target, nfc_proto);
+
+exit:
+       kfree(target);
+
+exit_free_skb:
+       dev_kfree_skb(resp);
+
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+static int digital_in_send_sel_req(struct nfc_digital_dev *ddev,
+                                  struct nfc_target *target,
+                                  struct digital_sdd_res *sdd_res)
+{
+       struct sk_buff *skb;
+       struct digital_sel_req *sel_req;
+       u8 sel_cmd;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_sel_req));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_sel_req));
+       sel_req = (struct digital_sel_req *)skb->data;
+
+       if (target->nfcid1_len <= 4)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL1;
+       else if (target->nfcid1_len < 10)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL2;
+       else
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL3;
+
+       sel_req->sel_cmd = sel_cmd;
+       sel_req->b2 = 0x70;
+       memcpy(sel_req->nfcid1, sdd_res->nfcid1, 4);
+       sel_req->bcc = sdd_res->bcc;
+
+       if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                               NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A);
+               if (rc)
+                       goto exit;
+       } else {
+               digital_skb_add_crc_a(skb);
+       }
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sel_res,
+                                target);
+exit:
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_in_recv_sdd_res(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       struct digital_sdd_res *sdd_res;
+       int rc;
+       u8 offset, size;
+       u8 i, bcc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < DIGITAL_SDD_RES_LEN) {
+               PROTOCOL_ERR("4.7.2.8");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       sdd_res = (struct digital_sdd_res *)resp->data;
+
+       for (i = 0, bcc = 0; i < 4; i++)
+               bcc ^= sdd_res->nfcid1[i];
+
+       if (bcc != sdd_res->bcc) {
+               PROTOCOL_ERR("4.7.2.6");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       if (sdd_res->nfcid1[0] == DIGITAL_SDD_RES_CT) {
+               offset = 1;
+               size = 3;
+       } else {
+               offset = 0;
+               size = 4;
+       }
+
+       memcpy(target->nfcid1 + target->nfcid1_len, sdd_res->nfcid1 + offset,
+              size);
+       target->nfcid1_len += size;
+
+       rc = digital_in_send_sel_req(ddev, target, sdd_res);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc) {
+               kfree(target);
+               digital_poll_next_tech(ddev);
+       }
+}
+
+static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev,
+                                  struct nfc_target *target)
+{
+       int rc;
+       struct sk_buff *skb;
+       u8 sel_cmd;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_STANDARD);
+       if (rc)
+               return rc;
+
+       skb = digital_skb_alloc(ddev, 2);
+       if (!skb)
+               return -ENOMEM;
+
+       if (target->nfcid1_len == 0)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL1;
+       else if (target->nfcid1_len == 3)
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL2;
+       else
+               sel_cmd = DIGITAL_CMD_SEL_REQ_CL3;
+
+       *skb_put(skb, sizeof(u8)) = sel_cmd;
+       *skb_put(skb, sizeof(u8)) = DIGITAL_SDD_REQ_SEL_PAR;
+
+       return digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sdd_res,
+                                  target);
+}
+
+static void digital_in_recv_sens_res(struct nfc_digital_dev *ddev, void *arg,
+                                    struct sk_buff *resp)
+{
+       struct nfc_target *target = NULL;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < sizeof(u16)) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL);
+       if (!target) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       target->sens_res = __le16_to_cpu(*(__le16 *)resp->data);
+
+       if (!DIGITAL_SENS_RES_IS_VALID(target->sens_res)) {
+               PROTOCOL_ERR("4.6.3.3");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       if (DIGITAL_SENS_RES_IS_T1T(target->sens_res))
+               rc = digital_target_found(ddev, target, NFC_PROTO_JEWEL);
+       else
+               rc = digital_in_send_sdd_req(ddev, target);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc) {
+               kfree(target);
+               digital_poll_next_tech(ddev);
+       }
+}
+
+int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct sk_buff *skb;
+       int rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+                                    NFC_DIGITAL_RF_TECH_106A);
+       if (rc)
+               return rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_SHORT);
+       if (rc)
+               return rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, sizeof(u8)) = DIGITAL_CMD_SENS_REQ;
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sens_res, NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+int digital_in_recv_mifare_res(struct sk_buff *resp)
+{
+       /* Successful READ command response is 16 data bytes + 2 CRC bytes long.
+        * Since the driver can't differentiate a ACK/NACK response from a valid
+        * READ response, the CRC calculation must be handled at digital level
+        * even if the driver supports it for this technology.
+        */
+       if (resp->len == DIGITAL_MIFARE_READ_RES_LEN + DIGITAL_CRC_LEN) {
+               if (digital_skb_check_crc_a(resp)) {
+                       PROTOCOL_ERR("9.4.1.2");
+                       return -EIO;
+               }
+
+               return 0;
+       }
+
+       /* ACK response (i.e. successful WRITE). */
+       if (resp->len == 1 && resp->data[0] == DIGITAL_MIFARE_ACK_RES) {
+               resp->data[0] = 0;
+               return 0;
+       }
+
+       /* NACK and any other responses are treated as error. */
+       return -EIO;
+}
+
+static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg,
+                                  struct sk_buff *resp)
+{
+       int rc;
+       u8 proto;
+       struct nfc_target target;
+       struct digital_sensf_res *sensf_res;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < DIGITAL_SENSF_RES_MIN_LENGTH) {
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
+               rc = digital_skb_check_crc_f(resp);
+               if (rc) {
+                       PROTOCOL_ERR("6.4.1.8");
+                       goto exit;
+               }
+       }
+
+       skb_pull(resp, 1);
+
+       memset(&target, 0, sizeof(struct nfc_target));
+
+       sensf_res = (struct digital_sensf_res *)resp->data;
+
+       memcpy(target.sensf_res, sensf_res, resp->len);
+       target.sensf_res_len = resp->len;
+
+       memcpy(target.nfcid2, sensf_res->nfcid2, NFC_NFCID2_MAXSIZE);
+       target.nfcid2_len = NFC_NFCID2_MAXSIZE;
+
+       if (target.nfcid2[0] == DIGITAL_SENSF_NFCID2_NFC_DEP_B1 &&
+           target.nfcid2[1] == DIGITAL_SENSF_NFCID2_NFC_DEP_B2)
+               proto = NFC_PROTO_NFC_DEP;
+       else
+               proto = NFC_PROTO_FELICA;
+
+       rc = digital_target_found(ddev, &target, proto);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct digital_sensf_req *sensf_req;
+       struct sk_buff *skb;
+       int rc;
+       u8 size;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+       if (rc)
+               return rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCF);
+       if (rc)
+               return rc;
+
+       size = sizeof(struct digital_sensf_req);
+
+       skb = digital_skb_alloc(ddev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, size);
+
+       sensf_req = (struct digital_sensf_req *)skb->data;
+       sensf_req->cmd = DIGITAL_CMD_SENSF_REQ;
+       sensf_req->sc1 = 0xFF;
+       sensf_req->sc2 = 0xFF;
+       sensf_req->rc = 0;
+       sensf_req->tsn = 0;
+
+       *skb_push(skb, 1) = size + 1;
+
+       if (!DIGITAL_DRV_CAPS_IN_CRC(ddev))
+               digital_skb_add_crc_f(skb);
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensf_res,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static int digital_tg_send_sel_res(struct nfc_digital_dev *ddev)
+{
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 1);
+       if (!skb)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = DIGITAL_SEL_RES_NFC_DEP;
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev))
+               digital_skb_add_crc_a(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_atr_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_tg_recv_sel_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
+               rc = digital_skb_check_crc_a(resp);
+               if (rc) {
+                       PROTOCOL_ERR("4.4.1.3");
+                       goto exit;
+               }
+       }
+
+       /* Silently ignore SEL_REQ content and send a SEL_RES for NFC-DEP */
+
+       rc = digital_tg_send_sel_res(ddev);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_sdd_res(struct nfc_digital_dev *ddev)
+{
+       struct sk_buff *skb;
+       struct digital_sdd_res *sdd_res;
+       int rc, i;
+
+       skb = digital_skb_alloc(ddev, sizeof(struct digital_sdd_res));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(struct digital_sdd_res));
+       sdd_res = (struct digital_sdd_res *)skb->data;
+
+       sdd_res->nfcid1[0] = 0x08;
+       get_random_bytes(sdd_res->nfcid1 + 1, 3);
+
+       sdd_res->bcc = 0;
+       for (i = 0; i < 4; i++)
+               sdd_res->bcc ^= sdd_res->nfcid1[i];
+
+       rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sel_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_tg_recv_sdd_req(struct nfc_digital_dev *ddev, void *arg,
+                                   struct sk_buff *resp)
+{
+       u8 *sdd_req;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       sdd_req = resp->data;
+
+       if (resp->len < 2 || sdd_req[0] != DIGITAL_CMD_SEL_REQ_CL1 ||
+           sdd_req[1] != DIGITAL_SDD_REQ_SEL_PAR) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_send_sdd_res(ddev);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_sens_res(struct nfc_digital_dev *ddev)
+{
+       struct sk_buff *skb;
+       u8 *sens_res;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, 2);
+       if (!skb)
+               return -ENOMEM;
+
+       sens_res = skb_put(skb, 2);
+
+       sens_res[0] = (DIGITAL_SENS_RES_NFC_DEP >> 8) & 0xFF;
+       sens_res[1] = DIGITAL_SENS_RES_NFC_DEP & 0xFF;
+
+       rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sdd_req,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg,
+                             struct sk_buff *resp)
+{
+       u8 sens_req;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       sens_req = resp->data[0];
+
+       if (!resp->len || (sens_req != DIGITAL_CMD_SENS_REQ &&
+           sens_req != DIGITAL_CMD_ALL_REQ)) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_send_sens_res(ddev);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
+                             struct digital_sensf_req *sensf_req)
+{
+       struct sk_buff *skb;
+       u8 size;
+       int rc;
+       struct digital_sensf_res *sensf_res;
+
+       size = sizeof(struct digital_sensf_res);
+
+       if (sensf_req->rc != DIGITAL_SENSF_REQ_RC_NONE)
+               size -= sizeof(sensf_res->rd);
+
+       skb = digital_skb_alloc(ddev, size);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, size);
+
+       sensf_res = (struct digital_sensf_res *)skb->data;
+
+       memset(sensf_res, 0, size);
+
+       sensf_res->cmd = DIGITAL_CMD_SENSF_RES;
+       sensf_res->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+       sensf_res->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+       get_random_bytes(&sensf_res->nfcid2[2], 6);
+
+       switch (sensf_req->rc) {
+       case DIGITAL_SENSF_REQ_RC_SC:
+               sensf_res->rd[0] = sensf_req->sc1;
+               sensf_res->rd[1] = sensf_req->sc2;
+               break;
+       case DIGITAL_SENSF_REQ_RC_AP:
+               sensf_res->rd[0] = DIGITAL_SENSF_RES_RD_AP_B1;
+               sensf_res->rd[1] = DIGITAL_SENSF_RES_RD_AP_B2;
+               break;
+       }
+
+       *skb_push(skb, sizeof(u8)) = size + 1;
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev))
+               digital_skb_add_crc_f(skb);
+
+       rc = digital_tg_send_cmd(ddev, skb, 300,
+                                digital_tg_recv_atr_req, NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg,
+                              struct sk_buff *resp)
+{
+       struct digital_sensf_req *sensf_req;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
+               rc = digital_skb_check_crc_f(resp);
+               if (rc) {
+                       PROTOCOL_ERR("6.4.1.8");
+                       goto exit;
+               }
+       }
+
+       if (resp->len != sizeof(struct digital_sensf_req) + 1) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       skb_pull(resp, 1);
+       sensf_req = (struct digital_sensf_req *)resp->data;
+
+       if (sensf_req->cmd != DIGITAL_CMD_SENSF_REQ) {
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       rc = digital_tg_send_sensf_res(ddev, sensf_req);
+
+exit:
+       if (rc)
+               digital_poll_next_tech(ddev);
+
+       dev_kfree_skb(resp);
+}
+
+int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       int rc;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+       if (rc)
+               return rc;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCA_NFC_DEP);
+       if (rc)
+               return rc;
+
+       return digital_tg_listen(ddev, 300, digital_tg_recv_sens_req, NULL);
+}
+
+int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       int rc;
+       u8 *nfcid2;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
+       if (rc)
+               return rc;
+
+       rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCF_NFC_DEP);
+       if (rc)
+               return rc;
+
+       nfcid2 = kzalloc(NFC_NFCID2_MAXSIZE, GFP_KERNEL);
+       if (!nfcid2)
+               return -ENOMEM;
+
+       nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1;
+       nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2;
+       get_random_bytes(nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2);
+
+       return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, nfcid2);
+}
index c7cf37b..f1d426f 100644 (file)
 #include <linux/export.h>
 #include <linux/spi/spi.h>
 #include <linux/crc-ccitt.h>
-#include <linux/nfc.h>
 #include <net/nfc/nci_core.h>
 
-#define NCI_SPI_HDR_LEN                        4
-#define NCI_SPI_CRC_LEN                        2
 #define NCI_SPI_ACK_SHIFT              6
 #define NCI_SPI_MSB_PAYLOAD_MASK       0x3F
 
 
 #define CRC_INIT               0xFFFF
 
-static int nci_spi_open(struct nci_dev *nci_dev)
-{
-       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
-
-       return ndev->ops->open(ndev);
-}
-
-static int nci_spi_close(struct nci_dev *nci_dev)
-{
-       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
-
-       return ndev->ops->close(ndev);
-}
-
-static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb)
+static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb,
+                         int cs_change)
 {
        struct spi_message m;
        struct spi_transfer t;
 
-       t.tx_buf = skb->data;
-       t.len = skb->len;
-       t.cs_change = 0;
-       t.delay_usecs = ndev->xfer_udelay;
+       memset(&t, 0, sizeof(struct spi_transfer));
+       /* a NULL skb means we just want the SPI chip select line to raise */
+       if (skb) {
+               t.tx_buf = skb->data;
+               t.len = skb->len;
+       } else {
+               /* still set tx_buf non NULL to make the driver happy */
+               t.tx_buf = &t;
+               t.len = 0;
+       }
+       t.cs_change = cs_change;
+       t.delay_usecs = nspi->xfer_udelay;
 
        spi_message_init(&m);
        spi_message_add_tail(&t, &m);
 
-       return spi_sync(ndev->spi, &m);
+       return spi_sync(nspi->spi, &m);
 }
 
-static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
+int nci_spi_send(struct nci_spi *nspi,
+                struct completion *write_handshake_completion,
+                struct sk_buff *skb)
 {
-       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
        unsigned int payload_len = skb->len;
        unsigned char *hdr;
        int ret;
        long completion_rc;
 
-       ndev->ops->deassert_int(ndev);
-
        /* add the NCI SPI header to the start of the buffer */
        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
        hdr[0] = NCI_SPI_DIRECT_WRITE;
-       hdr[1] = ndev->acknowledge_mode;
+       hdr[1] = nspi->acknowledge_mode;
        hdr[2] = payload_len >> 8;
        hdr[3] = payload_len & 0xFF;
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
                u16 crc;
 
                crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
@@ -96,123 +87,77 @@ static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
                *skb_put(skb, 1) = crc & 0xFF;
        }
 
-       ret = __nci_spi_send(ndev, skb);
+       if (write_handshake_completion) {
+               /* Trick SPI driver to raise chip select */
+               ret = __nci_spi_send(nspi, NULL, 1);
+               if (ret)
+                       goto done;
 
-       kfree_skb(skb);
-       ndev->ops->assert_int(ndev);
+               /* wait for NFC chip hardware handshake to complete */
+               if (wait_for_completion_timeout(write_handshake_completion,
+                                               msecs_to_jiffies(1000)) == 0) {
+                       ret = -ETIME;
+                       goto done;
+               }
+       }
 
-       if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
+       ret = __nci_spi_send(nspi, skb, 0);
+       if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED)
                goto done;
 
-       init_completion(&ndev->req_completion);
-       completion_rc =
-               wait_for_completion_interruptible_timeout(&ndev->req_completion,
-                                                         NCI_SPI_SEND_TIMEOUT);
+       init_completion(&nspi->req_completion);
+       completion_rc = wait_for_completion_interruptible_timeout(
+                                                       &nspi->req_completion,
+                                                       NCI_SPI_SEND_TIMEOUT);
 
-       if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK)
+       if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK)
                ret = -EIO;
 
 done:
+       kfree_skb(skb);
+
        return ret;
 }
-
-static struct nci_ops nci_spi_ops = {
-       .open = nci_spi_open,
-       .close = nci_spi_close,
-       .send = nci_spi_send,
-};
+EXPORT_SYMBOL_GPL(nci_spi_send);
 
 /* ---- Interface to NCI SPI drivers ---- */
 
 /**
- * nci_spi_allocate_device - allocate a new nci spi device
+ * nci_spi_allocate_spi - allocate a new nci spi
  *
  * @spi: SPI device
- * @ops: device operations
- * @supported_protocols: NFC protocols supported by the device
- * @supported_se: NFC Secure Elements supported by the device
- * @acknowledge_mode: Acknowledge mode used by the device
+ * @acknowledge_mode: Acknowledge mode used by the NFC device
  * @delay: delay between transactions in us
+ * @ndev: nci dev to send incoming nci frames to
  */
-struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
-                                               struct nci_spi_ops *ops,
-                                               u32 supported_protocols,
-                                               u32 supported_se,
-                                               u8 acknowledge_mode,
-                                               unsigned int delay)
+struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi,
+                                    u8 acknowledge_mode, unsigned int delay,
+                                    struct nci_dev *ndev)
 {
-       struct nci_spi_dev *ndev;
-       int tailroom = 0;
+       struct nci_spi *nspi;
 
-       if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
+       nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL);
+       if (!nspi)
                return NULL;
 
-       if (!supported_protocols)
-               return NULL;
-
-       ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL);
-       if (!ndev)
-               return NULL;
+       nspi->acknowledge_mode = acknowledge_mode;
+       nspi->xfer_udelay = delay;
 
-       ndev->ops = ops;
-       ndev->acknowledge_mode = acknowledge_mode;
-       ndev->xfer_udelay = delay;
+       nspi->spi = spi;
+       nspi->ndev = ndev;
 
-       if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
-               tailroom += NCI_SPI_CRC_LEN;
-
-       ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols,
-                                           NCI_SPI_HDR_LEN, tailroom);
-       if (!ndev->nci_dev)
-               return NULL;
-
-       nci_set_drvdata(ndev->nci_dev, ndev);
-
-       return ndev;
+       return nspi;
 }
-EXPORT_SYMBOL_GPL(nci_spi_allocate_device);
+EXPORT_SYMBOL_GPL(nci_spi_allocate_spi);
 
-/**
- * nci_spi_free_device - deallocate nci spi device
- *
- * @ndev: The nci spi device to deallocate
- */
-void nci_spi_free_device(struct nci_spi_dev *ndev)
-{
-       nci_free_device(ndev->nci_dev);
-}
-EXPORT_SYMBOL_GPL(nci_spi_free_device);
-
-/**
- * nci_spi_register_device - register a nci spi device in the nfc subsystem
- *
- * @pdev: The nci spi device to register
- */
-int nci_spi_register_device(struct nci_spi_dev *ndev)
-{
-       return nci_register_device(ndev->nci_dev);
-}
-EXPORT_SYMBOL_GPL(nci_spi_register_device);
-
-/**
- * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
- *
- * @dev: The nci spi device to unregister
- */
-void nci_spi_unregister_device(struct nci_spi_dev *ndev)
-{
-       nci_unregister_device(ndev->nci_dev);
-}
-EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
-
-static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
+static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge)
 {
        struct sk_buff *skb;
        unsigned char *hdr;
        u16 crc;
        int ret;
 
-       skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
+       skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL);
 
        /* add the NCI SPI header to the start of the buffer */
        hdr = skb_push(skb, NCI_SPI_HDR_LEN);
@@ -225,14 +170,14 @@ static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
        *skb_put(skb, 1) = crc >> 8;
        *skb_put(skb, 1) = crc & 0xFF;
 
-       ret = __nci_spi_send(ndev, skb);
+       ret = __nci_spi_send(nspi, skb, 0);
 
        kfree_skb(skb);
 
        return ret;
 }
 
-static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
+static struct sk_buff *__nci_spi_read(struct nci_spi *nspi)
 {
        struct sk_buff *skb;
        struct spi_message m;
@@ -242,43 +187,49 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
        int ret;
 
        spi_message_init(&m);
+
+       memset(&tx, 0, sizeof(struct spi_transfer));
        req[0] = NCI_SPI_DIRECT_READ;
-       req[1] = ndev->acknowledge_mode;
+       req[1] = nspi->acknowledge_mode;
        tx.tx_buf = req;
        tx.len = 2;
        tx.cs_change = 0;
        spi_message_add_tail(&tx, &m);
+
+       memset(&rx, 0, sizeof(struct spi_transfer));
        rx.rx_buf = resp_hdr;
        rx.len = 2;
        rx.cs_change = 1;
        spi_message_add_tail(&rx, &m);
-       ret = spi_sync(ndev->spi, &m);
 
+       ret = spi_sync(nspi->spi, &m);
        if (ret)
                return NULL;
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
                rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
                                resp_hdr[1] + NCI_SPI_CRC_LEN;
        else
                rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
 
-       skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
+       skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL);
        if (!skb)
                return NULL;
 
        spi_message_init(&m);
+
+       memset(&rx, 0, sizeof(struct spi_transfer));
        rx.rx_buf = skb_put(skb, rx_len);
        rx.len = rx_len;
        rx.cs_change = 0;
-       rx.delay_usecs = ndev->xfer_udelay;
+       rx.delay_usecs = nspi->xfer_udelay;
        spi_message_add_tail(&rx, &m);
-       ret = spi_sync(ndev->spi, &m);
 
+       ret = spi_sync(nspi->spi, &m);
        if (ret)
                goto receive_error;
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
                *skb_push(skb, 1) = resp_hdr[1];
                *skb_push(skb, 1) = resp_hdr[0];
        }
@@ -318,61 +269,53 @@ static u8 nci_spi_get_ack(struct sk_buff *skb)
 }
 
 /**
- * nci_spi_recv_frame - receive frame from NCI SPI drivers
+ * nci_spi_read - read frame from NCI SPI drivers
  *
- * @ndev: The nci spi device
+ * @nspi: The nci spi
  * Context: can sleep
  *
  * This call may only be used from a context that may sleep.  The sleep
  * is non-interruptible, and has no timeout.
  *
- * It returns zero on success, else a negative error code.
+ * It returns an allocated skb containing the frame on success, or NULL.
  */
-int nci_spi_recv_frame(struct nci_spi_dev *ndev)
+struct sk_buff *nci_spi_read(struct nci_spi *nspi)
 {
        struct sk_buff *skb;
-       int ret = 0;
-
-       ndev->ops->deassert_int(ndev);
 
        /* Retrieve frame from SPI */
-       skb = __nci_spi_recv_frame(ndev);
-       if (!skb) {
-               ret = -EIO;
+       skb = __nci_spi_read(nspi);
+       if (!skb)
                goto done;
-       }
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
                if (!nci_spi_check_crc(skb)) {
-                       send_acknowledge(ndev, ACKNOWLEDGE_NACK);
+                       send_acknowledge(nspi, ACKNOWLEDGE_NACK);
                        goto done;
                }
 
                /* In case of acknowledged mode: if ACK or NACK received,
                 * unblock completion of latest frame sent.
                 */
-               ndev->req_result = nci_spi_get_ack(skb);
-               if (ndev->req_result)
-                       complete(&ndev->req_completion);
+               nspi->req_result = nci_spi_get_ack(skb);
+               if (nspi->req_result)
+                       complete(&nspi->req_completion);
        }
 
        /* If there is no payload (ACK/NACK only frame),
         * free the socket buffer
         */
-       if (skb->len == 0) {
+       if (!skb->len) {
                kfree_skb(skb);
+               skb = NULL;
                goto done;
        }
 
-       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
-               send_acknowledge(ndev, ACKNOWLEDGE_ACK);
-
-       /* Forward skb to NCI core layer */
-       ret = nci_recv_frame(ndev->nci_dev, skb);
+       if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               send_acknowledge(nspi, ACKNOWLEDGE_ACK);
 
 done:
-       ndev->ops->assert_int(ndev);
 
-       return ret;
+       return skb;
 }
-EXPORT_SYMBOL_GPL(nci_spi_recv_frame);
+EXPORT_SYMBOL_GPL(nci_spi_read);
index 68063b2..84b7e3e 100644 (file)
@@ -58,6 +58,7 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
        [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
                                     .len = NFC_FIRMWARE_NAME_MAXSIZE },
+       [NFC_ATTR_SE_APDU] = { .type = NLA_BINARY },
 };
 
 static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
@@ -1278,6 +1279,91 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
        return 0;
 }
 
+struct se_io_ctx {
+       u32 dev_idx;
+       u32 se_idx;
+};
+
+static void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err)
+{
+       struct se_io_ctx *ctx = context;
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               kfree(ctx);
+               return;
+       }
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_CMD_SE_IO);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, ctx->dev_idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, ctx->se_idx) ||
+           nla_put(msg, NFC_ATTR_SE_APDU, apdu_len, apdu))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       kfree(ctx);
+
+       return;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       kfree(ctx);
+
+       return;
+}
+
+static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       struct se_io_ctx *ctx;
+       u32 dev_idx, se_idx;
+       u8 *apdu;
+       size_t apdu_len;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_APDU])
+               return -EINVAL;
+
+       dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(dev_idx);
+       if (!dev)
+               return -ENODEV;
+
+       if (!dev->ops || !dev->ops->se_io)
+               return -ENOTSUPP;
+
+       apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]);
+       if (apdu_len == 0)
+               return -EINVAL;
+
+       apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]);
+       if (!apdu)
+               return -EINVAL;
+
+       ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->dev_idx = dev_idx;
+       ctx->se_idx = se_idx;
+
+       return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -1358,6 +1444,11 @@ static struct genl_ops nfc_genl_ops[] = {
                .done = nfc_genl_dump_ses_done,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_SE_IO,
+               .doit = nfc_genl_se_io,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index 313bf1b..cd958b3 100644 (file)
@@ -142,11 +142,11 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
 
        err = rawsock_add_header(skb);
        if (err)
-               goto error;
+               goto error_skb;
 
        err = sock_queue_rcv_skb(sk, skb);
        if (err)
-               goto error;
+               goto error_skb;
 
        spin_lock_bh(&sk->sk_write_queue.lock);
        if (!skb_queue_empty(&sk->sk_write_queue))
@@ -158,6 +158,9 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb,
        sock_put(sk);
        return;
 
+error_skb:
+       kfree_skb(skb);
+
 error:
        rawsock_report_error(sk, err);
        sock_put(sk);
index ea36e99..3591cb5 100644 (file)
@@ -9,6 +9,8 @@ openvswitch-y := \
        datapath.o \
        dp_notify.o \
        flow.o \
+       flow_netlink.o \
+       flow_table.o \
        vport.o \
        vport-internal_dev.o \
        vport-netdev.o
index 2aa13bd..1408adc 100644 (file)
 
 #include "datapath.h"
 #include "flow.h"
+#include "flow_netlink.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
-
-#define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
-static void rehash_flow_table(struct work_struct *work);
-static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table);
-
 int ovs_net_id __read_mostly;
 
 static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
@@ -165,7 +161,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
 
-       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false);
+       ovs_flow_tbl_destroy(&dp->table);
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
        kfree(dp->ports);
@@ -225,6 +221,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        struct dp_stats_percpu *stats;
        struct sw_flow_key key;
        u64 *stats_counter;
+       u32 n_mask_hit;
        int error;
 
        stats = this_cpu_ptr(dp->stats_percpu);
@@ -237,7 +234,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        }
 
        /* Look up flow. */
-       flow = ovs_flow_lookup(rcu_dereference(dp->table), &key);
+       flow = ovs_flow_tbl_lookup(&dp->table, &key, &n_mask_hit);
        if (unlikely(!flow)) {
                struct dp_upcall_info upcall;
 
@@ -262,6 +259,7 @@ out:
        /* Update datapath statistics. */
        u64_stats_update_begin(&stats->sync);
        (*stats_counter)++;
+       stats->n_mask_hit += n_mask_hit;
        u64_stats_update_end(&stats->sync);
 }
 
@@ -435,7 +433,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
        upcall->dp_ifindex = dp_ifindex;
 
        nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);
-       ovs_flow_to_nlattrs(upcall_info->key, upcall_info->key, user_skb);
+       ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb);
        nla_nest_end(user_skb, nla);
 
        if (upcall_info->userdata)
@@ -455,398 +453,6 @@ out:
        return err;
 }
 
-/* Called with ovs_mutex. */
-static int flush_flows(struct datapath *dp)
-{
-       struct flow_table *old_table;
-       struct flow_table *new_table;
-
-       old_table = ovsl_dereference(dp->table);
-       new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS);
-       if (!new_table)
-               return -ENOMEM;
-
-       rcu_assign_pointer(dp->table, new_table);
-
-       ovs_flow_tbl_destroy(old_table, true);
-       return 0;
-}
-
-static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, int attr_len)
-{
-
-       struct sw_flow_actions *acts;
-       int new_acts_size;
-       int req_size = NLA_ALIGN(attr_len);
-       int next_offset = offsetof(struct sw_flow_actions, actions) +
-                                       (*sfa)->actions_len;
-
-       if (req_size <= (ksize(*sfa) - next_offset))
-               goto out;
-
-       new_acts_size = ksize(*sfa) * 2;
-
-       if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
-               if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
-                       return ERR_PTR(-EMSGSIZE);
-               new_acts_size = MAX_ACTIONS_BUFSIZE;
-       }
-
-       acts = ovs_flow_actions_alloc(new_acts_size);
-       if (IS_ERR(acts))
-               return (void *)acts;
-
-       memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len);
-       acts->actions_len = (*sfa)->actions_len;
-       kfree(*sfa);
-       *sfa = acts;
-
-out:
-       (*sfa)->actions_len += req_size;
-       return  (struct nlattr *) ((unsigned char *)(*sfa) + next_offset);
-}
-
-static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len)
-{
-       struct nlattr *a;
-
-       a = reserve_sfa_size(sfa, nla_attr_size(len));
-       if (IS_ERR(a))
-               return PTR_ERR(a);
-
-       a->nla_type = attrtype;
-       a->nla_len = nla_attr_size(len);
-
-       if (data)
-               memcpy(nla_data(a), data, len);
-       memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len));
-
-       return 0;
-}
-
-static inline int add_nested_action_start(struct sw_flow_actions **sfa, int attrtype)
-{
-       int used = (*sfa)->actions_len;
-       int err;
-
-       err = add_action(sfa, attrtype, NULL, 0);
-       if (err)
-               return err;
-
-       return used;
-}
-
-static inline void add_nested_action_end(struct sw_flow_actions *sfa, int st_offset)
-{
-       struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + st_offset);
-
-       a->nla_len = sfa->actions_len - st_offset;
-}
-
-static int validate_and_copy_actions(const struct nlattr *attr,
-                                    const struct sw_flow_key *key, int depth,
-                                    struct sw_flow_actions **sfa);
-
-static int validate_and_copy_sample(const struct nlattr *attr,
-                                   const struct sw_flow_key *key, int depth,
-                                   struct sw_flow_actions **sfa)
-{
-       const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
-       const struct nlattr *probability, *actions;
-       const struct nlattr *a;
-       int rem, start, err, st_acts;
-
-       memset(attrs, 0, sizeof(attrs));
-       nla_for_each_nested(a, attr, rem) {
-               int type = nla_type(a);
-               if (!type || type > OVS_SAMPLE_ATTR_MAX || attrs[type])
-                       return -EINVAL;
-               attrs[type] = a;
-       }
-       if (rem)
-               return -EINVAL;
-
-       probability = attrs[OVS_SAMPLE_ATTR_PROBABILITY];
-       if (!probability || nla_len(probability) != sizeof(u32))
-               return -EINVAL;
-
-       actions = attrs[OVS_SAMPLE_ATTR_ACTIONS];
-       if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))
-               return -EINVAL;
-
-       /* validation done, copy sample action. */
-       start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE);
-       if (start < 0)
-               return start;
-       err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, nla_data(probability), sizeof(u32));
-       if (err)
-               return err;
-       st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS);
-       if (st_acts < 0)
-               return st_acts;
-
-       err = validate_and_copy_actions(actions, key, depth + 1, sfa);
-       if (err)
-               return err;
-
-       add_nested_action_end(*sfa, st_acts);
-       add_nested_action_end(*sfa, start);
-
-       return 0;
-}
-
-static int validate_tp_port(const struct sw_flow_key *flow_key)
-{
-       if (flow_key->eth.type == htons(ETH_P_IP)) {
-               if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
-                       return 0;
-       } else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
-               if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
-                       return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int validate_and_copy_set_tun(const struct nlattr *attr,
-                                    struct sw_flow_actions **sfa)
-{
-       struct sw_flow_match match;
-       struct sw_flow_key key;
-       int err, start;
-
-       ovs_match_init(&match, &key, NULL);
-       err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &match, false);
-       if (err)
-               return err;
-
-       start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET);
-       if (start < 0)
-               return start;
-
-       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
-                       sizeof(match.key->tun_key));
-       add_nested_action_end(*sfa, start);
-
-       return err;
-}
-
-static int validate_set(const struct nlattr *a,
-                       const struct sw_flow_key *flow_key,
-                       struct sw_flow_actions **sfa,
-                       bool *set_tun)
-{
-       const struct nlattr *ovs_key = nla_data(a);
-       int key_type = nla_type(ovs_key);
-
-       /* There can be only one key in a action */
-       if (nla_total_size(nla_len(ovs_key)) != nla_len(a))
-               return -EINVAL;
-
-       if (key_type > OVS_KEY_ATTR_MAX ||
-          (ovs_key_lens[key_type] != nla_len(ovs_key) &&
-           ovs_key_lens[key_type] != -1))
-               return -EINVAL;
-
-       switch (key_type) {
-       const struct ovs_key_ipv4 *ipv4_key;
-       const struct ovs_key_ipv6 *ipv6_key;
-       int err;
-
-       case OVS_KEY_ATTR_PRIORITY:
-       case OVS_KEY_ATTR_SKB_MARK:
-       case OVS_KEY_ATTR_ETHERNET:
-               break;
-
-       case OVS_KEY_ATTR_TUNNEL:
-               *set_tun = true;
-               err = validate_and_copy_set_tun(a, sfa);
-               if (err)
-                       return err;
-               break;
-
-       case OVS_KEY_ATTR_IPV4:
-               if (flow_key->eth.type != htons(ETH_P_IP))
-                       return -EINVAL;
-
-               if (!flow_key->ip.proto)
-                       return -EINVAL;
-
-               ipv4_key = nla_data(ovs_key);
-               if (ipv4_key->ipv4_proto != flow_key->ip.proto)
-                       return -EINVAL;
-
-               if (ipv4_key->ipv4_frag != flow_key->ip.frag)
-                       return -EINVAL;
-
-               break;
-
-       case OVS_KEY_ATTR_IPV6:
-               if (flow_key->eth.type != htons(ETH_P_IPV6))
-                       return -EINVAL;
-
-               if (!flow_key->ip.proto)
-                       return -EINVAL;
-
-               ipv6_key = nla_data(ovs_key);
-               if (ipv6_key->ipv6_proto != flow_key->ip.proto)
-                       return -EINVAL;
-
-               if (ipv6_key->ipv6_frag != flow_key->ip.frag)
-                       return -EINVAL;
-
-               if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000)
-                       return -EINVAL;
-
-               break;
-
-       case OVS_KEY_ATTR_TCP:
-               if (flow_key->ip.proto != IPPROTO_TCP)
-                       return -EINVAL;
-
-               return validate_tp_port(flow_key);
-
-       case OVS_KEY_ATTR_UDP:
-               if (flow_key->ip.proto != IPPROTO_UDP)
-                       return -EINVAL;
-
-               return validate_tp_port(flow_key);
-
-       case OVS_KEY_ATTR_SCTP:
-               if (flow_key->ip.proto != IPPROTO_SCTP)
-                       return -EINVAL;
-
-               return validate_tp_port(flow_key);
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int validate_userspace(const struct nlattr *attr)
-{
-       static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] =   {
-               [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 },
-               [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC },
-       };
-       struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1];
-       int error;
-
-       error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX,
-                                attr, userspace_policy);
-       if (error)
-               return error;
-
-       if (!a[OVS_USERSPACE_ATTR_PID] ||
-           !nla_get_u32(a[OVS_USERSPACE_ATTR_PID]))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int copy_action(const struct nlattr *from,
-                      struct sw_flow_actions **sfa)
-{
-       int totlen = NLA_ALIGN(from->nla_len);
-       struct nlattr *to;
-
-       to = reserve_sfa_size(sfa, from->nla_len);
-       if (IS_ERR(to))
-               return PTR_ERR(to);
-
-       memcpy(to, from, totlen);
-       return 0;
-}
-
-static int validate_and_copy_actions(const struct nlattr *attr,
-                                    const struct sw_flow_key *key,
-                                    int depth,
-                                    struct sw_flow_actions **sfa)
-{
-       const struct nlattr *a;
-       int rem, err;
-
-       if (depth >= SAMPLE_ACTION_DEPTH)
-               return -EOVERFLOW;
-
-       nla_for_each_nested(a, attr, rem) {
-               /* Expected argument lengths, (u32)-1 for variable length. */
-               static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
-                       [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
-                       [OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
-                       [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
-                       [OVS_ACTION_ATTR_POP_VLAN] = 0,
-                       [OVS_ACTION_ATTR_SET] = (u32)-1,
-                       [OVS_ACTION_ATTR_SAMPLE] = (u32)-1
-               };
-               const struct ovs_action_push_vlan *vlan;
-               int type = nla_type(a);
-               bool skip_copy;
-
-               if (type > OVS_ACTION_ATTR_MAX ||
-                   (action_lens[type] != nla_len(a) &&
-                    action_lens[type] != (u32)-1))
-                       return -EINVAL;
-
-               skip_copy = false;
-               switch (type) {
-               case OVS_ACTION_ATTR_UNSPEC:
-                       return -EINVAL;
-
-               case OVS_ACTION_ATTR_USERSPACE:
-                       err = validate_userspace(a);
-                       if (err)
-                               return err;
-                       break;
-
-               case OVS_ACTION_ATTR_OUTPUT:
-                       if (nla_get_u32(a) >= DP_MAX_PORTS)
-                               return -EINVAL;
-                       break;
-
-
-               case OVS_ACTION_ATTR_POP_VLAN:
-                       break;
-
-               case OVS_ACTION_ATTR_PUSH_VLAN:
-                       vlan = nla_data(a);
-                       if (vlan->vlan_tpid != htons(ETH_P_8021Q))
-                               return -EINVAL;
-                       if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
-                               return -EINVAL;
-                       break;
-
-               case OVS_ACTION_ATTR_SET:
-                       err = validate_set(a, key, sfa, &skip_copy);
-                       if (err)
-                               return err;
-                       break;
-
-               case OVS_ACTION_ATTR_SAMPLE:
-                       err = validate_and_copy_sample(a, key, depth, sfa);
-                       if (err)
-                               return err;
-                       skip_copy = true;
-                       break;
-
-               default:
-                       return -EINVAL;
-               }
-               if (!skip_copy) {
-                       err = copy_action(a, sfa);
-                       if (err)
-                               return err;
-               }
-       }
-
-       if (rem > 0)
-               return -EINVAL;
-
-       return 0;
-}
-
 static void clear_stats(struct sw_flow *flow)
 {
        flow->used = 0;
@@ -902,15 +508,16 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto err_flow_free;
 
-       err = ovs_flow_metadata_from_nlattrs(flow, a[OVS_PACKET_ATTR_KEY]);
+       err = ovs_nla_get_flow_metadata(flow, a[OVS_PACKET_ATTR_KEY]);
        if (err)
                goto err_flow_free;
-       acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
+       acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
        err = PTR_ERR(acts);
        if (IS_ERR(acts))
                goto err_flow_free;
 
-       err = validate_and_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0, &acts);
+       err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS],
+                                  &flow->key, 0, &acts);
        rcu_assign_pointer(flow->sf_acts, acts);
        if (err)
                goto err_flow_free;
@@ -958,15 +565,18 @@ static struct genl_ops dp_packet_genl_ops[] = {
        }
 };
 
-static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats)
+static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
+                        struct ovs_dp_megaflow_stats *mega_stats)
 {
-       struct flow_table *table;
        int i;
 
-       table = rcu_dereference_check(dp->table, lockdep_ovsl_is_held());
-       stats->n_flows = ovs_flow_tbl_count(table);
+       memset(mega_stats, 0, sizeof(*mega_stats));
+
+       stats->n_flows = ovs_flow_tbl_count(&dp->table);
+       mega_stats->n_masks = ovs_flow_tbl_num_masks(&dp->table);
 
        stats->n_hit = stats->n_missed = stats->n_lost = 0;
+
        for_each_possible_cpu(i) {
                const struct dp_stats_percpu *percpu_stats;
                struct dp_stats_percpu local_stats;
@@ -982,6 +592,7 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats)
                stats->n_hit += local_stats.n_hit;
                stats->n_missed += local_stats.n_missed;
                stats->n_lost += local_stats.n_lost;
+               mega_stats->n_mask_hit += local_stats.n_mask_hit;
        }
 }
 
@@ -1005,100 +616,6 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = {
        .name = OVS_FLOW_MCGROUP
 };
 
-static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb);
-static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
-{
-       const struct nlattr *a;
-       struct nlattr *start;
-       int err = 0, rem;
-
-       start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE);
-       if (!start)
-               return -EMSGSIZE;
-
-       nla_for_each_nested(a, attr, rem) {
-               int type = nla_type(a);
-               struct nlattr *st_sample;
-
-               switch (type) {
-               case OVS_SAMPLE_ATTR_PROBABILITY:
-                       if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, sizeof(u32), nla_data(a)))
-                               return -EMSGSIZE;
-                       break;
-               case OVS_SAMPLE_ATTR_ACTIONS:
-                       st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS);
-                       if (!st_sample)
-                               return -EMSGSIZE;
-                       err = actions_to_attr(nla_data(a), nla_len(a), skb);
-                       if (err)
-                               return err;
-                       nla_nest_end(skb, st_sample);
-                       break;
-               }
-       }
-
-       nla_nest_end(skb, start);
-       return err;
-}
-
-static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
-{
-       const struct nlattr *ovs_key = nla_data(a);
-       int key_type = nla_type(ovs_key);
-       struct nlattr *start;
-       int err;
-
-       switch (key_type) {
-       case OVS_KEY_ATTR_IPV4_TUNNEL:
-               start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
-               if (!start)
-                       return -EMSGSIZE;
-
-               err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key),
-                                            nla_data(ovs_key));
-               if (err)
-                       return err;
-               nla_nest_end(skb, start);
-               break;
-       default:
-               if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key))
-                       return -EMSGSIZE;
-               break;
-       }
-
-       return 0;
-}
-
-static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb)
-{
-       const struct nlattr *a;
-       int rem, err;
-
-       nla_for_each_attr(a, attr, len, rem) {
-               int type = nla_type(a);
-
-               switch (type) {
-               case OVS_ACTION_ATTR_SET:
-                       err = set_action_to_attr(a, skb);
-                       if (err)
-                               return err;
-                       break;
-
-               case OVS_ACTION_ATTR_SAMPLE:
-                       err = sample_action_to_attr(a, skb);
-                       if (err)
-                               return err;
-                       break;
-               default:
-                       if (nla_put(skb, type, nla_len(a), nla_data(a)))
-                               return -EMSGSIZE;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
 static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 {
        return NLMSG_ALIGN(sizeof(struct ovs_header))
@@ -1135,8 +652,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        if (!nla)
                goto nla_put_failure;
 
-       err = ovs_flow_to_nlattrs(&flow->unmasked_key,
-                       &flow->unmasked_key, skb);
+       err = ovs_nla_put_flow(&flow->unmasked_key, &flow->unmasked_key, skb);
        if (err)
                goto error;
        nla_nest_end(skb, nla);
@@ -1145,7 +661,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        if (!nla)
                goto nla_put_failure;
 
-       err = ovs_flow_to_nlattrs(&flow->key, &flow->mask->key, skb);
+       err = ovs_nla_put_flow(&flow->key, &flow->mask->key, skb);
        if (err)
                goto error;
 
@@ -1155,7 +671,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        used = flow->used;
        stats.n_packets = flow->packet_count;
        stats.n_bytes = flow->byte_count;
-       tcp_flags = flow->tcp_flags;
+       tcp_flags = (u8)ntohs(flow->tcp_flags);
        spin_unlock_bh(&flow->lock);
 
        if (used &&
@@ -1188,7 +704,8 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                sf_acts = rcu_dereference_check(flow->sf_acts,
                                                lockdep_ovsl_is_held());
 
-               err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb);
+               err = ovs_nla_put_actions(sf_acts->actions,
+                                         sf_acts->actions_len, skb);
                if (!err)
                        nla_nest_end(skb, start);
                else {
@@ -1234,6 +751,14 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
        return skb;
 }
 
+static struct sw_flow *__ovs_flow_tbl_lookup(struct flow_table *tbl,
+                                             const struct sw_flow_key *key)
+{
+       u32 __always_unused n_mask_hit;
+
+       return ovs_flow_tbl_lookup(tbl, key, &n_mask_hit);
+}
+
 static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
@@ -1243,7 +768,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        struct sw_flow_mask mask;
        struct sk_buff *reply;
        struct datapath *dp;
-       struct flow_table *table;
        struct sw_flow_actions *acts = NULL;
        struct sw_flow_match match;
        int error;
@@ -1254,21 +778,21 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                goto error;
 
        ovs_match_init(&match, &key, &mask);
-       error = ovs_match_from_nlattrs(&match,
-                       a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
+       error = ovs_nla_get_match(&match,
+                                 a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
        if (error)
                goto error;
 
        /* Validate actions. */
        if (a[OVS_FLOW_ATTR_ACTIONS]) {
-               acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+               acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
                error = PTR_ERR(acts);
                if (IS_ERR(acts))
                        goto error;
 
-               ovs_flow_key_mask(&masked_key, &key, &mask);
-               error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
-                                                 &masked_key, 0, &acts);
+               ovs_flow_mask_key(&masked_key, &key, &mask);
+               error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
+                                            &masked_key, 0, &acts);
                if (error) {
                        OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
                        goto err_kfree;
@@ -1284,29 +808,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        if (!dp)
                goto err_unlock_ovs;
 
-       table = ovsl_dereference(dp->table);
-
        /* Check if this is a duplicate flow */
-       flow = ovs_flow_lookup(table, &key);
+       flow = __ovs_flow_tbl_lookup(&dp->table, &key);
        if (!flow) {
-               struct sw_flow_mask *mask_p;
                /* Bail out if we're not allowed to create a new flow. */
                error = -ENOENT;
                if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
                        goto err_unlock_ovs;
 
-               /* Expand table, if necessary, to make room. */
-               if (ovs_flow_tbl_need_to_expand(table)) {
-                       struct flow_table *new_table;
-
-                       new_table = ovs_flow_tbl_expand(table);
-                       if (!IS_ERR(new_table)) {
-                               rcu_assign_pointer(dp->table, new_table);
-                               ovs_flow_tbl_destroy(table, true);
-                               table = ovsl_dereference(dp->table);
-                       }
-               }
-
                /* Allocate flow. */
                flow = ovs_flow_alloc();
                if (IS_ERR(flow)) {
@@ -1317,25 +826,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
                flow->key = masked_key;
                flow->unmasked_key = key;
-
-               /* Make sure mask is unique in the system */
-               mask_p = ovs_sw_flow_mask_find(table, &mask);
-               if (!mask_p) {
-                       /* Allocate a new mask if none exsits. */
-                       mask_p = ovs_sw_flow_mask_alloc();
-                       if (!mask_p)
-                               goto err_flow_free;
-                       mask_p->key = mask.key;
-                       mask_p->range = mask.range;
-                       ovs_sw_flow_mask_insert(table, mask_p);
-               }
-
-               ovs_sw_flow_mask_add_ref(mask_p);
-               flow->mask = mask_p;
                rcu_assign_pointer(flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               ovs_flow_insert(table, flow);
+               error = ovs_flow_tbl_insert(&dp->table, flow, &mask);
+               if (error) {
+                       acts = NULL;
+                       goto err_flow_free;
+               }
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                                info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1356,7 +854,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
                /* The unmasked key has to be the same for flow updates. */
                error = -EINVAL;
-               if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end)) {
+               if (!ovs_flow_cmp_unmasked_key(flow, &match)) {
                        OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n");
                        goto err_unlock_ovs;
                }
@@ -1364,7 +862,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
                rcu_assign_pointer(flow->sf_acts, acts);
-               ovs_flow_deferred_free_acts(old_acts);
+               ovs_nla_free_flow_actions(old_acts);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                               info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1403,7 +901,6 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *reply;
        struct sw_flow *flow;
        struct datapath *dp;
-       struct flow_table *table;
        struct sw_flow_match match;
        int err;
 
@@ -1413,7 +910,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        }
 
        ovs_match_init(&match, &key, NULL);
-       err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL);
+       err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
        if (err)
                return err;
 
@@ -1424,9 +921,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
                goto unlock;
        }
 
-       table = ovsl_dereference(dp->table);
-       flow = ovs_flow_lookup_unmasked_key(table, &match);
-       if (!flow) {
+       flow = __ovs_flow_tbl_lookup(&dp->table, &key);
+       if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
                err = -ENOENT;
                goto unlock;
        }
@@ -1453,7 +949,6 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *reply;
        struct sw_flow *flow;
        struct datapath *dp;
-       struct flow_table *table;
        struct sw_flow_match match;
        int err;
 
@@ -1465,18 +960,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (!a[OVS_FLOW_ATTR_KEY]) {
-               err = flush_flows(dp);
+               err = ovs_flow_tbl_flush(&dp->table);
                goto unlock;
        }
 
        ovs_match_init(&match, &key, NULL);
-       err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL);
+       err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
        if (err)
                goto unlock;
 
-       table = ovsl_dereference(dp->table);
-       flow = ovs_flow_lookup_unmasked_key(table, &match);
-       if (!flow) {
+       flow = __ovs_flow_tbl_lookup(&dp->table, &key);
+       if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
                err = -ENOENT;
                goto unlock;
        }
@@ -1487,7 +981,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto unlock;
        }
 
-       ovs_flow_remove(table, flow);
+       ovs_flow_tbl_remove(&dp->table, flow);
 
        err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
                                     info->snd_seq, 0, OVS_FLOW_CMD_DEL);
@@ -1506,8 +1000,8 @@ unlock:
 static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh));
+       struct table_instance *ti;
        struct datapath *dp;
-       struct flow_table *table;
 
        rcu_read_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
@@ -1516,14 +1010,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                return -ENODEV;
        }
 
-       table = rcu_dereference(dp->table);
+       ti = rcu_dereference(dp->table.ti);
        for (;;) {
                struct sw_flow *flow;
                u32 bucket, obj;
 
                bucket = cb->args[0];
                obj = cb->args[1];
-               flow = ovs_flow_dump_next(table, &bucket, &obj);
+               flow = ovs_flow_tbl_dump_next(ti, &bucket, &obj);
                if (!flow)
                        break;
 
@@ -1589,6 +1083,7 @@ static size_t ovs_dp_cmd_msg_size(void)
 
        msgsize += nla_total_size(IFNAMSIZ);
        msgsize += nla_total_size(sizeof(struct ovs_dp_stats));
+       msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats));
 
        return msgsize;
 }
@@ -1598,6 +1093,7 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
 {
        struct ovs_header *ovs_header;
        struct ovs_dp_stats dp_stats;
+       struct ovs_dp_megaflow_stats dp_megaflow_stats;
        int err;
 
        ovs_header = genlmsg_put(skb, portid, seq, &dp_datapath_genl_family,
@@ -1613,8 +1109,14 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
        if (err)
                goto nla_put_failure;
 
-       get_dp_stats(dp, &dp_stats);
-       if (nla_put(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats), &dp_stats))
+       get_dp_stats(dp, &dp_stats, &dp_megaflow_stats);
+       if (nla_put(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats),
+                       &dp_stats))
+               goto nla_put_failure;
+
+       if (nla_put(skb, OVS_DP_ATTR_MEGAFLOW_STATS,
+                       sizeof(struct ovs_dp_megaflow_stats),
+                       &dp_megaflow_stats))
                goto nla_put_failure;
 
        return genlmsg_end(skb, ovs_header);
@@ -1687,9 +1189,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
        /* Allocate table. */
-       err = -ENOMEM;
-       rcu_assign_pointer(dp->table, ovs_flow_tbl_alloc(TBL_MIN_BUCKETS));
-       if (!dp->table)
+       err = ovs_flow_tbl_init(&dp->table);
+       if (err)
                goto err_free_dp;
 
        dp->stats_percpu = alloc_percpu(struct dp_stats_percpu);
@@ -1699,7 +1200,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        }
 
        dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
-                       GFP_KERNEL);
+                           GFP_KERNEL);
        if (!dp->ports) {
                err = -ENOMEM;
                goto err_destroy_percpu;
@@ -1746,7 +1247,7 @@ err_destroy_ports_array:
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
 err_destroy_table:
-       ovs_flow_tbl_destroy(ovsl_dereference(dp->table), false);
+       ovs_flow_tbl_destroy(&dp->table);
 err_free_dp:
        release_net(ovs_dp_get_net(dp));
        kfree(dp);
@@ -2336,32 +1837,6 @@ error:
        return err;
 }
 
-static void rehash_flow_table(struct work_struct *work)
-{
-       struct datapath *dp;
-       struct net *net;
-
-       ovs_lock();
-       rtnl_lock();
-       for_each_net(net) {
-               struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
-
-               list_for_each_entry(dp, &ovs_net->dps, list_node) {
-                       struct flow_table *old_table = ovsl_dereference(dp->table);
-                       struct flow_table *new_table;
-
-                       new_table = ovs_flow_tbl_rehash(old_table);
-                       if (!IS_ERR(new_table)) {
-                               rcu_assign_pointer(dp->table, new_table);
-                               ovs_flow_tbl_destroy(old_table, true);
-                       }
-               }
-       }
-       rtnl_unlock();
-       ovs_unlock();
-       schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL);
-}
-
 static int __net_init ovs_init_net(struct net *net)
 {
        struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
@@ -2419,8 +1894,6 @@ static int __init dp_init(void)
        if (err < 0)
                goto error_unreg_notifier;
 
-       schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL);
-
        return 0;
 
 error_unreg_notifier:
@@ -2437,7 +1910,6 @@ error:
 
 static void dp_cleanup(void)
 {
-       cancel_delayed_work_sync(&rehash_flow_wq);
        dp_unregister_genl(ARRAY_SIZE(dp_genl_families));
        unregister_netdevice_notifier(&ovs_dp_device_notifier);
        unregister_pernet_device(&ovs_net_ops);
index 4d109c1..d3d14a5 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/u64_stats_sync.h>
 
 #include "flow.h"
+#include "flow_table.h"
 #include "vport.h"
 
 #define DP_MAX_PORTS           USHRT_MAX
  * @n_lost: Number of received packets that had no matching flow in the flow
  * table that could not be sent to userspace (normally due to an overflow in
  * one of the datapath's queues).
+ * @n_mask_hit: Number of masks looked up for flow match.
+ *   @n_mask_hit / (@n_hit + @n_missed)  will be the average masks looked
+ *   up per packet.
  */
 struct dp_stats_percpu {
        u64 n_hit;
        u64 n_missed;
        u64 n_lost;
+       u64 n_mask_hit;
        struct u64_stats_sync sync;
 };
 
@@ -57,7 +62,7 @@ struct dp_stats_percpu {
  * struct datapath - datapath for flow-based packet switching
  * @rcu: RCU callback head for deferred destruction.
  * @list_node: Element in global 'dps' list.
- * @table: Current flow table.  Protected by ovs_mutex and RCU.
+ * @table: flow table.
  * @ports: Hash table for ports.  %OVSP_LOCAL port always exists.  Protected by
  * ovs_mutex and RCU.
  * @stats_percpu: Per-CPU datapath statistics.
@@ -71,7 +76,7 @@ struct datapath {
        struct list_head list_node;
 
        /* Flow table. */
-       struct flow_table __rcu *table;
+       struct flow_table table;
 
        /* Switch ports. */
        struct hlist_head *ports;
index c323567..5c2dab2 100644 (file)
@@ -65,8 +65,7 @@ void ovs_dp_notify_wq(struct work_struct *work)
                                        continue;
 
                                netdev_vport = netdev_vport_priv(vport);
-                               if (netdev_vport->dev->reg_state == NETREG_UNREGISTERED ||
-                                   netdev_vport->dev->reg_state == NETREG_UNREGISTERING)
+                               if (!(netdev_vport->dev->priv_flags & IFF_OVS_DATAPATH))
                                        dp_detach_port_notify(vport);
                        }
                }
@@ -88,6 +87,10 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
                return NOTIFY_DONE;
 
        if (event == NETDEV_UNREGISTER) {
+               /* upper_dev_unlink and decrement promisc immediately */
+               ovs_netdev_detach_dev(vport);
+
+               /* schedule vport destroy, dev_put and genl notification */
                ovs_net = net_generic(dev_net(dev), ovs_net_id);
                queue_work(system_wq, &ovs_net->dp_notify_work);
        }
index 410db90..b409f52 100644 (file)
 #include <net/ipv6.h>
 #include <net/ndisc.h>
 
-static struct kmem_cache *flow_cache;
-
-static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask,
-               struct sw_flow_key_range *range, u8 val);
-
-static void update_range__(struct sw_flow_match *match,
-                         size_t offset, size_t size, bool is_mask)
+u64 ovs_flow_used_time(unsigned long flow_jiffies)
 {
-       struct sw_flow_key_range *range = NULL;
-       size_t start = rounddown(offset, sizeof(long));
-       size_t end = roundup(offset + size, sizeof(long));
-
-       if (!is_mask)
-               range = &match->range;
-       else if (match->mask)
-               range = &match->mask->range;
-
-       if (!range)
-               return;
-
-       if (range->start == range->end) {
-               range->start = start;
-               range->end = end;
-               return;
-       }
-
-       if (range->start > start)
-               range->start = start;
+       struct timespec cur_ts;
+       u64 cur_ms, idle_ms;
 
-       if (range->end < end)
-               range->end = end;
-}
+       ktime_get_ts(&cur_ts);
+       idle_ms = jiffies_to_msecs(jiffies - flow_jiffies);
+       cur_ms = (u64)cur_ts.tv_sec * MSEC_PER_SEC +
+                cur_ts.tv_nsec / NSEC_PER_MSEC;
 
-#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \
-       do { \
-               update_range__(match, offsetof(struct sw_flow_key, field),  \
-                                    sizeof((match)->key->field), is_mask); \
-               if (is_mask) {                                              \
-                       if ((match)->mask)                                  \
-                               (match)->mask->key.field = value;           \
-               } else {                                                    \
-                       (match)->key->field = value;                        \
-               }                                                           \
-       } while (0)
-
-#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \
-       do { \
-               update_range__(match, offsetof(struct sw_flow_key, field),  \
-                               len, is_mask);                              \
-               if (is_mask) {                                              \
-                       if ((match)->mask)                                  \
-                               memcpy(&(match)->mask->key.field, value_p, len);\
-               } else {                                                    \
-                       memcpy(&(match)->key->field, value_p, len);         \
-               }                                                           \
-       } while (0)
-
-static u16 range_n_bytes(const struct sw_flow_key_range *range)
-{
-       return range->end - range->start;
+       return cur_ms - idle_ms;
 }
 
-void ovs_match_init(struct sw_flow_match *match,
-                   struct sw_flow_key *key,
-                   struct sw_flow_mask *mask)
-{
-       memset(match, 0, sizeof(*match));
-       match->key = key;
-       match->mask = mask;
+#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF))
 
-       memset(key, 0, sizeof(*key));
-
-       if (mask) {
-               memset(&mask->key, 0, sizeof(mask->key));
-               mask->range.start = mask->range.end = 0;
-       }
-}
-
-static bool ovs_match_validate(const struct sw_flow_match *match,
-               u64 key_attrs, u64 mask_attrs)
+void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
 {
-       u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET;
-       u64 mask_allowed = key_attrs;  /* At most allow all key attributes */
-
-       /* The following mask attributes allowed only if they
-        * pass the validation tests. */
-       mask_allowed &= ~((1 << OVS_KEY_ATTR_IPV4)
-                       | (1 << OVS_KEY_ATTR_IPV6)
-                       | (1 << OVS_KEY_ATTR_TCP)
-                       | (1 << OVS_KEY_ATTR_UDP)
-                       | (1 << OVS_KEY_ATTR_SCTP)
-                       | (1 << OVS_KEY_ATTR_ICMP)
-                       | (1 << OVS_KEY_ATTR_ICMPV6)
-                       | (1 << OVS_KEY_ATTR_ARP)
-                       | (1 << OVS_KEY_ATTR_ND));
-
-       /* Always allowed mask fields. */
-       mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL)
-                      | (1 << OVS_KEY_ATTR_IN_PORT)
-                      | (1 << OVS_KEY_ATTR_ETHERTYPE));
-
-       /* Check key attributes. */
-       if (match->key->eth.type == htons(ETH_P_ARP)
-                       || match->key->eth.type == htons(ETH_P_RARP)) {
-               key_expected |= 1 << OVS_KEY_ATTR_ARP;
-               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
-                       mask_allowed |= 1 << OVS_KEY_ATTR_ARP;
-       }
+       __be16 tcp_flags = 0;
 
-       if (match->key->eth.type == htons(ETH_P_IP)) {
-               key_expected |= 1 << OVS_KEY_ATTR_IPV4;
-               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
-                       mask_allowed |= 1 << OVS_KEY_ATTR_IPV4;
-
-               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
-                       if (match->key->ip.proto == IPPROTO_UDP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_UDP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_UDP;
-                       }
-
-                       if (match->key->ip.proto == IPPROTO_SCTP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_SCTP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_SCTP;
-                       }
-
-                       if (match->key->ip.proto == IPPROTO_TCP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_TCP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_TCP;
-                       }
-
-                       if (match->key->ip.proto == IPPROTO_ICMP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_ICMP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_ICMP;
-                       }
-               }
-       }
-
-       if (match->key->eth.type == htons(ETH_P_IPV6)) {
-               key_expected |= 1 << OVS_KEY_ATTR_IPV6;
-               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
-                       mask_allowed |= 1 << OVS_KEY_ATTR_IPV6;
-
-               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
-                       if (match->key->ip.proto == IPPROTO_UDP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_UDP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_UDP;
-                       }
-
-                       if (match->key->ip.proto == IPPROTO_SCTP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_SCTP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_SCTP;
-                       }
-
-                       if (match->key->ip.proto == IPPROTO_TCP) {
-                               key_expected |= 1 << OVS_KEY_ATTR_TCP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_TCP;
-                       }
-
-                       if (match->key->ip.proto == IPPROTO_ICMPV6) {
-                               key_expected |= 1 << OVS_KEY_ATTR_ICMPV6;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
-                                       mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6;
-
-                               if (match->key->ipv6.tp.src ==
-                                               htons(NDISC_NEIGHBOUR_SOLICITATION) ||
-                                   match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
-                                       key_expected |= 1 << OVS_KEY_ATTR_ND;
-                                       if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff)))
-                                               mask_allowed |= 1 << OVS_KEY_ATTR_ND;
-                               }
-                       }
-               }
-       }
-
-       if ((key_attrs & key_expected) != key_expected) {
-               /* Key attributes check failed. */
-               OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n",
-                               key_attrs, key_expected);
-               return false;
-       }
-
-       if ((mask_attrs & mask_allowed) != mask_attrs) {
-               /* Mask attributes check failed. */
-               OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n",
-                               mask_attrs, mask_allowed);
-               return false;
+       if ((flow->key.eth.type == htons(ETH_P_IP) ||
+            flow->key.eth.type == htons(ETH_P_IPV6)) &&
+           flow->key.ip.proto == IPPROTO_TCP &&
+           likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) {
+               tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb));
        }
 
-       return true;
+       spin_lock(&flow->lock);
+       flow->used = jiffies;
+       flow->packet_count++;
+       flow->byte_count += skb->len;
+       flow->tcp_flags |= tcp_flags;
+       spin_unlock(&flow->lock);
 }
 
 static int check_header(struct sk_buff *skb, int len)
@@ -311,19 +147,6 @@ static bool icmphdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmphdr));
 }
 
-u64 ovs_flow_used_time(unsigned long flow_jiffies)
-{
-       struct timespec cur_ts;
-       u64 cur_ms, idle_ms;
-
-       ktime_get_ts(&cur_ts);
-       idle_ms = jiffies_to_msecs(jiffies - flow_jiffies);
-       cur_ms = (u64)cur_ts.tv_sec * MSEC_PER_SEC +
-                cur_ts.tv_nsec / NSEC_PER_MSEC;
-
-       return cur_ms - idle_ms;
-}
-
 static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
 {
        unsigned int nh_ofs = skb_network_offset(skb);
@@ -372,311 +195,6 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmp6hdr));
 }
 
-void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
-                      const struct sw_flow_mask *mask)
-{
-       const long *m = (long *)((u8 *)&mask->key + mask->range.start);
-       const long *s = (long *)((u8 *)src + mask->range.start);
-       long *d = (long *)((u8 *)dst + mask->range.start);
-       int i;
-
-       /* The memory outside of the 'mask->range' are not set since
-        * further operations on 'dst' only uses contents within
-        * 'mask->range'.
-        */
-       for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long))
-               *d++ = *s++ & *m++;
-}
-
-#define TCP_FLAGS_OFFSET 13
-#define TCP_FLAG_MASK 0x3f
-
-void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
-{
-       u8 tcp_flags = 0;
-
-       if ((flow->key.eth.type == htons(ETH_P_IP) ||
-            flow->key.eth.type == htons(ETH_P_IPV6)) &&
-           flow->key.ip.proto == IPPROTO_TCP &&
-           likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) {
-               u8 *tcp = (u8 *)tcp_hdr(skb);
-               tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK;
-       }
-
-       spin_lock(&flow->lock);
-       flow->used = jiffies;
-       flow->packet_count++;
-       flow->byte_count += skb->len;
-       flow->tcp_flags |= tcp_flags;
-       spin_unlock(&flow->lock);
-}
-
-struct sw_flow_actions *ovs_flow_actions_alloc(int size)
-{
-       struct sw_flow_actions *sfa;
-
-       if (size > MAX_ACTIONS_BUFSIZE)
-               return ERR_PTR(-EINVAL);
-
-       sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
-       if (!sfa)
-               return ERR_PTR(-ENOMEM);
-
-       sfa->actions_len = 0;
-       return sfa;
-}
-
-struct sw_flow *ovs_flow_alloc(void)
-{
-       struct sw_flow *flow;
-
-       flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
-       if (!flow)
-               return ERR_PTR(-ENOMEM);
-
-       spin_lock_init(&flow->lock);
-       flow->sf_acts = NULL;
-       flow->mask = NULL;
-
-       return flow;
-}
-
-static struct hlist_head *find_bucket(struct flow_table *table, u32 hash)
-{
-       hash = jhash_1word(hash, table->hash_seed);
-       return flex_array_get(table->buckets,
-                               (hash & (table->n_buckets - 1)));
-}
-
-static struct flex_array *alloc_buckets(unsigned int n_buckets)
-{
-       struct flex_array *buckets;
-       int i, err;
-
-       buckets = flex_array_alloc(sizeof(struct hlist_head),
-                                  n_buckets, GFP_KERNEL);
-       if (!buckets)
-               return NULL;
-
-       err = flex_array_prealloc(buckets, 0, n_buckets, GFP_KERNEL);
-       if (err) {
-               flex_array_free(buckets);
-               return NULL;
-       }
-
-       for (i = 0; i < n_buckets; i++)
-               INIT_HLIST_HEAD((struct hlist_head *)
-                                       flex_array_get(buckets, i));
-
-       return buckets;
-}
-
-static void free_buckets(struct flex_array *buckets)
-{
-       flex_array_free(buckets);
-}
-
-static struct flow_table *__flow_tbl_alloc(int new_size)
-{
-       struct flow_table *table = kmalloc(sizeof(*table), GFP_KERNEL);
-
-       if (!table)
-               return NULL;
-
-       table->buckets = alloc_buckets(new_size);
-
-       if (!table->buckets) {
-               kfree(table);
-               return NULL;
-       }
-       table->n_buckets = new_size;
-       table->count = 0;
-       table->node_ver = 0;
-       table->keep_flows = false;
-       get_random_bytes(&table->hash_seed, sizeof(u32));
-       table->mask_list = NULL;
-
-       return table;
-}
-
-static void __flow_tbl_destroy(struct flow_table *table)
-{
-       int i;
-
-       if (table->keep_flows)
-               goto skip_flows;
-
-       for (i = 0; i < table->n_buckets; i++) {
-               struct sw_flow *flow;
-               struct hlist_head *head = flex_array_get(table->buckets, i);
-               struct hlist_node *n;
-               int ver = table->node_ver;
-
-               hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
-                       hlist_del(&flow->hash_node[ver]);
-                       ovs_flow_free(flow, false);
-               }
-       }
-
-       BUG_ON(!list_empty(table->mask_list));
-       kfree(table->mask_list);
-
-skip_flows:
-       free_buckets(table->buckets);
-       kfree(table);
-}
-
-struct flow_table *ovs_flow_tbl_alloc(int new_size)
-{
-       struct flow_table *table = __flow_tbl_alloc(new_size);
-
-       if (!table)
-               return NULL;
-
-       table->mask_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
-       if (!table->mask_list) {
-               table->keep_flows = true;
-               __flow_tbl_destroy(table);
-               return NULL;
-       }
-       INIT_LIST_HEAD(table->mask_list);
-
-       return table;
-}
-
-static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
-{
-       struct flow_table *table = container_of(rcu, struct flow_table, rcu);
-
-       __flow_tbl_destroy(table);
-}
-
-void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred)
-{
-       if (!table)
-               return;
-
-       if (deferred)
-               call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb);
-       else
-               __flow_tbl_destroy(table);
-}
-
-struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *last)
-{
-       struct sw_flow *flow;
-       struct hlist_head *head;
-       int ver;
-       int i;
-
-       ver = table->node_ver;
-       while (*bucket < table->n_buckets) {
-               i = 0;
-               head = flex_array_get(table->buckets, *bucket);
-               hlist_for_each_entry_rcu(flow, head, hash_node[ver]) {
-                       if (i < *last) {
-                               i++;
-                               continue;
-                       }
-                       *last = i + 1;
-                       return flow;
-               }
-               (*bucket)++;
-               *last = 0;
-       }
-
-       return NULL;
-}
-
-static void __tbl_insert(struct flow_table *table, struct sw_flow *flow)
-{
-       struct hlist_head *head;
-
-       head = find_bucket(table, flow->hash);
-       hlist_add_head_rcu(&flow->hash_node[table->node_ver], head);
-
-       table->count++;
-}
-
-static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new)
-{
-       int old_ver;
-       int i;
-
-       old_ver = old->node_ver;
-       new->node_ver = !old_ver;
-
-       /* Insert in new table. */
-       for (i = 0; i < old->n_buckets; i++) {
-               struct sw_flow *flow;
-               struct hlist_head *head;
-
-               head = flex_array_get(old->buckets, i);
-
-               hlist_for_each_entry(flow, head, hash_node[old_ver])
-                       __tbl_insert(new, flow);
-       }
-
-       new->mask_list = old->mask_list;
-       old->keep_flows = true;
-}
-
-static struct flow_table *__flow_tbl_rehash(struct flow_table *table, int n_buckets)
-{
-       struct flow_table *new_table;
-
-       new_table = __flow_tbl_alloc(n_buckets);
-       if (!new_table)
-               return ERR_PTR(-ENOMEM);
-
-       flow_table_copy_flows(table, new_table);
-
-       return new_table;
-}
-
-struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table)
-{
-       return __flow_tbl_rehash(table, table->n_buckets);
-}
-
-struct flow_table *ovs_flow_tbl_expand(struct flow_table *table)
-{
-       return __flow_tbl_rehash(table, table->n_buckets * 2);
-}
-
-static void __flow_free(struct sw_flow *flow)
-{
-       kfree((struct sf_flow_acts __force *)flow->sf_acts);
-       kmem_cache_free(flow_cache, flow);
-}
-
-static void rcu_free_flow_callback(struct rcu_head *rcu)
-{
-       struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu);
-
-       __flow_free(flow);
-}
-
-void ovs_flow_free(struct sw_flow *flow, bool deferred)
-{
-       if (!flow)
-               return;
-
-       ovs_sw_flow_mask_del_ref(flow->mask, deferred);
-
-       if (deferred)
-               call_rcu(&flow->rcu, rcu_free_flow_callback);
-       else
-               __flow_free(flow);
-}
-
-/* Schedules 'sf_acts' to be freed after the next RCU grace period.
- * The caller must hold rcu_read_lock for this to be sensible. */
-void ovs_flow_deferred_free_acts(struct sw_flow_actions *sf_acts)
-{
-       kfree_rcu(sf_acts, rcu);
-}
-
 static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 {
        struct qtag_prefix {
@@ -910,6 +428,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                struct tcphdr *tcp = tcp_hdr(skb);
                                key->ipv4.tp.src = tcp->source;
                                key->ipv4.tp.dst = tcp->dest;
+                               key->ipv4.tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == IPPROTO_UDP) {
                        if (udphdr_ok(skb)) {
@@ -978,6 +497,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                struct tcphdr *tcp = tcp_hdr(skb);
                                key->ipv6.tp.src = tcp->source;
                                key->ipv6.tp.dst = tcp->dest;
+                               key->ipv6.tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == NEXTHDR_UDP) {
                        if (udphdr_ok(skb)) {
@@ -1002,1080 +522,3 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
 
        return 0;
 }
-
-static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start,
-                        int key_end)
-{
-       u32 *hash_key = (u32 *)((u8 *)key + key_start);
-       int hash_u32s = (key_end - key_start) >> 2;
-
-       /* Make sure number of hash bytes are multiple of u32. */
-       BUILD_BUG_ON(sizeof(long) % sizeof(u32));
-
-       return jhash2(hash_key, hash_u32s, 0);
-}
-
-static int flow_key_start(const struct sw_flow_key *key)
-{
-       if (key->tun_key.ipv4_dst)
-               return 0;
-       else
-               return rounddown(offsetof(struct sw_flow_key, phy),
-                                         sizeof(long));
-}
-
-static bool __cmp_key(const struct sw_flow_key *key1,
-               const struct sw_flow_key *key2,  int key_start, int key_end)
-{
-       const long *cp1 = (long *)((u8 *)key1 + key_start);
-       const long *cp2 = (long *)((u8 *)key2 + key_start);
-       long diffs = 0;
-       int i;
-
-       for (i = key_start; i < key_end;  i += sizeof(long))
-               diffs |= *cp1++ ^ *cp2++;
-
-       return diffs == 0;
-}
-
-static bool __flow_cmp_masked_key(const struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_start, int key_end)
-{
-       return __cmp_key(&flow->key, key, key_start, key_end);
-}
-
-static bool __flow_cmp_unmasked_key(const struct sw_flow *flow,
-                 const struct sw_flow_key *key, int key_start, int key_end)
-{
-       return __cmp_key(&flow->unmasked_key, key, key_start, key_end);
-}
-
-bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_end)
-{
-       int key_start;
-       key_start = flow_key_start(key);
-
-       return __flow_cmp_unmasked_key(flow, key, key_start, key_end);
-
-}
-
-struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
-                                      struct sw_flow_match *match)
-{
-       struct sw_flow_key *unmasked = match->key;
-       int key_end = match->range.end;
-       struct sw_flow *flow;
-
-       flow = ovs_flow_lookup(table, unmasked);
-       if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_end)))
-               flow = NULL;
-
-       return flow;
-}
-
-static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
-                                   const struct sw_flow_key *unmasked,
-                                   struct sw_flow_mask *mask)
-{
-       struct sw_flow *flow;
-       struct hlist_head *head;
-       int key_start = mask->range.start;
-       int key_end = mask->range.end;
-       u32 hash;
-       struct sw_flow_key masked_key;
-
-       ovs_flow_key_mask(&masked_key, unmasked, mask);
-       hash = ovs_flow_hash(&masked_key, key_start, key_end);
-       head = find_bucket(table, hash);
-       hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
-               if (flow->mask == mask &&
-                   __flow_cmp_masked_key(flow, &masked_key,
-                                         key_start, key_end))
-                       return flow;
-       }
-       return NULL;
-}
-
-struct sw_flow *ovs_flow_lookup(struct flow_table *tbl,
-                               const struct sw_flow_key *key)
-{
-       struct sw_flow *flow = NULL;
-       struct sw_flow_mask *mask;
-
-       list_for_each_entry_rcu(mask, tbl->mask_list, list) {
-               flow = ovs_masked_flow_lookup(tbl, key, mask);
-               if (flow)  /* Found */
-                       break;
-       }
-
-       return flow;
-}
-
-
-void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow)
-{
-       flow->hash = ovs_flow_hash(&flow->key, flow->mask->range.start,
-                       flow->mask->range.end);
-       __tbl_insert(table, flow);
-}
-
-void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow)
-{
-       BUG_ON(table->count == 0);
-       hlist_del_rcu(&flow->hash_node[table->node_ver]);
-       table->count--;
-}
-
-/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
-const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
-       [OVS_KEY_ATTR_ENCAP] = -1,
-       [OVS_KEY_ATTR_PRIORITY] = sizeof(u32),
-       [OVS_KEY_ATTR_IN_PORT] = sizeof(u32),
-       [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32),
-       [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),
-       [OVS_KEY_ATTR_VLAN] = sizeof(__be16),
-       [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16),
-       [OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4),
-       [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
-       [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),
-       [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp),
-       [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp),
-       [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp),
-       [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
-       [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
-       [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
-       [OVS_KEY_ATTR_TUNNEL] = -1,
-};
-
-static bool is_all_zero(const u8 *fp, size_t size)
-{
-       int i;
-
-       if (!fp)
-               return false;
-
-       for (i = 0; i < size; i++)
-               if (fp[i])
-                       return false;
-
-       return true;
-}
-
-static int __parse_flow_nlattrs(const struct nlattr *attr,
-                             const struct nlattr *a[],
-                             u64 *attrsp, bool nz)
-{
-       const struct nlattr *nla;
-       u32 attrs;
-       int rem;
-
-       attrs = *attrsp;
-       nla_for_each_nested(nla, attr, rem) {
-               u16 type = nla_type(nla);
-               int expected_len;
-
-               if (type > OVS_KEY_ATTR_MAX) {
-                       OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n",
-                                 type, OVS_KEY_ATTR_MAX);
-                       return -EINVAL;
-               }
-
-               if (attrs & (1 << type)) {
-                       OVS_NLERR("Duplicate key attribute (type %d).\n", type);
-                       return -EINVAL;
-               }
-
-               expected_len = ovs_key_lens[type];
-               if (nla_len(nla) != expected_len && expected_len != -1) {
-                       OVS_NLERR("Key attribute has unexpected length (type=%d"
-                                 ", length=%d, expected=%d).\n", type,
-                                 nla_len(nla), expected_len);
-                       return -EINVAL;
-               }
-
-               if (!nz || !is_all_zero(nla_data(nla), expected_len)) {
-                       attrs |= 1 << type;
-                       a[type] = nla;
-               }
-       }
-       if (rem) {
-               OVS_NLERR("Message has %d unknown bytes.\n", rem);
-               return -EINVAL;
-       }
-
-       *attrsp = attrs;
-       return 0;
-}
-
-static int parse_flow_mask_nlattrs(const struct nlattr *attr,
-                             const struct nlattr *a[], u64 *attrsp)
-{
-       return __parse_flow_nlattrs(attr, a, attrsp, true);
-}
-
-static int parse_flow_nlattrs(const struct nlattr *attr,
-                             const struct nlattr *a[], u64 *attrsp)
-{
-       return __parse_flow_nlattrs(attr, a, attrsp, false);
-}
-
-int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
-                            struct sw_flow_match *match, bool is_mask)
-{
-       struct nlattr *a;
-       int rem;
-       bool ttl = false;
-       __be16 tun_flags = 0;
-
-       nla_for_each_nested(a, attr, rem) {
-               int type = nla_type(a);
-               static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
-                       [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64),
-                       [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32),
-                       [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = sizeof(u32),
-                       [OVS_TUNNEL_KEY_ATTR_TOS] = 1,
-                       [OVS_TUNNEL_KEY_ATTR_TTL] = 1,
-                       [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0,
-                       [OVS_TUNNEL_KEY_ATTR_CSUM] = 0,
-               };
-
-               if (type > OVS_TUNNEL_KEY_ATTR_MAX) {
-                       OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n",
-                       type, OVS_TUNNEL_KEY_ATTR_MAX);
-                       return -EINVAL;
-               }
-
-               if (ovs_tunnel_key_lens[type] != nla_len(a)) {
-                       OVS_NLERR("IPv4 tunnel attribute type has unexpected "
-                                 " length (type=%d, length=%d, expected=%d).\n",
-                                 type, nla_len(a), ovs_tunnel_key_lens[type]);
-                       return -EINVAL;
-               }
-
-               switch (type) {
-               case OVS_TUNNEL_KEY_ATTR_ID:
-                       SW_FLOW_KEY_PUT(match, tun_key.tun_id,
-                                       nla_get_be64(a), is_mask);
-                       tun_flags |= TUNNEL_KEY;
-                       break;
-               case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
-                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_src,
-                                       nla_get_be32(a), is_mask);
-                       break;
-               case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
-                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst,
-                                       nla_get_be32(a), is_mask);
-                       break;
-               case OVS_TUNNEL_KEY_ATTR_TOS:
-                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos,
-                                       nla_get_u8(a), is_mask);
-                       break;
-               case OVS_TUNNEL_KEY_ATTR_TTL:
-                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl,
-                                       nla_get_u8(a), is_mask);
-                       ttl = true;
-                       break;
-               case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
-                       tun_flags |= TUNNEL_DONT_FRAGMENT;
-                       break;
-               case OVS_TUNNEL_KEY_ATTR_CSUM:
-                       tun_flags |= TUNNEL_CSUM;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
-
-       if (rem > 0) {
-               OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem);
-               return -EINVAL;
-       }
-
-       if (!is_mask) {
-               if (!match->key->tun_key.ipv4_dst) {
-                       OVS_NLERR("IPv4 tunnel destination address is zero.\n");
-                       return -EINVAL;
-               }
-
-               if (!ttl) {
-                       OVS_NLERR("IPv4 tunnel TTL not specified.\n");
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
-                          const struct ovs_key_ipv4_tunnel *tun_key,
-                          const struct ovs_key_ipv4_tunnel *output)
-{
-       struct nlattr *nla;
-
-       nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
-       if (!nla)
-               return -EMSGSIZE;
-
-       if (output->tun_flags & TUNNEL_KEY &&
-           nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
-               return -EMSGSIZE;
-       if (output->ipv4_src &&
-               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
-               return -EMSGSIZE;
-       if (output->ipv4_dst &&
-               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
-               return -EMSGSIZE;
-       if (output->ipv4_tos &&
-               nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
-               return -EMSGSIZE;
-       if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl))
-               return -EMSGSIZE;
-       if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) &&
-               nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
-               return -EMSGSIZE;
-       if ((output->tun_flags & TUNNEL_CSUM) &&
-               nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
-               return -EMSGSIZE;
-
-       nla_nest_end(skb, nla);
-       return 0;
-}
-
-static int metadata_from_nlattrs(struct sw_flow_match *match,  u64 *attrs,
-               const struct nlattr **a, bool is_mask)
-{
-       if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) {
-               SW_FLOW_KEY_PUT(match, phy.priority,
-                         nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask);
-               *attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY);
-       }
-
-       if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
-               u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
-
-               if (is_mask)
-                       in_port = 0xffffffff; /* Always exact match in_port. */
-               else if (in_port >= DP_MAX_PORTS)
-                       return -EINVAL;
-
-               SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask);
-               *attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
-       } else if (!is_mask) {
-               SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask);
-       }
-
-       if (*attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
-               uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
-
-               SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask);
-               *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
-       }
-       if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) {
-               if (ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
-                                       is_mask))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL);
-       }
-       return 0;
-}
-
-static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
-               const struct nlattr **a, bool is_mask)
-{
-       int err;
-       u64 orig_attrs = attrs;
-
-       err = metadata_from_nlattrs(match, &attrs, a, is_mask);
-       if (err)
-               return err;
-
-       if (attrs & (1 << OVS_KEY_ATTR_ETHERNET)) {
-               const struct ovs_key_ethernet *eth_key;
-
-               eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
-               SW_FLOW_KEY_MEMCPY(match, eth.src,
-                               eth_key->eth_src, ETH_ALEN, is_mask);
-               SW_FLOW_KEY_MEMCPY(match, eth.dst,
-                               eth_key->eth_dst, ETH_ALEN, is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_VLAN)) {
-               __be16 tci;
-
-               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-               if (!(tci & htons(VLAN_TAG_PRESENT))) {
-                       if (is_mask)
-                               OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n");
-                       else
-                               OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n");
-
-                       return -EINVAL;
-               }
-
-               SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
-       } else if (!is_mask)
-               SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
-
-       if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
-               __be16 eth_type;
-
-               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
-               if (is_mask) {
-                       /* Always exact match EtherType. */
-                       eth_type = htons(0xffff);
-               } else if (ntohs(eth_type) < ETH_P_802_3_MIN) {
-                       OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n",
-                                       ntohs(eth_type), ETH_P_802_3_MIN);
-                       return -EINVAL;
-               }
-
-               SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
-       } else if (!is_mask) {
-               SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-               const struct ovs_key_ipv4 *ipv4_key;
-
-               ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
-               if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) {
-                       OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n",
-                               ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX);
-                       return -EINVAL;
-               }
-               SW_FLOW_KEY_PUT(match, ip.proto,
-                               ipv4_key->ipv4_proto, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.tos,
-                               ipv4_key->ipv4_tos, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.ttl,
-                               ipv4_key->ipv4_ttl, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.frag,
-                               ipv4_key->ipv4_frag, is_mask);
-               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
-                               ipv4_key->ipv4_src, is_mask);
-               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
-                               ipv4_key->ipv4_dst, is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_IPV6)) {
-               const struct ovs_key_ipv6 *ipv6_key;
-
-               ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
-               if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) {
-                       OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n",
-                               ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX);
-                       return -EINVAL;
-               }
-               SW_FLOW_KEY_PUT(match, ipv6.label,
-                               ipv6_key->ipv6_label, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.proto,
-                               ipv6_key->ipv6_proto, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.tos,
-                               ipv6_key->ipv6_tclass, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.ttl,
-                               ipv6_key->ipv6_hlimit, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.frag,
-                               ipv6_key->ipv6_frag, is_mask);
-               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src,
-                               ipv6_key->ipv6_src,
-                               sizeof(match->key->ipv6.addr.src),
-                               is_mask);
-               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst,
-                               ipv6_key->ipv6_dst,
-                               sizeof(match->key->ipv6.addr.dst),
-                               is_mask);
-
-               attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
-               const struct ovs_key_arp *arp_key;
-
-               arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
-               if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
-                       OVS_NLERR("Unknown ARP opcode (opcode=%d).\n",
-                                 arp_key->arp_op);
-                       return -EINVAL;
-               }
-
-               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
-                               arp_key->arp_sip, is_mask);
-               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
-                       arp_key->arp_tip, is_mask);
-               SW_FLOW_KEY_PUT(match, ip.proto,
-                               ntohs(arp_key->arp_op), is_mask);
-               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha,
-                               arp_key->arp_sha, ETH_ALEN, is_mask);
-               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha,
-                               arp_key->arp_tha, ETH_ALEN, is_mask);
-
-               attrs &= ~(1 << OVS_KEY_ATTR_ARP);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_TCP)) {
-               const struct ovs_key_tcp *tcp_key;
-
-               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                                       tcp_key->tcp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                                       tcp_key->tcp_dst, is_mask);
-               } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                                       tcp_key->tcp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                                       tcp_key->tcp_dst, is_mask);
-               }
-               attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_UDP)) {
-               const struct ovs_key_udp *udp_key;
-
-               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                                       udp_key->udp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                                       udp_key->udp_dst, is_mask);
-               } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                                       udp_key->udp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                                       udp_key->udp_dst, is_mask);
-               }
-               attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_SCTP)) {
-               const struct ovs_key_sctp *sctp_key;
-
-               sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]);
-               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                                       sctp_key->sctp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                                       sctp_key->sctp_dst, is_mask);
-               } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                                       sctp_key->sctp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                                       sctp_key->sctp_dst, is_mask);
-               }
-               attrs &= ~(1 << OVS_KEY_ATTR_SCTP);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_ICMP)) {
-               const struct ovs_key_icmp *icmp_key;
-
-               icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
-               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                               htons(icmp_key->icmp_type), is_mask);
-               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                               htons(icmp_key->icmp_code), is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_ICMPV6)) {
-               const struct ovs_key_icmpv6 *icmpv6_key;
-
-               icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
-               SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                               htons(icmpv6_key->icmpv6_type), is_mask);
-               SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                               htons(icmpv6_key->icmpv6_code), is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
-       }
-
-       if (attrs & (1 << OVS_KEY_ATTR_ND)) {
-               const struct ovs_key_nd *nd_key;
-
-               nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
-               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target,
-                       nd_key->nd_target,
-                       sizeof(match->key->ipv6.nd.target),
-                       is_mask);
-               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll,
-                       nd_key->nd_sll, ETH_ALEN, is_mask);
-               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll,
-                               nd_key->nd_tll, ETH_ALEN, is_mask);
-               attrs &= ~(1 << OVS_KEY_ATTR_ND);
-       }
-
-       if (attrs != 0)
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- * ovs_match_from_nlattrs - parses Netlink attributes into a flow key and
- * mask. In case the 'mask' is NULL, the flow is treated as exact match
- * flow. Otherwise, it is treated as a wildcarded flow, except the mask
- * does not include any don't care bit.
- * @match: receives the extracted flow match information.
- * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
- * sequence. The fields should of the packet that triggered the creation
- * of this flow.
- * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink
- * attribute specifies the mask field of the wildcarded flow.
- */
-int ovs_match_from_nlattrs(struct sw_flow_match *match,
-                          const struct nlattr *key,
-                          const struct nlattr *mask)
-{
-       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
-       const struct nlattr *encap;
-       u64 key_attrs = 0;
-       u64 mask_attrs = 0;
-       bool encap_valid = false;
-       int err;
-
-       err = parse_flow_nlattrs(key, a, &key_attrs);
-       if (err)
-               return err;
-
-       if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) &&
-           (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
-           (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
-               __be16 tci;
-
-               if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
-                     (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
-                       OVS_NLERR("Invalid Vlan frame.\n");
-                       return -EINVAL;
-               }
-
-               key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
-               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-               encap = a[OVS_KEY_ATTR_ENCAP];
-               key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
-               encap_valid = true;
-
-               if (tci & htons(VLAN_TAG_PRESENT)) {
-                       err = parse_flow_nlattrs(encap, a, &key_attrs);
-                       if (err)
-                               return err;
-               } else if (!tci) {
-                       /* Corner case for truncated 802.1Q header. */
-                       if (nla_len(encap)) {
-                               OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n");
-                               return -EINVAL;
-                       }
-               } else {
-                       OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
-                       return  -EINVAL;
-               }
-       }
-
-       err = ovs_key_from_nlattrs(match, key_attrs, a, false);
-       if (err)
-               return err;
-
-       if (mask) {
-               err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
-               if (err)
-                       return err;
-
-               if (mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP)  {
-                       __be16 eth_type = 0;
-                       __be16 tci = 0;
-
-                       if (!encap_valid) {
-                               OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n");
-                               return  -EINVAL;
-                       }
-
-                       mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
-                       if (a[OVS_KEY_ATTR_ETHERTYPE])
-                               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
-
-                       if (eth_type == htons(0xffff)) {
-                               mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
-                               encap = a[OVS_KEY_ATTR_ENCAP];
-                               err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
-                       } else {
-                               OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n",
-                                               ntohs(eth_type));
-                               return -EINVAL;
-                       }
-
-                       if (a[OVS_KEY_ATTR_VLAN])
-                               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-
-                       if (!(tci & htons(VLAN_TAG_PRESENT))) {
-                               OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci));
-                               return -EINVAL;
-                       }
-               }
-
-               err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
-               if (err)
-                       return err;
-       } else {
-               /* Populate exact match flow's key mask. */
-               if (match->mask)
-                       ovs_sw_flow_mask_set(match->mask, &match->range, 0xff);
-       }
-
-       if (!ovs_match_validate(match, key_attrs, mask_attrs))
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key.
- * @flow: Receives extracted in_port, priority, tun_key and skb_mark.
- * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
- * sequence.
- *
- * This parses a series of Netlink attributes that form a flow key, which must
- * take the same form accepted by flow_from_nlattrs(), but only enough of it to
- * get the metadata, that is, the parts of the flow key that cannot be
- * extracted from the packet itself.
- */
-
-int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow,
-               const struct nlattr *attr)
-{
-       struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
-       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
-       u64 attrs = 0;
-       int err;
-       struct sw_flow_match match;
-
-       flow->key.phy.in_port = DP_MAX_PORTS;
-       flow->key.phy.priority = 0;
-       flow->key.phy.skb_mark = 0;
-       memset(tun_key, 0, sizeof(flow->key.tun_key));
-
-       err = parse_flow_nlattrs(attr, a, &attrs);
-       if (err)
-               return -EINVAL;
-
-       memset(&match, 0, sizeof(match));
-       match.key = &flow->key;
-
-       err = metadata_from_nlattrs(&match, &attrs, a, false);
-       if (err)
-               return err;
-
-       return 0;
-}
-
-int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
-               const struct sw_flow_key *output, struct sk_buff *skb)
-{
-       struct ovs_key_ethernet *eth_key;
-       struct nlattr *nla, *encap;
-       bool is_mask = (swkey != output);
-
-       if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
-               goto nla_put_failure;
-
-       if ((swkey->tun_key.ipv4_dst || is_mask) &&
-           ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
-               goto nla_put_failure;
-
-       if (swkey->phy.in_port == DP_MAX_PORTS) {
-               if (is_mask && (output->phy.in_port == 0xffff))
-                       if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff))
-                               goto nla_put_failure;
-       } else {
-               u16 upper_u16;
-               upper_u16 = !is_mask ? 0 : 0xffff;
-
-               if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT,
-                               (upper_u16 << 16) | output->phy.in_port))
-                       goto nla_put_failure;
-       }
-
-       if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
-               goto nla_put_failure;
-
-       nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
-       if (!nla)
-               goto nla_put_failure;
-
-       eth_key = nla_data(nla);
-       memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN);
-       memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN);
-
-       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
-               __be16 eth_type;
-               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
-               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
-                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
-                       goto nla_put_failure;
-               encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
-               if (!swkey->eth.tci)
-                       goto unencap;
-       } else
-               encap = NULL;
-
-       if (swkey->eth.type == htons(ETH_P_802_2)) {
-               /*
-                * Ethertype 802.2 is represented in the netlink with omitted
-                * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and
-                * 0xffff in the mask attribute.  Ethertype can also
-                * be wildcarded.
-                */
-               if (is_mask && output->eth.type)
-                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
-                                               output->eth.type))
-                               goto nla_put_failure;
-               goto unencap;
-       }
-
-       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
-               goto nla_put_failure;
-
-       if (swkey->eth.type == htons(ETH_P_IP)) {
-               struct ovs_key_ipv4 *ipv4_key;
-
-               nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4, sizeof(*ipv4_key));
-               if (!nla)
-                       goto nla_put_failure;
-               ipv4_key = nla_data(nla);
-               ipv4_key->ipv4_src = output->ipv4.addr.src;
-               ipv4_key->ipv4_dst = output->ipv4.addr.dst;
-               ipv4_key->ipv4_proto = output->ip.proto;
-               ipv4_key->ipv4_tos = output->ip.tos;
-               ipv4_key->ipv4_ttl = output->ip.ttl;
-               ipv4_key->ipv4_frag = output->ip.frag;
-       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-               struct ovs_key_ipv6 *ipv6_key;
-
-               nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key));
-               if (!nla)
-                       goto nla_put_failure;
-               ipv6_key = nla_data(nla);
-               memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src,
-                               sizeof(ipv6_key->ipv6_src));
-               memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst,
-                               sizeof(ipv6_key->ipv6_dst));
-               ipv6_key->ipv6_label = output->ipv6.label;
-               ipv6_key->ipv6_proto = output->ip.proto;
-               ipv6_key->ipv6_tclass = output->ip.tos;
-               ipv6_key->ipv6_hlimit = output->ip.ttl;
-               ipv6_key->ipv6_frag = output->ip.frag;
-       } else if (swkey->eth.type == htons(ETH_P_ARP) ||
-                  swkey->eth.type == htons(ETH_P_RARP)) {
-               struct ovs_key_arp *arp_key;
-
-               nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key));
-               if (!nla)
-                       goto nla_put_failure;
-               arp_key = nla_data(nla);
-               memset(arp_key, 0, sizeof(struct ovs_key_arp));
-               arp_key->arp_sip = output->ipv4.addr.src;
-               arp_key->arp_tip = output->ipv4.addr.dst;
-               arp_key->arp_op = htons(output->ip.proto);
-               memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN);
-               memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN);
-       }
-
-       if ((swkey->eth.type == htons(ETH_P_IP) ||
-            swkey->eth.type == htons(ETH_P_IPV6)) &&
-            swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-
-               if (swkey->ip.proto == IPPROTO_TCP) {
-                       struct ovs_key_tcp *tcp_key;
-
-                       nla = nla_reserve(skb, OVS_KEY_ATTR_TCP, sizeof(*tcp_key));
-                       if (!nla)
-                               goto nla_put_failure;
-                       tcp_key = nla_data(nla);
-                       if (swkey->eth.type == htons(ETH_P_IP)) {
-                               tcp_key->tcp_src = output->ipv4.tp.src;
-                               tcp_key->tcp_dst = output->ipv4.tp.dst;
-                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               tcp_key->tcp_src = output->ipv6.tp.src;
-                               tcp_key->tcp_dst = output->ipv6.tp.dst;
-                       }
-               } else if (swkey->ip.proto == IPPROTO_UDP) {
-                       struct ovs_key_udp *udp_key;
-
-                       nla = nla_reserve(skb, OVS_KEY_ATTR_UDP, sizeof(*udp_key));
-                       if (!nla)
-                               goto nla_put_failure;
-                       udp_key = nla_data(nla);
-                       if (swkey->eth.type == htons(ETH_P_IP)) {
-                               udp_key->udp_src = output->ipv4.tp.src;
-                               udp_key->udp_dst = output->ipv4.tp.dst;
-                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               udp_key->udp_src = output->ipv6.tp.src;
-                               udp_key->udp_dst = output->ipv6.tp.dst;
-                       }
-               } else if (swkey->ip.proto == IPPROTO_SCTP) {
-                       struct ovs_key_sctp *sctp_key;
-
-                       nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key));
-                       if (!nla)
-                               goto nla_put_failure;
-                       sctp_key = nla_data(nla);
-                       if (swkey->eth.type == htons(ETH_P_IP)) {
-                               sctp_key->sctp_src = swkey->ipv4.tp.src;
-                               sctp_key->sctp_dst = swkey->ipv4.tp.dst;
-                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               sctp_key->sctp_src = swkey->ipv6.tp.src;
-                               sctp_key->sctp_dst = swkey->ipv6.tp.dst;
-                       }
-               } else if (swkey->eth.type == htons(ETH_P_IP) &&
-                          swkey->ip.proto == IPPROTO_ICMP) {
-                       struct ovs_key_icmp *icmp_key;
-
-                       nla = nla_reserve(skb, OVS_KEY_ATTR_ICMP, sizeof(*icmp_key));
-                       if (!nla)
-                               goto nla_put_failure;
-                       icmp_key = nla_data(nla);
-                       icmp_key->icmp_type = ntohs(output->ipv4.tp.src);
-                       icmp_key->icmp_code = ntohs(output->ipv4.tp.dst);
-               } else if (swkey->eth.type == htons(ETH_P_IPV6) &&
-                          swkey->ip.proto == IPPROTO_ICMPV6) {
-                       struct ovs_key_icmpv6 *icmpv6_key;
-
-                       nla = nla_reserve(skb, OVS_KEY_ATTR_ICMPV6,
-                                               sizeof(*icmpv6_key));
-                       if (!nla)
-                               goto nla_put_failure;
-                       icmpv6_key = nla_data(nla);
-                       icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src);
-                       icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst);
-
-                       if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION ||
-                           icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
-                               struct ovs_key_nd *nd_key;
-
-                               nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key));
-                               if (!nla)
-                                       goto nla_put_failure;
-                               nd_key = nla_data(nla);
-                               memcpy(nd_key->nd_target, &output->ipv6.nd.target,
-                                                       sizeof(nd_key->nd_target));
-                               memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN);
-                               memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN);
-                       }
-               }
-       }
-
-unencap:
-       if (encap)
-               nla_nest_end(skb, encap);
-
-       return 0;
-
-nla_put_failure:
-       return -EMSGSIZE;
-}
-
-/* Initializes the flow module.
- * Returns zero if successful or a negative error code. */
-int ovs_flow_init(void)
-{
-       BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long));
-       BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long));
-
-       flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0,
-                                       0, NULL);
-       if (flow_cache == NULL)
-               return -ENOMEM;
-
-       return 0;
-}
-
-/* Uninitializes the flow module. */
-void ovs_flow_exit(void)
-{
-       kmem_cache_destroy(flow_cache);
-}
-
-struct sw_flow_mask *ovs_sw_flow_mask_alloc(void)
-{
-       struct sw_flow_mask *mask;
-
-       mask = kmalloc(sizeof(*mask), GFP_KERNEL);
-       if (mask)
-               mask->ref_count = 0;
-
-       return mask;
-}
-
-void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *mask)
-{
-       mask->ref_count++;
-}
-
-void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
-{
-       if (!mask)
-               return;
-
-       BUG_ON(!mask->ref_count);
-       mask->ref_count--;
-
-       if (!mask->ref_count) {
-               list_del_rcu(&mask->list);
-               if (deferred)
-                       kfree_rcu(mask, rcu);
-               else
-                       kfree(mask);
-       }
-}
-
-static bool ovs_sw_flow_mask_equal(const struct sw_flow_mask *a,
-               const struct sw_flow_mask *b)
-{
-       u8 *a_ = (u8 *)&a->key + a->range.start;
-       u8 *b_ = (u8 *)&b->key + b->range.start;
-
-       return  (a->range.end == b->range.end)
-               && (a->range.start == b->range.start)
-               && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0);
-}
-
-struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl,
-                                           const struct sw_flow_mask *mask)
-{
-       struct list_head *ml;
-
-       list_for_each(ml, tbl->mask_list) {
-               struct sw_flow_mask *m;
-               m = container_of(ml, struct sw_flow_mask, list);
-               if (ovs_sw_flow_mask_equal(mask, m))
-                       return m;
-       }
-
-       return NULL;
-}
-
-/**
- * add a new mask into the mask list.
- * The caller needs to make sure that 'mask' is not the same
- * as any masks that are already on the list.
- */
-void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask)
-{
-       list_add_rcu(&mask->list, tbl->mask_list);
-}
-
-/**
- * Set 'range' fields in the mask to the value of 'val'.
- */
-static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask,
-               struct sw_flow_key_range *range, u8 val)
-{
-       u8 *m = (u8 *)&mask->key + range->start;
-
-       mask->range = *range;
-       memset(m, val, range_n_bytes(range));
-}
index 212fbf7..1510f51 100644 (file)
 #include <net/inet_ecn.h>
 
 struct sk_buff;
-struct sw_flow_mask;
-struct flow_table;
-
-struct sw_flow_actions {
-       struct rcu_head rcu;
-       u32 actions_len;
-       struct nlattr actions[];
-};
 
 /* Used to memset ovs_key_ipv4_tunnel padding. */
 #define OVS_TUNNEL_KEY_SIZE                                    \
@@ -101,6 +93,7 @@ struct sw_flow_key {
                                struct {
                                        __be16 src;             /* TCP/UDP/SCTP source port. */
                                        __be16 dst;             /* TCP/UDP/SCTP destination port. */
+                                       __be16 flags;           /* TCP flags. */
                                } tp;
                                struct {
                                        u8 sha[ETH_ALEN];       /* ARP source hardware address. */
@@ -117,6 +110,7 @@ struct sw_flow_key {
                        struct {
                                __be16 src;             /* TCP/UDP/SCTP source port. */
                                __be16 dst;             /* TCP/UDP/SCTP destination port. */
+                               __be16 flags;           /* TCP flags. */
                        } tp;
                        struct {
                                struct in6_addr target; /* ND target address. */
@@ -127,6 +121,31 @@ struct sw_flow_key {
        };
 } __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */
 
+struct sw_flow_key_range {
+       size_t start;
+       size_t end;
+};
+
+struct sw_flow_mask {
+       int ref_count;
+       struct rcu_head rcu;
+       struct list_head list;
+       struct sw_flow_key_range range;
+       struct sw_flow_key key;
+};
+
+struct sw_flow_match {
+       struct sw_flow_key *key;
+       struct sw_flow_key_range range;
+       struct sw_flow_mask *mask;
+};
+
+struct sw_flow_actions {
+       struct rcu_head rcu;
+       u32 actions_len;
+       struct nlattr actions[];
+};
+
 struct sw_flow {
        struct rcu_head rcu;
        struct hlist_node hash_node[2];
@@ -141,23 +160,9 @@ struct sw_flow {
        unsigned long used;     /* Last used time (in jiffies). */
        u64 packet_count;       /* Number of packets matched. */
        u64 byte_count;         /* Number of bytes matched. */
-       u8 tcp_flags;           /* Union of seen TCP flags. */
-};
-
-struct sw_flow_key_range {
-       size_t start;
-       size_t end;
+       __be16 tcp_flags;       /* Union of seen TCP flags. */
 };
 
-struct sw_flow_match {
-       struct sw_flow_key *key;
-       struct sw_flow_key_range range;
-       struct sw_flow_mask *mask;
-};
-
-void ovs_match_init(struct sw_flow_match *match,
-               struct sw_flow_key *key, struct sw_flow_mask *mask);
-
 struct arp_eth_header {
        __be16      ar_hrd;     /* format of hardware address   */
        __be16      ar_pro;     /* format of protocol address   */
@@ -172,88 +177,9 @@ struct arp_eth_header {
        unsigned char       ar_tip[4];          /* target IP address        */
 } __packed;
 
-int ovs_flow_init(void);
-void ovs_flow_exit(void);
-
-struct sw_flow *ovs_flow_alloc(void);
-void ovs_flow_deferred_free(struct sw_flow *);
-void ovs_flow_free(struct sw_flow *, bool deferred);
-
-struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
-void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
-
-int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
 void ovs_flow_used(struct sw_flow *, struct sk_buff *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
-int ovs_flow_to_nlattrs(const struct sw_flow_key *,
-               const struct sw_flow_key *, struct sk_buff *);
-int ovs_match_from_nlattrs(struct sw_flow_match *match,
-                     const struct nlattr *,
-                     const struct nlattr *);
-int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow,
-               const struct nlattr *attr);
 
-#define MAX_ACTIONS_BUFSIZE    (32 * 1024)
-#define TBL_MIN_BUCKETS                1024
-
-struct flow_table {
-       struct flex_array *buckets;
-       unsigned int count, n_buckets;
-       struct rcu_head rcu;
-       struct list_head *mask_list;
-       int node_ver;
-       u32 hash_seed;
-       bool keep_flows;
-};
-
-static inline int ovs_flow_tbl_count(struct flow_table *table)
-{
-       return table->count;
-}
-
-static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table)
-{
-       return (table->count > table->n_buckets);
-}
-
-struct sw_flow *ovs_flow_lookup(struct flow_table *,
-                               const struct sw_flow_key *);
-struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
-                                   struct sw_flow_match *match);
-
-void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred);
-struct flow_table *ovs_flow_tbl_alloc(int new_size);
-struct flow_table *ovs_flow_tbl_expand(struct flow_table *table);
-struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table);
-
-void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow);
-void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow);
-
-struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx);
-extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
-int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
-                            struct sw_flow_match *match, bool is_mask);
-int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
-                          const struct ovs_key_ipv4_tunnel *tun_key,
-                          const struct ovs_key_ipv4_tunnel *output);
-
-bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_end);
-
-struct sw_flow_mask {
-       int ref_count;
-       struct rcu_head rcu;
-       struct list_head list;
-       struct sw_flow_key_range range;
-       struct sw_flow_key key;
-};
+int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
 
-struct sw_flow_mask *ovs_sw_flow_mask_alloc(void);
-void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *);
-void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred);
-void ovs_sw_flow_mask_insert(struct flow_table *, struct sw_flow_mask *);
-struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *,
-               const struct sw_flow_mask *);
-void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
-                      const struct sw_flow_mask *mask);
 #endif /* flow.h */
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
new file mode 100644 (file)
index 0000000..2bc1bc1
--- /dev/null
@@ -0,0 +1,1630 @@
+/*
+ * Copyright (c) 2007-2013 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "flow.h"
+#include "datapath.h"
+#include <linux/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <net/llc_pdu.h>
+#include <linux/kernel.h>
+#include <linux/jhash.h>
+#include <linux/jiffies.h>
+#include <linux/llc.h>
+#include <linux/module.h>
+#include <linux/in.h>
+#include <linux/rcupdate.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/sctp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+#include <linux/rculist.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+
+#include "flow_netlink.h"
+
+static void update_range__(struct sw_flow_match *match,
+                          size_t offset, size_t size, bool is_mask)
+{
+       struct sw_flow_key_range *range = NULL;
+       size_t start = rounddown(offset, sizeof(long));
+       size_t end = roundup(offset + size, sizeof(long));
+
+       if (!is_mask)
+               range = &match->range;
+       else if (match->mask)
+               range = &match->mask->range;
+
+       if (!range)
+               return;
+
+       if (range->start == range->end) {
+               range->start = start;
+               range->end = end;
+               return;
+       }
+
+       if (range->start > start)
+               range->start = start;
+
+       if (range->end < end)
+               range->end = end;
+}
+
+#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \
+       do { \
+               update_range__(match, offsetof(struct sw_flow_key, field),  \
+                                    sizeof((match)->key->field), is_mask); \
+               if (is_mask) {                                              \
+                       if ((match)->mask)                                  \
+                               (match)->mask->key.field = value;           \
+               } else {                                                    \
+                       (match)->key->field = value;                        \
+               }                                                           \
+       } while (0)
+
+#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \
+       do { \
+               update_range__(match, offsetof(struct sw_flow_key, field),  \
+                               len, is_mask);                              \
+               if (is_mask) {                                              \
+                       if ((match)->mask)                                  \
+                               memcpy(&(match)->mask->key.field, value_p, len);\
+               } else {                                                    \
+                       memcpy(&(match)->key->field, value_p, len);         \
+               }                                                           \
+       } while (0)
+
+static u16 range_n_bytes(const struct sw_flow_key_range *range)
+{
+       return range->end - range->start;
+}
+
+static bool match_validate(const struct sw_flow_match *match,
+                          u64 key_attrs, u64 mask_attrs)
+{
+       u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET;
+       u64 mask_allowed = key_attrs;  /* At most allow all key attributes */
+
+       /* The following mask attributes allowed only if they
+        * pass the validation tests. */
+       mask_allowed &= ~((1 << OVS_KEY_ATTR_IPV4)
+                       | (1 << OVS_KEY_ATTR_IPV6)
+                       | (1 << OVS_KEY_ATTR_TCP)
+                       | (1 << OVS_KEY_ATTR_TCP_FLAGS)
+                       | (1 << OVS_KEY_ATTR_UDP)
+                       | (1 << OVS_KEY_ATTR_SCTP)
+                       | (1 << OVS_KEY_ATTR_ICMP)
+                       | (1 << OVS_KEY_ATTR_ICMPV6)
+                       | (1 << OVS_KEY_ATTR_ARP)
+                       | (1 << OVS_KEY_ATTR_ND));
+
+       /* Always allowed mask fields. */
+       mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL)
+                      | (1 << OVS_KEY_ATTR_IN_PORT)
+                      | (1 << OVS_KEY_ATTR_ETHERTYPE));
+
+       /* Check key attributes. */
+       if (match->key->eth.type == htons(ETH_P_ARP)
+                       || match->key->eth.type == htons(ETH_P_RARP)) {
+               key_expected |= 1 << OVS_KEY_ATTR_ARP;
+               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+                       mask_allowed |= 1 << OVS_KEY_ATTR_ARP;
+       }
+
+       if (match->key->eth.type == htons(ETH_P_IP)) {
+               key_expected |= 1 << OVS_KEY_ATTR_IPV4;
+               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+                       mask_allowed |= 1 << OVS_KEY_ATTR_IPV4;
+
+               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+                       if (match->key->ip.proto == IPPROTO_UDP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_UDP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_UDP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_SCTP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_SCTP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_SCTP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_TCP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_TCP;
+                               key_expected |= 1 << OVS_KEY_ATTR_TCP_FLAGS;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff)) {
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_TCP;
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_TCP_FLAGS;
+                               }
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_ICMP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_ICMP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_ICMP;
+                       }
+               }
+       }
+
+       if (match->key->eth.type == htons(ETH_P_IPV6)) {
+               key_expected |= 1 << OVS_KEY_ATTR_IPV6;
+               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+                       mask_allowed |= 1 << OVS_KEY_ATTR_IPV6;
+
+               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+                       if (match->key->ip.proto == IPPROTO_UDP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_UDP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_UDP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_SCTP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_SCTP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_SCTP;
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_TCP) {
+                               key_expected |= 1 << OVS_KEY_ATTR_TCP;
+                               key_expected |= 1 << OVS_KEY_ATTR_TCP_FLAGS;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff)) {
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_TCP;
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_TCP_FLAGS;
+                               }
+                       }
+
+                       if (match->key->ip.proto == IPPROTO_ICMPV6) {
+                               key_expected |= 1 << OVS_KEY_ATTR_ICMPV6;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6;
+
+                               if (match->key->ipv6.tp.src ==
+                                               htons(NDISC_NEIGHBOUR_SOLICITATION) ||
+                                   match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
+                                       key_expected |= 1 << OVS_KEY_ATTR_ND;
+                                       if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff)))
+                                               mask_allowed |= 1 << OVS_KEY_ATTR_ND;
+                               }
+                       }
+               }
+       }
+
+       if ((key_attrs & key_expected) != key_expected) {
+               /* Key attributes check failed. */
+               OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n",
+                               key_attrs, key_expected);
+               return false;
+       }
+
+       if ((mask_attrs & mask_allowed) != mask_attrs) {
+               /* Mask attributes check failed. */
+               OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n",
+                               mask_attrs, mask_allowed);
+               return false;
+       }
+
+       return true;
+}
+
+/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
+static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
+       [OVS_KEY_ATTR_ENCAP] = -1,
+       [OVS_KEY_ATTR_PRIORITY] = sizeof(u32),
+       [OVS_KEY_ATTR_IN_PORT] = sizeof(u32),
+       [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32),
+       [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),
+       [OVS_KEY_ATTR_VLAN] = sizeof(__be16),
+       [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16),
+       [OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4),
+       [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
+       [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),
+       [OVS_KEY_ATTR_TCP_FLAGS] = sizeof(__be16),
+       [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp),
+       [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp),
+       [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp),
+       [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
+       [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
+       [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
+       [OVS_KEY_ATTR_TUNNEL] = -1,
+};
+
+static bool is_all_zero(const u8 *fp, size_t size)
+{
+       int i;
+
+       if (!fp)
+               return false;
+
+       for (i = 0; i < size; i++)
+               if (fp[i])
+                       return false;
+
+       return true;
+}
+
+static int __parse_flow_nlattrs(const struct nlattr *attr,
+                               const struct nlattr *a[],
+                               u64 *attrsp, bool nz)
+{
+       const struct nlattr *nla;
+       u64 attrs;
+       int rem;
+
+       attrs = *attrsp;
+       nla_for_each_nested(nla, attr, rem) {
+               u16 type = nla_type(nla);
+               int expected_len;
+
+               if (type > OVS_KEY_ATTR_MAX) {
+                       OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n",
+                                 type, OVS_KEY_ATTR_MAX);
+                       return -EINVAL;
+               }
+
+               if (attrs & (1 << type)) {
+                       OVS_NLERR("Duplicate key attribute (type %d).\n", type);
+                       return -EINVAL;
+               }
+
+               expected_len = ovs_key_lens[type];
+               if (nla_len(nla) != expected_len && expected_len != -1) {
+                       OVS_NLERR("Key attribute has unexpected length (type=%d"
+                                 ", length=%d, expected=%d).\n", type,
+                                 nla_len(nla), expected_len);
+                       return -EINVAL;
+               }
+
+               if (!nz || !is_all_zero(nla_data(nla), expected_len)) {
+                       attrs |= 1 << type;
+                       a[type] = nla;
+               }
+       }
+       if (rem) {
+               OVS_NLERR("Message has %d unknown bytes.\n", rem);
+               return -EINVAL;
+       }
+
+       *attrsp = attrs;
+       return 0;
+}
+
+static int parse_flow_mask_nlattrs(const struct nlattr *attr,
+                                  const struct nlattr *a[], u64 *attrsp)
+{
+       return __parse_flow_nlattrs(attr, a, attrsp, true);
+}
+
+static int parse_flow_nlattrs(const struct nlattr *attr,
+                             const struct nlattr *a[], u64 *attrsp)
+{
+       return __parse_flow_nlattrs(attr, a, attrsp, false);
+}
+
+static int ipv4_tun_from_nlattr(const struct nlattr *attr,
+                               struct sw_flow_match *match, bool is_mask)
+{
+       struct nlattr *a;
+       int rem;
+       bool ttl = false;
+       __be16 tun_flags = 0;
+
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+               static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
+                       [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64),
+                       [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32),
+                       [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = sizeof(u32),
+                       [OVS_TUNNEL_KEY_ATTR_TOS] = 1,
+                       [OVS_TUNNEL_KEY_ATTR_TTL] = 1,
+                       [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0,
+                       [OVS_TUNNEL_KEY_ATTR_CSUM] = 0,
+               };
+
+               if (type > OVS_TUNNEL_KEY_ATTR_MAX) {
+                       OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n",
+                       type, OVS_TUNNEL_KEY_ATTR_MAX);
+                       return -EINVAL;
+               }
+
+               if (ovs_tunnel_key_lens[type] != nla_len(a)) {
+                       OVS_NLERR("IPv4 tunnel attribute type has unexpected "
+                                 " length (type=%d, length=%d, expected=%d).\n",
+                                 type, nla_len(a), ovs_tunnel_key_lens[type]);
+                       return -EINVAL;
+               }
+
+               switch (type) {
+               case OVS_TUNNEL_KEY_ATTR_ID:
+                       SW_FLOW_KEY_PUT(match, tun_key.tun_id,
+                                       nla_get_be64(a), is_mask);
+                       tun_flags |= TUNNEL_KEY;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_src,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_TOS:
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos,
+                                       nla_get_u8(a), is_mask);
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_TTL:
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl,
+                                       nla_get_u8(a), is_mask);
+                       ttl = true;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
+                       tun_flags |= TUNNEL_DONT_FRAGMENT;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_CSUM:
+                       tun_flags |= TUNNEL_CSUM;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
+
+       if (rem > 0) {
+               OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem);
+               return -EINVAL;
+       }
+
+       if (!is_mask) {
+               if (!match->key->tun_key.ipv4_dst) {
+                       OVS_NLERR("IPv4 tunnel destination address is zero.\n");
+                       return -EINVAL;
+               }
+
+               if (!ttl) {
+                       OVS_NLERR("IPv4 tunnel TTL not specified.\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int ipv4_tun_to_nlattr(struct sk_buff *skb,
+                             const struct ovs_key_ipv4_tunnel *tun_key,
+                             const struct ovs_key_ipv4_tunnel *output)
+{
+       struct nlattr *nla;
+
+       nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
+       if (!nla)
+               return -EMSGSIZE;
+
+       if (output->tun_flags & TUNNEL_KEY &&
+           nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
+               return -EMSGSIZE;
+       if (output->ipv4_src &&
+               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
+               return -EMSGSIZE;
+       if (output->ipv4_dst &&
+               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
+               return -EMSGSIZE;
+       if (output->ipv4_tos &&
+               nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
+               return -EMSGSIZE;
+       if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl))
+               return -EMSGSIZE;
+       if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) &&
+               nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
+               return -EMSGSIZE;
+       if ((output->tun_flags & TUNNEL_CSUM) &&
+               nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
+               return -EMSGSIZE;
+
+       nla_nest_end(skb, nla);
+       return 0;
+}
+
+
+static int metadata_from_nlattrs(struct sw_flow_match *match,  u64 *attrs,
+                                const struct nlattr **a, bool is_mask)
+{
+       if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) {
+               SW_FLOW_KEY_PUT(match, phy.priority,
+                         nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask);
+               *attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY);
+       }
+
+       if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
+               u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
+
+               if (is_mask)
+                       in_port = 0xffffffff; /* Always exact match in_port. */
+               else if (in_port >= DP_MAX_PORTS)
+                       return -EINVAL;
+
+               SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask);
+               *attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
+       } else if (!is_mask) {
+               SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask);
+       }
+
+       if (*attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
+               uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
+
+               SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask);
+               *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
+       }
+       if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) {
+               if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
+                                        is_mask))
+                       return -EINVAL;
+               *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL);
+       }
+       return 0;
+}
+
+static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
+                               const struct nlattr **a, bool is_mask)
+{
+       int err;
+       u64 orig_attrs = attrs;
+
+       err = metadata_from_nlattrs(match, &attrs, a, is_mask);
+       if (err)
+               return err;
+
+       if (attrs & (1 << OVS_KEY_ATTR_ETHERNET)) {
+               const struct ovs_key_ethernet *eth_key;
+
+               eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
+               SW_FLOW_KEY_MEMCPY(match, eth.src,
+                               eth_key->eth_src, ETH_ALEN, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, eth.dst,
+                               eth_key->eth_dst, ETH_ALEN, is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_VLAN)) {
+               __be16 tci;
+
+               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+               if (!(tci & htons(VLAN_TAG_PRESENT))) {
+                       if (is_mask)
+                               OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n");
+                       else
+                               OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n");
+
+                       return -EINVAL;
+               }
+
+               SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
+       } else if (!is_mask)
+               SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
+
+       if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
+               __be16 eth_type;
+
+               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+               if (is_mask) {
+                       /* Always exact match EtherType. */
+                       eth_type = htons(0xffff);
+               } else if (ntohs(eth_type) < ETH_P_802_3_MIN) {
+                       OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n",
+                                       ntohs(eth_type), ETH_P_802_3_MIN);
+                       return -EINVAL;
+               }
+
+               SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
+       } else if (!is_mask) {
+               SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
+               const struct ovs_key_ipv4 *ipv4_key;
+
+               ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
+               if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) {
+                       OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n",
+                               ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX);
+                       return -EINVAL;
+               }
+               SW_FLOW_KEY_PUT(match, ip.proto,
+                               ipv4_key->ipv4_proto, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.tos,
+                               ipv4_key->ipv4_tos, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.ttl,
+                               ipv4_key->ipv4_ttl, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.frag,
+                               ipv4_key->ipv4_frag, is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+                               ipv4_key->ipv4_src, is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+                               ipv4_key->ipv4_dst, is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_IPV6)) {
+               const struct ovs_key_ipv6 *ipv6_key;
+
+               ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
+               if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) {
+                       OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n",
+                               ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX);
+                       return -EINVAL;
+               }
+               SW_FLOW_KEY_PUT(match, ipv6.label,
+                               ipv6_key->ipv6_label, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.proto,
+                               ipv6_key->ipv6_proto, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.tos,
+                               ipv6_key->ipv6_tclass, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.ttl,
+                               ipv6_key->ipv6_hlimit, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.frag,
+                               ipv6_key->ipv6_frag, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src,
+                               ipv6_key->ipv6_src,
+                               sizeof(match->key->ipv6.addr.src),
+                               is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst,
+                               ipv6_key->ipv6_dst,
+                               sizeof(match->key->ipv6.addr.dst),
+                               is_mask);
+
+               attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
+               const struct ovs_key_arp *arp_key;
+
+               arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
+               if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
+                       OVS_NLERR("Unknown ARP opcode (opcode=%d).\n",
+                                 arp_key->arp_op);
+                       return -EINVAL;
+               }
+
+               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+                               arp_key->arp_sip, is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+                       arp_key->arp_tip, is_mask);
+               SW_FLOW_KEY_PUT(match, ip.proto,
+                               ntohs(arp_key->arp_op), is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha,
+                               arp_key->arp_sha, ETH_ALEN, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha,
+                               arp_key->arp_tha, ETH_ALEN, is_mask);
+
+               attrs &= ~(1 << OVS_KEY_ATTR_ARP);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_TCP)) {
+               const struct ovs_key_tcp *tcp_key;
+
+               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
+               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                       tcp_key->tcp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                       tcp_key->tcp_dst, is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                       tcp_key->tcp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                       tcp_key->tcp_dst, is_mask);
+               }
+               attrs &= ~(1 << OVS_KEY_ATTR_TCP);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
+               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.flags,
+                                       nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+                                       is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.flags,
+                                       nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+                                       is_mask);
+               }
+               attrs &= ~(1 << OVS_KEY_ATTR_TCP_FLAGS);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_UDP)) {
+               const struct ovs_key_udp *udp_key;
+
+               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
+               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                       udp_key->udp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                       udp_key->udp_dst, is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                       udp_key->udp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                       udp_key->udp_dst, is_mask);
+               }
+               attrs &= ~(1 << OVS_KEY_ATTR_UDP);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_SCTP)) {
+               const struct ovs_key_sctp *sctp_key;
+
+               sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]);
+               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                       sctp_key->sctp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                       sctp_key->sctp_dst, is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                       sctp_key->sctp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                       sctp_key->sctp_dst, is_mask);
+               }
+               attrs &= ~(1 << OVS_KEY_ATTR_SCTP);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_ICMP)) {
+               const struct ovs_key_icmp *icmp_key;
+
+               icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
+               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                               htons(icmp_key->icmp_type), is_mask);
+               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                               htons(icmp_key->icmp_code), is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_ICMPV6)) {
+               const struct ovs_key_icmpv6 *icmpv6_key;
+
+               icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
+               SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                               htons(icmpv6_key->icmpv6_type), is_mask);
+               SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                               htons(icmpv6_key->icmpv6_code), is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
+       }
+
+       if (attrs & (1 << OVS_KEY_ATTR_ND)) {
+               const struct ovs_key_nd *nd_key;
+
+               nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target,
+                       nd_key->nd_target,
+                       sizeof(match->key->ipv6.nd.target),
+                       is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll,
+                       nd_key->nd_sll, ETH_ALEN, is_mask);
+               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll,
+                               nd_key->nd_tll, ETH_ALEN, is_mask);
+               attrs &= ~(1 << OVS_KEY_ATTR_ND);
+       }
+
+       if (attrs != 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void sw_flow_mask_set(struct sw_flow_mask *mask,
+                            struct sw_flow_key_range *range, u8 val)
+{
+       u8 *m = (u8 *)&mask->key + range->start;
+
+       mask->range = *range;
+       memset(m, val, range_n_bytes(range));
+}
+
+/**
+ * ovs_nla_get_match - parses Netlink attributes into a flow key and
+ * mask. In case the 'mask' is NULL, the flow is treated as exact match
+ * flow. Otherwise, it is treated as a wildcarded flow, except the mask
+ * does not include any don't care bit.
+ * @match: receives the extracted flow match information.
+ * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
+ * sequence. The fields should of the packet that triggered the creation
+ * of this flow.
+ * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink
+ * attribute specifies the mask field of the wildcarded flow.
+ */
+int ovs_nla_get_match(struct sw_flow_match *match,
+                     const struct nlattr *key,
+                     const struct nlattr *mask)
+{
+       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
+       const struct nlattr *encap;
+       u64 key_attrs = 0;
+       u64 mask_attrs = 0;
+       bool encap_valid = false;
+       int err;
+
+       err = parse_flow_nlattrs(key, a, &key_attrs);
+       if (err)
+               return err;
+
+       if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) &&
+           (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
+           (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
+               __be16 tci;
+
+               if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
+                     (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
+                       OVS_NLERR("Invalid Vlan frame.\n");
+                       return -EINVAL;
+               }
+
+               key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
+               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+               encap = a[OVS_KEY_ATTR_ENCAP];
+               key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
+               encap_valid = true;
+
+               if (tci & htons(VLAN_TAG_PRESENT)) {
+                       err = parse_flow_nlattrs(encap, a, &key_attrs);
+                       if (err)
+                               return err;
+               } else if (!tci) {
+                       /* Corner case for truncated 802.1Q header. */
+                       if (nla_len(encap)) {
+                               OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
+                       return  -EINVAL;
+               }
+       }
+
+       err = ovs_key_from_nlattrs(match, key_attrs, a, false);
+       if (err)
+               return err;
+
+       if (mask) {
+               err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
+               if (err)
+                       return err;
+
+               if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP)  {
+                       __be16 eth_type = 0;
+                       __be16 tci = 0;
+
+                       if (!encap_valid) {
+                               OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n");
+                               return  -EINVAL;
+                       }
+
+                       mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
+                       if (a[OVS_KEY_ATTR_ETHERTYPE])
+                               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+
+                       if (eth_type == htons(0xffff)) {
+                               mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
+                               encap = a[OVS_KEY_ATTR_ENCAP];
+                               err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
+                       } else {
+                               OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n",
+                                               ntohs(eth_type));
+                               return -EINVAL;
+                       }
+
+                       if (a[OVS_KEY_ATTR_VLAN])
+                               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+
+                       if (!(tci & htons(VLAN_TAG_PRESENT))) {
+                               OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci));
+                               return -EINVAL;
+                       }
+               }
+
+               err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
+               if (err)
+                       return err;
+       } else {
+               /* Populate exact match flow's key mask. */
+               if (match->mask)
+                       sw_flow_mask_set(match->mask, &match->range, 0xff);
+       }
+
+       if (!match_validate(match, key_attrs, mask_attrs))
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * ovs_nla_get_flow_metadata - parses Netlink attributes into a flow key.
+ * @flow: Receives extracted in_port, priority, tun_key and skb_mark.
+ * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
+ * sequence.
+ *
+ * This parses a series of Netlink attributes that form a flow key, which must
+ * take the same form accepted by flow_from_nlattrs(), but only enough of it to
+ * get the metadata, that is, the parts of the flow key that cannot be
+ * extracted from the packet itself.
+ */
+
+int ovs_nla_get_flow_metadata(struct sw_flow *flow,
+                             const struct nlattr *attr)
+{
+       struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
+       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
+       u64 attrs = 0;
+       int err;
+       struct sw_flow_match match;
+
+       flow->key.phy.in_port = DP_MAX_PORTS;
+       flow->key.phy.priority = 0;
+       flow->key.phy.skb_mark = 0;
+       memset(tun_key, 0, sizeof(flow->key.tun_key));
+
+       err = parse_flow_nlattrs(attr, a, &attrs);
+       if (err)
+               return -EINVAL;
+
+       memset(&match, 0, sizeof(match));
+       match.key = &flow->key;
+
+       err = metadata_from_nlattrs(&match, &attrs, a, false);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+int ovs_nla_put_flow(const struct sw_flow_key *swkey,
+                    const struct sw_flow_key *output, struct sk_buff *skb)
+{
+       struct ovs_key_ethernet *eth_key;
+       struct nlattr *nla, *encap;
+       bool is_mask = (swkey != output);
+
+       if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
+               goto nla_put_failure;
+
+       if ((swkey->tun_key.ipv4_dst || is_mask) &&
+           ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
+               goto nla_put_failure;
+
+       if (swkey->phy.in_port == DP_MAX_PORTS) {
+               if (is_mask && (output->phy.in_port == 0xffff))
+                       if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff))
+                               goto nla_put_failure;
+       } else {
+               u16 upper_u16;
+               upper_u16 = !is_mask ? 0 : 0xffff;
+
+               if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT,
+                               (upper_u16 << 16) | output->phy.in_port))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
+               goto nla_put_failure;
+
+       nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
+       if (!nla)
+               goto nla_put_failure;
+
+       eth_key = nla_data(nla);
+       memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN);
+       memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN);
+
+       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
+               __be16 eth_type;
+               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
+               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
+                   nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
+                       goto nla_put_failure;
+               encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
+               if (!swkey->eth.tci)
+                       goto unencap;
+       } else
+               encap = NULL;
+
+       if (swkey->eth.type == htons(ETH_P_802_2)) {
+               /*
+                * Ethertype 802.2 is represented in the netlink with omitted
+                * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and
+                * 0xffff in the mask attribute.  Ethertype can also
+                * be wildcarded.
+                */
+               if (is_mask && output->eth.type)
+                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
+                                               output->eth.type))
+                               goto nla_put_failure;
+               goto unencap;
+       }
+
+       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
+               goto nla_put_failure;
+
+       if (swkey->eth.type == htons(ETH_P_IP)) {
+               struct ovs_key_ipv4 *ipv4_key;
+
+               nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4, sizeof(*ipv4_key));
+               if (!nla)
+                       goto nla_put_failure;
+               ipv4_key = nla_data(nla);
+               ipv4_key->ipv4_src = output->ipv4.addr.src;
+               ipv4_key->ipv4_dst = output->ipv4.addr.dst;
+               ipv4_key->ipv4_proto = output->ip.proto;
+               ipv4_key->ipv4_tos = output->ip.tos;
+               ipv4_key->ipv4_ttl = output->ip.ttl;
+               ipv4_key->ipv4_frag = output->ip.frag;
+       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+               struct ovs_key_ipv6 *ipv6_key;
+
+               nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key));
+               if (!nla)
+                       goto nla_put_failure;
+               ipv6_key = nla_data(nla);
+               memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src,
+                               sizeof(ipv6_key->ipv6_src));
+               memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst,
+                               sizeof(ipv6_key->ipv6_dst));
+               ipv6_key->ipv6_label = output->ipv6.label;
+               ipv6_key->ipv6_proto = output->ip.proto;
+               ipv6_key->ipv6_tclass = output->ip.tos;
+               ipv6_key->ipv6_hlimit = output->ip.ttl;
+               ipv6_key->ipv6_frag = output->ip.frag;
+       } else if (swkey->eth.type == htons(ETH_P_ARP) ||
+                  swkey->eth.type == htons(ETH_P_RARP)) {
+               struct ovs_key_arp *arp_key;
+
+               nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key));
+               if (!nla)
+                       goto nla_put_failure;
+               arp_key = nla_data(nla);
+               memset(arp_key, 0, sizeof(struct ovs_key_arp));
+               arp_key->arp_sip = output->ipv4.addr.src;
+               arp_key->arp_tip = output->ipv4.addr.dst;
+               arp_key->arp_op = htons(output->ip.proto);
+               memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN);
+               memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN);
+       }
+
+       if ((swkey->eth.type == htons(ETH_P_IP) ||
+            swkey->eth.type == htons(ETH_P_IPV6)) &&
+            swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
+
+               if (swkey->ip.proto == IPPROTO_TCP) {
+                       struct ovs_key_tcp *tcp_key;
+
+                       nla = nla_reserve(skb, OVS_KEY_ATTR_TCP, sizeof(*tcp_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       tcp_key = nla_data(nla);
+                       if (swkey->eth.type == htons(ETH_P_IP)) {
+                               tcp_key->tcp_src = output->ipv4.tp.src;
+                               tcp_key->tcp_dst = output->ipv4.tp.dst;
+                               if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+                                                output->ipv4.tp.flags))
+                                       goto nla_put_failure;
+                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+                               tcp_key->tcp_src = output->ipv6.tp.src;
+                               tcp_key->tcp_dst = output->ipv6.tp.dst;
+                               if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+                                                output->ipv6.tp.flags))
+                                       goto nla_put_failure;
+                       }
+               } else if (swkey->ip.proto == IPPROTO_UDP) {
+                       struct ovs_key_udp *udp_key;
+
+                       nla = nla_reserve(skb, OVS_KEY_ATTR_UDP, sizeof(*udp_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       udp_key = nla_data(nla);
+                       if (swkey->eth.type == htons(ETH_P_IP)) {
+                               udp_key->udp_src = output->ipv4.tp.src;
+                               udp_key->udp_dst = output->ipv4.tp.dst;
+                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+                               udp_key->udp_src = output->ipv6.tp.src;
+                               udp_key->udp_dst = output->ipv6.tp.dst;
+                       }
+               } else if (swkey->ip.proto == IPPROTO_SCTP) {
+                       struct ovs_key_sctp *sctp_key;
+
+                       nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       sctp_key = nla_data(nla);
+                       if (swkey->eth.type == htons(ETH_P_IP)) {
+                               sctp_key->sctp_src = swkey->ipv4.tp.src;
+                               sctp_key->sctp_dst = swkey->ipv4.tp.dst;
+                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+                               sctp_key->sctp_src = swkey->ipv6.tp.src;
+                               sctp_key->sctp_dst = swkey->ipv6.tp.dst;
+                       }
+               } else if (swkey->eth.type == htons(ETH_P_IP) &&
+                          swkey->ip.proto == IPPROTO_ICMP) {
+                       struct ovs_key_icmp *icmp_key;
+
+                       nla = nla_reserve(skb, OVS_KEY_ATTR_ICMP, sizeof(*icmp_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       icmp_key = nla_data(nla);
+                       icmp_key->icmp_type = ntohs(output->ipv4.tp.src);
+                       icmp_key->icmp_code = ntohs(output->ipv4.tp.dst);
+               } else if (swkey->eth.type == htons(ETH_P_IPV6) &&
+                          swkey->ip.proto == IPPROTO_ICMPV6) {
+                       struct ovs_key_icmpv6 *icmpv6_key;
+
+                       nla = nla_reserve(skb, OVS_KEY_ATTR_ICMPV6,
+                                               sizeof(*icmpv6_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       icmpv6_key = nla_data(nla);
+                       icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src);
+                       icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst);
+
+                       if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION ||
+                           icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
+                               struct ovs_key_nd *nd_key;
+
+                               nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key));
+                               if (!nla)
+                                       goto nla_put_failure;
+                               nd_key = nla_data(nla);
+                               memcpy(nd_key->nd_target, &output->ipv6.nd.target,
+                                                       sizeof(nd_key->nd_target));
+                               memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN);
+                               memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN);
+                       }
+               }
+       }
+
+unencap:
+       if (encap)
+               nla_nest_end(skb, encap);
+
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+#define MAX_ACTIONS_BUFSIZE    (32 * 1024)
+
+struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size)
+{
+       struct sw_flow_actions *sfa;
+
+       if (size > MAX_ACTIONS_BUFSIZE)
+               return ERR_PTR(-EINVAL);
+
+       sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
+       if (!sfa)
+               return ERR_PTR(-ENOMEM);
+
+       sfa->actions_len = 0;
+       return sfa;
+}
+
+/* RCU callback used by ovs_nla_free_flow_actions. */
+static void rcu_free_acts_callback(struct rcu_head *rcu)
+{
+       struct sw_flow_actions *sf_acts = container_of(rcu,
+                       struct sw_flow_actions, rcu);
+       kfree(sf_acts);
+}
+
+/* Schedules 'sf_acts' to be freed after the next RCU grace period.
+ * The caller must hold rcu_read_lock for this to be sensible. */
+void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
+{
+       call_rcu(&sf_acts->rcu, rcu_free_acts_callback);
+}
+
+static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
+                                      int attr_len)
+{
+
+       struct sw_flow_actions *acts;
+       int new_acts_size;
+       int req_size = NLA_ALIGN(attr_len);
+       int next_offset = offsetof(struct sw_flow_actions, actions) +
+                                       (*sfa)->actions_len;
+
+       if (req_size <= (ksize(*sfa) - next_offset))
+               goto out;
+
+       new_acts_size = ksize(*sfa) * 2;
+
+       if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
+               if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
+                       return ERR_PTR(-EMSGSIZE);
+               new_acts_size = MAX_ACTIONS_BUFSIZE;
+       }
+
+       acts = ovs_nla_alloc_flow_actions(new_acts_size);
+       if (IS_ERR(acts))
+               return (void *)acts;
+
+       memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len);
+       acts->actions_len = (*sfa)->actions_len;
+       kfree(*sfa);
+       *sfa = acts;
+
+out:
+       (*sfa)->actions_len += req_size;
+       return  (struct nlattr *) ((unsigned char *)(*sfa) + next_offset);
+}
+
+static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len)
+{
+       struct nlattr *a;
+
+       a = reserve_sfa_size(sfa, nla_attr_size(len));
+       if (IS_ERR(a))
+               return PTR_ERR(a);
+
+       a->nla_type = attrtype;
+       a->nla_len = nla_attr_size(len);
+
+       if (data)
+               memcpy(nla_data(a), data, len);
+       memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len));
+
+       return 0;
+}
+
+static inline int add_nested_action_start(struct sw_flow_actions **sfa,
+                                         int attrtype)
+{
+       int used = (*sfa)->actions_len;
+       int err;
+
+       err = add_action(sfa, attrtype, NULL, 0);
+       if (err)
+               return err;
+
+       return used;
+}
+
+static inline void add_nested_action_end(struct sw_flow_actions *sfa,
+                                        int st_offset)
+{
+       struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions +
+                                                              st_offset);
+
+       a->nla_len = sfa->actions_len - st_offset;
+}
+
+static int validate_and_copy_sample(const struct nlattr *attr,
+                                   const struct sw_flow_key *key, int depth,
+                                   struct sw_flow_actions **sfa)
+{
+       const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
+       const struct nlattr *probability, *actions;
+       const struct nlattr *a;
+       int rem, start, err, st_acts;
+
+       memset(attrs, 0, sizeof(attrs));
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+               if (!type || type > OVS_SAMPLE_ATTR_MAX || attrs[type])
+                       return -EINVAL;
+               attrs[type] = a;
+       }
+       if (rem)
+               return -EINVAL;
+
+       probability = attrs[OVS_SAMPLE_ATTR_PROBABILITY];
+       if (!probability || nla_len(probability) != sizeof(u32))
+               return -EINVAL;
+
+       actions = attrs[OVS_SAMPLE_ATTR_ACTIONS];
+       if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))
+               return -EINVAL;
+
+       /* validation done, copy sample action. */
+       start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE);
+       if (start < 0)
+               return start;
+       err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY,
+                        nla_data(probability), sizeof(u32));
+       if (err)
+               return err;
+       st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS);
+       if (st_acts < 0)
+               return st_acts;
+
+       err = ovs_nla_copy_actions(actions, key, depth + 1, sfa);
+       if (err)
+               return err;
+
+       add_nested_action_end(*sfa, st_acts);
+       add_nested_action_end(*sfa, start);
+
+       return 0;
+}
+
+static int validate_tp_port(const struct sw_flow_key *flow_key)
+{
+       if (flow_key->eth.type == htons(ETH_P_IP)) {
+               if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
+                       return 0;
+       } else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
+               if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
+                       return 0;
+       }
+
+       return -EINVAL;
+}
+
+void ovs_match_init(struct sw_flow_match *match,
+                   struct sw_flow_key *key,
+                   struct sw_flow_mask *mask)
+{
+       memset(match, 0, sizeof(*match));
+       match->key = key;
+       match->mask = mask;
+
+       memset(key, 0, sizeof(*key));
+
+       if (mask) {
+               memset(&mask->key, 0, sizeof(mask->key));
+               mask->range.start = mask->range.end = 0;
+       }
+}
+
+static int validate_and_copy_set_tun(const struct nlattr *attr,
+                                    struct sw_flow_actions **sfa)
+{
+       struct sw_flow_match match;
+       struct sw_flow_key key;
+       int err, start;
+
+       ovs_match_init(&match, &key, NULL);
+       err = ipv4_tun_from_nlattr(nla_data(attr), &match, false);
+       if (err)
+               return err;
+
+       start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET);
+       if (start < 0)
+               return start;
+
+       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
+                       sizeof(match.key->tun_key));
+       add_nested_action_end(*sfa, start);
+
+       return err;
+}
+
+static int validate_set(const struct nlattr *a,
+                       const struct sw_flow_key *flow_key,
+                       struct sw_flow_actions **sfa,
+                       bool *set_tun)
+{
+       const struct nlattr *ovs_key = nla_data(a);
+       int key_type = nla_type(ovs_key);
+
+       /* There can be only one key in a action */
+       if (nla_total_size(nla_len(ovs_key)) != nla_len(a))
+               return -EINVAL;
+
+       if (key_type > OVS_KEY_ATTR_MAX ||
+           (ovs_key_lens[key_type] != nla_len(ovs_key) &&
+            ovs_key_lens[key_type] != -1))
+               return -EINVAL;
+
+       switch (key_type) {
+       const struct ovs_key_ipv4 *ipv4_key;
+       const struct ovs_key_ipv6 *ipv6_key;
+       int err;
+
+       case OVS_KEY_ATTR_PRIORITY:
+       case OVS_KEY_ATTR_SKB_MARK:
+       case OVS_KEY_ATTR_ETHERNET:
+               break;
+
+       case OVS_KEY_ATTR_TUNNEL:
+               *set_tun = true;
+               err = validate_and_copy_set_tun(a, sfa);
+               if (err)
+                       return err;
+               break;
+
+       case OVS_KEY_ATTR_IPV4:
+               if (flow_key->eth.type != htons(ETH_P_IP))
+                       return -EINVAL;
+
+               if (!flow_key->ip.proto)
+                       return -EINVAL;
+
+               ipv4_key = nla_data(ovs_key);
+               if (ipv4_key->ipv4_proto != flow_key->ip.proto)
+                       return -EINVAL;
+
+               if (ipv4_key->ipv4_frag != flow_key->ip.frag)
+                       return -EINVAL;
+
+               break;
+
+       case OVS_KEY_ATTR_IPV6:
+               if (flow_key->eth.type != htons(ETH_P_IPV6))
+                       return -EINVAL;
+
+               if (!flow_key->ip.proto)
+                       return -EINVAL;
+
+               ipv6_key = nla_data(ovs_key);
+               if (ipv6_key->ipv6_proto != flow_key->ip.proto)
+                       return -EINVAL;
+
+               if (ipv6_key->ipv6_frag != flow_key->ip.frag)
+                       return -EINVAL;
+
+               if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000)
+                       return -EINVAL;
+
+               break;
+
+       case OVS_KEY_ATTR_TCP:
+               if (flow_key->ip.proto != IPPROTO_TCP)
+                       return -EINVAL;
+
+               return validate_tp_port(flow_key);
+
+       case OVS_KEY_ATTR_UDP:
+               if (flow_key->ip.proto != IPPROTO_UDP)
+                       return -EINVAL;
+
+               return validate_tp_port(flow_key);
+
+       case OVS_KEY_ATTR_SCTP:
+               if (flow_key->ip.proto != IPPROTO_SCTP)
+                       return -EINVAL;
+
+               return validate_tp_port(flow_key);
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int validate_userspace(const struct nlattr *attr)
+{
+       static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = {
+               [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 },
+               [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC },
+       };
+       struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1];
+       int error;
+
+       error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX,
+                                attr, userspace_policy);
+       if (error)
+               return error;
+
+       if (!a[OVS_USERSPACE_ATTR_PID] ||
+           !nla_get_u32(a[OVS_USERSPACE_ATTR_PID]))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int copy_action(const struct nlattr *from,
+                      struct sw_flow_actions **sfa)
+{
+       int totlen = NLA_ALIGN(from->nla_len);
+       struct nlattr *to;
+
+       to = reserve_sfa_size(sfa, from->nla_len);
+       if (IS_ERR(to))
+               return PTR_ERR(to);
+
+       memcpy(to, from, totlen);
+       return 0;
+}
+
+int ovs_nla_copy_actions(const struct nlattr *attr,
+                        const struct sw_flow_key *key,
+                        int depth,
+                        struct sw_flow_actions **sfa)
+{
+       const struct nlattr *a;
+       int rem, err;
+
+       if (depth >= SAMPLE_ACTION_DEPTH)
+               return -EOVERFLOW;
+
+       nla_for_each_nested(a, attr, rem) {
+               /* Expected argument lengths, (u32)-1 for variable length. */
+               static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
+                       [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
+                       [OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
+                       [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
+                       [OVS_ACTION_ATTR_POP_VLAN] = 0,
+                       [OVS_ACTION_ATTR_SET] = (u32)-1,
+                       [OVS_ACTION_ATTR_SAMPLE] = (u32)-1
+               };
+               const struct ovs_action_push_vlan *vlan;
+               int type = nla_type(a);
+               bool skip_copy;
+
+               if (type > OVS_ACTION_ATTR_MAX ||
+                   (action_lens[type] != nla_len(a) &&
+                    action_lens[type] != (u32)-1))
+                       return -EINVAL;
+
+               skip_copy = false;
+               switch (type) {
+               case OVS_ACTION_ATTR_UNSPEC:
+                       return -EINVAL;
+
+               case OVS_ACTION_ATTR_USERSPACE:
+                       err = validate_userspace(a);
+                       if (err)
+                               return err;
+                       break;
+
+               case OVS_ACTION_ATTR_OUTPUT:
+                       if (nla_get_u32(a) >= DP_MAX_PORTS)
+                               return -EINVAL;
+                       break;
+
+
+               case OVS_ACTION_ATTR_POP_VLAN:
+                       break;
+
+               case OVS_ACTION_ATTR_PUSH_VLAN:
+                       vlan = nla_data(a);
+                       if (vlan->vlan_tpid != htons(ETH_P_8021Q))
+                               return -EINVAL;
+                       if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
+                               return -EINVAL;
+                       break;
+
+               case OVS_ACTION_ATTR_SET:
+                       err = validate_set(a, key, sfa, &skip_copy);
+                       if (err)
+                               return err;
+                       break;
+
+               case OVS_ACTION_ATTR_SAMPLE:
+                       err = validate_and_copy_sample(a, key, depth, sfa);
+                       if (err)
+                               return err;
+                       skip_copy = true;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+               if (!skip_copy) {
+                       err = copy_action(a, sfa);
+                       if (err)
+                               return err;
+               }
+       }
+
+       if (rem > 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
+{
+       const struct nlattr *a;
+       struct nlattr *start;
+       int err = 0, rem;
+
+       start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE);
+       if (!start)
+               return -EMSGSIZE;
+
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+               struct nlattr *st_sample;
+
+               switch (type) {
+               case OVS_SAMPLE_ATTR_PROBABILITY:
+                       if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY,
+                                   sizeof(u32), nla_data(a)))
+                               return -EMSGSIZE;
+                       break;
+               case OVS_SAMPLE_ATTR_ACTIONS:
+                       st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS);
+                       if (!st_sample)
+                               return -EMSGSIZE;
+                       err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb);
+                       if (err)
+                               return err;
+                       nla_nest_end(skb, st_sample);
+                       break;
+               }
+       }
+
+       nla_nest_end(skb, start);
+       return err;
+}
+
+static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
+{
+       const struct nlattr *ovs_key = nla_data(a);
+       int key_type = nla_type(ovs_key);
+       struct nlattr *start;
+       int err;
+
+       switch (key_type) {
+       case OVS_KEY_ATTR_IPV4_TUNNEL:
+               start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
+               if (!start)
+                       return -EMSGSIZE;
+
+               err = ipv4_tun_to_nlattr(skb, nla_data(ovs_key),
+                                            nla_data(ovs_key));
+               if (err)
+                       return err;
+               nla_nest_end(skb, start);
+               break;
+       default:
+               if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key))
+                       return -EMSGSIZE;
+               break;
+       }
+
+       return 0;
+}
+
+int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb)
+{
+       const struct nlattr *a;
+       int rem, err;
+
+       nla_for_each_attr(a, attr, len, rem) {
+               int type = nla_type(a);
+
+               switch (type) {
+               case OVS_ACTION_ATTR_SET:
+                       err = set_action_to_attr(a, skb);
+                       if (err)
+                               return err;
+                       break;
+
+               case OVS_ACTION_ATTR_SAMPLE:
+                       err = sample_action_to_attr(a, skb);
+                       if (err)
+                               return err;
+                       break;
+               default:
+                       if (nla_put(skb, type, nla_len(a), nla_data(a)))
+                               return -EMSGSIZE;
+                       break;
+               }
+       }
+
+       return 0;
+}
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
new file mode 100644 (file)
index 0000000..4401510
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2007-2013 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef FLOW_NETLINK_H
+#define FLOW_NETLINK_H 1
+
+#include <linux/kernel.h>
+#include <linux/netlink.h>
+#include <linux/openvswitch.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/if_ether.h>
+#include <linux/in6.h>
+#include <linux/jiffies.h>
+#include <linux/time.h>
+#include <linux/flex_array.h>
+
+#include <net/inet_ecn.h>
+#include <net/ip_tunnels.h>
+
+#include "flow.h"
+
+void ovs_match_init(struct sw_flow_match *match,
+                   struct sw_flow_key *key, struct sw_flow_mask *mask);
+
+int ovs_nla_put_flow(const struct sw_flow_key *,
+                    const struct sw_flow_key *, struct sk_buff *);
+int ovs_nla_get_flow_metadata(struct sw_flow *flow,
+                             const struct nlattr *attr);
+int ovs_nla_get_match(struct sw_flow_match *match,
+                     const struct nlattr *,
+                     const struct nlattr *);
+
+int ovs_nla_copy_actions(const struct nlattr *attr,
+                        const struct sw_flow_key *key, int depth,
+                        struct sw_flow_actions **sfa);
+int ovs_nla_put_actions(const struct nlattr *attr,
+                       int len, struct sk_buff *skb);
+
+struct sw_flow_actions *ovs_nla_alloc_flow_actions(int actions_len);
+void ovs_nla_free_flow_actions(struct sw_flow_actions *);
+
+#endif /* flow_netlink.h */
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
new file mode 100644 (file)
index 0000000..e425427
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2007-2013 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "flow.h"
+#include "datapath.h"
+#include <linux/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <net/llc_pdu.h>
+#include <linux/kernel.h>
+#include <linux/jhash.h>
+#include <linux/jiffies.h>
+#include <linux/llc.h>
+#include <linux/module.h>
+#include <linux/in.h>
+#include <linux/rcupdate.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/sctp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+#include <linux/rculist.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+
+#include "datapath.h"
+
+#define TBL_MIN_BUCKETS                1024
+#define REHASH_INTERVAL                (10 * 60 * HZ)
+
+static struct kmem_cache *flow_cache;
+
+static u16 range_n_bytes(const struct sw_flow_key_range *range)
+{
+       return range->end - range->start;
+}
+
+void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
+                      const struct sw_flow_mask *mask)
+{
+       const long *m = (long *)((u8 *)&mask->key + mask->range.start);
+       const long *s = (long *)((u8 *)src + mask->range.start);
+       long *d = (long *)((u8 *)dst + mask->range.start);
+       int i;
+
+       /* The memory outside of the 'mask->range' are not set since
+        * further operations on 'dst' only uses contents within
+        * 'mask->range'.
+        */
+       for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long))
+               *d++ = *s++ & *m++;
+}
+
+struct sw_flow *ovs_flow_alloc(void)
+{
+       struct sw_flow *flow;
+
+       flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
+       if (!flow)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_init(&flow->lock);
+       flow->sf_acts = NULL;
+       flow->mask = NULL;
+
+       return flow;
+}
+
+int ovs_flow_tbl_count(struct flow_table *table)
+{
+       return table->count;
+}
+
+static struct flex_array *alloc_buckets(unsigned int n_buckets)
+{
+       struct flex_array *buckets;
+       int i, err;
+
+       buckets = flex_array_alloc(sizeof(struct hlist_head),
+                                  n_buckets, GFP_KERNEL);
+       if (!buckets)
+               return NULL;
+
+       err = flex_array_prealloc(buckets, 0, n_buckets, GFP_KERNEL);
+       if (err) {
+               flex_array_free(buckets);
+               return NULL;
+       }
+
+       for (i = 0; i < n_buckets; i++)
+               INIT_HLIST_HEAD((struct hlist_head *)
+                                       flex_array_get(buckets, i));
+
+       return buckets;
+}
+
+static void flow_free(struct sw_flow *flow)
+{
+       kfree((struct sf_flow_acts __force *)flow->sf_acts);
+       kmem_cache_free(flow_cache, flow);
+}
+
+static void rcu_free_flow_callback(struct rcu_head *rcu)
+{
+       struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu);
+
+       flow_free(flow);
+}
+
+static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu)
+{
+       struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu);
+
+       kfree(mask);
+}
+
+static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
+{
+       if (!mask)
+               return;
+
+       BUG_ON(!mask->ref_count);
+       mask->ref_count--;
+
+       if (!mask->ref_count) {
+               list_del_rcu(&mask->list);
+               if (deferred)
+                       call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb);
+               else
+                       kfree(mask);
+       }
+}
+
+void ovs_flow_free(struct sw_flow *flow, bool deferred)
+{
+       if (!flow)
+               return;
+
+       flow_mask_del_ref(flow->mask, deferred);
+
+       if (deferred)
+               call_rcu(&flow->rcu, rcu_free_flow_callback);
+       else
+               flow_free(flow);
+}
+
+static void free_buckets(struct flex_array *buckets)
+{
+       flex_array_free(buckets);
+}
+
+static void __table_instance_destroy(struct table_instance *ti)
+{
+       int i;
+
+       if (ti->keep_flows)
+               goto skip_flows;
+
+       for (i = 0; i < ti->n_buckets; i++) {
+               struct sw_flow *flow;
+               struct hlist_head *head = flex_array_get(ti->buckets, i);
+               struct hlist_node *n;
+               int ver = ti->node_ver;
+
+               hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
+                       hlist_del(&flow->hash_node[ver]);
+                       ovs_flow_free(flow, false);
+               }
+       }
+
+skip_flows:
+       free_buckets(ti->buckets);
+       kfree(ti);
+}
+
+static struct table_instance *table_instance_alloc(int new_size)
+{
+       struct table_instance *ti = kmalloc(sizeof(*ti), GFP_KERNEL);
+
+       if (!ti)
+               return NULL;
+
+       ti->buckets = alloc_buckets(new_size);
+
+       if (!ti->buckets) {
+               kfree(ti);
+               return NULL;
+       }
+       ti->n_buckets = new_size;
+       ti->node_ver = 0;
+       ti->keep_flows = false;
+       get_random_bytes(&ti->hash_seed, sizeof(u32));
+
+       return ti;
+}
+
+int ovs_flow_tbl_init(struct flow_table *table)
+{
+       struct table_instance *ti;
+
+       ti = table_instance_alloc(TBL_MIN_BUCKETS);
+
+       if (!ti)
+               return -ENOMEM;
+
+       rcu_assign_pointer(table->ti, ti);
+       INIT_LIST_HEAD(&table->mask_list);
+       table->last_rehash = jiffies;
+       table->count = 0;
+       return 0;
+}
+
+static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu)
+{
+       struct table_instance *ti = container_of(rcu, struct table_instance, rcu);
+
+       __table_instance_destroy(ti);
+}
+
+static void table_instance_destroy(struct table_instance *ti, bool deferred)
+{
+       if (!ti)
+               return;
+
+       if (deferred)
+               call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb);
+       else
+               __table_instance_destroy(ti);
+}
+
+void ovs_flow_tbl_destroy(struct flow_table *table)
+{
+       struct table_instance *ti = ovsl_dereference(table->ti);
+
+       table_instance_destroy(ti, false);
+}
+
+struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
+                                      u32 *bucket, u32 *last)
+{
+       struct sw_flow *flow;
+       struct hlist_head *head;
+       int ver;
+       int i;
+
+       ver = ti->node_ver;
+       while (*bucket < ti->n_buckets) {
+               i = 0;
+               head = flex_array_get(ti->buckets, *bucket);
+               hlist_for_each_entry_rcu(flow, head, hash_node[ver]) {
+                       if (i < *last) {
+                               i++;
+                               continue;
+                       }
+                       *last = i + 1;
+                       return flow;
+               }
+               (*bucket)++;
+               *last = 0;
+       }
+
+       return NULL;
+}
+
+static struct hlist_head *find_bucket(struct table_instance *ti, u32 hash)
+{
+       hash = jhash_1word(hash, ti->hash_seed);
+       return flex_array_get(ti->buckets,
+                               (hash & (ti->n_buckets - 1)));
+}
+
+static void table_instance_insert(struct table_instance *ti, struct sw_flow *flow)
+{
+       struct hlist_head *head;
+
+       head = find_bucket(ti, flow->hash);
+       hlist_add_head_rcu(&flow->hash_node[ti->node_ver], head);
+}
+
+static void flow_table_copy_flows(struct table_instance *old,
+                                 struct table_instance *new)
+{
+       int old_ver;
+       int i;
+
+       old_ver = old->node_ver;
+       new->node_ver = !old_ver;
+
+       /* Insert in new table. */
+       for (i = 0; i < old->n_buckets; i++) {
+               struct sw_flow *flow;
+               struct hlist_head *head;
+
+               head = flex_array_get(old->buckets, i);
+
+               hlist_for_each_entry(flow, head, hash_node[old_ver])
+                       table_instance_insert(new, flow);
+       }
+
+       old->keep_flows = true;
+}
+
+static struct table_instance *table_instance_rehash(struct table_instance *ti,
+                                           int n_buckets)
+{
+       struct table_instance *new_ti;
+
+       new_ti = table_instance_alloc(n_buckets);
+       if (!new_ti)
+               return NULL;
+
+       flow_table_copy_flows(ti, new_ti);
+
+       return new_ti;
+}
+
+int ovs_flow_tbl_flush(struct flow_table *flow_table)
+{
+       struct table_instance *old_ti;
+       struct table_instance *new_ti;
+
+       old_ti = ovsl_dereference(flow_table->ti);
+       new_ti = table_instance_alloc(TBL_MIN_BUCKETS);
+       if (!new_ti)
+               return -ENOMEM;
+
+       rcu_assign_pointer(flow_table->ti, new_ti);
+       flow_table->last_rehash = jiffies;
+       flow_table->count = 0;
+
+       table_instance_destroy(old_ti, true);
+       return 0;
+}
+
+static u32 flow_hash(const struct sw_flow_key *key, int key_start,
+                    int key_end)
+{
+       u32 *hash_key = (u32 *)((u8 *)key + key_start);
+       int hash_u32s = (key_end - key_start) >> 2;
+
+       /* Make sure number of hash bytes are multiple of u32. */
+       BUILD_BUG_ON(sizeof(long) % sizeof(u32));
+
+       return jhash2(hash_key, hash_u32s, 0);
+}
+
+static int flow_key_start(const struct sw_flow_key *key)
+{
+       if (key->tun_key.ipv4_dst)
+               return 0;
+       else
+               return rounddown(offsetof(struct sw_flow_key, phy),
+                                         sizeof(long));
+}
+
+static bool cmp_key(const struct sw_flow_key *key1,
+                   const struct sw_flow_key *key2,
+                   int key_start, int key_end)
+{
+       const long *cp1 = (long *)((u8 *)key1 + key_start);
+       const long *cp2 = (long *)((u8 *)key2 + key_start);
+       long diffs = 0;
+       int i;
+
+       for (i = key_start; i < key_end;  i += sizeof(long))
+               diffs |= *cp1++ ^ *cp2++;
+
+       return diffs == 0;
+}
+
+static bool flow_cmp_masked_key(const struct sw_flow *flow,
+                               const struct sw_flow_key *key,
+                               int key_start, int key_end)
+{
+       return cmp_key(&flow->key, key, key_start, key_end);
+}
+
+bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
+                              struct sw_flow_match *match)
+{
+       struct sw_flow_key *key = match->key;
+       int key_start = flow_key_start(key);
+       int key_end = match->range.end;
+
+       return cmp_key(&flow->unmasked_key, key, key_start, key_end);
+}
+
+static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
+                                         const struct sw_flow_key *unmasked,
+                                         struct sw_flow_mask *mask)
+{
+       struct sw_flow *flow;
+       struct hlist_head *head;
+       int key_start = mask->range.start;
+       int key_end = mask->range.end;
+       u32 hash;
+       struct sw_flow_key masked_key;
+
+       ovs_flow_mask_key(&masked_key, unmasked, mask);
+       hash = flow_hash(&masked_key, key_start, key_end);
+       head = find_bucket(ti, hash);
+       hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) {
+               if (flow->mask == mask && flow->hash == hash &&
+                   flow_cmp_masked_key(flow, &masked_key,
+                                         key_start, key_end))
+                       return flow;
+       }
+       return NULL;
+}
+
+struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
+                                   const struct sw_flow_key *key,
+                                   u32 *n_mask_hit)
+{
+       struct table_instance *ti = rcu_dereference(tbl->ti);
+       struct sw_flow_mask *mask;
+       struct sw_flow *flow;
+
+       *n_mask_hit = 0;
+       list_for_each_entry_rcu(mask, &tbl->mask_list, list) {
+               (*n_mask_hit)++;
+               flow = masked_flow_lookup(ti, key, mask);
+               if (flow)  /* Found */
+                       return flow;
+       }
+       return NULL;
+}
+
+int ovs_flow_tbl_num_masks(const struct flow_table *table)
+{
+       struct sw_flow_mask *mask;
+       int num = 0;
+
+       list_for_each_entry(mask, &table->mask_list, list)
+               num++;
+
+       return num;
+}
+
+static struct table_instance *table_instance_expand(struct table_instance *ti)
+{
+       return table_instance_rehash(ti, ti->n_buckets * 2);
+}
+
+void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
+{
+       struct table_instance *ti = ovsl_dereference(table->ti);
+
+       BUG_ON(table->count == 0);
+       hlist_del_rcu(&flow->hash_node[ti->node_ver]);
+       table->count--;
+}
+
+static struct sw_flow_mask *mask_alloc(void)
+{
+       struct sw_flow_mask *mask;
+
+       mask = kmalloc(sizeof(*mask), GFP_KERNEL);
+       if (mask)
+               mask->ref_count = 0;
+
+       return mask;
+}
+
+static void mask_add_ref(struct sw_flow_mask *mask)
+{
+       mask->ref_count++;
+}
+
+static bool mask_equal(const struct sw_flow_mask *a,
+                      const struct sw_flow_mask *b)
+{
+       u8 *a_ = (u8 *)&a->key + a->range.start;
+       u8 *b_ = (u8 *)&b->key + b->range.start;
+
+       return  (a->range.end == b->range.end)
+               && (a->range.start == b->range.start)
+               && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0);
+}
+
+static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl,
+                                          const struct sw_flow_mask *mask)
+{
+       struct list_head *ml;
+
+       list_for_each(ml, &tbl->mask_list) {
+               struct sw_flow_mask *m;
+               m = container_of(ml, struct sw_flow_mask, list);
+               if (mask_equal(mask, m))
+                       return m;
+       }
+
+       return NULL;
+}
+
+/**
+ * add a new mask into the mask list.
+ * The caller needs to make sure that 'mask' is not the same
+ * as any masks that are already on the list.
+ */
+static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
+                           struct sw_flow_mask *new)
+{
+       struct sw_flow_mask *mask;
+       mask = flow_mask_find(tbl, new);
+       if (!mask) {
+               /* Allocate a new mask if none exsits. */
+               mask = mask_alloc();
+               if (!mask)
+                       return -ENOMEM;
+               mask->key = new->key;
+               mask->range = new->range;
+               list_add_rcu(&mask->list, &tbl->mask_list);
+       }
+
+       mask_add_ref(mask);
+       flow->mask = mask;
+       return 0;
+}
+
+int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
+                       struct sw_flow_mask *mask)
+{
+       struct table_instance *new_ti = NULL;
+       struct table_instance *ti;
+       int err;
+
+       err = flow_mask_insert(table, flow, mask);
+       if (err)
+               return err;
+
+       flow->hash = flow_hash(&flow->key, flow->mask->range.start,
+                       flow->mask->range.end);
+       ti = ovsl_dereference(table->ti);
+       table_instance_insert(ti, flow);
+       table->count++;
+
+       /* Expand table, if necessary, to make room. */
+       if (table->count > ti->n_buckets)
+               new_ti = table_instance_expand(ti);
+       else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL))
+               new_ti = table_instance_rehash(ti, ti->n_buckets);
+
+       if (new_ti) {
+               rcu_assign_pointer(table->ti, new_ti);
+               table_instance_destroy(ti, true);
+               table->last_rehash = jiffies;
+       }
+       return 0;
+}
+
+/* Initializes the flow module.
+ * Returns zero if successful or a negative error code. */
+int ovs_flow_init(void)
+{
+       BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long));
+       BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long));
+
+       flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0,
+                                       0, NULL);
+       if (flow_cache == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/* Uninitializes the flow module. */
+void ovs_flow_exit(void)
+{
+       kmem_cache_destroy(flow_cache);
+}
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h
new file mode 100644 (file)
index 0000000..fbe45d5
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2007-2013 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef FLOW_TABLE_H
+#define FLOW_TABLE_H 1
+
+#include <linux/kernel.h>
+#include <linux/netlink.h>
+#include <linux/openvswitch.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/if_ether.h>
+#include <linux/in6.h>
+#include <linux/jiffies.h>
+#include <linux/time.h>
+#include <linux/flex_array.h>
+
+#include <net/inet_ecn.h>
+#include <net/ip_tunnels.h>
+
+#include "flow.h"
+
+struct table_instance {
+       struct flex_array *buckets;
+       unsigned int n_buckets;
+       struct rcu_head rcu;
+       int node_ver;
+       u32 hash_seed;
+       bool keep_flows;
+};
+
+struct flow_table {
+       struct table_instance __rcu *ti;
+       struct list_head mask_list;
+       unsigned long last_rehash;
+       unsigned int count;
+};
+
+int ovs_flow_init(void);
+void ovs_flow_exit(void);
+
+struct sw_flow *ovs_flow_alloc(void);
+void ovs_flow_free(struct sw_flow *, bool deferred);
+
+int ovs_flow_tbl_init(struct flow_table *);
+int ovs_flow_tbl_count(struct flow_table *table);
+void ovs_flow_tbl_destroy(struct flow_table *table);
+int ovs_flow_tbl_flush(struct flow_table *flow_table);
+
+int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
+                       struct sw_flow_mask *mask);
+void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
+int  ovs_flow_tbl_num_masks(const struct flow_table *table);
+struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table,
+                                      u32 *bucket, u32 *idx);
+struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
+                                   const struct sw_flow_key *,
+                                   u32 *n_mask_hit);
+
+bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
+                              struct sw_flow_match *match);
+
+void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
+                      const struct sw_flow_mask *mask);
+#endif /* flow_table.h */
index c99dea5..a3d6951 100644 (file)
@@ -24,8 +24,6 @@
 #include <linux/if_tunnel.h>
 #include <linux/if_vlan.h>
 #include <linux/in.h>
-#include <linux/if_vlan.h>
-#include <linux/in.h>
 #include <linux/in_route.h>
 #include <linux/inetdevice.h>
 #include <linux/jhash.h>
index 98d3edb..729c687 100644 (file)
@@ -134,7 +134,7 @@ static void do_setup(struct net_device *netdev)
        netdev->tx_queue_len = 0;
 
        netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
-                          NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO;
+                          NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE;
 
        netdev->vlan_features = netdev->features;
        netdev->features |= NETIF_F_HW_VLAN_CTAG_TX;
index 09d93c1..d21f77d 100644 (file)
@@ -150,15 +150,25 @@ static void free_port_rcu(struct rcu_head *rcu)
        ovs_vport_free(vport_from_priv(netdev_vport));
 }
 
-static void netdev_destroy(struct vport *vport)
+void ovs_netdev_detach_dev(struct vport *vport)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
 
-       rtnl_lock();
+       ASSERT_RTNL();
        netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
        netdev_rx_handler_unregister(netdev_vport->dev);
-       netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
+       netdev_upper_dev_unlink(netdev_vport->dev,
+                               netdev_master_upper_dev_get(netdev_vport->dev));
        dev_set_promiscuity(netdev_vport->dev, -1);
+}
+
+static void netdev_destroy(struct vport *vport)
+{
+       struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
+
+       rtnl_lock();
+       if (netdev_vport->dev->priv_flags & IFF_OVS_DATAPATH)
+               ovs_netdev_detach_dev(vport);
        rtnl_unlock();
 
        call_rcu(&netdev_vport->rcu, free_port_rcu);
index dd298b5..8df01c1 100644 (file)
@@ -39,5 +39,6 @@ netdev_vport_priv(const struct vport *vport)
 }
 
 const char *ovs_netdev_get_name(const struct vport *);
+void ovs_netdev_detach_dev(struct vport *);
 
 #endif /* vport_netdev.h */
index a481c03..e797a50 100644 (file)
@@ -29,7 +29,6 @@
 #include <net/ip.h>
 #include <net/udp.h>
 #include <net/ip_tunnels.h>
-#include <net/udp.h>
 #include <net/rtnetlink.h>
 #include <net/route.h>
 #include <net/dsfield.h>
@@ -173,7 +172,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
 
        skb->local_df = 1;
 
-       inet_get_local_port_range(&port_min, &port_max);
+       inet_get_local_port_range(net, &port_min, &port_max);
        src_port = vxlan_src_port(port_min, port_max, skb);
 
        err = vxlan_xmit_skb(vxlan_port->vs, rt, skb,
index 642ad42..378c3a6 100644 (file)
@@ -51,10 +51,16 @@ static struct kmem_cache *rds_conn_slab;
 
 static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr)
 {
+       static u32 rds_hash_secret __read_mostly;
+
+       unsigned long hash;
+
+       net_get_random_once(&rds_hash_secret, sizeof(rds_hash_secret));
+
        /* Pass NULL, don't need struct net for hash */
-       unsigned long hash = inet_ehashfn(NULL,
-                                         be32_to_cpu(laddr), 0,
-                                         be32_to_cpu(faddr), 0);
+       hash = __inet_ehashfn(be32_to_cpu(laddr), 0,
+                             be32_to_cpu(faddr), 0,
+                             rds_hash_secret);
        return &rds_conn_hash[hash & RDS_CONNECTION_HASH_MASK];
 }
 
index ec1d731..48f8ffc 100644 (file)
@@ -749,7 +749,7 @@ void rds_atomic_send_complete(struct rds_message *rm, int wc_status);
 int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,
                    struct cmsghdr *cmsg);
 
-extern void __rds_put_mr_final(struct rds_mr *mr);
+void __rds_put_mr_final(struct rds_mr *mr);
 static inline void rds_mr_put(struct rds_mr *mr)
 {
        if (atomic_dec_and_test(&mr->r_refcount))
index 78efe89..4c10e7e 100644 (file)
@@ -36,7 +36,7 @@ config RFKILL_REGULATOR
 
 config RFKILL_GPIO
        tristate "GPIO RFKILL driver"
-       depends on RFKILL && GPIOLIB && HAVE_CLK
+       depends on RFKILL && GPIOLIB
        default n
        help
          If you say yes here you get support of a generic gpio RFKILL
index fb076cd..5620d3c 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/acpi_gpio.h>
 
 #include <linux/rfkill-gpio.h>
 
-enum rfkill_gpio_clk_state {
-       UNSPECIFIED = 0,
-       PWR_ENABLED,
-       PWR_DISABLED
-};
+struct rfkill_gpio_data {
+       const char              *name;
+       enum rfkill_type        type;
+       int                     reset_gpio;
+       int                     shutdown_gpio;
 
-#define PWR_CLK_SET(_RF, _EN) \
-       ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED))
-#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED)
-#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED)
+       struct rfkill           *rfkill_dev;
+       char                    *reset_name;
+       char                    *shutdown_name;
+       struct clk              *clk;
 
-struct rfkill_gpio_data {
-       struct rfkill_gpio_platform_data        *pdata;
-       struct rfkill                           *rfkill_dev;
-       char                                    *reset_name;
-       char                                    *shutdown_name;
-       enum rfkill_gpio_clk_state              pwr_clk_enabled;
-       struct clk                              *pwr_clk;
+       bool                    clk_enabled;
 };
 
 static int rfkill_gpio_set_power(void *data, bool blocked)
@@ -52,23 +48,22 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
        struct rfkill_gpio_data *rfkill = data;
 
        if (blocked) {
-               if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-                       gpio_direction_output(rfkill->pdata->shutdown_gpio, 0);
-               if (gpio_is_valid(rfkill->pdata->reset_gpio))
-                       gpio_direction_output(rfkill->pdata->reset_gpio, 0);
-               if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
-                       clk_disable(rfkill->pwr_clk);
+               if (gpio_is_valid(rfkill->shutdown_gpio))
+                       gpio_set_value(rfkill->shutdown_gpio, 0);
+               if (gpio_is_valid(rfkill->reset_gpio))
+                       gpio_set_value(rfkill->reset_gpio, 0);
+               if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
+                       clk_disable(rfkill->clk);
        } else {
-               if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill))
-                       clk_enable(rfkill->pwr_clk);
-               if (gpio_is_valid(rfkill->pdata->reset_gpio))
-                       gpio_direction_output(rfkill->pdata->reset_gpio, 1);
-               if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-                       gpio_direction_output(rfkill->pdata->shutdown_gpio, 1);
+               if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
+                       clk_enable(rfkill->clk);
+               if (gpio_is_valid(rfkill->reset_gpio))
+                       gpio_set_value(rfkill->reset_gpio, 1);
+               if (gpio_is_valid(rfkill->shutdown_gpio))
+                       gpio_set_value(rfkill->shutdown_gpio, 1);
        }
 
-       if (rfkill->pwr_clk)
-               PWR_CLK_SET(rfkill, blocked);
+       rfkill->clk_enabled = blocked;
 
        return 0;
 }
@@ -77,117 +72,112 @@ static const struct rfkill_ops rfkill_gpio_ops = {
        .set_block = rfkill_gpio_set_power,
 };
 
+static int rfkill_gpio_acpi_probe(struct device *dev,
+                                 struct rfkill_gpio_data *rfkill)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+
+       rfkill->name = dev_name(dev);
+       rfkill->type = (unsigned)id->driver_data;
+       rfkill->reset_gpio = acpi_get_gpio_by_index(dev, 0, NULL);
+       rfkill->shutdown_gpio = acpi_get_gpio_by_index(dev, 1, NULL);
+
+       return 0;
+}
+
 static int rfkill_gpio_probe(struct platform_device *pdev)
 {
-       struct rfkill_gpio_data *rfkill;
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct rfkill_gpio_data *rfkill;
+       const char *clk_name = NULL;
        int ret = 0;
        int len = 0;
 
-       if (!pdata) {
-               pr_warn("%s: No platform data specified\n", __func__);
-               return -EINVAL;
+       rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
+       if (!rfkill)
+               return -ENOMEM;
+
+       if (ACPI_HANDLE(&pdev->dev)) {
+               ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill);
+               if (ret)
+                       return ret;
+       } else if (pdata) {
+               clk_name = pdata->power_clk_name;
+               rfkill->name = pdata->name;
+               rfkill->type = pdata->type;
+               rfkill->reset_gpio = pdata->reset_gpio;
+               rfkill->shutdown_gpio = pdata->shutdown_gpio;
+       } else {
+               return -ENODEV;
        }
 
        /* make sure at-least one of the GPIO is defined and that
         * a name is specified for this instance */
-       if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) &&
-               !gpio_is_valid(pdata->shutdown_gpio))) {
+       if ((!gpio_is_valid(rfkill->reset_gpio) &&
+            !gpio_is_valid(rfkill->shutdown_gpio)) || !rfkill->name) {
                pr_warn("%s: invalid platform data\n", __func__);
                return -EINVAL;
        }
 
-       rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
-       if (!rfkill)
-               return -ENOMEM;
-
-       if (pdata->gpio_runtime_setup) {
+       if (pdata && pdata->gpio_runtime_setup) {
                ret = pdata->gpio_runtime_setup(pdev);
                if (ret) {
                        pr_warn("%s: can't set up gpio\n", __func__);
-                       goto fail_alloc;
+                       return ret;
                }
        }
 
-       rfkill->pdata = pdata;
-
-       len = strlen(pdata->name);
-       rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
-       if (!rfkill->reset_name) {
-               ret = -ENOMEM;
-               goto fail_alloc;
-       }
+       len = strlen(rfkill->name);
+       rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
+       if (!rfkill->reset_name)
+               return -ENOMEM;
 
-       rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
-       if (!rfkill->shutdown_name) {
-               ret = -ENOMEM;
-               goto fail_reset_name;
-       }
+       rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
+       if (!rfkill->shutdown_name)
+               return -ENOMEM;
 
-       snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
-       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
+       snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
+       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
 
-       if (pdata->power_clk_name) {
-               rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name);
-               if (IS_ERR(rfkill->pwr_clk)) {
-                       pr_warn("%s: can't find pwr_clk.\n", __func__);
-                       ret = PTR_ERR(rfkill->pwr_clk);
-                       goto fail_shutdown_name;
-               }
-       }
+       rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
 
-       if (gpio_is_valid(pdata->reset_gpio)) {
-               ret = gpio_request(pdata->reset_gpio, rfkill->reset_name);
+       if (gpio_is_valid(rfkill->reset_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, rfkill->reset_gpio,
+                                           0, rfkill->reset_name);
                if (ret) {
                        pr_warn("%s: failed to get reset gpio.\n", __func__);
-                       goto fail_clock;
+                       return ret;
                }
        }
 
-       if (gpio_is_valid(pdata->shutdown_gpio)) {
-               ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name);
+       if (gpio_is_valid(rfkill->shutdown_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, rfkill->shutdown_gpio,
+                                           0, rfkill->shutdown_name);
                if (ret) {
                        pr_warn("%s: failed to get shutdown gpio.\n", __func__);
-                       goto fail_reset;
+                       return ret;
                }
        }
 
-       rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
-                                         &rfkill_gpio_ops, rfkill);
-       if (!rfkill->rfkill_dev) {
-               ret = -ENOMEM;
-               goto fail_shutdown;
-       }
+       rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
+                                         rfkill->type, &rfkill_gpio_ops,
+                                         rfkill);
+       if (!rfkill->rfkill_dev)
+               return -ENOMEM;
 
        ret = rfkill_register(rfkill->rfkill_dev);
        if (ret < 0)
-               goto fail_rfkill;
+               return ret;
 
        platform_set_drvdata(pdev, rfkill);
 
-       dev_info(&pdev->dev, "%s device registered.\n", pdata->name);
+       dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
 
        return 0;
-
-fail_rfkill:
-       rfkill_destroy(rfkill->rfkill_dev);
-fail_shutdown:
-       if (gpio_is_valid(pdata->shutdown_gpio))
-               gpio_free(pdata->shutdown_gpio);
-fail_reset:
-       if (gpio_is_valid(pdata->reset_gpio))
-               gpio_free(pdata->reset_gpio);
-fail_clock:
-       if (rfkill->pwr_clk)
-               clk_put(rfkill->pwr_clk);
-fail_shutdown_name:
-       kfree(rfkill->shutdown_name);
-fail_reset_name:
-       kfree(rfkill->reset_name);
-fail_alloc:
-       kfree(rfkill);
-
-       return ret;
 }
 
 static int rfkill_gpio_remove(struct platform_device *pdev)
@@ -195,31 +185,26 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
        struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 
-       if (pdata->gpio_runtime_close)
+       if (pdata && pdata->gpio_runtime_close)
                pdata->gpio_runtime_close(pdev);
        rfkill_unregister(rfkill->rfkill_dev);
        rfkill_destroy(rfkill->rfkill_dev);
-       if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
-               gpio_free(rfkill->pdata->shutdown_gpio);
-       if (gpio_is_valid(rfkill->pdata->reset_gpio))
-               gpio_free(rfkill->pdata->reset_gpio);
-       if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
-               clk_disable(rfkill->pwr_clk);
-       if (rfkill->pwr_clk)
-               clk_put(rfkill->pwr_clk);
-       kfree(rfkill->shutdown_name);
-       kfree(rfkill->reset_name);
-       kfree(rfkill);
 
        return 0;
 }
 
+static const struct acpi_device_id rfkill_acpi_match[] = {
+       { "BCM4752", RFKILL_TYPE_GPS },
+       { },
+};
+
 static struct platform_driver rfkill_gpio_driver = {
        .probe = rfkill_gpio_probe,
        .remove = rfkill_gpio_remove,
        .driver = {
-                  .name = "rfkill_gpio",
-                  .owner = THIS_MODULE,
+               .name = "rfkill_gpio",
+               .owner = THIS_MODULE,
+               .acpi_match_table = ACPI_PTR(rfkill_acpi_match),
        },
 };
 
index a693aca..5f43675 100644 (file)
@@ -426,17 +426,16 @@ extern struct workqueue_struct *rxrpc_workqueue;
 /*
  * ar-accept.c
  */
-extern void rxrpc_accept_incoming_calls(struct work_struct *);
-extern struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *,
-                                           unsigned long);
-extern int rxrpc_reject_call(struct rxrpc_sock *);
+void rxrpc_accept_incoming_calls(struct work_struct *);
+struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *, unsigned long);
+int rxrpc_reject_call(struct rxrpc_sock *);
 
 /*
  * ar-ack.c
  */
-extern void __rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool);
-extern void rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool);
-extern void rxrpc_process_call(struct work_struct *);
+void __rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool);
+void rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool);
+void rxrpc_process_call(struct work_struct *);
 
 /*
  * ar-call.c
@@ -445,19 +444,18 @@ extern struct kmem_cache *rxrpc_call_jar;
 extern struct list_head rxrpc_calls;
 extern rwlock_t rxrpc_call_lock;
 
-extern struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *,
-                                               struct rxrpc_transport *,
-                                               struct rxrpc_conn_bundle *,
-                                               unsigned long, int, gfp_t);
-extern struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *,
-                                             struct rxrpc_connection *,
-                                             struct rxrpc_header *, gfp_t);
-extern struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *,
-                                                unsigned long);
-extern void rxrpc_release_call(struct rxrpc_call *);
-extern void rxrpc_release_calls_on_socket(struct rxrpc_sock *);
-extern void __rxrpc_put_call(struct rxrpc_call *);
-extern void __exit rxrpc_destroy_all_calls(void);
+struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *,
+                                        struct rxrpc_transport *,
+                                        struct rxrpc_conn_bundle *,
+                                        unsigned long, int, gfp_t);
+struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *,
+                                      struct rxrpc_connection *,
+                                      struct rxrpc_header *, gfp_t);
+struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *, unsigned long);
+void rxrpc_release_call(struct rxrpc_call *);
+void rxrpc_release_calls_on_socket(struct rxrpc_sock *);
+void __rxrpc_put_call(struct rxrpc_call *);
+void __exit rxrpc_destroy_all_calls(void);
 
 /*
  * ar-connection.c
@@ -465,19 +463,16 @@ extern void __exit rxrpc_destroy_all_calls(void);
 extern struct list_head rxrpc_connections;
 extern rwlock_t rxrpc_connection_lock;
 
-extern struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *,
-                                                 struct rxrpc_transport *,
-                                                 struct key *,
-                                                 __be16, gfp_t);
-extern void rxrpc_put_bundle(struct rxrpc_transport *,
-                            struct rxrpc_conn_bundle *);
-extern int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *,
-                             struct rxrpc_conn_bundle *, struct rxrpc_call *,
-                             gfp_t);
-extern void rxrpc_put_connection(struct rxrpc_connection *);
-extern void __exit rxrpc_destroy_all_connections(void);
-extern struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *,
-                                                     struct rxrpc_header *);
+struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *,
+                                          struct rxrpc_transport *,
+                                          struct key *, __be16, gfp_t);
+void rxrpc_put_bundle(struct rxrpc_transport *, struct rxrpc_conn_bundle *);
+int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *,
+                      struct rxrpc_conn_bundle *, struct rxrpc_call *, gfp_t);
+void rxrpc_put_connection(struct rxrpc_connection *);
+void __exit rxrpc_destroy_all_connections(void);
+struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *,
+                                              struct rxrpc_header *);
 extern struct rxrpc_connection *
 rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *,
                          gfp_t);
@@ -485,15 +480,15 @@ rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *,
 /*
  * ar-connevent.c
  */
-extern void rxrpc_process_connection(struct work_struct *);
-extern void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *);
-extern void rxrpc_reject_packets(struct work_struct *);
+void rxrpc_process_connection(struct work_struct *);
+void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *);
+void rxrpc_reject_packets(struct work_struct *);
 
 /*
  * ar-error.c
  */
-extern void rxrpc_UDP_error_report(struct sock *);
-extern void rxrpc_UDP_error_handler(struct work_struct *);
+void rxrpc_UDP_error_report(struct sock *);
+void rxrpc_UDP_error_handler(struct work_struct *);
 
 /*
  * ar-input.c
@@ -501,18 +496,17 @@ extern void rxrpc_UDP_error_handler(struct work_struct *);
 extern unsigned long rxrpc_ack_timeout;
 extern const char *rxrpc_pkts[];
 
-extern void rxrpc_data_ready(struct sock *, int);
-extern int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool,
-                              bool);
-extern void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *);
+void rxrpc_data_ready(struct sock *, int);
+int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool, bool);
+void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *);
 
 /*
  * ar-local.c
  */
 extern rwlock_t rxrpc_local_lock;
-extern struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *);
-extern void rxrpc_put_local(struct rxrpc_local *);
-extern void __exit rxrpc_destroy_all_locals(void);
+struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *);
+void rxrpc_put_local(struct rxrpc_local *);
+void __exit rxrpc_destroy_all_locals(void);
 
 /*
  * ar-key.c
@@ -520,31 +514,29 @@ extern void __exit rxrpc_destroy_all_locals(void);
 extern struct key_type key_type_rxrpc;
 extern struct key_type key_type_rxrpc_s;
 
-extern int rxrpc_request_key(struct rxrpc_sock *, char __user *, int);
-extern int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int);
-extern int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *,
-                                    time_t, u32);
+int rxrpc_request_key(struct rxrpc_sock *, char __user *, int);
+int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int);
+int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time_t,
+                             u32);
 
 /*
  * ar-output.c
  */
 extern int rxrpc_resend_timeout;
 
-extern int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *);
-extern int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *,
-                               struct rxrpc_transport *, struct msghdr *,
-                               size_t);
-extern int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *,
-                               struct msghdr *, size_t);
+int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *);
+int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *,
+                        struct rxrpc_transport *, struct msghdr *, size_t);
+int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *, struct msghdr *,
+                        size_t);
 
 /*
  * ar-peer.c
  */
-extern struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t);
-extern void rxrpc_put_peer(struct rxrpc_peer *);
-extern struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *,
-                                         __be32, __be16);
-extern void __exit rxrpc_destroy_all_peers(void);
+struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t);
+void rxrpc_put_peer(struct rxrpc_peer *);
+struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, __be32, __be16);
+void __exit rxrpc_destroy_all_peers(void);
 
 /*
  * ar-proc.c
@@ -556,38 +548,36 @@ extern const struct file_operations rxrpc_connection_seq_fops;
 /*
  * ar-recvmsg.c
  */
-extern void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *);
-extern int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *,
-                        size_t, int);
+void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *);
+int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t,
+                 int);
 
 /*
  * ar-security.c
  */
-extern int rxrpc_register_security(struct rxrpc_security *);
-extern void rxrpc_unregister_security(struct rxrpc_security *);
-extern int rxrpc_init_client_conn_security(struct rxrpc_connection *);
-extern int rxrpc_init_server_conn_security(struct rxrpc_connection *);
-extern int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *,
-                              size_t, void *);
-extern int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *,
-                              u32 *);
-extern void rxrpc_clear_conn_security(struct rxrpc_connection *);
+int rxrpc_register_security(struct rxrpc_security *);
+void rxrpc_unregister_security(struct rxrpc_security *);
+int rxrpc_init_client_conn_security(struct rxrpc_connection *);
+int rxrpc_init_server_conn_security(struct rxrpc_connection *);
+int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *, size_t,
+                       void *);
+int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *, u32 *);
+void rxrpc_clear_conn_security(struct rxrpc_connection *);
 
 /*
  * ar-skbuff.c
  */
-extern void rxrpc_packet_destructor(struct sk_buff *);
+void rxrpc_packet_destructor(struct sk_buff *);
 
 /*
  * ar-transport.c
  */
-extern struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *,
-                                                  struct rxrpc_peer *,
-                                                  gfp_t);
-extern void rxrpc_put_transport(struct rxrpc_transport *);
-extern void __exit rxrpc_destroy_all_transports(void);
-extern struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
-                                                   struct rxrpc_peer *);
+struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *,
+                                           struct rxrpc_peer *, gfp_t);
+void rxrpc_put_transport(struct rxrpc_transport *);
+void __exit rxrpc_destroy_all_transports(void);
+struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
+                                            struct rxrpc_peer *);
 
 /*
  * debug tracing
index c03a32a..ad1f1d8 100644 (file)
@@ -443,6 +443,16 @@ config NET_CLS_CGROUP
          To compile this code as a module, choose M here: the
          module will be called cls_cgroup.
 
+config NET_CLS_BPF
+       tristate "BPF-based classifier"
+       select NET_CLS
+       ---help---
+         If you say Y here, you will be able to classify packets based on
+         programmable BPF (JIT'ed) filters as an alternative to ematches.
+
+         To compile this code as a module, choose M here: the module will
+         be called cls_bpf.
+
 config NET_EMATCH
        bool "Extended Matches"
        select NET_CLS
index e5f9abe..35fa47a 100644 (file)
@@ -50,6 +50,7 @@ obj-$(CONFIG_NET_CLS_RSVP6)   += cls_rsvp6.o
 obj-$(CONFIG_NET_CLS_BASIC)    += cls_basic.o
 obj-$(CONFIG_NET_CLS_FLOW)     += cls_flow.o
 obj-$(CONFIG_NET_CLS_CGROUP)   += cls_cgroup.o
+obj-$(CONFIG_NET_CLS_BPF)      += cls_bpf.o
 obj-$(CONFIG_NET_EMATCH)       += ematch.o
 obj-$(CONFIG_NET_EMATCH_CMP)   += em_cmp.o
 obj-$(CONFIG_NET_EMATCH_NBYTE) += em_nbyte.o
index 189e3c5..272d8e9 100644 (file)
@@ -231,14 +231,14 @@ override:
        }
        if (R_tab) {
                police->rate_present = true;
-               psched_ratecfg_precompute(&police->rate, &R_tab->rate);
+               psched_ratecfg_precompute(&police->rate, &R_tab->rate, 0);
                qdisc_put_rtab(R_tab);
        } else {
                police->rate_present = false;
        }
        if (P_tab) {
                police->peak_present = true;
-               psched_ratecfg_precompute(&police->peak, &P_tab->rate);
+               psched_ratecfg_precompute(&police->peak, &P_tab->rate, 0);
                qdisc_put_rtab(P_tab);
        } else {
                police->peak_present = false;
index d76a35d..636d913 100644 (file)
@@ -137,7 +137,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
                           struct nlattr **tb,
                           struct nlattr *est)
 {
-       int err = -EINVAL;
+       int err;
        struct tcf_exts e;
        struct tcf_ematch_tree t;
 
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
new file mode 100644 (file)
index 0000000..1002a82
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Berkeley Packet Filter based traffic classifier
+ *
+ * Might be used to classify traffic through flexible, user-defined and
+ * possibly JIT-ed BPF filters for traffic control as an alternative to
+ * ematches.
+ *
+ * (C) 2013 Daniel Borkmann <dborkman@redhat.com>
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/filter.h>
+#include <net/rtnetlink.h>
+#include <net/pkt_cls.h>
+#include <net/sock.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
+MODULE_DESCRIPTION("TC BPF based classifier");
+
+struct cls_bpf_head {
+       struct list_head plist;
+       u32 hgen;
+};
+
+struct cls_bpf_prog {
+       struct sk_filter *filter;
+       struct sock_filter *bpf_ops;
+       struct tcf_exts exts;
+       struct tcf_result res;
+       struct list_head link;
+       u32 handle;
+       u16 bpf_len;
+};
+
+static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = {
+       [TCA_BPF_CLASSID]       = { .type = NLA_U32 },
+       [TCA_BPF_OPS_LEN]       = { .type = NLA_U16 },
+       [TCA_BPF_OPS]           = { .type = NLA_BINARY,
+                                   .len = sizeof(struct sock_filter) * BPF_MAXINSNS },
+};
+
+static const struct tcf_ext_map bpf_ext_map = {
+       .action = TCA_BPF_ACT,
+       .police = TCA_BPF_POLICE,
+};
+
+static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
+                           struct tcf_result *res)
+{
+       struct cls_bpf_head *head = tp->root;
+       struct cls_bpf_prog *prog;
+       int ret;
+
+       list_for_each_entry(prog, &head->plist, link) {
+               int filter_res = SK_RUN_FILTER(prog->filter, skb);
+
+               if (filter_res == 0)
+                       continue;
+
+               *res = prog->res;
+               if (filter_res != -1)
+                       res->classid = filter_res;
+
+               ret = tcf_exts_exec(skb, &prog->exts, res);
+               if (ret < 0)
+                       continue;
+
+               return ret;
+       }
+
+       return -1;
+}
+
+static int cls_bpf_init(struct tcf_proto *tp)
+{
+       struct cls_bpf_head *head;
+
+       head = kzalloc(sizeof(*head), GFP_KERNEL);
+       if (head == NULL)
+               return -ENOBUFS;
+
+       INIT_LIST_HEAD(&head->plist);
+       tp->root = head;
+
+       return 0;
+}
+
+static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog)
+{
+       tcf_unbind_filter(tp, &prog->res);
+       tcf_exts_destroy(tp, &prog->exts);
+
+       sk_unattached_filter_destroy(prog->filter);
+
+       kfree(prog->bpf_ops);
+       kfree(prog);
+}
+
+static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg)
+{
+       struct cls_bpf_head *head = tp->root;
+       struct cls_bpf_prog *prog, *todel = (struct cls_bpf_prog *) arg;
+
+       list_for_each_entry(prog, &head->plist, link) {
+               if (prog == todel) {
+                       tcf_tree_lock(tp);
+                       list_del(&prog->link);
+                       tcf_tree_unlock(tp);
+
+                       cls_bpf_delete_prog(tp, prog);
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+static void cls_bpf_destroy(struct tcf_proto *tp)
+{
+       struct cls_bpf_head *head = tp->root;
+       struct cls_bpf_prog *prog, *tmp;
+
+       list_for_each_entry_safe(prog, tmp, &head->plist, link) {
+               list_del(&prog->link);
+               cls_bpf_delete_prog(tp, prog);
+       }
+
+       kfree(head);
+}
+
+static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
+{
+       struct cls_bpf_head *head = tp->root;
+       struct cls_bpf_prog *prog;
+       unsigned long ret = 0UL;
+
+       if (head == NULL)
+               return 0UL;
+
+       list_for_each_entry(prog, &head->plist, link) {
+               if (prog->handle == handle) {
+                       ret = (unsigned long) prog;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static void cls_bpf_put(struct tcf_proto *tp, unsigned long f)
+{
+}
+
+static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
+                                  struct cls_bpf_prog *prog,
+                                  unsigned long base, struct nlattr **tb,
+                                  struct nlattr *est)
+{
+       struct sock_filter *bpf_ops, *bpf_old;
+       struct tcf_exts exts;
+       struct sock_fprog tmp;
+       struct sk_filter *fp, *fp_old;
+       u16 bpf_size, bpf_len;
+       u32 classid;
+       int ret;
+
+       if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID])
+               return -EINVAL;
+
+       ret = tcf_exts_validate(net, tp, tb, est, &exts, &bpf_ext_map);
+       if (ret < 0)
+               return ret;
+
+       classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
+       bpf_len = nla_get_u16(tb[TCA_BPF_OPS_LEN]);
+       if (bpf_len > BPF_MAXINSNS || bpf_len == 0) {
+               ret = -EINVAL;
+               goto errout;
+       }
+
+       bpf_size = bpf_len * sizeof(*bpf_ops);
+       bpf_ops = kzalloc(bpf_size, GFP_KERNEL);
+       if (bpf_ops == NULL) {
+               ret = -ENOMEM;
+               goto errout;
+       }
+
+       memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size);
+
+       tmp.len = bpf_len;
+       tmp.filter = (struct sock_filter __user *) bpf_ops;
+
+       ret = sk_unattached_filter_create(&fp, &tmp);
+       if (ret)
+               goto errout_free;
+
+       tcf_tree_lock(tp);
+       fp_old = prog->filter;
+       bpf_old = prog->bpf_ops;
+
+       prog->bpf_len = bpf_len;
+       prog->bpf_ops = bpf_ops;
+       prog->filter = fp;
+       prog->res.classid = classid;
+       tcf_tree_unlock(tp);
+
+       tcf_bind_filter(tp, &prog->res, base);
+       tcf_exts_change(tp, &prog->exts, &exts);
+
+       if (fp_old)
+               sk_unattached_filter_destroy(fp_old);
+       if (bpf_old)
+               kfree(bpf_old);
+
+       return 0;
+
+errout_free:
+       kfree(bpf_ops);
+errout:
+       tcf_exts_destroy(tp, &exts);
+       return ret;
+}
+
+static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
+                                  struct cls_bpf_head *head)
+{
+       unsigned int i = 0x80000000;
+
+       do {
+               if (++head->hgen == 0x7FFFFFFF)
+                       head->hgen = 1;
+       } while (--i > 0 && cls_bpf_get(tp, head->hgen));
+       if (i == 0)
+               pr_err("Insufficient number of handles\n");
+
+       return i;
+}
+
+static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
+                         struct tcf_proto *tp, unsigned long base,
+                         u32 handle, struct nlattr **tca,
+                         unsigned long *arg)
+{
+       struct cls_bpf_head *head = tp->root;
+       struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg;
+       struct nlattr *tb[TCA_BPF_MAX + 1];
+       int ret;
+
+       if (tca[TCA_OPTIONS] == NULL)
+               return -EINVAL;
+
+       ret = nla_parse_nested(tb, TCA_BPF_MAX, tca[TCA_OPTIONS], bpf_policy);
+       if (ret < 0)
+               return ret;
+
+       if (prog != NULL) {
+               if (handle && prog->handle != handle)
+                       return -EINVAL;
+               return cls_bpf_modify_existing(net, tp, prog, base, tb,
+                                              tca[TCA_RATE]);
+       }
+
+       prog = kzalloc(sizeof(*prog), GFP_KERNEL);
+       if (prog == NULL)
+               return -ENOBUFS;
+
+       if (handle == 0)
+               prog->handle = cls_bpf_grab_new_handle(tp, head);
+       else
+               prog->handle = handle;
+       if (prog->handle == 0) {
+               ret = -EINVAL;
+               goto errout;
+       }
+
+       ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]);
+       if (ret < 0)
+               goto errout;
+
+       tcf_tree_lock(tp);
+       list_add(&prog->link, &head->plist);
+       tcf_tree_unlock(tp);
+
+       *arg = (unsigned long) prog;
+
+       return 0;
+errout:
+       if (*arg == 0UL && prog)
+               kfree(prog);
+
+       return ret;
+}
+
+static int cls_bpf_dump(struct tcf_proto *tp, unsigned long fh,
+                       struct sk_buff *skb, struct tcmsg *tm)
+{
+       struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh;
+       struct nlattr *nest, *nla;
+
+       if (prog == NULL)
+               return skb->len;
+
+       tm->tcm_handle = prog->handle;
+
+       nest = nla_nest_start(skb, TCA_OPTIONS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       if (nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid))
+               goto nla_put_failure;
+       if (nla_put_u16(skb, TCA_BPF_OPS_LEN, prog->bpf_len))
+               goto nla_put_failure;
+
+       nla = nla_reserve(skb, TCA_BPF_OPS, prog->bpf_len *
+                         sizeof(struct sock_filter));
+       if (nla == NULL)
+               goto nla_put_failure;
+
+        memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla));
+
+       if (tcf_exts_dump(skb, &prog->exts, &bpf_ext_map) < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+
+       if (tcf_exts_dump_stats(skb, &prog->exts, &bpf_ext_map) < 0)
+               goto nla_put_failure;
+
+       return skb->len;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -1;
+}
+
+static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+{
+       struct cls_bpf_head *head = tp->root;
+       struct cls_bpf_prog *prog;
+
+       list_for_each_entry(prog, &head->plist, link) {
+               if (arg->count < arg->skip)
+                       goto skip;
+               if (arg->fn(tp, (unsigned long) prog, arg) < 0) {
+                       arg->stop = 1;
+                       break;
+               }
+skip:
+               arg->count++;
+       }
+}
+
+static struct tcf_proto_ops cls_bpf_ops __read_mostly = {
+       .kind           =       "bpf",
+       .owner          =       THIS_MODULE,
+       .classify       =       cls_bpf_classify,
+       .init           =       cls_bpf_init,
+       .destroy        =       cls_bpf_destroy,
+       .get            =       cls_bpf_get,
+       .put            =       cls_bpf_put,
+       .change         =       cls_bpf_change,
+       .delete         =       cls_bpf_delete,
+       .walk           =       cls_bpf_walk,
+       .dump           =       cls_bpf_dump,
+};
+
+static int __init cls_bpf_init_mod(void)
+{
+       return register_tcf_proto_ops(&cls_bpf_ops);
+}
+
+static void __exit cls_bpf_exit_mod(void)
+{
+       unregister_tcf_proto_ops(&cls_bpf_ops);
+}
+
+module_init(cls_bpf_init_mod);
+module_exit(cls_bpf_exit_mod);
index 867b4a3..16006c9 100644 (file)
@@ -72,11 +72,11 @@ static void cgrp_attach(struct cgroup_subsys_state *css,
                        struct cgroup_taskset *tset)
 {
        struct task_struct *p;
-       void *v;
+       struct cgroup_cls_state *cs = css_cls_state(css);
+       void *v = (void *)(unsigned long)cs->classid;
 
        cgroup_taskset_for_each(p, css, tset) {
                task_lock(p);
-               v = (void *)(unsigned long)task_cls_classid(p);
                iterate_fd(p->files, 0, update_classid, v);
                task_unlock(p);
        }
index 938b7cb..527aeb7 100644 (file)
@@ -24,11 +24,12 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
 {
        struct xt_set_info *set = data;
        ip_set_id_t index;
+       struct net *net = dev_net(qdisc_dev(tp->q));
 
        if (data_len != sizeof(*set))
                return -EINVAL;
 
-       index = ip_set_nfnl_get_byindex(set->index);
+       index = ip_set_nfnl_get_byindex(net, set->index);
        if (index == IPSET_INVALID_ID)
                return -ENOENT;
 
@@ -37,7 +38,7 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
        if (em->data)
                return 0;
 
-       ip_set_nfnl_put(index);
+       ip_set_nfnl_put(net, index);
        return -ENOMEM;
 }
 
@@ -45,7 +46,7 @@ static void em_ipset_destroy(struct tcf_proto *p, struct tcf_ematch *em)
 {
        const struct xt_set_info *set = (const void *) em->data;
        if (set) {
-               ip_set_nfnl_put(set->index);
+               ip_set_nfnl_put(dev_net(qdisc_dev(p->q)), set->index);
                kfree((void *) em->data);
        }
 }
index 7c3de6f..e5cef95 100644 (file)
@@ -793,8 +793,10 @@ static int em_meta_change(struct tcf_proto *tp, void *data, int len,
                goto errout;
 
        meta = kzalloc(sizeof(*meta), GFP_KERNEL);
-       if (meta == NULL)
+       if (meta == NULL) {
+               err = -ENOMEM;
                goto errout;
+       }
 
        memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
        memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
index 2adda7f..cd81505 100644 (file)
@@ -737,9 +737,11 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
        const struct Qdisc_class_ops *cops;
        unsigned long cl;
        u32 parentid;
+       int drops;
 
        if (n == 0)
                return;
+       drops = max_t(int, n, 0);
        while ((parentid = sch->parent)) {
                if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS))
                        return;
@@ -756,6 +758,7 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
                        cops->put(sch, cl);
                }
                sch->q.qlen -= n;
+               sch->qstats.drops += drops;
        }
 }
 EXPORT_SYMBOL(qdisc_tree_decrease_qlen);
index 32ad015..fdc041c 100644 (file)
@@ -255,6 +255,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
                                     f->socket_hash != sk->sk_hash)) {
                                f->credit = q->initial_quantum;
                                f->socket_hash = sk->sk_hash;
+                               f->time_next_packet = 0ULL;
                        }
                        return f;
                }
@@ -285,7 +286,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
 
 
 /* remove one skb from head of flow queue */
-static struct sk_buff *fq_dequeue_head(struct fq_flow *flow)
+static struct sk_buff *fq_dequeue_head(struct Qdisc *sch, struct fq_flow *flow)
 {
        struct sk_buff *skb = flow->head;
 
@@ -293,6 +294,8 @@ static struct sk_buff *fq_dequeue_head(struct fq_flow *flow)
                flow->head = skb->next;
                skb->next = NULL;
                flow->qlen--;
+               sch->qstats.backlog -= qdisc_pkt_len(skb);
+               sch->q.qlen--;
        }
        return skb;
 }
@@ -418,8 +421,9 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
        struct fq_flow_head *head;
        struct sk_buff *skb;
        struct fq_flow *f;
+       u32 rate;
 
-       skb = fq_dequeue_head(&q->internal);
+       skb = fq_dequeue_head(sch, &q->internal);
        if (skb)
                goto out;
        fq_check_throttled(q, now);
@@ -449,7 +453,7 @@ begin:
                goto begin;
        }
 
-       skb = fq_dequeue_head(f);
+       skb = fq_dequeue_head(sch, f);
        if (!skb) {
                head->first = f->next;
                /* force a pass through old_flows to prevent starvation */
@@ -466,43 +470,70 @@ begin:
        f->time_next_packet = now;
        f->credit -= qdisc_pkt_len(skb);
 
-       if (f->credit <= 0 &&
-           q->rate_enable &&
-           skb->sk && skb->sk->sk_state != TCP_TIME_WAIT) {
-               u32 rate = skb->sk->sk_pacing_rate ?: q->flow_default_rate;
+       if (f->credit > 0 || !q->rate_enable)
+               goto out;
 
-               rate = min(rate, q->flow_max_rate);
-               if (rate) {
-                       u64 len = (u64)qdisc_pkt_len(skb) * NSEC_PER_SEC;
+       rate = q->flow_max_rate;
+       if (skb->sk && skb->sk->sk_state != TCP_TIME_WAIT)
+               rate = min(skb->sk->sk_pacing_rate, rate);
 
-                       do_div(len, rate);
-                       /* Since socket rate can change later,
-                        * clamp the delay to 125 ms.
-                        * TODO: maybe segment the too big skb, as in commit
-                        * e43ac79a4bc ("sch_tbf: segment too big GSO packets")
-                        */
-                       if (unlikely(len > 125 * NSEC_PER_MSEC)) {
-                               len = 125 * NSEC_PER_MSEC;
-                               q->stat_pkts_too_long++;
-                       }
+       if (rate != ~0U) {
+               u32 plen = max(qdisc_pkt_len(skb), q->quantum);
+               u64 len = (u64)plen * NSEC_PER_SEC;
 
-                       f->time_next_packet = now + len;
+               if (likely(rate))
+                       do_div(len, rate);
+               /* Since socket rate can change later,
+                * clamp the delay to 125 ms.
+                * TODO: maybe segment the too big skb, as in commit
+                * e43ac79a4bc ("sch_tbf: segment too big GSO packets")
+                */
+               if (unlikely(len > 125 * NSEC_PER_MSEC)) {
+                       len = 125 * NSEC_PER_MSEC;
+                       q->stat_pkts_too_long++;
                }
+
+               f->time_next_packet = now + len;
        }
 out:
-       sch->qstats.backlog -= qdisc_pkt_len(skb);
        qdisc_bstats_update(sch, skb);
-       sch->q.qlen--;
        qdisc_unthrottled(sch);
        return skb;
 }
 
 static void fq_reset(struct Qdisc *sch)
 {
+       struct fq_sched_data *q = qdisc_priv(sch);
+       struct rb_root *root;
        struct sk_buff *skb;
+       struct rb_node *p;
+       struct fq_flow *f;
+       unsigned int idx;
 
-       while ((skb = fq_dequeue(sch)) != NULL)
+       while ((skb = fq_dequeue_head(sch, &q->internal)) != NULL)
                kfree_skb(skb);
+
+       if (!q->fq_root)
+               return;
+
+       for (idx = 0; idx < (1U << q->fq_trees_log); idx++) {
+               root = &q->fq_root[idx];
+               while ((p = rb_first(root)) != NULL) {
+                       f = container_of(p, struct fq_flow, fq_node);
+                       rb_erase(p, root);
+
+                       while ((skb = fq_dequeue_head(sch, f)) != NULL)
+                               kfree_skb(skb);
+
+                       kmem_cache_free(fq_flow_cachep, f);
+               }
+       }
+       q->new_flows.first      = NULL;
+       q->old_flows.first      = NULL;
+       q->delayed              = RB_ROOT;
+       q->flows                = 0;
+       q->inactive_flows       = 0;
+       q->throttled_flows      = 0;
 }
 
 static void fq_rehash(struct fq_sched_data *q,
@@ -622,7 +653,7 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
                q->quantum = nla_get_u32(tb[TCA_FQ_QUANTUM]);
 
        if (tb[TCA_FQ_INITIAL_QUANTUM])
-               q->quantum = nla_get_u32(tb[TCA_FQ_INITIAL_QUANTUM]);
+               q->initial_quantum = nla_get_u32(tb[TCA_FQ_INITIAL_QUANTUM]);
 
        if (tb[TCA_FQ_FLOW_DEFAULT_RATE])
                q->flow_default_rate = nla_get_u32(tb[TCA_FQ_FLOW_DEFAULT_RATE]);
@@ -645,6 +676,8 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
        while (sch->q.qlen > sch->limit) {
                struct sk_buff *skb = fq_dequeue(sch);
 
+               if (!skb)
+                       break;
                kfree_skb(skb);
                drop_count++;
        }
@@ -657,21 +690,9 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
 static void fq_destroy(struct Qdisc *sch)
 {
        struct fq_sched_data *q = qdisc_priv(sch);
-       struct rb_root *root;
-       struct rb_node *p;
-       unsigned int idx;
 
-       if (q->fq_root) {
-               for (idx = 0; idx < (1U << q->fq_trees_log); idx++) {
-                       root = &q->fq_root[idx];
-                       while ((p = rb_first(root)) != NULL) {
-                               rb_erase(p, root);
-                               kmem_cache_free(fq_flow_cachep,
-                                               container_of(p, struct fq_flow, fq_node));
-                       }
-               }
-               kfree(q->fq_root);
-       }
+       fq_reset(sch);
+       kfree(q->fq_root);
        qdisc_watchdog_cancel(&q->watchdog);
 }
 
@@ -711,12 +732,14 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (opts == NULL)
                goto nla_put_failure;
 
+       /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore,
+        * do not bother giving its value
+        */
        if (nla_put_u32(skb, TCA_FQ_PLIMIT, sch->limit) ||
            nla_put_u32(skb, TCA_FQ_FLOW_PLIMIT, q->flow_plimit) ||
            nla_put_u32(skb, TCA_FQ_QUANTUM, q->quantum) ||
            nla_put_u32(skb, TCA_FQ_INITIAL_QUANTUM, q->initial_quantum) ||
            nla_put_u32(skb, TCA_FQ_RATE_ENABLE, q->rate_enable) ||
-           nla_put_u32(skb, TCA_FQ_FLOW_DEFAULT_RATE, q->flow_default_rate) ||
            nla_put_u32(skb, TCA_FQ_FLOW_MAX_RATE, q->flow_max_rate) ||
            nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log))
                goto nla_put_failure;
index a74e278..922a094 100644 (file)
@@ -126,7 +126,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
 
        HARD_TX_LOCK(dev, txq, smp_processor_id());
        if (!netif_xmit_frozen_or_stopped(txq))
-               ret = dev_hard_start_xmit(skb, dev, txq);
+               ret = dev_hard_start_xmit(skb, dev, txq, NULL);
 
        HARD_TX_UNLOCK(dev, txq);
 
@@ -829,7 +829,7 @@ void dev_deactivate_many(struct list_head *head)
        struct net_device *dev;
        bool sync_needed = false;
 
-       list_for_each_entry(dev, head, unreg_list) {
+       list_for_each_entry(dev, head, close_list) {
                netdev_for_each_tx_queue(dev, dev_deactivate_queue,
                                         &noop_qdisc);
                if (dev_ingress_queue(dev))
@@ -848,7 +848,7 @@ void dev_deactivate_many(struct list_head *head)
                synchronize_net();
 
        /* Wait for outstanding qdisc_run calls. */
-       list_for_each_entry(dev, head, unreg_list)
+       list_for_each_entry(dev, head, close_list)
                while (some_qdisc_is_busy(dev))
                        yield();
 }
@@ -857,7 +857,7 @@ void dev_deactivate(struct net_device *dev)
 {
        LIST_HEAD(single);
 
-       list_add(&dev->unreg_list, &single);
+       list_add(&dev->close_list, &single);
        dev_deactivate_many(&single);
        list_del(&single);
 }
@@ -910,11 +910,12 @@ void dev_shutdown(struct net_device *dev)
 }
 
 void psched_ratecfg_precompute(struct psched_ratecfg *r,
-                              const struct tc_ratespec *conf)
+                              const struct tc_ratespec *conf,
+                              u64 rate64)
 {
        memset(r, 0, sizeof(*r));
        r->overhead = conf->overhead;
-       r->rate_bytes_ps = conf->rate;
+       r->rate_bytes_ps = max_t(u64, conf->rate, rate64);
        r->linklayer = (conf->linklayer & TC_LINKLAYER_MASK);
        r->mult = 1;
        /*
index 863846c..0e1e38b 100644 (file)
@@ -997,6 +997,8 @@ static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = {
        [TCA_HTB_CTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
        [TCA_HTB_RTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
        [TCA_HTB_DIRECT_QLEN] = { .type = NLA_U32 },
+       [TCA_HTB_RATE64] = { .type = NLA_U64 },
+       [TCA_HTB_CEIL64] = { .type = NLA_U64 },
 };
 
 static void htb_work_func(struct work_struct *work)
@@ -1114,6 +1116,12 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
        opt.level = cl->level;
        if (nla_put(skb, TCA_HTB_PARMS, sizeof(opt), &opt))
                goto nla_put_failure;
+       if ((cl->rate.rate_bytes_ps >= (1ULL << 32)) &&
+           nla_put_u64(skb, TCA_HTB_RATE64, cl->rate.rate_bytes_ps))
+               goto nla_put_failure;
+       if ((cl->ceil.rate_bytes_ps >= (1ULL << 32)) &&
+           nla_put_u64(skb, TCA_HTB_CEIL64, cl->ceil.rate_bytes_ps))
+               goto nla_put_failure;
 
        nla_nest_end(skb, nest);
        spin_unlock_bh(root_lock);
@@ -1332,6 +1340,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
        struct qdisc_rate_table *rtab = NULL, *ctab = NULL;
        struct nlattr *tb[TCA_HTB_MAX + 1];
        struct tc_htb_opt *hopt;
+       u64 rate64, ceil64;
 
        /* extract all subattrs from opt attr */
        if (!opt)
@@ -1491,8 +1500,12 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                        cl->prio = TC_HTB_NUMPRIO - 1;
        }
 
-       psched_ratecfg_precompute(&cl->rate, &hopt->rate);
-       psched_ratecfg_precompute(&cl->ceil, &hopt->ceil);
+       rate64 = tb[TCA_HTB_RATE64] ? nla_get_u64(tb[TCA_HTB_RATE64]) : 0;
+
+       ceil64 = tb[TCA_HTB_CEIL64] ? nla_get_u64(tb[TCA_HTB_CEIL64]) : 0;
+
+       psched_ratecfg_precompute(&cl->rate, &hopt->rate, rate64);
+       psched_ratecfg_precompute(&cl->ceil, &hopt->ceil, ceil64);
 
        cl->buffer = PSCHED_TICKS2NS(hopt->buffer);
        cl->cbuffer = PSCHED_TICKS2NS(hopt->cbuffer);
index a6d788d..75c94e5 100644 (file)
@@ -235,7 +235,6 @@ static bool loss_4state(struct netem_sched_data *q)
                        clg->state = 2;
                else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) {
                        clg->state = 1;
-                       return true;
                } else if (clg->a2 + clg->a3 < rnd) {
                        clg->state = 3;
                        return true;
@@ -358,6 +357,21 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
        return PSCHED_NS2TICKS(ticks);
 }
 
+static void tfifo_reset(struct Qdisc *sch)
+{
+       struct netem_sched_data *q = qdisc_priv(sch);
+       struct rb_node *p;
+
+       while ((p = rb_first(&q->t_root))) {
+               struct sk_buff *skb = netem_rb_to_skb(p);
+
+               rb_erase(p, &q->t_root);
+               skb->next = NULL;
+               skb->prev = NULL;
+               kfree_skb(skb);
+       }
+}
+
 static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
@@ -520,6 +534,7 @@ static unsigned int netem_drop(struct Qdisc *sch)
                        skb->next = NULL;
                        skb->prev = NULL;
                        len = qdisc_pkt_len(skb);
+                       sch->qstats.backlog -= len;
                        kfree_skb(skb);
                }
        }
@@ -609,6 +624,7 @@ static void netem_reset(struct Qdisc *sch)
        struct netem_sched_data *q = qdisc_priv(sch);
 
        qdisc_reset_queue(sch);
+       tfifo_reset(sch);
        if (q->qdisc)
                qdisc_reset(q->qdisc);
        qdisc_watchdog_cancel(&q->watchdog);
index 1aaf1b6..68f9859 100644 (file)
@@ -266,20 +266,23 @@ static const struct nla_policy tbf_policy[TCA_TBF_MAX + 1] = {
        [TCA_TBF_PARMS] = { .len = sizeof(struct tc_tbf_qopt) },
        [TCA_TBF_RTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
        [TCA_TBF_PTAB]  = { .type = NLA_BINARY, .len = TC_RTAB_SIZE },
+       [TCA_TBF_RATE64]        = { .type = NLA_U64 },
+       [TCA_TBF_PRATE64]       = { .type = NLA_U64 },
 };
 
 static int tbf_change(struct Qdisc *sch, struct nlattr *opt)
 {
        int err;
        struct tbf_sched_data *q = qdisc_priv(sch);
-       struct nlattr *tb[TCA_TBF_PTAB + 1];
+       struct nlattr *tb[TCA_TBF_MAX + 1];
        struct tc_tbf_qopt *qopt;
        struct qdisc_rate_table *rtab = NULL;
        struct qdisc_rate_table *ptab = NULL;
        struct Qdisc *child = NULL;
        int max_size, n;
+       u64 rate64 = 0, prate64 = 0;
 
-       err = nla_parse_nested(tb, TCA_TBF_PTAB, opt, tbf_policy);
+       err = nla_parse_nested(tb, TCA_TBF_MAX, opt, tbf_policy);
        if (err < 0)
                return err;
 
@@ -341,9 +344,13 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt)
        q->tokens = q->buffer;
        q->ptokens = q->mtu;
 
-       psched_ratecfg_precompute(&q->rate, &rtab->rate);
+       if (tb[TCA_TBF_RATE64])
+               rate64 = nla_get_u64(tb[TCA_TBF_RATE64]);
+       psched_ratecfg_precompute(&q->rate, &rtab->rate, rate64);
        if (ptab) {
-               psched_ratecfg_precompute(&q->peak, &ptab->rate);
+               if (tb[TCA_TBF_PRATE64])
+                       prate64 = nla_get_u64(tb[TCA_TBF_PRATE64]);
+               psched_ratecfg_precompute(&q->peak, &ptab->rate, prate64);
                q->peak_present = true;
        } else {
                q->peak_present = false;
@@ -402,6 +409,13 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
        opt.buffer = PSCHED_NS2TICKS(q->buffer);
        if (nla_put(skb, TCA_TBF_PARMS, sizeof(opt), &opt))
                goto nla_put_failure;
+       if (q->rate.rate_bytes_ps >= (1ULL << 32) &&
+           nla_put_u64(skb, TCA_TBF_RATE64, q->rate.rate_bytes_ps))
+               goto nla_put_failure;
+       if (q->peak_present &&
+           q->peak.rate_bytes_ps >= (1ULL << 32) &&
+           nla_put_u64(skb, TCA_TBF_PRATE64, q->peak.rate_bytes_ps))
+               goto nla_put_failure;
 
        nla_nest_end(skb, nest);
        return skb->len;
index cef5099..c9b91cb 100644 (file)
@@ -602,7 +602,7 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
 
                /* Start a T3 timer here in case it wasn't running so
                 * that these migrated packets have a chance to get
-                * retrnasmitted.
+                * retransmitted.
                 */
                if (!timer_pending(&active->T3_rtx_timer))
                        if (!mod_timer(&active->T3_rtx_timer,
@@ -665,7 +665,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
        /* Set the path max_retrans.  */
        peer->pathmaxrxt = asoc->pathmaxrxt;
 
-       /* And the partial failure retrnas threshold */
+       /* And the partial failure retrans threshold */
        peer->pf_retrans = asoc->pf_retrans;
 
        /* Initialize the peer's SACK delay timeout based on the
index 8c4fa5d..46b5977 100644 (file)
@@ -539,18 +539,14 @@ struct sctp_hmac *sctp_auth_asoc_get_hmac(const struct sctp_association *asoc)
        for (i = 0; i < n_elt; i++) {
                id = ntohs(hmacs->hmac_ids[i]);
 
-               /* Check the id is in the supported range */
-               if (id > SCTP_AUTH_HMAC_ID_MAX) {
-                       id = 0;
-                       continue;
-               }
-
-               /* See is we support the id.  Supported IDs have name and
-                * length fields set, so that we can allocated and use
+               /* Check the id is in the supported range. And
+                * see if we support the id.  Supported IDs have name and
+                * length fields set, so that we can allocate and use
                 * them.  We can safely just check for name, for without the
                 * name, we can't allocate the TFM.
                 */
-               if (!sctp_hmac_list[id].hmac_name) {
+               if (id > SCTP_AUTH_HMAC_ID_MAX ||
+                   !sctp_hmac_list[id].hmac_name) {
                        id = 0;
                        continue;
                }
index 7bd5ed4..f2044fc 100644 (file)
@@ -201,7 +201,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
 
        max = asoc->frag_point;
        /* If the the peer requested that we authenticate DATA chunks
-        * we need to accound for bundling of the AUTH chunks along with
+        * we need to account for bundling of the AUTH chunks along with
         * DATA.
         */
        if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) {
index e7b2d4f..7567e6f 100644 (file)
@@ -279,7 +279,9 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
                sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port));
                rcu_read_lock();
                list_for_each_entry_rcu(laddr, &bp->address_list, list) {
-                       if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
+                       if (!laddr->valid || laddr->state == SCTP_ADDR_DEL ||
+                           (laddr->state != SCTP_ADDR_SRC &&
+                            !asoc->src_out_of_asoc_ok))
                                continue;
 
                        /* Do not compare against v4 addrs */
@@ -426,20 +428,20 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)
 {
        addr->v6.sin6_family = AF_INET6;
        addr->v6.sin6_port = 0;
-       addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr;
+       addr->v6.sin6_addr = sk->sk_v6_rcv_saddr;
 }
 
 /* Initialize sk->sk_rcv_saddr from sctp_addr. */
 static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
 {
        if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) {
-               inet6_sk(sk)->rcv_saddr.s6_addr32[0] = 0;
-               inet6_sk(sk)->rcv_saddr.s6_addr32[1] = 0;
-               inet6_sk(sk)->rcv_saddr.s6_addr32[2] = htonl(0x0000ffff);
-               inet6_sk(sk)->rcv_saddr.s6_addr32[3] =
+               sk->sk_v6_rcv_saddr.s6_addr32[0] = 0;
+               sk->sk_v6_rcv_saddr.s6_addr32[1] = 0;
+               sk->sk_v6_rcv_saddr.s6_addr32[2] = htonl(0x0000ffff);
+               sk->sk_v6_rcv_saddr.s6_addr32[3] =
                        addr->v4.sin_addr.s_addr;
        } else {
-               inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr;
+               sk->sk_v6_rcv_saddr = addr->v6.sin6_addr;
        }
 }
 
@@ -447,12 +449,12 @@ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
 static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
 {
        if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) {
-               inet6_sk(sk)->daddr.s6_addr32[0] = 0;
-               inet6_sk(sk)->daddr.s6_addr32[1] = 0;
-               inet6_sk(sk)->daddr.s6_addr32[2] = htonl(0x0000ffff);
-               inet6_sk(sk)->daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr;
+               sk->sk_v6_daddr.s6_addr32[0] = 0;
+               sk->sk_v6_daddr.s6_addr32[1] = 0;
+               sk->sk_v6_daddr.s6_addr32[2] = htonl(0x0000ffff);
+               sk->sk_v6_daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr;
        } else {
-               inet6_sk(sk)->daddr = addr->v6.sin6_addr;
+               sk->sk_v6_daddr = addr->v6.sin6_addr;
        }
 }
 
index 0ac3a65..e650978 100644 (file)
@@ -390,7 +390,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
        __u8 has_data = 0;
        struct dst_entry *dst = tp->dst;
        unsigned char *auth = NULL;     /* pointer to auth in skb data */
-       __u32 cksum_buf_len = sizeof(struct sctphdr);
 
        pr_debug("%s: packet:%p\n", __func__, packet);
 
@@ -493,7 +492,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
                if (chunk == packet->auth)
                        auth = skb_tail_pointer(nskb);
 
-               cksum_buf_len += chunk->skb->len;
                memcpy(skb_put(nskb, chunk->skb->len),
                               chunk->skb->data, chunk->skb->len);
 
@@ -536,13 +534,9 @@ int sctp_packet_transmit(struct sctp_packet *packet)
         * by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
         */
        if (!sctp_checksum_disable) {
-               if (!(dst->dev->features & NETIF_F_SCTP_CSUM)) {
-                       __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len);
-
-                       /* 3) Put the resultant value into the checksum field in the
-                        *    common header, and leave the rest of the bits unchanged.
-                        */
-                       sh->checksum = sctp_end_cksum(crc32);
+               if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
+                   (dst_xfrm(dst) != NULL) || packet->ipfragok) {
+                       sh->checksum = sctp_compute_cksum(nskb, 0);
                } else {
                        /* no need to seed pseudo checksum for SCTP */
                        nskb->ip_summed = CHECKSUM_PARTIAL;
index d244a23..fe69032 100644 (file)
@@ -1297,6 +1297,13 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc)
 
 /* Turn an skb into a chunk.
  * FIXME: Eventually move the structure directly inside the skb->cb[].
+ *
+ * sctpimpguide-05.txt Section 2.8.2
+ * M1) Each time a new DATA chunk is transmitted
+ * set the 'TSN.Missing.Report' count for that TSN to 0. The
+ * 'TSN.Missing.Report' count will be used to determine missing chunks
+ * and when to fast retransmit.
+ *
  */
 struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
                            const struct sctp_association *asoc,
@@ -1314,29 +1321,9 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
        INIT_LIST_HEAD(&retval->list);
        retval->skb             = skb;
        retval->asoc            = (struct sctp_association *)asoc;
-       retval->has_tsn         = 0;
-       retval->has_ssn         = 0;
-       retval->rtt_in_progress = 0;
-       retval->sent_at         = 0;
        retval->singleton       = 1;
-       retval->end_of_packet   = 0;
-       retval->ecn_ce_done     = 0;
-       retval->pdiscard        = 0;
-
-       /* sctpimpguide-05.txt Section 2.8.2
-        * M1) Each time a new DATA chunk is transmitted
-        * set the 'TSN.Missing.Report' count for that TSN to 0. The
-        * 'TSN.Missing.Report' count will be used to determine missing chunks
-        * and when to fast retransmit.
-        */
-       retval->tsn_missing_report = 0;
-       retval->tsn_gap_acked = 0;
-       retval->fast_retransmit = SCTP_CAN_FRTX;
 
-       /* If this is a fragmented message, track all fragments
-        * of the message (for SEND_FAILED).
-        */
-       retval->msg = NULL;
+       retval->fast_retransmit = SCTP_CAN_FRTX;
 
        /* Polish the bead hole.  */
        INIT_LIST_HEAD(&retval->transmitted_list);
index 666c668..1a6eef3 100644 (file)
@@ -860,7 +860,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
            (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
                return;
 
-       BUG_ON(asoc->peer.primary_path == NULL);
        sctp_unhash_established(asoc);
        sctp_association_free(asoc);
 }
index 911b71b..72046b9 100644 (file)
@@ -5890,7 +5890,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                int low, high, remaining, index;
                unsigned int rover;
 
-               inet_get_local_port_range(&low, &high);
+               inet_get_local_port_range(sock_net(sk), &low, &high);
                remaining = (high - low) + 1;
                rover = net_random() % remaining + low;
 
index ebed4b6..c226ace 100644 (file)
@@ -1964,6 +1964,16 @@ struct used_address {
        unsigned int name_len;
 };
 
+static int copy_msghdr_from_user(struct msghdr *kmsg,
+                                struct msghdr __user *umsg)
+{
+       if (copy_from_user(kmsg, umsg, sizeof(struct msghdr)))
+               return -EFAULT;
+       if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
+               return -EINVAL;
+       return 0;
+}
+
 static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
                         struct msghdr *msg_sys, unsigned int flags,
                         struct used_address *used_address)
@@ -1982,8 +1992,11 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
        if (MSG_CMSG_COMPAT & flags) {
                if (get_compat_msghdr(msg_sys, msg_compat))
                        return -EFAULT;
-       } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
-               return -EFAULT;
+       } else {
+               err = copy_msghdr_from_user(msg_sys, msg);
+               if (err)
+                       return err;
+       }
 
        if (msg_sys->msg_iovlen > UIO_FASTIOV) {
                err = -EMSGSIZE;
@@ -2191,8 +2204,11 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
        if (MSG_CMSG_COMPAT & flags) {
                if (get_compat_msghdr(msg_sys, msg_compat))
                        return -EFAULT;
-       } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr)))
-               return -EFAULT;
+       } else {
+               err = copy_msghdr_from_user(msg_sys, msg);
+               if (err)
+                       return err;
+       }
 
        if (msg_sys->msg_iovlen > UIO_FASTIOV) {
                err = -EMSGSIZE;
index fcac5d1..0846566 100644 (file)
@@ -1075,6 +1075,15 @@ gss_destroy(struct rpc_auth *auth)
        kref_put(&gss_auth->kref, gss_free_callback);
 }
 
+/*
+ * Auths may be shared between rpc clients that were cloned from a
+ * common client with the same xprt, if they also share the flavor and
+ * target_name.
+ *
+ * The auth is looked up from the oldest parent sharing the same
+ * cl_xprt, and the auth itself references only that common parent
+ * (which is guaranteed to last as long as any of its descendants).
+ */
 static struct gss_auth *
 gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
                struct rpc_clnt *clnt,
@@ -1088,6 +1097,8 @@ gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
                        gss_auth,
                        hash,
                        hashval) {
+               if (gss_auth->client != clnt)
+                       continue;
                if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
                        continue;
                if (gss_auth->target_name != args->target_name) {
index 9c9caaa..b6e59f0 100644 (file)
@@ -291,12 +291,14 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
                                &inet_sk(sk)->inet_rcv_saddr,
                                inet_sk(sk)->inet_num);
                break;
+#if IS_ENABLED(CONFIG_IPV6)
        case PF_INET6:
                len = snprintf(buf, remaining, "ipv6 %s %pI6 %d\n",
                                proto_name,
-                               &inet6_sk(sk)->rcv_saddr,
+                               &sk->sk_v6_rcv_saddr,
                                inet_sk(sk)->inet_num);
                break;
+#endif
        default:
                len = snprintf(buf, remaining, "*unknown-%d*\n",
                                sk->sk_family);
index 9bc6db0..e7000be 100644 (file)
@@ -47,12 +47,12 @@ static int net_ctl_permissions(struct ctl_table_header *head,
 
        /* Allow network administrator to have same access as root. */
        if (ns_capable(net->user_ns, CAP_NET_ADMIN) ||
-           uid_eq(root_uid, current_uid())) {
+           uid_eq(root_uid, current_euid())) {
                int mode = (table->mode >> 6) & 7;
                return (mode << 6) | (mode << 3) | mode;
        }
        /* Allow netns root group to have the same access as the root group */
-       if (gid_eq(root_gid, current_gid())) {
+       if (in_egroup_p(root_gid)) {
                int mode = (table->mode >> 3) & 7;
                return (mode << 3) | mode;
        }
index 716de1a..0d44025 100644 (file)
@@ -480,18 +480,24 @@ receive:
                        tipc_node_unlock(node);
                        tipc_link_recv_bundle(buf);
                } else if (msg_user(msg) == MSG_FRAGMENTER) {
-                       int ret = tipc_link_recv_fragment(&node->bclink.defragm,
-                                                     &buf, &msg);
-                       if (ret < 0)
+                       int ret;
+                       ret = tipc_link_recv_fragment(&node->bclink.reasm_head,
+                                                     &node->bclink.reasm_tail,
+                                                     &buf);
+                       if (ret == LINK_REASM_ERROR)
                                goto unlock;
                        spin_lock_bh(&bc_lock);
                        bclink_accept_pkt(node, seqno);
                        bcl->stats.recv_fragments++;
-                       if (ret > 0)
+                       if (ret == LINK_REASM_COMPLETE) {
                                bcl->stats.recv_fragmented++;
+                               /* Point msg to inner header */
+                               msg = buf_msg(buf);
+                               spin_unlock_bh(&bc_lock);
+                               goto receive;
+                       }
                        spin_unlock_bh(&bc_lock);
                        tipc_node_unlock(node);
-                       tipc_net_route_msg(buf);
                } else if (msg_user(msg) == NAME_DISTRIBUTOR) {
                        spin_lock_bh(&bc_lock);
                        bclink_accept_pkt(node, seqno);
index 609c30c..3f9707a 100644 (file)
@@ -387,7 +387,7 @@ restart:
 
        b_ptr = &tipc_bearers[bearer_id];
        strcpy(b_ptr->name, name);
-       res = m_ptr->enable_bearer(b_ptr);
+       res = m_ptr->enable_media(b_ptr);
        if (res) {
                pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
                        name, -res);
@@ -420,23 +420,15 @@ exit:
 }
 
 /**
- * tipc_block_bearer - Block the bearer with the given name, and reset all its links
+ * tipc_block_bearer - Block the bearer, and reset all its links
  */
-int tipc_block_bearer(const char *name)
+int tipc_block_bearer(struct tipc_bearer *b_ptr)
 {
-       struct tipc_bearer *b_ptr = NULL;
        struct tipc_link *l_ptr;
        struct tipc_link *temp_l_ptr;
 
        read_lock_bh(&tipc_net_lock);
-       b_ptr = tipc_bearer_find(name);
-       if (!b_ptr) {
-               pr_warn("Attempt to block unknown bearer <%s>\n", name);
-               read_unlock_bh(&tipc_net_lock);
-               return -EINVAL;
-       }
-
-       pr_info("Blocking bearer <%s>\n", name);
+       pr_info("Blocking bearer <%s>\n", b_ptr->name);
        spin_lock_bh(&b_ptr->lock);
        b_ptr->blocked = 1;
        list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
@@ -465,7 +457,7 @@ static void bearer_disable(struct tipc_bearer *b_ptr)
        pr_info("Disabling bearer <%s>\n", b_ptr->name);
        spin_lock_bh(&b_ptr->lock);
        b_ptr->blocked = 1;
-       b_ptr->media->disable_bearer(b_ptr);
+       b_ptr->media->disable_media(b_ptr);
        list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
                tipc_link_delete(l_ptr);
        }
index 09c869a..e5e04be 100644 (file)
@@ -75,8 +75,8 @@ struct tipc_bearer;
 /**
  * struct tipc_media - TIPC media information available to internal users
  * @send_msg: routine which handles buffer transmission
- * @enable_bearer: routine which enables a bearer
- * @disable_bearer: routine which disables a bearer
+ * @enable_media: routine which enables a media
+ * @disable_media: routine which disables a media
  * @addr2str: routine which converts media address to string
  * @addr2msg: routine which converts media address to protocol message area
  * @msg2addr: routine which converts media address from protocol message area
@@ -91,8 +91,8 @@ struct tipc_media {
        int (*send_msg)(struct sk_buff *buf,
                        struct tipc_bearer *b_ptr,
                        struct tipc_media_addr *dest);
-       int (*enable_bearer)(struct tipc_bearer *b_ptr);
-       void (*disable_bearer)(struct tipc_bearer *b_ptr);
+       int (*enable_media)(struct tipc_bearer *b_ptr);
+       void (*disable_media)(struct tipc_bearer *b_ptr);
        int (*addr2str)(struct tipc_media_addr *a, char *str_buf, int str_size);
        int (*addr2msg)(struct tipc_media_addr *a, char *msg_area);
        int (*msg2addr)(const struct tipc_bearer *b_ptr,
@@ -163,7 +163,7 @@ int tipc_register_media(struct tipc_media *m_ptr);
 
 void tipc_recv_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr);
 
-int  tipc_block_bearer(const char *name);
+int  tipc_block_bearer(struct tipc_bearer *b_ptr);
 void tipc_continue(struct tipc_bearer *tb_ptr);
 
 int tipc_enable_bearer(const char *bearer_name, u32 disc_domain, u32 priority);
index be72f8c..94895d4 100644 (file)
@@ -90,21 +90,21 @@ extern int tipc_random __read_mostly;
 /*
  * Routines available to privileged subsystems
  */
-extern int tipc_core_start_net(unsigned long);
-extern int  tipc_handler_start(void);
-extern void tipc_handler_stop(void);
-extern int  tipc_netlink_start(void);
-extern void tipc_netlink_stop(void);
-extern int  tipc_socket_init(void);
-extern void tipc_socket_stop(void);
-extern int tipc_sock_create_local(int type, struct socket **res);
-extern void tipc_sock_release_local(struct socket *sock);
-extern int tipc_sock_accept_local(struct socket *sock,
-                                 struct socket **newsock, int flags);
+int tipc_core_start_net(unsigned long);
+int tipc_handler_start(void);
+void tipc_handler_stop(void);
+int tipc_netlink_start(void);
+void tipc_netlink_stop(void);
+int tipc_socket_init(void);
+void tipc_socket_stop(void);
+int tipc_sock_create_local(int type, struct socket **res);
+void tipc_sock_release_local(struct socket *sock);
+int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
+                          int flags);
 
 #ifdef CONFIG_SYSCTL
-extern int tipc_register_sysctl(void);
-extern void tipc_unregister_sysctl(void);
+int tipc_register_sysctl(void);
+void tipc_unregister_sysctl(void);
 #else
 #define tipc_register_sysctl() 0
 #define tipc_unregister_sysctl()
@@ -201,6 +201,6 @@ static inline struct tipc_msg *buf_msg(struct sk_buff *skb)
        return (struct tipc_msg *)skb->data;
 }
 
-extern struct sk_buff *tipc_buf_acquire(u32 size);
+struct sk_buff *tipc_buf_acquire(u32 size);
 
 #endif
index 40ea40c..f80d59f 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/eth_media.c: Ethernet bearer support for TIPC
  *
  * Copyright (c) 2001-2007, Ericsson AB
- * Copyright (c) 2005-2008, 2011, Wind River Systems
+ * Copyright (c) 2005-2008, 2011-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "core.h"
 #include "bearer.h"
 
-#define MAX_ETH_BEARERS                MAX_BEARERS
+#define MAX_ETH_MEDIA          MAX_BEARERS
 
 #define ETH_ADDR_OFFSET        4       /* message header offset of MAC address */
 
 /**
- * struct eth_bearer - Ethernet bearer data structure
+ * struct eth_media - Ethernet bearer data structure
  * @bearer: ptr to associated "generic" bearer structure
  * @dev: ptr to associated Ethernet network device
  * @tipc_packet_type: used in binding TIPC to Ethernet driver
  * @setup: work item used when enabling bearer
  * @cleanup: work item used when disabling bearer
  */
-struct eth_bearer {
+struct eth_media {
        struct tipc_bearer *bearer;
        struct net_device *dev;
        struct packet_type tipc_packet_type;
@@ -58,7 +58,7 @@ struct eth_bearer {
 };
 
 static struct tipc_media eth_media_info;
-static struct eth_bearer eth_bearers[MAX_ETH_BEARERS];
+static struct eth_media eth_media_array[MAX_ETH_MEDIA];
 static int eth_started;
 
 static int recv_notification(struct notifier_block *nb, unsigned long evt,
@@ -100,7 +100,7 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
        if (!clone)
                return 0;
 
-       dev = ((struct eth_bearer *)(tb_ptr->usr_handle))->dev;
+       dev = ((struct eth_media *)(tb_ptr->usr_handle))->dev;
        delta = dev->hard_header_len - skb_headroom(buf);
 
        if ((delta > 0) &&
@@ -128,43 +128,43 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
 static int recv_msg(struct sk_buff *buf, struct net_device *dev,
                    struct packet_type *pt, struct net_device *orig_dev)
 {
-       struct eth_bearer *eb_ptr = (struct eth_bearer *)pt->af_packet_priv;
+       struct eth_media *eb_ptr = (struct eth_media *)pt->af_packet_priv;
 
        if (!net_eq(dev_net(dev), &init_net)) {
                kfree_skb(buf);
-               return 0;
+               return NET_RX_DROP;
        }
 
        if (likely(eb_ptr->bearer)) {
                if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
                        buf->next = NULL;
                        tipc_recv_msg(buf, eb_ptr->bearer);
-                       return 0;
+                       return NET_RX_SUCCESS;
                }
        }
        kfree_skb(buf);
-       return 0;
+       return NET_RX_DROP;
 }
 
 /**
- * setup_bearer - setup association between Ethernet bearer and interface
+ * setup_media - setup association between Ethernet bearer and interface
  */
-static void setup_bearer(struct work_struct *work)
+static void setup_media(struct work_struct *work)
 {
-       struct eth_bearer *eb_ptr =
-               container_of(work, struct eth_bearer, setup);
+       struct eth_media *eb_ptr =
+               container_of(work, struct eth_media, setup);
 
        dev_add_pack(&eb_ptr->tipc_packet_type);
 }
 
 /**
- * enable_bearer - attach TIPC bearer to an Ethernet interface
+ * enable_media - attach TIPC bearer to an Ethernet interface
  */
-static int enable_bearer(struct tipc_bearer *tb_ptr)
+static int enable_media(struct tipc_bearer *tb_ptr)
 {
        struct net_device *dev;
-       struct eth_bearer *eb_ptr = &eth_bearers[0];
-       struct eth_bearer *stop = &eth_bearers[MAX_ETH_BEARERS];
+       struct eth_media *eb_ptr = &eth_media_array[0];
+       struct eth_media *stop = &eth_media_array[MAX_ETH_MEDIA];
        char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
        int pending_dev = 0;
 
@@ -188,7 +188,7 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
        eb_ptr->tipc_packet_type.func = recv_msg;
        eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr;
        INIT_LIST_HEAD(&(eb_ptr->tipc_packet_type.list));
-       INIT_WORK(&eb_ptr->setup, setup_bearer);
+       INIT_WORK(&eb_ptr->setup, setup_media);
        schedule_work(&eb_ptr->setup);
 
        /* Associate TIPC bearer with Ethernet bearer */
@@ -205,14 +205,14 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
 }
 
 /**
- * cleanup_bearer - break association between Ethernet bearer and interface
+ * cleanup_media - break association between Ethernet bearer and interface
  *
  * This routine must be invoked from a work queue because it can sleep.
  */
-static void cleanup_bearer(struct work_struct *work)
+static void cleanup_media(struct work_struct *work)
 {
-       struct eth_bearer *eb_ptr =
-               container_of(work, struct eth_bearer, cleanup);
+       struct eth_media *eb_ptr =
+               container_of(work, struct eth_media, cleanup);
 
        dev_remove_pack(&eb_ptr->tipc_packet_type);
        dev_put(eb_ptr->dev);
@@ -220,18 +220,18 @@ static void cleanup_bearer(struct work_struct *work)
 }
 
 /**
- * disable_bearer - detach TIPC bearer from an Ethernet interface
+ * disable_media - detach TIPC bearer from an Ethernet interface
  *
  * Mark Ethernet bearer as inactive so that incoming buffers are thrown away,
  * then get worker thread to complete bearer cleanup.  (Can't do cleanup
  * here because cleanup code needs to sleep and caller holds spinlocks.)
  */
-static void disable_bearer(struct tipc_bearer *tb_ptr)
+static void disable_media(struct tipc_bearer *tb_ptr)
 {
-       struct eth_bearer *eb_ptr = (struct eth_bearer *)tb_ptr->usr_handle;
+       struct eth_media *eb_ptr = (struct eth_media *)tb_ptr->usr_handle;
 
        eb_ptr->bearer = NULL;
-       INIT_WORK(&eb_ptr->cleanup, cleanup_bearer);
+       INIT_WORK(&eb_ptr->cleanup, cleanup_media);
        schedule_work(&eb_ptr->cleanup);
 }
 
@@ -245,8 +245,8 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt,
                             void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-       struct eth_bearer *eb_ptr = &eth_bearers[0];
-       struct eth_bearer *stop = &eth_bearers[MAX_ETH_BEARERS];
+       struct eth_media *eb_ptr = &eth_media_array[0];
+       struct eth_media *stop = &eth_media_array[MAX_ETH_MEDIA];
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
@@ -265,17 +265,17 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt,
                if (netif_carrier_ok(dev))
                        tipc_continue(eb_ptr->bearer);
                else
-                       tipc_block_bearer(eb_ptr->bearer->name);
+                       tipc_block_bearer(eb_ptr->bearer);
                break;
        case NETDEV_UP:
                tipc_continue(eb_ptr->bearer);
                break;
        case NETDEV_DOWN:
-               tipc_block_bearer(eb_ptr->bearer->name);
+               tipc_block_bearer(eb_ptr->bearer);
                break;
        case NETDEV_CHANGEMTU:
        case NETDEV_CHANGEADDR:
-               tipc_block_bearer(eb_ptr->bearer->name);
+               tipc_block_bearer(eb_ptr->bearer);
                tipc_continue(eb_ptr->bearer);
                break;
        case NETDEV_UNREGISTER:
@@ -327,8 +327,8 @@ static int eth_msg2addr(const struct tipc_bearer *tb_ptr,
  */
 static struct tipc_media eth_media_info = {
        .send_msg       = send_msg,
-       .enable_bearer  = enable_bearer,
-       .disable_bearer = disable_bearer,
+       .enable_media   = enable_media,
+       .disable_media  = disable_media,
        .addr2str       = eth_addr2str,
        .addr2msg       = eth_addr2msg,
        .msg2addr       = eth_msg2addr,
index 9934a32..c139892 100644 (file)
 #include "core.h"
 #include "bearer.h"
 
-#define MAX_IB_BEARERS         MAX_BEARERS
+#define MAX_IB_MEDIA           MAX_BEARERS
 
 /**
- * struct ib_bearer - Infiniband bearer data structure
+ * struct ib_media - Infiniband media data structure
  * @bearer: ptr to associated "generic" bearer structure
  * @dev: ptr to associated Infiniband network device
  * @tipc_packet_type: used in binding TIPC to Infiniband driver
  * @cleanup: work item used when disabling bearer
  */
 
-struct ib_bearer {
+struct ib_media {
        struct tipc_bearer *bearer;
        struct net_device *dev;
        struct packet_type tipc_packet_type;
@@ -61,7 +61,7 @@ struct ib_bearer {
 };
 
 static struct tipc_media ib_media_info;
-static struct ib_bearer ib_bearers[MAX_IB_BEARERS];
+static struct ib_media ib_media_array[MAX_IB_MEDIA];
 static int ib_started;
 
 /**
@@ -93,7 +93,7 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
        if (!clone)
                return 0;
 
-       dev = ((struct ib_bearer *)(tb_ptr->usr_handle))->dev;
+       dev = ((struct ib_media *)(tb_ptr->usr_handle))->dev;
        delta = dev->hard_header_len - skb_headroom(buf);
 
        if ((delta > 0) &&
@@ -121,43 +121,43 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
 static int recv_msg(struct sk_buff *buf, struct net_device *dev,
                    struct packet_type *pt, struct net_device *orig_dev)
 {
-       struct ib_bearer *ib_ptr = (struct ib_bearer *)pt->af_packet_priv;
+       struct ib_media *ib_ptr = (struct ib_media *)pt->af_packet_priv;
 
        if (!net_eq(dev_net(dev), &init_net)) {
                kfree_skb(buf);
-               return 0;
+               return NET_RX_DROP;
        }
 
        if (likely(ib_ptr->bearer)) {
                if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
                        buf->next = NULL;
                        tipc_recv_msg(buf, ib_ptr->bearer);
-                       return 0;
+                       return NET_RX_SUCCESS;
                }
        }
        kfree_skb(buf);
-       return 0;
+       return NET_RX_DROP;
 }
 
 /**
  * setup_bearer - setup association between InfiniBand bearer and interface
  */
-static void setup_bearer(struct work_struct *work)
+static void setup_media(struct work_struct *work)
 {
-       struct ib_bearer *ib_ptr =
-               container_of(work, struct ib_bearer, setup);
+       struct ib_media *ib_ptr =
+               container_of(work, struct ib_media, setup);
 
        dev_add_pack(&ib_ptr->tipc_packet_type);
 }
 
 /**
- * enable_bearer - attach TIPC bearer to an InfiniBand interface
+ * enable_media - attach TIPC bearer to an InfiniBand interface
  */
-static int enable_bearer(struct tipc_bearer *tb_ptr)
+static int enable_media(struct tipc_bearer *tb_ptr)
 {
        struct net_device *dev;
-       struct ib_bearer *ib_ptr = &ib_bearers[0];
-       struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS];
+       struct ib_media *ib_ptr = &ib_media_array[0];
+       struct ib_media *stop = &ib_media_array[MAX_IB_MEDIA];
        char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
        int pending_dev = 0;
 
@@ -181,7 +181,7 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
        ib_ptr->tipc_packet_type.func = recv_msg;
        ib_ptr->tipc_packet_type.af_packet_priv = ib_ptr;
        INIT_LIST_HEAD(&(ib_ptr->tipc_packet_type.list));
-       INIT_WORK(&ib_ptr->setup, setup_bearer);
+       INIT_WORK(&ib_ptr->setup, setup_media);
        schedule_work(&ib_ptr->setup);
 
        /* Associate TIPC bearer with InfiniBand bearer */
@@ -204,8 +204,8 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
  */
 static void cleanup_bearer(struct work_struct *work)
 {
-       struct ib_bearer *ib_ptr =
-               container_of(work, struct ib_bearer, cleanup);
+       struct ib_media *ib_ptr =
+               container_of(work, struct ib_media, cleanup);
 
        dev_remove_pack(&ib_ptr->tipc_packet_type);
        dev_put(ib_ptr->dev);
@@ -213,15 +213,15 @@ static void cleanup_bearer(struct work_struct *work)
 }
 
 /**
- * disable_bearer - detach TIPC bearer from an InfiniBand interface
+ * disable_media - detach TIPC bearer from an InfiniBand interface
  *
  * Mark InfiniBand bearer as inactive so that incoming buffers are thrown away,
  * then get worker thread to complete bearer cleanup.  (Can't do cleanup
  * here because cleanup code needs to sleep and caller holds spinlocks.)
  */
-static void disable_bearer(struct tipc_bearer *tb_ptr)
+static void disable_media(struct tipc_bearer *tb_ptr)
 {
-       struct ib_bearer *ib_ptr = (struct ib_bearer *)tb_ptr->usr_handle;
+       struct ib_media *ib_ptr = (struct ib_media *)tb_ptr->usr_handle;
 
        ib_ptr->bearer = NULL;
        INIT_WORK(&ib_ptr->cleanup, cleanup_bearer);
@@ -238,8 +238,8 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt,
                             void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-       struct ib_bearer *ib_ptr = &ib_bearers[0];
-       struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS];
+       struct ib_media *ib_ptr = &ib_media_array[0];
+       struct ib_media *stop = &ib_media_array[MAX_IB_MEDIA];
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
@@ -258,17 +258,17 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt,
                if (netif_carrier_ok(dev))
                        tipc_continue(ib_ptr->bearer);
                else
-                       tipc_block_bearer(ib_ptr->bearer->name);
+                       tipc_block_bearer(ib_ptr->bearer);
                break;
        case NETDEV_UP:
                tipc_continue(ib_ptr->bearer);
                break;
        case NETDEV_DOWN:
-               tipc_block_bearer(ib_ptr->bearer->name);
+               tipc_block_bearer(ib_ptr->bearer);
                break;
        case NETDEV_CHANGEMTU:
        case NETDEV_CHANGEADDR:
-               tipc_block_bearer(ib_ptr->bearer->name);
+               tipc_block_bearer(ib_ptr->bearer);
                tipc_continue(ib_ptr->bearer);
                break;
        case NETDEV_UNREGISTER:
@@ -323,8 +323,8 @@ static int ib_msg2addr(const struct tipc_bearer *tb_ptr,
  */
 static struct tipc_media ib_media_info = {
        .send_msg       = send_msg,
-       .enable_bearer  = enable_bearer,
-       .disable_bearer = disable_bearer,
+       .enable_media   = enable_media,
+       .disable_media  = disable_media,
        .addr2str       = ib_addr2str,
        .addr2msg       = ib_addr2msg,
        .msg2addr       = ib_msg2addr,
index 0cc3d90..cf465d6 100644 (file)
@@ -75,20 +75,6 @@ static const char *link_unk_evt = "Unknown link event ";
  */
 #define START_CHANGEOVER 100000u
 
-/**
- * struct tipc_link_name - deconstructed link name
- * @addr_local: network address of node at this end
- * @if_local: name of interface at this end
- * @addr_peer: network address of node at far end
- * @if_peer: name of interface at far end
- */
-struct tipc_link_name {
-       u32 addr_local;
-       char if_local[TIPC_MAX_IF_NAME];
-       u32 addr_peer;
-       char if_peer[TIPC_MAX_IF_NAME];
-};
-
 static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
                                       struct sk_buff *buf);
 static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf);
@@ -97,8 +83,7 @@ static int  link_recv_changeover_msg(struct tipc_link **l_ptr,
 static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance);
 static int  link_send_sections_long(struct tipc_port *sender,
                                    struct iovec const *msg_sect,
-                                   u32 num_sect, unsigned int total_len,
-                                   u32 destnode);
+                                   unsigned int len, u32 destnode);
 static void link_state_event(struct tipc_link *l_ptr, u32 event);
 static void link_reset_statistics(struct tipc_link *l_ptr);
 static void link_print(struct tipc_link *l_ptr, const char *str);
@@ -160,72 +145,6 @@ int tipc_link_is_active(struct tipc_link *l_ptr)
                (l_ptr->owner->active_links[1] == l_ptr);
 }
 
-/**
- * link_name_validate - validate & (optionally) deconstruct tipc_link name
- * @name: ptr to link name string
- * @name_parts: ptr to area for link name components (or NULL if not needed)
- *
- * Returns 1 if link name is valid, otherwise 0.
- */
-static int link_name_validate(const char *name,
-                               struct tipc_link_name *name_parts)
-{
-       char name_copy[TIPC_MAX_LINK_NAME];
-       char *addr_local;
-       char *if_local;
-       char *addr_peer;
-       char *if_peer;
-       char dummy;
-       u32 z_local, c_local, n_local;
-       u32 z_peer, c_peer, n_peer;
-       u32 if_local_len;
-       u32 if_peer_len;
-
-       /* copy link name & ensure length is OK */
-       name_copy[TIPC_MAX_LINK_NAME - 1] = 0;
-       /* need above in case non-Posix strncpy() doesn't pad with nulls */
-       strncpy(name_copy, name, TIPC_MAX_LINK_NAME);
-       if (name_copy[TIPC_MAX_LINK_NAME - 1] != 0)
-               return 0;
-
-       /* ensure all component parts of link name are present */
-       addr_local = name_copy;
-       if_local = strchr(addr_local, ':');
-       if (if_local == NULL)
-               return 0;
-       *(if_local++) = 0;
-       addr_peer = strchr(if_local, '-');
-       if (addr_peer == NULL)
-               return 0;
-       *(addr_peer++) = 0;
-       if_local_len = addr_peer - if_local;
-       if_peer = strchr(addr_peer, ':');
-       if (if_peer == NULL)
-               return 0;
-       *(if_peer++) = 0;
-       if_peer_len = strlen(if_peer) + 1;
-
-       /* validate component parts of link name */
-       if ((sscanf(addr_local, "%u.%u.%u%c",
-                   &z_local, &c_local, &n_local, &dummy) != 3) ||
-           (sscanf(addr_peer, "%u.%u.%u%c",
-                   &z_peer, &c_peer, &n_peer, &dummy) != 3) ||
-           (z_local > 255) || (c_local > 4095) || (n_local > 4095) ||
-           (z_peer  > 255) || (c_peer  > 4095) || (n_peer  > 4095) ||
-           (if_local_len <= 1) || (if_local_len > TIPC_MAX_IF_NAME) ||
-           (if_peer_len  <= 1) || (if_peer_len  > TIPC_MAX_IF_NAME))
-               return 0;
-
-       /* return link name components, if necessary */
-       if (name_parts) {
-               name_parts->addr_local = tipc_addr(z_local, c_local, n_local);
-               strcpy(name_parts->if_local, if_local);
-               name_parts->addr_peer = tipc_addr(z_peer, c_peer, n_peer);
-               strcpy(name_parts->if_peer, if_peer);
-       }
-       return 1;
-}
-
 /**
  * link_timeout - handle expiration of link timer
  * @l_ptr: pointer to link
@@ -485,15 +404,9 @@ static void link_release_outqueue(struct tipc_link *l_ptr)
  */
 void tipc_link_reset_fragments(struct tipc_link *l_ptr)
 {
-       struct sk_buff *buf = l_ptr->defragm_buf;
-       struct sk_buff *next;
-
-       while (buf) {
-               next = buf->next;
-               kfree_skb(buf);
-               buf = next;
-       }
-       l_ptr->defragm_buf = NULL;
+       kfree_skb(l_ptr->reasm_head);
+       l_ptr->reasm_head = NULL;
+       l_ptr->reasm_tail = NULL;
 }
 
 /**
@@ -1065,8 +978,7 @@ static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
  */
 int tipc_link_send_sections_fast(struct tipc_port *sender,
                                 struct iovec const *msg_sect,
-                                const u32 num_sect, unsigned int total_len,
-                                u32 destaddr)
+                                unsigned int len, u32 destaddr)
 {
        struct tipc_msg *hdr = &sender->phdr;
        struct tipc_link *l_ptr;
@@ -1080,8 +992,7 @@ again:
         * Try building message using port's max_pkt hint.
         * (Must not hold any locks while building message.)
         */
-       res = tipc_msg_build(hdr, msg_sect, num_sect, total_len,
-                            sender->max_pkt, &buf);
+       res = tipc_msg_build(hdr, msg_sect, len, sender->max_pkt, &buf);
        /* Exit if build request was invalid */
        if (unlikely(res < 0))
                return res;
@@ -1121,8 +1032,7 @@ exit:
                        if ((msg_hdr_sz(hdr) + res) <= sender->max_pkt)
                                goto again;
 
-                       return link_send_sections_long(sender, msg_sect,
-                                                      num_sect, total_len,
+                       return link_send_sections_long(sender, msg_sect, len,
                                                       destaddr);
                }
                tipc_node_unlock(node);
@@ -1133,8 +1043,8 @@ exit:
        if (buf)
                return tipc_reject_msg(buf, TIPC_ERR_NO_NODE);
        if (res >= 0)
-               return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect,
-                                                total_len, TIPC_ERR_NO_NODE);
+               return tipc_port_reject_sections(sender, hdr, msg_sect,
+                                                len, TIPC_ERR_NO_NODE);
        return res;
 }
 
@@ -1154,18 +1064,17 @@ exit:
  */
 static int link_send_sections_long(struct tipc_port *sender,
                                   struct iovec const *msg_sect,
-                                  u32 num_sect, unsigned int total_len,
-                                  u32 destaddr)
+                                  unsigned int len, u32 destaddr)
 {
        struct tipc_link *l_ptr;
        struct tipc_node *node;
        struct tipc_msg *hdr = &sender->phdr;
-       u32 dsz = total_len;
+       u32 dsz = len;
        u32 max_pkt, fragm_sz, rest;
        struct tipc_msg fragm_hdr;
        struct sk_buff *buf, *buf_chain, *prev;
        u32 fragm_crs, fragm_rest, hsz, sect_rest;
-       const unchar *sect_crs;
+       const unchar __user *sect_crs;
        int curr_sect;
        u32 fragm_no;
        int res = 0;
@@ -1207,7 +1116,7 @@ again:
 
                if (!sect_rest) {
                        sect_rest = msg_sect[++curr_sect].iov_len;
-                       sect_crs = (const unchar *)msg_sect[curr_sect].iov_base;
+                       sect_crs = msg_sect[curr_sect].iov_base;
                }
 
                if (sect_rest < fragm_rest)
@@ -1283,8 +1192,8 @@ reject:
                        buf = buf_chain->next;
                        kfree_skb(buf_chain);
                }
-               return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect,
-                                                total_len, TIPC_ERR_NO_NODE);
+               return tipc_port_reject_sections(sender, hdr, msg_sect,
+                                                len, TIPC_ERR_NO_NODE);
        }
 
        /* Append chain of fragments to send queue & send them */
@@ -1592,15 +1501,15 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
 
                /* Ensure bearer is still enabled */
                if (unlikely(!b_ptr->active))
-                       goto cont;
+                       goto discard;
 
                /* Ensure message is well-formed */
                if (unlikely(!link_recv_buf_validate(buf)))
-                       goto cont;
+                       goto discard;
 
                /* Ensure message data is a single contiguous unit */
                if (unlikely(skb_linearize(buf)))
-                       goto cont;
+                       goto discard;
 
                /* Handle arrival of a non-unicast link message */
                msg = buf_msg(buf);
@@ -1616,20 +1525,18 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
                /* Discard unicast link messages destined for another node */
                if (unlikely(!msg_short(msg) &&
                             (msg_destnode(msg) != tipc_own_addr)))
-                       goto cont;
+                       goto discard;
 
                /* Locate neighboring node that sent message */
                n_ptr = tipc_node_find(msg_prevnode(msg));
                if (unlikely(!n_ptr))
-                       goto cont;
+                       goto discard;
                tipc_node_lock(n_ptr);
 
                /* Locate unicast link endpoint that should handle message */
                l_ptr = n_ptr->links[b_ptr->identity];
-               if (unlikely(!l_ptr)) {
-                       tipc_node_unlock(n_ptr);
-                       goto cont;
-               }
+               if (unlikely(!l_ptr))
+                       goto unlock_discard;
 
                /* Verify that communication with node is currently allowed */
                if ((n_ptr->block_setup & WAIT_PEER_DOWN) &&
@@ -1639,10 +1546,8 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
                        !msg_redundant_link(msg))
                        n_ptr->block_setup &= ~WAIT_PEER_DOWN;
 
-               if (n_ptr->block_setup) {
-                       tipc_node_unlock(n_ptr);
-                       goto cont;
-               }
+               if (n_ptr->block_setup)
+                       goto unlock_discard;
 
                /* Validate message sequence number info */
                seq_no = msg_seqno(msg);
@@ -1678,98 +1583,100 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
 
                /* Now (finally!) process the incoming message */
 protocol_check:
-               if (likely(link_working_working(l_ptr))) {
-                       if (likely(seq_no == mod(l_ptr->next_in_no))) {
-                               l_ptr->next_in_no++;
-                               if (unlikely(l_ptr->oldest_deferred_in))
-                                       head = link_insert_deferred_queue(l_ptr,
-                                                                         head);
-deliver:
-                               if (likely(msg_isdata(msg))) {
-                                       tipc_node_unlock(n_ptr);
-                                       tipc_port_recv_msg(buf);
-                                       continue;
-                               }
-                               switch (msg_user(msg)) {
-                                       int ret;
-                               case MSG_BUNDLER:
-                                       l_ptr->stats.recv_bundles++;
-                                       l_ptr->stats.recv_bundled +=
-                                               msg_msgcnt(msg);
-                                       tipc_node_unlock(n_ptr);
-                                       tipc_link_recv_bundle(buf);
-                                       continue;
-                               case NAME_DISTRIBUTOR:
-                                       n_ptr->bclink.recv_permitted = true;
-                                       tipc_node_unlock(n_ptr);
-                                       tipc_named_recv(buf);
-                                       continue;
-                               case BCAST_PROTOCOL:
-                                       tipc_link_recv_sync(n_ptr, buf);
-                                       tipc_node_unlock(n_ptr);
-                                       continue;
-                               case CONN_MANAGER:
-                                       tipc_node_unlock(n_ptr);
-                                       tipc_port_recv_proto_msg(buf);
-                                       continue;
-                               case MSG_FRAGMENTER:
-                                       l_ptr->stats.recv_fragments++;
-                                       ret = tipc_link_recv_fragment(
-                                               &l_ptr->defragm_buf,
-                                               &buf, &msg);
-                                       if (ret == 1) {
-                                               l_ptr->stats.recv_fragmented++;
-                                               goto deliver;
-                                       }
-                                       if (ret == -1)
-                                               l_ptr->next_in_no--;
-                                       break;
-                               case CHANGEOVER_PROTOCOL:
-                                       type = msg_type(msg);
-                                       if (link_recv_changeover_msg(&l_ptr,
-                                                                    &buf)) {
-                                               msg = buf_msg(buf);
-                                               seq_no = msg_seqno(msg);
-                                               if (type == ORIGINAL_MSG)
-                                                       goto deliver;
-                                               goto protocol_check;
-                                       }
-                                       break;
-                               default:
-                                       kfree_skb(buf);
-                                       buf = NULL;
-                                       break;
-                               }
+               if (unlikely(!link_working_working(l_ptr))) {
+                       if (msg_user(msg) == LINK_PROTOCOL) {
+                               link_recv_proto_msg(l_ptr, buf);
+                               head = link_insert_deferred_queue(l_ptr, head);
+                               tipc_node_unlock(n_ptr);
+                               continue;
+                       }
+
+                       /* Traffic message. Conditionally activate link */
+                       link_state_event(l_ptr, TRAFFIC_MSG_EVT);
+
+                       if (link_working_working(l_ptr)) {
+                               /* Re-insert buffer in front of queue */
+                               buf->next = head;
+                               head = buf;
                                tipc_node_unlock(n_ptr);
-                               tipc_net_route_msg(buf);
                                continue;
                        }
+                       goto unlock_discard;
+               }
+
+               /* Link is now in state WORKING_WORKING */
+               if (unlikely(seq_no != mod(l_ptr->next_in_no))) {
                        link_handle_out_of_seq_msg(l_ptr, buf);
                        head = link_insert_deferred_queue(l_ptr, head);
                        tipc_node_unlock(n_ptr);
                        continue;
                }
-
-               /* Link is not in state WORKING_WORKING */
-               if (msg_user(msg) == LINK_PROTOCOL) {
-                       link_recv_proto_msg(l_ptr, buf);
+               l_ptr->next_in_no++;
+               if (unlikely(l_ptr->oldest_deferred_in))
                        head = link_insert_deferred_queue(l_ptr, head);
+deliver:
+               if (likely(msg_isdata(msg))) {
                        tipc_node_unlock(n_ptr);
+                       tipc_port_recv_msg(buf);
                        continue;
                }
-
-               /* Traffic message. Conditionally activate link */
-               link_state_event(l_ptr, TRAFFIC_MSG_EVT);
-
-               if (link_working_working(l_ptr)) {
-                       /* Re-insert buffer in front of queue */
-                       buf->next = head;
-                       head = buf;
+               switch (msg_user(msg)) {
+                       int ret;
+               case MSG_BUNDLER:
+                       l_ptr->stats.recv_bundles++;
+                       l_ptr->stats.recv_bundled += msg_msgcnt(msg);
+                       tipc_node_unlock(n_ptr);
+                       tipc_link_recv_bundle(buf);
+                       continue;
+               case NAME_DISTRIBUTOR:
+                       n_ptr->bclink.recv_permitted = true;
+                       tipc_node_unlock(n_ptr);
+                       tipc_named_recv(buf);
+                       continue;
+               case BCAST_PROTOCOL:
+                       tipc_link_recv_sync(n_ptr, buf);
+                       tipc_node_unlock(n_ptr);
+                       continue;
+               case CONN_MANAGER:
+                       tipc_node_unlock(n_ptr);
+                       tipc_port_recv_proto_msg(buf);
+                       continue;
+               case MSG_FRAGMENTER:
+                       l_ptr->stats.recv_fragments++;
+                       ret = tipc_link_recv_fragment(&l_ptr->reasm_head,
+                                                     &l_ptr->reasm_tail,
+                                                     &buf);
+                       if (ret == LINK_REASM_COMPLETE) {
+                               l_ptr->stats.recv_fragmented++;
+                               msg = buf_msg(buf);
+                               goto deliver;
+                       }
+                       if (ret == LINK_REASM_ERROR)
+                               tipc_link_reset(l_ptr);
                        tipc_node_unlock(n_ptr);
                        continue;
+               case CHANGEOVER_PROTOCOL:
+                       type = msg_type(msg);
+                       if (link_recv_changeover_msg(&l_ptr, &buf)) {
+                               msg = buf_msg(buf);
+                               seq_no = msg_seqno(msg);
+                               if (type == ORIGINAL_MSG)
+                                       goto deliver;
+                               goto protocol_check;
+                       }
+                       break;
+               default:
+                       kfree_skb(buf);
+                       buf = NULL;
+                       break;
                }
                tipc_node_unlock(n_ptr);
-cont:
+               tipc_net_route_msg(buf);
+               continue;
+unlock_discard:
+
+               tipc_node_unlock(n_ptr);
+discard:
                kfree_skb(buf);
        }
        read_unlock_bh(&tipc_net_lock);
@@ -2431,115 +2338,48 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
        return dsz;
 }
 
-/*
- * A pending message being re-assembled must store certain values
- * to handle subsequent fragments correctly. The following functions
- * help storing these values in unused, available fields in the
- * pending message. This makes dynamic memory allocation unnecessary.
- */
-static void set_long_msg_seqno(struct sk_buff *buf, u32 seqno)
-{
-       msg_set_seqno(buf_msg(buf), seqno);
-}
-
-static u32 get_fragm_size(struct sk_buff *buf)
-{
-       return msg_ack(buf_msg(buf));
-}
-
-static void set_fragm_size(struct sk_buff *buf, u32 sz)
-{
-       msg_set_ack(buf_msg(buf), sz);
-}
-
-static u32 get_expected_frags(struct sk_buff *buf)
-{
-       return msg_bcast_ack(buf_msg(buf));
-}
-
-static void set_expected_frags(struct sk_buff *buf, u32 exp)
-{
-       msg_set_bcast_ack(buf_msg(buf), exp);
-}
-
 /*
  * tipc_link_recv_fragment(): Called with node lock on. Returns
  * the reassembled buffer if message is complete.
  */
-int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
-                           struct tipc_msg **m)
-{
-       struct sk_buff *prev = NULL;
-       struct sk_buff *fbuf = *fb;
-       struct tipc_msg *fragm = buf_msg(fbuf);
-       struct sk_buff *pbuf = *pending;
-       u32 long_msg_seq_no = msg_long_msgno(fragm);
-
-       *fb = NULL;
-
-       /* Is there an incomplete message waiting for this fragment? */
-       while (pbuf && ((buf_seqno(pbuf) != long_msg_seq_no) ||
-                       (msg_orignode(fragm) != msg_orignode(buf_msg(pbuf))))) {
-               prev = pbuf;
-               pbuf = pbuf->next;
-       }
-
-       if (!pbuf && (msg_type(fragm) == FIRST_FRAGMENT)) {
-               struct tipc_msg *imsg = (struct tipc_msg *)msg_data(fragm);
-               u32 msg_sz = msg_size(imsg);
-               u32 fragm_sz = msg_data_sz(fragm);
-               u32 exp_fragm_cnt;
-               u32 max =  TIPC_MAX_USER_MSG_SIZE + NAMED_H_SIZE;
-
-               if (msg_type(imsg) == TIPC_MCAST_MSG)
-                       max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE;
-               if (fragm_sz == 0 || msg_size(imsg) > max) {
-                       kfree_skb(fbuf);
-                       return 0;
-               }
-               exp_fragm_cnt = msg_sz / fragm_sz + !!(msg_sz % fragm_sz);
-               pbuf = tipc_buf_acquire(msg_size(imsg));
-               if (pbuf != NULL) {
-                       pbuf->next = *pending;
-                       *pending = pbuf;
-                       skb_copy_to_linear_data(pbuf, imsg,
-                                               msg_data_sz(fragm));
-                       /*  Prepare buffer for subsequent fragments. */
-                       set_long_msg_seqno(pbuf, long_msg_seq_no);
-                       set_fragm_size(pbuf, fragm_sz);
-                       set_expected_frags(pbuf, exp_fragm_cnt - 1);
-               } else {
-                       pr_debug("Link unable to reassemble fragmented message\n");
-                       kfree_skb(fbuf);
-                       return -1;
-               }
-               kfree_skb(fbuf);
-               return 0;
-       } else if (pbuf && (msg_type(fragm) != FIRST_FRAGMENT)) {
-               u32 dsz = msg_data_sz(fragm);
-               u32 fsz = get_fragm_size(pbuf);
-               u32 crs = ((msg_fragm_no(fragm) - 1) * fsz);
-               u32 exp_frags = get_expected_frags(pbuf) - 1;
-               skb_copy_to_linear_data_offset(pbuf, crs,
-                                              msg_data(fragm), dsz);
-               kfree_skb(fbuf);
-
-               /* Is message complete? */
-               if (exp_frags == 0) {
-                       if (prev)
-                               prev->next = pbuf->next;
-                       else
-                               *pending = pbuf->next;
-                       msg_reset_reroute_cnt(buf_msg(pbuf));
-                       *fb = pbuf;
-                       *m = buf_msg(pbuf);
-                       return 1;
-               }
-               set_expected_frags(pbuf, exp_frags);
+int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail,
+                           struct sk_buff **fbuf)
+{
+       struct sk_buff *frag = *fbuf;
+       struct tipc_msg *msg = buf_msg(frag);
+       u32 fragid = msg_type(msg);
+       bool headstolen;
+       int delta;
+
+       skb_pull(frag, msg_hdr_sz(msg));
+       if (fragid == FIRST_FRAGMENT) {
+               if (*head || skb_unclone(frag, GFP_ATOMIC))
+                       goto out_free;
+               *head = frag;
+               skb_frag_list_init(*head);
                return 0;
+       } else if (skb_try_coalesce(*head, frag, &headstolen, &delta)) {
+               kfree_skb_partial(frag, headstolen);
+       } else {
+               if (!*head)
+                       goto out_free;
+               if (!skb_has_frag_list(*head))
+                       skb_shinfo(*head)->frag_list = frag;
+               else
+                       (*tail)->next = frag;
+               *tail = frag;
+               (*head)->truesize += frag->truesize;
+       }
+       if (fragid == LAST_FRAGMENT) {
+               *fbuf = *head;
+               *tail = *head = NULL;
+               return LINK_REASM_COMPLETE;
        }
-       kfree_skb(fbuf);
        return 0;
+out_free:
+       pr_warn_ratelimited("Link unable to reassemble fragmented message\n");
+       kfree_skb(*fbuf);
+       return LINK_REASM_ERROR;
 }
 
 static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance)
@@ -2585,25 +2425,21 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window)
 static struct tipc_link *link_find_link(const char *name,
                                        struct tipc_node **node)
 {
-       struct tipc_link_name link_name_parts;
-       struct tipc_bearer *b_ptr;
        struct tipc_link *l_ptr;
+       struct tipc_node *n_ptr;
+       int i;
 
-       if (!link_name_validate(name, &link_name_parts))
-               return NULL;
-
-       b_ptr = tipc_bearer_find_interface(link_name_parts.if_local);
-       if (!b_ptr)
-               return NULL;
-
-       *node = tipc_node_find(link_name_parts.addr_peer);
-       if (!*node)
-               return NULL;
-
-       l_ptr = (*node)->links[b_ptr->identity];
-       if (!l_ptr || strcmp(l_ptr->name, name))
-               return NULL;
-
+       list_for_each_entry(n_ptr, &tipc_node_list, list) {
+               for (i = 0; i < MAX_BEARERS; i++) {
+                       l_ptr = n_ptr->links[i];
+                       if (l_ptr && !strcmp(l_ptr->name, name))
+                               goto found;
+               }
+       }
+       l_ptr = NULL;
+       n_ptr = NULL;
+found:
+       *node = n_ptr;
        return l_ptr;
 }
 
@@ -2646,6 +2482,7 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd)
        struct tipc_link *l_ptr;
        struct tipc_bearer *b_ptr;
        struct tipc_media *m_ptr;
+       int res = 0;
 
        l_ptr = link_find_link(name, &node);
        if (l_ptr) {
@@ -2668,9 +2505,12 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd)
                case TIPC_CMD_SET_LINK_WINDOW:
                        tipc_link_set_queue_limits(l_ptr, new_value);
                        break;
+               default:
+                       res = -EINVAL;
+                       break;
                }
                tipc_node_unlock(node);
-               return 0;
+               return res;
        }
 
        b_ptr = tipc_bearer_find(name);
@@ -2678,15 +2518,18 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd)
                switch (cmd) {
                case TIPC_CMD_SET_LINK_TOL:
                        b_ptr->tolerance = new_value;
-                       return 0;
+                       break;
                case TIPC_CMD_SET_LINK_PRI:
                        b_ptr->priority = new_value;
-                       return 0;
+                       break;
                case TIPC_CMD_SET_LINK_WINDOW:
                        b_ptr->window = new_value;
-                       return 0;
+                       break;
+               default:
+                       res = -EINVAL;
+                       break;
                }
-               return -EINVAL;
+               return res;
        }
 
        m_ptr = tipc_media_find(name);
@@ -2695,15 +2538,18 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd)
        switch (cmd) {
        case TIPC_CMD_SET_LINK_TOL:
                m_ptr->tolerance = new_value;
-               return 0;
+               break;
        case TIPC_CMD_SET_LINK_PRI:
                m_ptr->priority = new_value;
-               return 0;
+               break;
        case TIPC_CMD_SET_LINK_WINDOW:
                m_ptr->window = new_value;
-               return 0;
+               break;
+       default:
+               res = -EINVAL;
+               break;
        }
-       return -EINVAL;
+       return res;
 }
 
 struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space,
index c048ed1..8a6c102 100644 (file)
 #include "msg.h"
 #include "node.h"
 
+/*
+ * Link reassembly status codes
+ */
+#define LINK_REASM_ERROR       -1
+#define LINK_REASM_COMPLETE    1
+
 /*
  * Out-of-range value for link sequence numbers
  */
@@ -134,7 +140,8 @@ struct tipc_stats {
  * @next_out: ptr to first unsent outbound message in queue
  * @waiting_ports: linked list of ports waiting for link congestion to abate
  * @long_msg_seq_no: next identifier to use for outbound fragmented messages
- * @defragm_buf: list of partially reassembled inbound message fragments
+ * @reasm_head: list head of partially reassembled inbound message fragments
+ * @reasm_tail: last fragment received
  * @stats: collects statistics regarding link activity
  */
 struct tipc_link {
@@ -196,9 +203,10 @@ struct tipc_link {
        struct sk_buff *next_out;
        struct list_head waiting_ports;
 
-       /* Fragmentation/defragmentation */
+       /* Fragmentation/reassembly */
        u32 long_msg_seq_no;
-       struct sk_buff *defragm_buf;
+       struct sk_buff *reasm_head;
+       struct sk_buff *reasm_tail;
 
        /* Statistics */
        struct tipc_stats stats;
@@ -227,13 +235,11 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf);
 u32 tipc_link_get_max_pkt(u32 dest, u32 selector);
 int tipc_link_send_sections_fast(struct tipc_port *sender,
                                 struct iovec const *msg_sect,
-                                const u32 num_sect,
-                                unsigned int total_len,
-                                u32 destnode);
+                                unsigned int len, u32 destnode);
 void tipc_link_recv_bundle(struct sk_buff *buf);
-int  tipc_link_recv_fragment(struct sk_buff **pending,
-                            struct sk_buff **fb,
-                            struct tipc_msg **msg);
+int  tipc_link_recv_fragment(struct sk_buff **reasm_head,
+                            struct sk_buff **reasm_tail,
+                            struct sk_buff **fbuf);
 void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ, int prob,
                              u32 gap, u32 tolerance, u32 priority,
                              u32 acked_mtu);
index ced60e2..e525f8c 100644 (file)
@@ -73,13 +73,13 @@ void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize,
  * Returns message data size or errno
  */
 int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
-                  u32 num_sect, unsigned int total_len, int max_size,
-                  struct sk_buff **buf)
+                  unsigned int len, int max_size, struct sk_buff **buf)
 {
-       int dsz, sz, hsz, pos, res, cnt;
+       int dsz, sz, hsz;
+       unsigned char *to;
 
-       dsz = total_len;
-       pos = hsz = msg_hdr_sz(hdr);
+       dsz = len;
+       hsz = msg_hdr_sz(hdr);
        sz = hsz + dsz;
        msg_set_size(hdr, sz);
        if (unlikely(sz > max_size)) {
@@ -91,16 +91,11 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
        if (!(*buf))
                return -ENOMEM;
        skb_copy_to_linear_data(*buf, hdr, hsz);
-       for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) {
-               skb_copy_to_linear_data_offset(*buf, pos,
-                                              msg_sect[cnt].iov_base,
-                                              msg_sect[cnt].iov_len);
-               pos += msg_sect[cnt].iov_len;
+       to = (*buf)->data + hsz;
+       if (len && memcpy_fromiovecend(to, msg_sect, 0, dsz)) {
+               kfree_skb(*buf);
+               *buf = NULL;
+               return -EFAULT;
        }
-       if (likely(res))
-               return dsz;
-
-       kfree_skb(*buf);
-       *buf = NULL;
-       return -EFAULT;
+       return dsz;
 }
index 5e4ccf5..76d1269 100644 (file)
@@ -554,12 +554,6 @@ static inline void msg_set_last_bcast(struct tipc_msg *m, u32 n)
        msg_set_bits(m, 4, 16, 0xffff, n);
 }
 
-
-static inline u32 msg_fragm_no(struct tipc_msg *m)
-{
-       return msg_bits(m, 4, 16, 0xffff);
-}
-
 static inline void msg_set_fragm_no(struct tipc_msg *m, u32 n)
 {
        msg_set_bits(m, 4, 16, 0xffff, n);
@@ -576,12 +570,6 @@ static inline void msg_set_next_sent(struct tipc_msg *m, u32 n)
        msg_set_bits(m, 4, 0, 0xffff, n);
 }
 
-
-static inline u32 msg_long_msgno(struct tipc_msg *m)
-{
-       return msg_bits(m, 4, 0, 0xffff);
-}
-
 static inline void msg_set_long_msgno(struct tipc_msg *m, u32 n)
 {
        msg_set_bits(m, 4, 0, 0xffff, n);
@@ -722,6 +710,5 @@ u32 tipc_msg_tot_importance(struct tipc_msg *m);
 void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize,
                   u32 destnode);
 int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
-                  u32 num_sect, unsigned int total_len, int max_size,
-                  struct sk_buff **buf);
+                  unsigned int len, int max_size, struct sk_buff **buf);
 #endif
index 6e6c434..25100c0 100644 (file)
@@ -298,9 +298,10 @@ static void node_lost_contact(struct tipc_node *n_ptr)
                }
                n_ptr->bclink.deferred_size = 0;
 
-               if (n_ptr->bclink.defragm) {
-                       kfree_skb(n_ptr->bclink.defragm);
-                       n_ptr->bclink.defragm = NULL;
+               if (n_ptr->bclink.reasm_head) {
+                       kfree_skb(n_ptr->bclink.reasm_head);
+                       n_ptr->bclink.reasm_head = NULL;
+                       n_ptr->bclink.reasm_tail = NULL;
                }
 
                tipc_bclink_remove_node(n_ptr->addr);
index 3c189b3..e5e96c0 100644 (file)
@@ -74,7 +74,8 @@
  *    @deferred_size: number of OOS b'cast messages in deferred queue
  *    @deferred_head: oldest OOS b'cast message received from node
  *    @deferred_tail: newest OOS b'cast message received from node
- *    @defragm: list of partially reassembled b'cast message fragments from node
+ *    @reasm_head: broadcast reassembly queue head from node
+ *    @reasm_tail: last broadcast fragment received from node
  *    @recv_permitted: true if node is allowed to receive b'cast messages
  */
 struct tipc_node {
@@ -98,7 +99,8 @@ struct tipc_node {
                u32 deferred_size;
                struct sk_buff *deferred_head;
                struct sk_buff *deferred_tail;
-               struct sk_buff *defragm;
+               struct sk_buff *reasm_head;
+               struct sk_buff *reasm_tail;
                bool recv_permitted;
        } bclink;
 };
index b3ed2fc..c081a76 100644 (file)
@@ -90,8 +90,7 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg)
  * tipc_multicast - send a multicast message to local and remote destinations
  */
 int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
-                  u32 num_sect, struct iovec const *msg_sect,
-                  unsigned int total_len)
+                  struct iovec const *msg_sect, unsigned int len)
 {
        struct tipc_msg *hdr;
        struct sk_buff *buf;
@@ -114,8 +113,7 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
        msg_set_namelower(hdr, seq->lower);
        msg_set_nameupper(hdr, seq->upper);
        msg_set_hdr_sz(hdr, MCAST_H_SIZE);
-       res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE,
-                            &buf);
+       res = tipc_msg_build(hdr, msg_sect, len, MAX_MSG_SIZE, &buf);
        if (unlikely(!buf))
                return res;
 
@@ -436,14 +434,13 @@ exit:
 }
 
 int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr,
-                             struct iovec const *msg_sect, u32 num_sect,
-                             unsigned int total_len, int err)
+                             struct iovec const *msg_sect, unsigned int len,
+                             int err)
 {
        struct sk_buff *buf;
        int res;
 
-       res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE,
-                            &buf);
+       res = tipc_msg_build(hdr, msg_sect, len, MAX_MSG_SIZE, &buf);
        if (!buf)
                return res;
 
@@ -918,15 +915,14 @@ int tipc_port_recv_msg(struct sk_buff *buf)
  *  tipc_port_recv_sections(): Concatenate and deliver sectioned
  *                        message for this node.
  */
-static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_sect,
+static int tipc_port_recv_sections(struct tipc_port *sender,
                                   struct iovec const *msg_sect,
-                                  unsigned int total_len)
+                                  unsigned int len)
 {
        struct sk_buff *buf;
        int res;
 
-       res = tipc_msg_build(&sender->phdr, msg_sect, num_sect, total_len,
-                            MAX_MSG_SIZE, &buf);
+       res = tipc_msg_build(&sender->phdr, msg_sect, len, MAX_MSG_SIZE, &buf);
        if (likely(buf))
                tipc_port_recv_msg(buf);
        return res;
@@ -935,8 +931,7 @@ static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_se
 /**
  * tipc_send - send message sections on connection
  */
-int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect,
-             unsigned int total_len)
+int tipc_send(u32 ref, struct iovec const *msg_sect, unsigned int len)
 {
        struct tipc_port *p_ptr;
        u32 destnode;
@@ -950,11 +945,10 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect,
        if (!tipc_port_congested(p_ptr)) {
                destnode = port_peernode(p_ptr);
                if (likely(!in_own_node(destnode)))
-                       res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect,
-                                                          total_len, destnode);
+                       res = tipc_link_send_sections_fast(p_ptr, msg_sect,
+                                                          len, destnode);
                else
-                       res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect,
-                                                     total_len);
+                       res = tipc_port_recv_sections(p_ptr, msg_sect, len);
 
                if (likely(res != -ELINKCONG)) {
                        p_ptr->congested = 0;
@@ -965,7 +959,7 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect,
        }
        if (port_unreliable(p_ptr)) {
                p_ptr->congested = 0;
-               return total_len;
+               return len;
        }
        return -ELINKCONG;
 }
@@ -974,8 +968,7 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect,
  * tipc_send2name - send message sections to port name
  */
 int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain,
-                  unsigned int num_sect, struct iovec const *msg_sect,
-                  unsigned int total_len)
+                  struct iovec const *msg_sect, unsigned int len)
 {
        struct tipc_port *p_ptr;
        struct tipc_msg *msg;
@@ -999,36 +992,32 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain,
 
        if (likely(destport || destnode)) {
                if (likely(in_own_node(destnode)))
-                       res = tipc_port_recv_sections(p_ptr, num_sect,
-                                                     msg_sect, total_len);
+                       res = tipc_port_recv_sections(p_ptr, msg_sect, len);
                else if (tipc_own_addr)
                        res = tipc_link_send_sections_fast(p_ptr, msg_sect,
-                                                          num_sect, total_len,
-                                                          destnode);
+                                                          len, destnode);
                else
                        res = tipc_port_reject_sections(p_ptr, msg, msg_sect,
-                                                       num_sect, total_len,
-                                                       TIPC_ERR_NO_NODE);
+                                                       len, TIPC_ERR_NO_NODE);
                if (likely(res != -ELINKCONG)) {
                        if (res > 0)
                                p_ptr->sent++;
                        return res;
                }
                if (port_unreliable(p_ptr)) {
-                       return total_len;
+                       return len;
                }
                return -ELINKCONG;
        }
-       return tipc_port_reject_sections(p_ptr, msg, msg_sect, num_sect,
-                                        total_len, TIPC_ERR_NO_NAME);
+       return tipc_port_reject_sections(p_ptr, msg, msg_sect, len,
+                                        TIPC_ERR_NO_NAME);
 }
 
 /**
  * tipc_send2port - send message sections to port identity
  */
 int tipc_send2port(u32 ref, struct tipc_portid const *dest,
-                  unsigned int num_sect, struct iovec const *msg_sect,
-                  unsigned int total_len)
+                  struct iovec const *msg_sect, unsigned int len)
 {
        struct tipc_port *p_ptr;
        struct tipc_msg *msg;
@@ -1046,21 +1035,20 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest,
        msg_set_hdr_sz(msg, BASIC_H_SIZE);
 
        if (in_own_node(dest->node))
-               res =  tipc_port_recv_sections(p_ptr, num_sect, msg_sect,
-                                              total_len);
+               res =  tipc_port_recv_sections(p_ptr, msg_sect, len);
        else if (tipc_own_addr)
-               res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect,
-                                                  total_len, dest->node);
+               res = tipc_link_send_sections_fast(p_ptr, msg_sect, len,
+                                                  dest->node);
        else
-               res = tipc_port_reject_sections(p_ptr, msg, msg_sect, num_sect,
-                                               total_len, TIPC_ERR_NO_NODE);
+               res = tipc_port_reject_sections(p_ptr, msg, msg_sect, len,
+                                               TIPC_ERR_NO_NODE);
        if (likely(res != -ELINKCONG)) {
                if (res > 0)
                        p_ptr->sent++;
                return res;
        }
        if (port_unreliable(p_ptr)) {
-               return total_len;
+               return len;
        }
        return -ELINKCONG;
 }
index 5a7026b..9122535 100644 (file)
@@ -151,24 +151,20 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg);
  * TIPC messaging routines
  */
 int tipc_port_recv_msg(struct sk_buff *buf);
-int tipc_send(u32 portref, unsigned int num_sect, struct iovec const *msg_sect,
-             unsigned int total_len);
+int tipc_send(u32 portref, struct iovec const *msg_sect, unsigned int len);
 
 int tipc_send2name(u32 portref, struct tipc_name const *name, u32 domain,
-                  unsigned int num_sect, struct iovec const *msg_sect,
-                  unsigned int total_len);
+                  struct iovec const *msg_sect, unsigned int len);
 
 int tipc_send2port(u32 portref, struct tipc_portid const *dest,
-                  unsigned int num_sect, struct iovec const *msg_sect,
-                  unsigned int total_len);
+                  struct iovec const *msg_sect, unsigned int len);
 
 int tipc_multicast(u32 portref, struct tipc_name_seq const *seq,
-                  unsigned int section_count, struct iovec const *msg,
-                  unsigned int total_len);
+                  struct iovec const *msg, unsigned int len);
 
 int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr,
-                             struct iovec const *msg_sect, u32 num_sect,
-                             unsigned int total_len, int err);
+                             struct iovec const *msg_sect, unsigned int len,
+                             int err);
 struct sk_buff *tipc_port_get_ports(void);
 void tipc_port_recv_proto_msg(struct sk_buff *buf);
 void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp);
index 6cc7ddd..3906527 100644 (file)
@@ -338,7 +338,7 @@ static int release(struct socket *sock)
                buf = __skb_dequeue(&sk->sk_receive_queue);
                if (buf == NULL)
                        break;
-               if (TIPC_SKB_CB(buf)->handle != 0)
+               if (TIPC_SKB_CB(buf)->handle != NULL)
                        kfree_skb(buf);
                else {
                        if ((sock->state == SS_CONNECTING) ||
@@ -622,13 +622,11 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
                        res = tipc_send2name(tport->ref,
                                             &dest->addr.name.name,
                                             dest->addr.name.domain,
-                                            m->msg_iovlen,
                                             m->msg_iov,
                                             total_len);
                } else if (dest->addrtype == TIPC_ADDR_ID) {
                        res = tipc_send2port(tport->ref,
                                             &dest->addr.id,
-                                            m->msg_iovlen,
                                             m->msg_iov,
                                             total_len);
                } else if (dest->addrtype == TIPC_ADDR_MCAST) {
@@ -641,7 +639,6 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
                                break;
                        res = tipc_multicast(tport->ref,
                                             &dest->addr.nameseq,
-                                            m->msg_iovlen,
                                             m->msg_iov,
                                             total_len);
                }
@@ -707,8 +704,7 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,
                        break;
                }
 
-               res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov,
-                               total_len);
+               res = tipc_send(tport->ref, m->msg_iov, total_len);
                if (likely(res != -ELINKCONG))
                        break;
                if (timeout_val <= 0L) {
@@ -1368,7 +1364,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
                return TIPC_ERR_OVERLOAD;
 
        /* Enqueue message */
-       TIPC_SKB_CB(buf)->handle = 0;
+       TIPC_SKB_CB(buf)->handle = NULL;
        __skb_queue_tail(&sk->sk_receive_queue, buf);
        skb_set_owner_r(buf, sk);
 
@@ -1691,7 +1687,7 @@ restart:
                /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */
                buf = __skb_dequeue(&sk->sk_receive_queue);
                if (buf) {
-                       if (TIPC_SKB_CB(buf)->handle != 0) {
+                       if (TIPC_SKB_CB(buf)->handle != NULL) {
                                kfree_skb(buf);
                                goto restart;
                        }
index 86de99a..c1f403b 100644 (file)
@@ -1246,6 +1246,15 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
        return 0;
 }
 
+static void unix_sock_inherit_flags(const struct socket *old,
+                                   struct socket *new)
+{
+       if (test_bit(SOCK_PASSCRED, &old->flags))
+               set_bit(SOCK_PASSCRED, &new->flags);
+       if (test_bit(SOCK_PASSSEC, &old->flags))
+               set_bit(SOCK_PASSSEC, &new->flags);
+}
+
 static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
 {
        struct sock *sk = sock->sk;
@@ -1280,6 +1289,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
        /* attach accepted sock to socket */
        unix_state_lock(tsk);
        newsock->state = SS_CONNECTED;
+       unix_sock_inherit_flags(sock, newsock);
        sock_graft(tsk, newsock);
        unix_state_unlock(tsk);
        return 0;
index d591091..86fa0f3 100644 (file)
@@ -124,6 +124,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r
        rep->udiag_family = AF_UNIX;
        rep->udiag_type = sk->sk_type;
        rep->udiag_state = sk->sk_state;
+       rep->pad = 0;
        rep->udiag_ino = sk_ino;
        sock_diag_save_cookie(sk, rep->udiag_cookie);
 
index 1e743d2..5dcd9c0 100644 (file)
@@ -63,11 +63,11 @@ void __wimax_state_set(struct wimax_dev *wimax_dev, enum wimax_st state)
 {
        wimax_dev->state = state;
 }
-extern void __wimax_state_change(struct wimax_dev *, enum wimax_st);
+void __wimax_state_change(struct wimax_dev *, enum wimax_st);
 
 #ifdef CONFIG_DEBUG_FS
-extern int wimax_debugfs_add(struct wimax_dev *);
-extern void wimax_debugfs_rm(struct wimax_dev *);
+int wimax_debugfs_add(struct wimax_dev *);
+void wimax_debugfs_rm(struct wimax_dev *);
 #else
 static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev)
 {
@@ -76,13 +76,13 @@ static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev)
 static inline void wimax_debugfs_rm(struct wimax_dev *wimax_dev) {}
 #endif
 
-extern void wimax_id_table_add(struct wimax_dev *);
-extern struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int);
-extern void wimax_id_table_rm(struct wimax_dev *);
-extern void wimax_id_table_release(void);
+void wimax_id_table_add(struct wimax_dev *);
+struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int);
+void wimax_id_table_rm(struct wimax_dev *);
+void wimax_id_table_release(void);
 
-extern int wimax_rfkill_add(struct wimax_dev *);
-extern void wimax_rfkill_rm(struct wimax_dev *);
+int wimax_rfkill_add(struct wimax_dev *);
+void wimax_rfkill_rm(struct wimax_dev *);
 
 extern struct genl_family wimax_gnl_family;
 extern struct genl_multicast_group wimax_gnl_mcg;
index 50f6195..9b8cc87 100644 (file)
@@ -328,6 +328,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
        return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
                                               width);
 }
+EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
 
 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
                                        u32 center_freq, u32 bandwidth,
@@ -503,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_ADHOC:
                if (wdev->current_bss) {
                        *chan = wdev->current_bss->pub.channel;
-                       *chanmode = wdev->ibss_fixed
+                       *chanmode = (wdev->ibss_fixed &&
+                                    !wdev->ibss_dfs_possible)
                                  ? CHAN_MODE_SHARED
                                  : CHAN_MODE_EXCLUSIVE;
                        return;
index 3159e9c..af10e59 100644 (file)
@@ -234,10 +234,10 @@ struct cfg80211_beacon_registration {
 };
 
 /* free object */
-extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
+void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
 
-extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
-                              char *newname);
+int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
+                       char *newname);
 
 void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
 
@@ -382,15 +382,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 enum cfg80211_chan_mode chanmode,
                                 u8 radar_detect);
 
-/**
- * cfg80211_chandef_dfs_required - checks if radar detection is required
- * @wiphy: the wiphy to validate against
- * @chandef: the channel definition to check
- * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
- */
-int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
-                                 const struct cfg80211_chan_def *c);
-
 void cfg80211_set_dfs_state(struct wiphy *wiphy,
                            const struct cfg80211_chan_def *chandef,
                            enum nl80211_dfs_state dfs_state);
index 90d0500..4541577 100644 (file)
@@ -47,17 +47,19 @@ static int ht_print_chan(struct ieee80211_channel *chan,
                return 0;
 
        if (chan->flags & IEEE80211_CHAN_DISABLED)
-               return snprintf(buf + offset,
-                               buf_size - offset,
-                               "%d Disabled\n",
-                               chan->center_freq);
-
-       return snprintf(buf + offset,
-                       buf_size - offset,
-                       "%d HT40 %c%c\n",
-                       chan->center_freq,
-                       (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-',
-                       (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)  ? ' ' : '+');
+               return scnprintf(buf + offset,
+                                buf_size - offset,
+                                "%d Disabled\n",
+                                chan->center_freq);
+
+       return scnprintf(buf + offset,
+                        buf_size - offset,
+                        "%d HT40 %c%c\n",
+                        chan->center_freq,
+                        (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
+                               ' ' : '-',
+                        (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
+                               ' ' : '+');
 }
 
 static ssize_t ht40allow_map_read(struct file *file,
index 9392f8c..42ed274 100644 (file)
@@ -46,6 +46,12 @@ BEGIN {
        sub(/:/, "", country)
        printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
        printf "\t.alpha2 = \"%s\",\n", country
+       if ($NF ~ /DFS-ETSI/)
+               printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
+       else if ($NF ~ /DFS-FCC/)
+               printf "\t.dfs_region = NL80211_DFS_FCC,\n"
+       else if ($NF ~ /DFS-JP/)
+               printf "\t.dfs_region = NL80211_DFS_JP,\n"
        printf "\t.reg_rules = {\n"
        active = 1
        regdb = regdb "\t&regdom_" country ",\n"
index 403fe29..9d797df 100644 (file)
@@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
                         struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_channel *check_chan;
+       u8 radar_detect_width = 0;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        wdev->connect_keys = connkeys;
 
        wdev->ibss_fixed = params->channel_fixed;
+       wdev->ibss_dfs_possible = params->userspace_handles_dfs;
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
+       check_chan = params->chandef.chan;
+       if (params->userspace_handles_dfs) {
+               /* use channel NULL to check for radar even if the current
+                * channel is not a radar channel - it might decide to change
+                * to DFS channel later.
+                */
+               radar_detect_width = BIT(params->chandef.width);
+               check_chan = NULL;
+       }
+
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          check_chan,
+                                          (params->channel_fixed &&
+                                           !radar_detect_width)
+                                          ? CHAN_MODE_SHARED
+                                          : CHAN_MODE_EXCLUSIVE,
+                                          radar_detect_width);
 
-       err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
-                                   params->channel_fixed
-                                   ? CHAN_MODE_SHARED
-                                   : CHAN_MODE_EXCLUSIVE);
        if (err) {
                wdev->connect_keys = NULL;
                return err;
index 8d49c1c..6a6b1c8 100644 (file)
@@ -707,11 +707,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
                        if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
                                continue;
 
-                       timeout = c->dfs_state_entered +
-                                 IEEE80211_DFS_MIN_NOP_TIME_MS;
+                       timeout = c->dfs_state_entered + msecs_to_jiffies(
+                                       IEEE80211_DFS_MIN_NOP_TIME_MS);
 
                        if (time_after_eq(jiffies, timeout)) {
                                c->dfs_state = NL80211_DFS_USABLE;
+                               c->dfs_state_entered = jiffies;
+
                                cfg80211_chandef_create(&chandef, c,
                                                        NL80211_CHAN_NO_HT);
 
index 626dc3b..a7f4e79 100644 (file)
@@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
        [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
        [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+       [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+       [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,
        return 0;
 }
 
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+                                     struct station_parameters *params)
+{
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+               params->supported_channels =
+                    nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               params->supported_channels_len =
+                    nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               /*
+                * Need to include at least one (first channel, number of
+                * channels) tuple for each subband, and must have proper
+                * tuples for the rest of the data as well.
+                */
+               if (params->supported_channels_len < 2)
+                       return -EINVAL;
+               if (params->supported_channels_len % 2)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+               params->supported_oper_classes =
+                nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               params->supported_oper_classes_len =
+                 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               /*
+                * The value of the Length field of the Supported Operating
+                * Classes element is between 2 and 253.
+                */
+               if (params->supported_oper_classes_len < 2 ||
+                   params->supported_oper_classes_len > 253)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
 static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
 {
+       int err;
        /* Dummy STA entry gets updated once the peer capabilities are known */
        if (info->attrs[NL80211_ATTR_PEER_AID])
                params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
@@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                params->vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+       err = nl80211_parse_sta_channel_info(info, params);
+       if (err)
+               return err;
+
        return nl80211_parse_sta_wme(info, params);
 }
 
@@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       err = nl80211_parse_sta_channel_info(info, &params);
+       if (err)
+               return err;
+
        err = nl80211_parse_sta_wme(info, &params);
        if (err)
                return err;
@@ -5591,6 +5638,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (err)
                return err;
 
+       if (netif_carrier_ok(dev))
+               return -EBUSY;
+
        if (wdev->cac_started)
                return -EBUSY;
 
@@ -5634,15 +5684,27 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
        u8 radar_detect_width = 0;
        int err;
+       bool need_new_beacon = false;
 
        if (!rdev->ops->channel_switch ||
            !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
                return -EOPNOTSUPP;
 
-       /* may add IBSS support later */
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+               need_new_beacon = true;
+
+               /* useless if AP is not running */
+               if (!wdev->beacon_interval)
+                       return -EINVAL;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+               break;
+       default:
                return -EOPNOTSUPP;
+       }
 
        memset(&params, 0, sizeof(params));
 
@@ -5651,15 +5713,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        /* only important for AP, IBSS and mesh create IEs internally */
-       if (!info->attrs[NL80211_ATTR_CSA_IES])
-               return -EINVAL;
-
-       /* useless if AP is not running */
-       if (!wdev->beacon_interval)
+       if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
                return -EINVAL;
 
        params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
 
+       if (!need_new_beacon)
+               goto skip_beacons;
+
        err = nl80211_parse_beacon(info->attrs, &params.beacon_after);
        if (err)
                return err;
@@ -5699,6 +5760,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+skip_beacons:
        err = nl80211_parse_chandef(rdev, info, &params.chandef);
        if (err)
                return err;
@@ -5706,12 +5768,17 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
-       if (err < 0) {
-               return err;
-       } else if (err) {
-               radar_detect_width = BIT(params.chandef.width);
-               params.radar_required = true;
+       if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
+               err = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                   &params.chandef);
+               if (err < 0) {
+                       return err;
+               } else if (err) {
+                       radar_detect_width = BIT(params.chandef.width);
+                       params.radar_required = true;
+               }
        }
 
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
@@ -6535,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        ibss.control_port =
                nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 
+       ibss.userspace_handles_dfs =
+               nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
@@ -10740,7 +10810,9 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
        wdev_lock(wdev);
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+                   wdev->iftype != NL80211_IFTYPE_ADHOC &&
+                   wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                goto out;
 
        wdev->channel = chandef->chan;
index de06d5d..7da67fd 100644 (file)
@@ -172,11 +172,21 @@ static const struct ieee80211_regdomain world_regdom = {
                        NL80211_RRF_NO_IBSS |
                        NL80211_RRF_NO_OFDM),
                /* IEEE 802.11a, channel 36..48 */
-               REG_RULE(5180-10, 5240+10, 80, 6, 20,
+               REG_RULE(5180-10, 5240+10, 160, 6, 20,
                         NL80211_RRF_PASSIVE_SCAN |
                         NL80211_RRF_NO_IBSS),
 
-               /* NB: 5260 MHz - 5700 MHz requires DFS */
+               /* IEEE 802.11a, channel 52..64 - DFS required */
+               REG_RULE(5260-10, 5320+10, 160, 6, 20,
+                       NL80211_RRF_PASSIVE_SCAN |
+                       NL80211_RRF_NO_IBSS |
+                       NL80211_RRF_DFS),
+
+               /* IEEE 802.11a, channel 100..144 - DFS required */
+               REG_RULE(5500-10, 5720+10, 160, 6, 20,
+                       NL80211_RRF_PASSIVE_SCAN |
+                       NL80211_RRF_NO_IBSS |
+                       NL80211_RRF_DFS),
 
                /* IEEE 802.11a, channel 149..165 */
                REG_RULE(5745-10, 5825+10, 80, 6, 20,
@@ -758,24 +768,25 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(freq_reg_info);
 
-#ifdef CONFIG_CFG80211_REG_DEBUG
-static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
+const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 {
        switch (initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
-               return "Set by core";
+               return "core";
        case NL80211_REGDOM_SET_BY_USER:
-               return "Set by user";
+               return "user";
        case NL80211_REGDOM_SET_BY_DRIVER:
-               return "Set by driver";
+               return "driver";
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-               return "Set by country IE";
+               return "country IE";
        default:
                WARN_ON(1);
-               return "Set by bug";
+               return "bug";
        }
 }
+EXPORT_SYMBOL(reg_initiator_name);
 
+#ifdef CONFIG_CFG80211_REG_DEBUG
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
@@ -962,6 +973,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 }
 #endif
 
+static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
+{
+       if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
+           !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+               return true;
+       return false;
+}
 
 static bool ignore_reg_update(struct wiphy *wiphy,
                              enum nl80211_reg_initiator initiator)
@@ -969,14 +987,17 @@ static bool ignore_reg_update(struct wiphy *wiphy,
        struct regulatory_request *lr = get_last_request();
 
        if (!lr) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n",
+               REG_DBG_PRINT("Ignoring regulatory request set by %s "
+                             "since last_request is not set\n",
                              reg_initiator_name(initiator));
                return true;
        }
 
        if (initiator == NL80211_REGDOM_SET_BY_CORE &&
            wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n",
+               REG_DBG_PRINT("Ignoring regulatory request set by %s "
+                             "since the driver uses its own custom "
+                             "regulatory domain\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -985,10 +1006,12 @@ static bool ignore_reg_update(struct wiphy *wiphy,
         * wiphy->regd will be set once the device has its own
         * desired regulatory domain set
         */
-       if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
+       if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
            initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
            !is_world_regdom(lr->alpha2)) {
-               REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n",
+               REG_DBG_PRINT("Ignoring regulatory request set by %s "
+                             "since the driver requires its own regulatory "
+                             "domain to be set first\n",
                              reg_initiator_name(initiator));
                return true;
        }
@@ -1689,8 +1712,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 }
 EXPORT_SYMBOL(regulatory_hint);
 
-void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
-                        const u8 *country_ie, u8 country_ie_len)
+void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
+                               const u8 *country_ie, u8 country_ie_len)
 {
        char alpha2[2];
        enum environment_cap env = ENVIRON_ANY;
index af2d5f8..9677e3c 100644 (file)
@@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
                                 gfp_t gfp);
 
 /**
- * regulatory_hint_11d - hints a country IE as a regulatory domain
+ * regulatory_hint_country_ie - hints a country IE as a regulatory domain
  * @wiphy: the wireless device giving the hint (used only for reporting
  *     conflicts)
  * @band: the band on which the country IE was received on. This determines
@@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
  * not observed. For this reason if a triplet is seen with channel
  * information for a band the BSS is not present in it will be ignored.
  */
-void regulatory_hint_11d(struct wiphy *wiphy,
+void regulatory_hint_country_ie(struct wiphy *wiphy,
                         enum ieee80211_band band,
                         const u8 *country_ie,
                         u8 country_ie_len);
index eeb7148..d4397eb 100644 (file)
@@ -254,10 +254,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
        rdev = container_of(wk, struct cfg80211_registered_device,
                            sched_scan_results_wk);
 
-       request = rdev->sched_scan_req;
-
        rtnl_lock();
 
+       request = rdev->sched_scan_req;
+
        /* we don't have sched_scan_req anymore if the scan is stopping */
        if (request) {
                if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
index 20e86a9..65f8008 100644 (file)
@@ -682,8 +682,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
         * - country_ie + 2, the start of the country ie data, and
         * - and country_ie[1] which is the IE length
         */
-       regulatory_hint_11d(wdev->wiphy, bss->channel->band,
-                           country_ie + 2, country_ie[1]);
+       regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
+                                  country_ie + 2, country_ie[1]);
        kfree(country_ie);
 }
 
index 65acbeb..b533ed7 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef __WIRELESS_SYSFS_H
 #define __WIRELESS_SYSFS_H
 
-extern int wiphy_sysfs_init(void);
-extern void wiphy_sysfs_exit(void);
+int wiphy_sysfs_init(void);
+void wiphy_sysfs_exit(void);
 
 extern struct class ieee80211_class;
 
index ce090c1..935dea9 100644 (file)
@@ -10,6 +10,7 @@
 #include <net/cfg80211.h>
 #include <net/ip.h>
 #include <net/dsfield.h>
+#include <linux/if_vlan.h>
 #include "core.h"
 #include "rdev-ops.h"
 
@@ -691,6 +692,7 @@ EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
 unsigned int cfg80211_classify8021d(struct sk_buff *skb)
 {
        unsigned int dscp;
+       unsigned char vlan_priority;
 
        /* skb->priority values from 256->263 are magic values to
         * directly indicate a specific 802.1d priority.  This is used
@@ -700,6 +702,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb)
        if (skb->priority >= 256 && skb->priority <= 263)
                return skb->priority - 256;
 
+       if (vlan_tx_tag_present(skb)) {
+               vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK)
+                       >> VLAN_PRIO_SHIFT;
+               if (vlan_priority > 0)
+                       return vlan_priority;
+       }
+
        switch (skb->protocol) {
        case htons(ETH_P_IP):
                dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
@@ -1240,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
-       bool radar_required;
+       bool radar_required = false;
        int i, j;
 
        ASSERT_RTNL();
@@ -1255,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_WDS:
-               radar_required = !!(chan &&
-                                   (chan->flags & IEEE80211_CHAN_RADAR));
+               /* if the interface could potentially choose a DFS channel,
+                * then mark DFS as required.
+                */
+               if (!chan) {
+                       if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
+                               radar_required = true;
+                       break;
+               }
+               radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_MONITOR:
-               radar_required = false;
                break;
        case NUM_NL80211_IFTYPES:
        case NL80211_IFTYPE_UNSPECIFIED:
index c959312..e2fa133 100644 (file)
@@ -16,8 +16,8 @@ config X25
          if you want that) and the lower level data link layer protocol LAPB
          (say Y to "LAPB Data Link Driver" below if you want that).
 
-         You can read more about X.25 at <http://www.sangoma.com/x25.htm> and
-         <http://www.cisco.com/univercd/cc/td/doc/product/software/ios11/cbook/cx25.htm>.
+         You can read more about X.25 at <http://www.sangoma.com/tutorials/x25/> and
+         <http://docwiki.cisco.com/wiki/X.25>.
          Information about X.25 for Linux is contained in the files
          <file:Documentation/networking/x25.txt> and
          <file:Documentation/networking/x25-iface.txt>.
index ab4ef72..debe733 100644 (file)
@@ -802,17 +802,4 @@ int xfrm_count_pfkey_enc_supported(void)
 }
 EXPORT_SYMBOL_GPL(xfrm_count_pfkey_enc_supported);
 
-#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
-
-void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
-{
-       if (tail != skb) {
-               skb->data_len += len;
-               skb->len += len;
-       }
-       return skb_put(tail, len);
-}
-EXPORT_SYMBOL_GPL(pskb_put);
-#endif
-
 MODULE_LICENSE("GPL");
index 716502a..0622d31 100644 (file)
@@ -130,7 +130,7 @@ static inline unsigned int __addr_hash(const xfrm_address_t *daddr,
        return h & hmask;
 }
 
-extern struct hlist_head *xfrm_hash_alloc(unsigned int sz);
-extern void xfrm_hash_free(struct hlist_head *n, unsigned int sz);
+struct hlist_head *xfrm_hash_alloc(unsigned int sz);
+void xfrm_hash_free(struct hlist_head *n, unsigned int sz);
 
 #endif /* _XFRM_HASH_H */
index 2906d52..ccfdc71 100644 (file)
@@ -141,14 +141,14 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
        const int plen = skb->len;
        int dlen = IPCOMP_SCRATCH_SIZE;
        u8 *start = skb->data;
-       const int cpu = get_cpu();
-       u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
-       struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
+       struct crypto_comp *tfm;
+       u8 *scratch;
        int err;
 
        local_bh_disable();
+       scratch = *this_cpu_ptr(ipcomp_scratches);
+       tfm = *this_cpu_ptr(ipcd->tfms);
        err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
-       local_bh_enable();
        if (err)
                goto out;
 
@@ -158,13 +158,13 @@ static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
        }
 
        memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
-       put_cpu();
+       local_bh_enable();
 
        pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
        return 0;
 
 out:
-       put_cpu();
+       local_bh_enable();
        return err;
 }
 
@@ -220,8 +220,8 @@ static void ipcomp_free_scratches(void)
 
 static void * __percpu *ipcomp_alloc_scratches(void)
 {
-       int i;
        void * __percpu *scratches;
+       int i;
 
        if (ipcomp_scratch_users++)
                return ipcomp_scratches;
@@ -233,7 +233,9 @@ static void * __percpu *ipcomp_alloc_scratches(void)
        ipcomp_scratches = scratches;
 
        for_each_possible_cpu(i) {
-               void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
+               void *scratch;
+
+               scratch = vmalloc_node(IPCOMP_SCRATCH_SIZE, cpu_to_node(i));
                if (!scratch)
                        return NULL;
                *per_cpu_ptr(scratches, i) = scratch;
index ed38d5d..9a91f74 100644 (file)
@@ -334,7 +334,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
 
        atomic_inc(&policy->genid);
 
-       del_timer(&policy->polq.hold_timer);
+       if (del_timer(&policy->polq.hold_timer))
+               xfrm_pol_put(policy);
        xfrm_queue_purge(&policy->polq.hold_queue);
 
        if (del_timer(&policy->timer))
@@ -589,7 +590,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
 
        spin_lock_bh(&pq->hold_queue.lock);
        skb_queue_splice_init(&pq->hold_queue, &list);
-       del_timer(&pq->hold_timer);
+       if (del_timer(&pq->hold_timer))
+               xfrm_pol_put(old);
        spin_unlock_bh(&pq->hold_queue.lock);
 
        if (skb_queue_empty(&list))
@@ -600,7 +602,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
        spin_lock_bh(&pq->hold_queue.lock);
        skb_queue_splice(&list, &pq->hold_queue);
        pq->timeout = XFRM_QUEUE_TMO_MIN;
-       mod_timer(&pq->hold_timer, jiffies);
+       if (!mod_timer(&pq->hold_timer, jiffies))
+               xfrm_pol_hold(new);
        spin_unlock_bh(&pq->hold_queue.lock);
 }
 
@@ -1769,6 +1772,10 @@ static void xfrm_policy_queue_process(unsigned long arg)
 
        spin_lock(&pq->hold_queue.lock);
        skb = skb_peek(&pq->hold_queue);
+       if (!skb) {
+               spin_unlock(&pq->hold_queue.lock);
+               goto out;
+       }
        dst = skb_dst(skb);
        sk = skb->sk;
        xfrm_decode_session(skb, &fl, dst->ops->family);
@@ -1787,8 +1794,9 @@ static void xfrm_policy_queue_process(unsigned long arg)
                        goto purge_queue;
 
                pq->timeout = pq->timeout << 1;
-               mod_timer(&pq->hold_timer, jiffies + pq->timeout);
-               return;
+               if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout))
+                       xfrm_pol_hold(pol);
+       goto out;
        }
 
        dst_release(dst);
@@ -1819,11 +1827,14 @@ static void xfrm_policy_queue_process(unsigned long arg)
                err = dst_output(skb);
        }
 
+out:
+       xfrm_pol_put(pol);
        return;
 
 purge_queue:
        pq->timeout = 0;
        xfrm_queue_purge(&pq->hold_queue);
+       xfrm_pol_put(pol);
 }
 
 static int xdst_queue_output(struct sk_buff *skb)
@@ -1831,7 +1842,15 @@ static int xdst_queue_output(struct sk_buff *skb)
        unsigned long sched_next;
        struct dst_entry *dst = skb_dst(skb);
        struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
-       struct xfrm_policy_queue *pq = &xdst->pols[0]->polq;
+       struct xfrm_policy *pol = xdst->pols[0];
+       struct xfrm_policy_queue *pq = &pol->polq;
+       const struct sk_buff *fclone = skb + 1;
+
+       if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
+                    fclone->fclone == SKB_FCLONE_CLONE)) {
+               kfree_skb(skb);
+               return 0;
+       }
 
        if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) {
                kfree_skb(skb);
@@ -1850,10 +1869,12 @@ static int xdst_queue_output(struct sk_buff *skb)
        if (del_timer(&pq->hold_timer)) {
                if (time_before(pq->hold_timer.expires, sched_next))
                        sched_next = pq->hold_timer.expires;
+               xfrm_pol_put(pol);
        }
 
        __skb_queue_tail(&pq->hold_queue, skb);
-       mod_timer(&pq->hold_timer, sched_next);
+       if (!mod_timer(&pq->hold_timer, sched_next))
+               xfrm_pol_hold(pol);
 
        spin_unlock_bh(&pq->hold_queue.lock);
 
index 8dafe6d..dab57da 100644 (file)
@@ -61,9 +61,9 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event)
 
        switch (event) {
        case XFRM_REPLAY_UPDATE:
-               if (x->replay_maxdiff &&
-                   (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
-                   (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
+               if (!x->replay_maxdiff ||
+                   ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
+                   (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {
                        if (x->xflags & XFRM_TIME_DEFER)
                                event = XFRM_REPLAY_TIMEOUT;
                        else
@@ -129,8 +129,7 @@ static int xfrm_replay_check(struct xfrm_state *x,
                return 0;
 
        diff = x->replay.seq - seq;
-       if (diff >= min_t(unsigned int, x->props.replay_window,
-                         sizeof(x->replay.bitmap) * 8)) {
+       if (diff >= x->props.replay_window) {
                x->stats.replay_window++;
                goto err;
        }
@@ -302,9 +301,10 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
 
        switch (event) {
        case XFRM_REPLAY_UPDATE:
-               if (x->replay_maxdiff &&
-                   (replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
-                   (replay_esn->oseq - preplay_esn->oseq < x->replay_maxdiff)) {
+               if (!x->replay_maxdiff ||
+                   ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
+                   (replay_esn->oseq - preplay_esn->oseq
+                    < x->replay_maxdiff))) {
                        if (x->xflags & XFRM_TIME_DEFER)
                                event = XFRM_REPLAY_TIMEOUT;
                        else
@@ -353,28 +353,30 @@ static void xfrm_replay_notify_esn(struct xfrm_state *x, int event)
 
        switch (event) {
        case XFRM_REPLAY_UPDATE:
-               if (!x->replay_maxdiff)
-                       break;
-
-               if (replay_esn->seq_hi == preplay_esn->seq_hi)
-                       seq_diff = replay_esn->seq - preplay_esn->seq;
-               else
-                       seq_diff = ~preplay_esn->seq + replay_esn->seq + 1;
-
-               if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
-                       oseq_diff = replay_esn->oseq - preplay_esn->oseq;
-               else
-                       oseq_diff = ~preplay_esn->oseq + replay_esn->oseq + 1;
-
-               if (seq_diff < x->replay_maxdiff &&
-                   oseq_diff < x->replay_maxdiff) {
+               if (x->replay_maxdiff) {
+                       if (replay_esn->seq_hi == preplay_esn->seq_hi)
+                               seq_diff = replay_esn->seq - preplay_esn->seq;
+                       else
+                               seq_diff = ~preplay_esn->seq + replay_esn->seq
+                                          + 1;
 
-                       if (x->xflags & XFRM_TIME_DEFER)
-                               event = XFRM_REPLAY_TIMEOUT;
+                       if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
+                               oseq_diff = replay_esn->oseq
+                                           - preplay_esn->oseq;
                        else
-                               return;
+                               oseq_diff = ~preplay_esn->oseq
+                                           + replay_esn->oseq + 1;
+
+                       if (seq_diff >= x->replay_maxdiff ||
+                           oseq_diff >= x->replay_maxdiff)
+                               break;
                }
 
+               if (x->xflags & XFRM_TIME_DEFER)
+                       event = XFRM_REPLAY_TIMEOUT;
+               else
+                       return;
+
                break;
 
        case XFRM_REPLAY_TIMEOUT:
index b9c3f9e..68c2f35 100644 (file)
@@ -468,7 +468,7 @@ expired:
        }
 
        err = __xfrm_state_delete(x);
-       if (!err && x->id.spi)
+       if (!err)
                km_state_expired(x, 1, 0);
 
        xfrm_audit_state_delete(x, err ? 0 : 1,
@@ -815,7 +815,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
                        xfrm_state_look_at(pol, x, fl, encap_family,
                                           &best, &acquire_in_progress, &error);
        }
-       if (best)
+       if (best || acquire_in_progress)
                goto found;
 
        h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family);
@@ -824,7 +824,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
                    x->props.reqid == tmpl->reqid &&
                    (mark & x->mark.m) == x->mark.v &&
                    !(x->props.flags & XFRM_STATE_WILDRECV) &&
-                   xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
+                   xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
                    tmpl->mode == x->props.mode &&
                    tmpl->id.proto == x->id.proto &&
                    (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
index 3f565e4..f964d4c 100644 (file)
@@ -446,7 +446,8 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
        memcpy(&x->sel, &p->sel, sizeof(x->sel));
        memcpy(&x->lft, &p->lft, sizeof(x->lft));
        x->props.mode = p->mode;
-       x->props.replay_window = p->replay_window;
+       x->props.replay_window = min_t(unsigned int, p->replay_window,
+                                       sizeof(x->replay.bitmap) * 8);
        x->props.reqid = p->reqid;
        x->props.family = p->family;
        memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
@@ -1856,7 +1857,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (x->km.state != XFRM_STATE_VALID)
                goto out;
 
-       err = xfrm_replay_verify_len(x->replay_esn, rp);
+       err = xfrm_replay_verify_len(x->replay_esn, re);
        if (err)
                goto out;
 
index 47016c3..66cad50 100755 (executable)
@@ -3975,8 +3975,8 @@ sub string_find_replace {
 # check for new externs in .h files.
                if ($realfile =~ /\.h$/ &&
                    $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) {
-                       if (WARN("AVOID_EXTERNS",
-                                "extern prototypes should be avoided in .h files\n" . $herecurr) &&
+                       if (CHK("AVOID_EXTERNS",
+                               "extern prototypes should be avoided in .h files\n" . $herecurr) &&
                            $fix) {
                                $fixed[$linenr - 1] =~ s/(.*)\bextern\b\s*(.*)/$1$2/;
                        }
index 487ac6f..9a11f9f 100644 (file)
@@ -55,6 +55,7 @@ static struct sym_entry *table;
 static unsigned int table_size, table_cnt;
 static int all_symbols = 0;
 static char symbol_prefix_char = '\0';
+static unsigned long long kernel_start_addr = 0;
 
 int token_profit[0x10000];
 
@@ -65,7 +66,10 @@ unsigned char best_table_len[256];
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: kallsyms [--all-symbols] [--symbol-prefix=<prefix char>] < in.map > out.S\n");
+       fprintf(stderr, "Usage: kallsyms [--all-symbols] "
+                       "[--symbol-prefix=<prefix char>] "
+                       "[--page-offset=<CONFIG_PAGE_OFFSET>] "
+                       "< in.map > out.S\n");
        exit(1);
 }
 
@@ -194,6 +198,9 @@ static int symbol_valid(struct sym_entry *s)
        int i;
        int offset = 1;
 
+       if (s->addr < kernel_start_addr)
+               return 0;
+
        /* skip prefix char */
        if (symbol_prefix_char && *(s->sym + 1) == symbol_prefix_char)
                offset++;
@@ -646,6 +653,9 @@ int main(int argc, char **argv)
                                if ((*p == '"' && *(p+2) == '"') || (*p == '\'' && *(p+2) == '\''))
                                        p++;
                                symbol_prefix_char = *p;
+                       } else if (strncmp(argv[i], "--page-offset=", 14) == 0) {
+                               const char *p = &argv[i][14];
+                               kernel_start_addr = strtoull(p, NULL, 16);
                        } else
                                usage();
                }
index 0149949..32b10f5 100644 (file)
@@ -82,6 +82,8 @@ kallsyms()
                kallsymopt="${kallsymopt} --all-symbols"
        fi
 
+       kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET"
+
        local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL}               \
                      ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
 
index 95c2b26..7db9954 100644 (file)
@@ -580,15 +580,13 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
 
        /* check if the next ns is a sibling, parent, gp, .. */
        parent = ns->parent;
-       while (parent) {
+       while (ns != root) {
                mutex_unlock(&ns->lock);
                next = list_entry_next(ns, base.list);
                if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
                        mutex_lock(&next->lock);
                        return next;
                }
-               if (parent == root)
-                       return NULL;
                ns = parent;
                parent = parent->parent;
        }
index d6222ba..532471d 100644 (file)
  * it should be.
  */
 
-#include <linux/crypto.h>
+#include <crypto/hash.h>
 
 #include "include/apparmor.h"
 #include "include/crypto.h"
 
 static unsigned int apparmor_hash_size;
 
-static struct crypto_hash *apparmor_tfm;
+static struct crypto_shash *apparmor_tfm;
 
 unsigned int aa_hash_size(void)
 {
@@ -32,35 +32,33 @@ unsigned int aa_hash_size(void)
 int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
                         size_t len)
 {
-       struct scatterlist sg[2];
-       struct hash_desc desc = {
-               .tfm = apparmor_tfm,
-               .flags = 0
-       };
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(apparmor_tfm)];
+       } desc;
        int error = -ENOMEM;
        u32 le32_version = cpu_to_le32(version);
 
        if (!apparmor_tfm)
                return 0;
 
-       sg_init_table(sg, 2);
-       sg_set_buf(&sg[0], &le32_version, 4);
-       sg_set_buf(&sg[1], (u8 *) start, len);
-
        profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
        if (!profile->hash)
                goto fail;
 
-       error = crypto_hash_init(&desc);
+       desc.shash.tfm = apparmor_tfm;
+       desc.shash.flags = 0;
+
+       error = crypto_shash_init(&desc.shash);
        if (error)
                goto fail;
-       error = crypto_hash_update(&desc, &sg[0], 4);
+       error = crypto_shash_update(&desc.shash, (u8 *) &le32_version, 4);
        if (error)
                goto fail;
-       error = crypto_hash_update(&desc, &sg[1], len);
+       error = crypto_shash_update(&desc.shash, (u8 *) start, len);
        if (error)
                goto fail;
-       error = crypto_hash_final(&desc, profile->hash);
+       error = crypto_shash_final(&desc.shash, profile->hash);
        if (error)
                goto fail;
 
@@ -75,19 +73,19 @@ fail:
 
 static int __init init_profile_hash(void)
 {
-       struct crypto_hash *tfm;
+       struct crypto_shash *tfm;
 
        if (!apparmor_initialized)
                return 0;
 
-       tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
+       tfm = crypto_alloc_shash("sha1", 0, CRYPTO_ALG_ASYNC);
        if (IS_ERR(tfm)) {
                int error = PTR_ERR(tfm);
                AA_ERROR("failed to setup profile sha1 hashing: %d\n", error);
                return error;
        }
        apparmor_tfm = tfm;
-       apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm);
+       apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm);
 
        aa_info_message("AppArmor sha1 policy hashing enabled");
 
index f2d4b63..c28b0f2 100644 (file)
@@ -360,7 +360,9 @@ static inline void aa_put_replacedby(struct aa_replacedby *p)
 static inline void __aa_update_replacedby(struct aa_profile *orig,
                                          struct aa_profile *new)
 {
-       struct aa_profile *tmp = rcu_dereference(orig->replacedby->profile);
+       struct aa_profile *tmp;
+       tmp = rcu_dereference_protected(orig->replacedby->profile,
+                                       mutex_is_locked(&orig->ns->lock));
        rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
        orig->flags |= PFLAG_INVALID;
        aa_put_profile(tmp);
index 6172509..705c287 100644 (file)
@@ -563,7 +563,8 @@ void __init aa_free_root_ns(void)
 static void free_replacedby(struct aa_replacedby *r)
 {
        if (r) {
-               aa_put_profile(rcu_dereference(r->profile));
+               /* r->profile will not be updated any more as r is dead */
+               aa_put_profile(rcu_dereference_protected(r->profile, true));
                kzfree(r);
        }
 }
@@ -609,6 +610,7 @@ void aa_free_profile(struct aa_profile *profile)
        aa_put_dfa(profile->policy.dfa);
        aa_put_replacedby(profile->replacedby);
 
+       kzfree(profile->hash);
        kzfree(profile);
 }
 
index 8d8d97d..234bc2a 100644 (file)
@@ -302,18 +302,19 @@ static void dump_common_audit_data(struct audit_buffer *ab,
                                                "faddr", "fport");
                                break;
                        }
+#if IS_ENABLED(CONFIG_IPV6)
                        case AF_INET6: {
                                struct inet_sock *inet = inet_sk(sk);
-                               struct ipv6_pinfo *inet6 = inet6_sk(sk);
 
-                               print_ipv6_addr(ab, &inet6->rcv_saddr,
+                               print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr,
                                                inet->inet_sport,
                                                "laddr", "lport");
-                               print_ipv6_addr(ab, &inet6->daddr,
+                               print_ipv6_addr(ab, &sk->sk_v6_daddr,
                                                inet->inet_dport,
                                                "faddr", "fport");
                                break;
                        }
+#endif
                        case AF_UNIX:
                                u = unix_sk(sk);
                                if (u->path.dentry) {
index dad36a6..fc3e662 100644 (file)
@@ -746,7 +746,6 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
  * @tclass: target security class
  * @requested: requested permissions, interpreted based on @tclass
  * @auditdata: auxiliary audit data
- * @flags: VFS walk flags
  *
  * Check the AVC to determine whether the @requested permissions are granted
  * for the SID pair (@ssid, @tsid), interpreting the permissions
@@ -756,17 +755,15 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
  * permissions are granted, -%EACCES if any permissions are denied, or
  * another -errno upon other errors.
  */
-int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
-                      u32 requested, struct common_audit_data *auditdata,
-                      unsigned flags)
+int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
+                u32 requested, struct common_audit_data *auditdata)
 {
        struct av_decision avd;
        int rc, rc2;
 
        rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
 
-       rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata,
-                       flags);
+       rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
        if (rc2)
                return rc2;
        return rc;
index a5091ec..c540795 100644 (file)
@@ -1502,7 +1502,7 @@ static int cred_has_capability(const struct cred *cred,
 
        rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
        if (audit == SECURITY_CAP_AUDIT) {
-               int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
+               int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
                if (rc2)
                        return rc2;
        }
@@ -1525,8 +1525,7 @@ static int task_has_system(struct task_struct *tsk,
 static int inode_has_perm(const struct cred *cred,
                          struct inode *inode,
                          u32 perms,
-                         struct common_audit_data *adp,
-                         unsigned flags)
+                         struct common_audit_data *adp)
 {
        struct inode_security_struct *isec;
        u32 sid;
@@ -1539,7 +1538,7 @@ static int inode_has_perm(const struct cred *cred,
        sid = cred_sid(cred);
        isec = inode->i_security;
 
-       return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
+       return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
 }
 
 /* Same as inode_has_perm, but pass explicit audit data containing
@@ -1554,7 +1553,7 @@ static inline int dentry_has_perm(const struct cred *cred,
 
        ad.type = LSM_AUDIT_DATA_DENTRY;
        ad.u.dentry = dentry;
-       return inode_has_perm(cred, inode, av, &ad, 0);
+       return inode_has_perm(cred, inode, av, &ad);
 }
 
 /* Same as inode_has_perm, but pass explicit audit data containing
@@ -1569,7 +1568,7 @@ static inline int path_has_perm(const struct cred *cred,
 
        ad.type = LSM_AUDIT_DATA_PATH;
        ad.u.path = *path;
-       return inode_has_perm(cred, inode, av, &ad, 0);
+       return inode_has_perm(cred, inode, av, &ad);
 }
 
 /* Same as path_has_perm, but uses the inode from the file struct. */
@@ -1581,7 +1580,7 @@ static inline int file_path_has_perm(const struct cred *cred,
 
        ad.type = LSM_AUDIT_DATA_PATH;
        ad.u.path = file->f_path;
-       return inode_has_perm(cred, file_inode(file), av, &ad, 0);
+       return inode_has_perm(cred, file_inode(file), av, &ad);
 }
 
 /* Check whether a task can use an open file descriptor to
@@ -1617,7 +1616,7 @@ static int file_has_perm(const struct cred *cred,
        /* av is zero if only checking access to the descriptor. */
        rc = 0;
        if (av)
-               rc = inode_has_perm(cred, inode, av, &ad, 0);
+               rc = inode_has_perm(cred, inode, av, &ad);
 
 out:
        return rc;
@@ -3929,7 +3928,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                if (snum) {
                        int low, high;
 
-                       inet_get_local_port_range(&low, &high);
+                       inet_get_local_port_range(sock_net(sk), &low, &high);
 
                        if (snum < max(PROT_SOCK, low) || snum > high) {
                                err = sel_netport_sid(sk->sk_protocol,
@@ -4668,7 +4667,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_forward(unsigned int hooknum,
+static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
                                         struct sk_buff *skb,
                                         const struct net_device *in,
                                         const struct net_device *out,
@@ -4678,7 +4677,7 @@ static unsigned int selinux_ipv4_forward(unsigned int hooknum,
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_forward(unsigned int hooknum,
+static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
                                         struct sk_buff *skb,
                                         const struct net_device *in,
                                         const struct net_device *out,
@@ -4710,7 +4709,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_output(unsigned int hooknum,
+static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,
                                        struct sk_buff *skb,
                                        const struct net_device *in,
                                        const struct net_device *out,
@@ -4837,7 +4836,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
        return NF_ACCEPT;
 }
 
-static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
+static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
                                           struct sk_buff *skb,
                                           const struct net_device *in,
                                           const struct net_device *out,
@@ -4847,7 +4846,7 @@ static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
+static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
                                           struct sk_buff *skb,
                                           const struct net_device *in,
                                           const struct net_device *out,
index 92d0ab5..f53ee3c 100644 (file)
@@ -130,7 +130,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
                            u16 tclass, u32 requested,
                            struct av_decision *avd,
                            int result,
-                           struct common_audit_data *a, unsigned flags)
+                           struct common_audit_data *a)
 {
        u32 audited, denied;
        audited = avc_audit_required(requested, avd, result, 0, &denied);
@@ -138,7 +138,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
                return 0;
        return slow_avc_audit(ssid, tsid, tclass,
                              requested, audited, denied,
-                             a, flags);
+                             a, 0);
 }
 
 #define AVC_STRICT 1 /* Ignore permissive mode. */
@@ -147,17 +147,9 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                         unsigned flags,
                         struct av_decision *avd);
 
-int avc_has_perm_flags(u32 ssid, u32 tsid,
-                      u16 tclass, u32 requested,
-                      struct common_audit_data *auditdata,
-                      unsigned);
-
-static inline int avc_has_perm(u32 ssid, u32 tsid,
-                              u16 tclass, u32 requested,
-                              struct common_audit_data *auditdata)
-{
-       return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0);
-}
+int avc_has_perm(u32 ssid, u32 tsid,
+                u16 tclass, u32 requested,
+                struct common_audit_data *auditdata);
 
 u32 avc_policy_seqno(void);
 
index 9896954..bea523a 100644 (file)
@@ -139,6 +139,18 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 static int snd_compr_free(struct inode *inode, struct file *f)
 {
        struct snd_compr_file *data = f->private_data;
+       struct snd_compr_runtime *runtime = data->stream.runtime;
+
+       switch (runtime->state) {
+       case SNDRV_PCM_STATE_RUNNING:
+       case SNDRV_PCM_STATE_DRAINING:
+       case SNDRV_PCM_STATE_PAUSED:
+               data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
+               break;
+       default:
+               break;
+       }
+
        data->stream.ops->free(&data->stream);
        kfree(data->stream.runtime->buffer);
        kfree(data->stream.runtime);
@@ -837,7 +849,8 @@ static int snd_compress_dev_disconnect(struct snd_device *device)
        struct snd_compr *compr;
 
        compr = device->device_data;
-       snd_unregister_device(compr->direction, compr->card, compr->device);
+       snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
+               compr->device);
        return 0;
 }
 
index 17f45e8..e1e9e0c 100644 (file)
@@ -49,6 +49,8 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
        struct snd_pcm *pcm;
 
        list_for_each_entry(pcm, &snd_pcm_devices, list) {
+               if (pcm->internal)
+                       continue;
                if (pcm->card == card && pcm->device == device)
                        return pcm;
        }
@@ -60,6 +62,8 @@ static int snd_pcm_next(struct snd_card *card, int device)
        struct snd_pcm *pcm;
 
        list_for_each_entry(pcm, &snd_pcm_devices, list) {
+               if (pcm->internal)
+                       continue;
                if (pcm->card == card && pcm->device > device)
                        return pcm->device;
                else if (pcm->card->number > card->number)
index 445ca48..bf578ba 100644 (file)
@@ -175,6 +175,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x54524106, 0xffffffff, "TR28026",           NULL,           NULL },
 { 0x54524108, 0xffffffff, "TR28028",           patch_tritech_tr28028,  NULL }, // added by xin jin [07/09/99]
 { 0x54524123, 0xffffffff, "TR28602",           NULL,           NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
+{ 0x54584e03, 0xffffffff, "TLV320AIC27",       NULL,           NULL },
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",       NULL,           NULL },
 { 0x56494161, 0xffffffff, "VIA1612A",          NULL,           NULL }, // modified ICE1232 with S/PDIF
 { 0x56494170, 0xffffffff, "VIA1617A",          patch_vt1617a,  NULL }, // modified VT1616 with S/PDIF
index 5b6c4e3..748c6a9 100644 (file)
@@ -4864,8 +4864,8 @@ static void hda_power_work(struct work_struct *work)
        spin_unlock(&codec->power_lock);
 
        state = hda_call_codec_suspend(codec, true);
-       codec->pm_down_notified = 0;
-       if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
+       if (!codec->pm_down_notified &&
+           !bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
                codec->pm_down_notified = 1;
                hda_call_pm_notify(bus, false);
        }
index ac41e9c..b7c89df 100644 (file)
@@ -3531,7 +3531,7 @@ static int create_capture_mixers(struct hda_codec *codec)
                if (!multi)
                        err = create_single_cap_vol_ctl(codec, n, vol, sw,
                                                        inv_dmic);
-               else if (!multi_cap_vol)
+               else if (!multi_cap_vol && !inv_dmic)
                        err = create_bind_cap_vol_ctl(codec, n, vol, sw);
                else
                        err = create_multi_cap_vol_ctl(codec);
@@ -4475,9 +4475,11 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
                                            true, &spec->vmaster_mute.sw_kctl);
                if (err < 0)
                        return err;
-               if (spec->vmaster_mute.hook)
+               if (spec->vmaster_mute.hook) {
                        snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
                                                 spec->vmaster_mute_enum);
+                       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+               }
        }
 
        free_kctls(spec); /* no longer needed */
index 0cbdd87..2aa2f57 100644 (file)
@@ -968,6 +968,15 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
        }
 }
 
+static void ad1884_fixup_thinkpad(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       struct ad198x_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               spec->gen.keep_eapd_on = 1;
+}
+
 /* set magic COEFs for dmic */
 static const struct hda_verb ad1884_dmic_init_verbs[] = {
        {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
@@ -979,6 +988,7 @@ enum {
        AD1884_FIXUP_AMP_OVERRIDE,
        AD1884_FIXUP_HP_EAPD,
        AD1884_FIXUP_DMIC_COEF,
+       AD1884_FIXUP_THINKPAD,
        AD1884_FIXUP_HP_TOUCHSMART,
 };
 
@@ -997,6 +1007,12 @@ static const struct hda_fixup ad1884_fixups[] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = ad1884_dmic_init_verbs,
        },
+       [AD1884_FIXUP_THINKPAD] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = ad1884_fixup_thinkpad,
+               .chained = true,
+               .chain_id = AD1884_FIXUP_DMIC_COEF,
+       },
        [AD1884_FIXUP_HP_TOUCHSMART] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = ad1884_dmic_init_verbs,
@@ -1008,7 +1024,7 @@ static const struct hda_fixup ad1884_fixups[] = {
 static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
-       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF),
+       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),
        {}
 };
 
index b524f89..18d9725 100644 (file)
@@ -111,6 +111,9 @@ enum {
 /* 0x0009 - 0x0014 -> 12 test regs */
 /* 0x0015 - visibility reg */
 
+/* Cirrus Logic CS4208 */
+#define CS4208_VENDOR_NID      0x24
+
 /*
  * Cirrus Logic CS4210
  *
@@ -223,6 +226,16 @@ static const struct hda_verb cs_coef_init_verbs[] = {
        {} /* terminator */
 };
 
+static const struct hda_verb cs4208_coef_init_verbs[] = {
+       {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+       {0x24, AC_VERB_SET_PROC_STATE, 0x01},  /* VPW: processing on */
+       {0x24, AC_VERB_SET_COEF_INDEX, 0x0033},
+       {0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */
+       {0x24, AC_VERB_SET_COEF_INDEX, 0x0034},
+       {0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */
+       {} /* terminator */
+};
+
 /* Errata: CS4207 rev C0/C1/C2 Silicon
  *
  * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
@@ -295,6 +308,8 @@ static int cs_init(struct hda_codec *codec)
                /* init_verb sequence for C0/C1/C2 errata*/
                snd_hda_sequence_write(codec, cs_errata_init_verbs);
                snd_hda_sequence_write(codec, cs_coef_init_verbs);
+       } else if (spec->vendor_nid == CS4208_VENDOR_NID) {
+               snd_hda_sequence_write(codec, cs4208_coef_init_verbs);
        }
 
        snd_hda_gen_init(codec);
@@ -434,6 +449,29 @@ static const struct hda_pintbl mba42_pincfgs[] = {
        {} /* terminator */
 };
 
+static const struct hda_pintbl mba6_pincfgs[] = {
+       { 0x10, 0x032120f0 }, /* HP */
+       { 0x11, 0x500000f0 },
+       { 0x12, 0x90100010 }, /* Speaker */
+       { 0x13, 0x500000f0 },
+       { 0x14, 0x500000f0 },
+       { 0x15, 0x770000f0 },
+       { 0x16, 0x770000f0 },
+       { 0x17, 0x430000f0 },
+       { 0x18, 0x43ab9030 }, /* Mic */
+       { 0x19, 0x770000f0 },
+       { 0x1a, 0x770000f0 },
+       { 0x1b, 0x770000f0 },
+       { 0x1c, 0x90a00090 },
+       { 0x1d, 0x500000f0 },
+       { 0x1e, 0x500000f0 },
+       { 0x1f, 0x500000f0 },
+       { 0x20, 0x500000f0 },
+       { 0x21, 0x430000f0 },
+       { 0x22, 0x430000f0 },
+       {} /* terminator */
+};
+
 static void cs420x_fixup_gpio_13(struct hda_codec *codec,
                                 const struct hda_fixup *fix, int action)
 {
@@ -556,22 +594,23 @@ static int patch_cs420x(struct hda_codec *codec)
 
 /*
  * CS4208 support:
- * Its layout is no longer compatible with CS4206/CS4207, and the generic
- * parser seems working fairly well, except for trivial fixups.
+ * Its layout is no longer compatible with CS4206/CS4207
  */
 enum {
+       CS4208_MBA6,
        CS4208_GPIO0,
 };
 
 static const struct hda_model_fixup cs4208_models[] = {
        { .id = CS4208_GPIO0, .name = "gpio0" },
+       { .id = CS4208_MBA6, .name = "mba6" },
        {}
 };
 
 static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
        /* codec SSID */
-       SND_PCI_QUIRK(0x106b, 0x7100, "MacBookPro 6,1", CS4208_GPIO0),
-       SND_PCI_QUIRK(0x106b, 0x7200, "MacBookPro 6,2", CS4208_GPIO0),
+       SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
+       SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
        {} /* terminator */
 };
 
@@ -588,18 +627,35 @@ static void cs4208_fixup_gpio0(struct hda_codec *codec,
 }
 
 static const struct hda_fixup cs4208_fixups[] = {
+       [CS4208_MBA6] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = mba6_pincfgs,
+               .chained = true,
+               .chain_id = CS4208_GPIO0,
+       },
        [CS4208_GPIO0] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cs4208_fixup_gpio0,
        },
 };
 
+/* correct the 0dB offset of input pins */
+static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
+{
+       unsigned int caps;
+
+       caps = query_amp_caps(codec, adc, HDA_INPUT);
+       caps &= ~(AC_AMPCAP_OFFSET);
+       caps |= 0x02;
+       snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
+}
+
 static int patch_cs4208(struct hda_codec *codec)
 {
        struct cs_spec *spec;
        int err;
 
-       spec = cs_alloc_spec(codec, 0); /* no specific w/a */
+       spec = cs_alloc_spec(codec, CS4208_VENDOR_NID);
        if (!spec)
                return -ENOMEM;
 
@@ -609,6 +665,12 @@ static int patch_cs4208(struct hda_codec *codec)
                           cs4208_fixups);
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
 
+       snd_hda_override_wcaps(codec, 0x18,
+                              get_wcaps(codec, 0x18) | AC_WCAP_STEREO);
+       cs4208_fix_amp_caps(codec, 0x18);
+       cs4208_fix_amp_caps(codec, 0x1b);
+       cs4208_fix_amp_caps(codec, 0x1c);
+
        err = cs_parse_auto_config(codec);
        if (err < 0)
                goto error;
index 4edd2d0..ec68eae 100644 (file)
@@ -3231,6 +3231,7 @@ enum {
        CXT_FIXUP_INC_MIC_BOOST,
        CXT_FIXUP_HEADPHONE_MIC_PIN,
        CXT_FIXUP_HEADPHONE_MIC,
+       CXT_FIXUP_GPIO1,
 };
 
 static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@@ -3375,6 +3376,15 @@ static const struct hda_fixup cxt_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cxt_fixup_headphone_mic,
        },
+       [CXT_FIXUP_GPIO1] = {
+               .type = HDA_FIXUP_VERBS,
+               .v.verbs = (const struct hda_verb[]) {
+                       { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+                       { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+                       { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 },
+                       { }
+               },
+       },
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3384,6 +3394,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
 
 static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+       SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1),
        SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
index 3d8cd04..50173d4 100644 (file)
@@ -936,6 +936,14 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                return;
        }
 
+       /*
+        * always configure channel mapping, it may have been changed by the
+        * user in the meantime
+        */
+       hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
+                                  channels, per_pin->chmap,
+                                  per_pin->chmap_set);
+
        /*
         * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
         * sizeof(*dp_ai) to avoid partial match/update problems when
@@ -947,20 +955,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                            "pin=%d channels=%d\n",
                            pin_nid,
                            channels);
-               hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                          channels, per_pin->chmap,
-                                          per_pin->chmap_set);
                hdmi_stop_infoframe_trans(codec, pin_nid);
                hdmi_fill_audio_infoframe(codec, pin_nid,
                                            ai.bytes, sizeof(ai));
                hdmi_start_infoframe_trans(codec, pin_nid);
-       } else {
-               /* For non-pcm audio switch, setup new channel mapping
-                * accordingly */
-               if (per_pin->non_pcm != non_pcm)
-                       hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                                  channels, per_pin->chmap,
-                                                  per_pin->chmap_set);
        }
 
        per_pin->non_pcm = non_pcm;
@@ -1149,32 +1147,43 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 }
 
 static void haswell_config_cvts(struct hda_codec *codec,
-                       int pin_id, int mux_id)
+                       hda_nid_t pin_nid, int mux_idx)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin;
-       int pin_idx, mux_idx;
-       int curr;
-       int err;
+       hda_nid_t nid, end_nid;
+       int cvt_idx, curr;
+       struct hdmi_spec_per_cvt *per_cvt;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               per_pin = get_pin(spec, pin_idx);
+       /* configure all pins, including "no physical connection" ones */
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               unsigned int wid_caps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wid_caps);
+
+               if (wid_type != AC_WID_PIN)
+                       continue;
 
-               if (pin_idx == pin_id)
+               if (nid == pin_nid)
                        continue;
 
-               curr = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+               curr = snd_hda_codec_read(codec, nid, 0,
                                          AC_VERB_GET_CONNECT_SEL, 0);
+               if (curr != mux_idx)
+                       continue;
 
-               /* Choose another unused converter */
-               if (curr == mux_id) {
-                       err = hdmi_choose_cvt(codec, pin_idx, NULL, &mux_idx);
-                       if (err < 0)
-                               return;
-                       snd_printdd("HDMI: choose converter %d for pin %d\n", mux_idx, pin_idx);
-                       snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+               /* choose an unassigned converter. The conveters in the
+                * connection list are in the same order as in the codec.
+                */
+               for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+                       per_cvt = get_cvt(spec, cvt_idx);
+                       if (!per_cvt->assigned) {
+                               snd_printdd("choose cvt %d for pin nid %d\n",
+                                       cvt_idx, nid);
+                               snd_hda_codec_write_cache(codec, nid, 0,
                                            AC_VERB_SET_CONNECT_SEL,
-                                           mux_idx);
+                                           cvt_idx);
+                               break;
+                       }
                }
        }
 }
@@ -1216,7 +1225,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 
        /* configure unused pins to choose other converters */
        if (is_haswell(codec))
-               haswell_config_cvts(codec, pin_idx, mux_idx);
+               haswell_config_cvts(codec, per_pin->pin_nid, mux_idx);
 
        snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
 
index bc07d36..8ad5543 100644 (file)
@@ -2819,6 +2819,15 @@ static void alc269_fixup_hweq(struct hda_codec *codec,
        alc_write_coef_idx(codec, 0x1e, coef | 0x80);
 }
 
+static void alc269_fixup_headset_mic(struct hda_codec *codec,
+                                      const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+}
+
 static void alc271_fixup_dmic(struct hda_codec *codec,
                              const struct hda_fixup *fix, int action)
 {
@@ -3439,6 +3448,9 @@ static void alc283_fixup_chromebook(struct hda_codec *codec,
                /* Set to manual mode */
                val = alc_read_coef_idx(codec, 0x06);
                alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+               /* Enable Line1 input control by verb */
+               val = alc_read_coef_idx(codec, 0x1a);
+               alc_write_coef_idx(codec, 0x1a, val | (1 << 4));
                break;
        }
 }
@@ -3493,6 +3505,15 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec,
        }
 }
 
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+                                      const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               /* Remove DAC node 0x03, as it seems to be
+                  giving mono output */
+               snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
 enum {
        ALC269_FIXUP_SONY_VAIO,
        ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -3504,6 +3525,7 @@ enum {
        ALC271_FIXUP_DMIC,
        ALC269_FIXUP_PCM_44K,
        ALC269_FIXUP_STEREO_DMIC,
+       ALC269_FIXUP_HEADSET_MIC,
        ALC269_FIXUP_QUANTA_MUTE,
        ALC269_FIXUP_LIFEBOOK,
        ALC269_FIXUP_AMIC,
@@ -3516,9 +3538,11 @@ enum {
        ALC269_FIXUP_HP_GPIO_LED,
        ALC269_FIXUP_INV_DMIC,
        ALC269_FIXUP_LENOVO_DOCK,
+       ALC286_FIXUP_SONY_MIC_NO_PRESENCE,
        ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
        ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
        ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+       ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
        ALC269_FIXUP_HEADSET_MODE,
        ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
        ALC269_FIXUP_ASUS_X101_FUNC,
@@ -3531,6 +3555,8 @@ enum {
        ALC269VB_FIXUP_ORDISSIMO_EVE2,
        ALC283_FIXUP_CHROME_BOOK,
        ALC282_FIXUP_ASUS_TX300,
+       ALC283_FIXUP_INT_MIC,
+       ALC290_FIXUP_MONO_SPEAKERS,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -3599,6 +3625,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_stereo_dmic,
        },
+       [ALC269_FIXUP_HEADSET_MIC] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc269_fixup_headset_mic,
+       },
        [ALC269_FIXUP_QUANTA_MUTE] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_quanta_mute,
@@ -3708,6 +3738,15 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
        },
+       [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+       },
        [ALC269_FIXUP_HEADSET_MODE] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_mode,
@@ -3716,6 +3755,15 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_mode_no_hp_mic,
        },
+       [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MIC
+       },
        [ALC269_FIXUP_ASUS_X101_FUNC] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_x101_headset_mic,
@@ -3790,6 +3838,22 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc282_fixup_asus_tx300,
        },
+       [ALC283_FIXUP_INT_MIC] = {
+               .type = HDA_FIXUP_VERBS,
+               .v.verbs = (const struct hda_verb[]) {
+                       {0x20, AC_VERB_SET_COEF_INDEX, 0x1a},
+                       {0x20, AC_VERB_SET_PROC_COEF, 0x0011},
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+       },
+       [ALC290_FIXUP_MONO_SPEAKERS] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc290_fixup_mono_speakers,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -3831,6 +3895,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS),
        SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -3853,6 +3918,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),
+       SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
        SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
        SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
@@ -3874,7 +3940,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
-       SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
        SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
@@ -3938,6 +4004,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
        {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
        {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
+       {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"},
        {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
        {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
        {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
@@ -4555,6 +4622,8 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+       SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4),
+       SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_ASUS_MODE4),
        SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
        SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
        SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
index 4f255df..f59a321 100644 (file)
@@ -4845,6 +4845,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
                        if ((err = hdsp_get_iobox_version(hdsp)) < 0)
                                return err;
                }
+               memset(&hdsp_version, 0, sizeof(hdsp_version));
                hdsp_version.io_type = hdsp->io_type;
                hdsp_version.firmware_rev = hdsp->firmware_rev;
                if ((err = copy_to_user(argp, &hdsp_version, sizeof(hdsp_version))))
index c02405c..5810a06 100644 (file)
@@ -88,6 +88,7 @@ static int bfin_i2s_hw_params(struct snd_pcm_substream *substream,
        case SNDRV_PCM_FORMAT_S8:
                param.spctl |= 0x70;
                sport->wdsize = 1;
+               break;
        case SNDRV_PCM_FORMAT_S16_LE:
                param.spctl |= 0xf0;
                sport->wdsize = 2;
index 8af0434..259d1ac 100644 (file)
@@ -349,6 +349,9 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
        val = ucontrol->value.integer.value[0];
        val2 = ucontrol->value.integer.value[1];
 
+       if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
+               return -EINVAL;
+
        err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
        if (err < 0)
                return err;
index b8ba0ad..80555d7 100644 (file)
@@ -1225,13 +1225,18 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
        struct device *dev = codec->dev;
        bool apply_fir, apply_iir;
-       int req, status;
+       unsigned int req;
+       int status;
 
        dev_dbg(dev, "%s: Enter.\n", __func__);
 
        mutex_lock(&drvdata->anc_lock);
 
        req = ucontrol->value.integer.value[0];
+       if (req >= ARRAY_SIZE(enum_anc_state)) {
+               status = -EINVAL;
+               goto cleanup;
+       }
        if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR &&
                req != ANC_APPLY_IIR) {
                dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n",
index 41cdd16..8dbcacd 100644 (file)
@@ -1863,7 +1863,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
        struct max98095_pdata *pdata = max98095->pdata;
        int channel = max98095_get_eq_channel(kcontrol->id.name);
        struct max98095_cdata *cdata;
-       int sel = ucontrol->value.integer.value[0];
+       unsigned int sel = ucontrol->value.integer.value[0];
        struct max98095_eq_cfg *coef_set;
        int fs, best, best_val, i;
        int regmask, regsave;
@@ -2016,7 +2016,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
        struct max98095_pdata *pdata = max98095->pdata;
        int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
        struct max98095_cdata *cdata;
-       int sel = ucontrol->value.integer.value[0];
+       unsigned int sel = ucontrol->value.integer.value[0];
        struct max98095_biquad_cfg *coef_set;
        int fs, best, best_val, i;
        int regmask, regsave;
index 651ce09..c91eba5 100644 (file)
@@ -270,7 +270,7 @@ MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
 static const struct regmap_config pcm1681_regmap = {
        .reg_bits               = 8,
        .val_bits               = 8,
-       .max_register           = ARRAY_SIZE(pcm1681_reg_defaults) + 1,
+       .max_register           = 0x13,
        .reg_defaults           = pcm1681_reg_defaults,
        .num_reg_defaults       = ARRAY_SIZE(pcm1681_reg_defaults),
        .writeable_reg          = pcm1681_writeable_reg,
index 2a8eccf..7613181 100644 (file)
@@ -188,7 +188,7 @@ MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
 static const struct regmap_config pcm1792a_regmap = {
        .reg_bits               = 8,
        .val_bits               = 8,
-       .max_register           = 24,
+       .max_register           = 23,
        .reg_defaults           = pcm1792a_reg_defaults,
        .num_reg_defaults       = ARRAY_SIZE(pcm1792a_reg_defaults),
        .writeable_reg          = pcm1792a_writeable_reg,
index 6e3f269..64ad84d 100644 (file)
@@ -674,6 +674,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        /* Left Input */
        {"Left Line1L Mux", "single-ended", "LINE1L"},
        {"Left Line1L Mux", "differential", "LINE1L"},
+       {"Left Line1R Mux", "single-ended", "LINE1R"},
+       {"Left Line1R Mux", "differential", "LINE1R"},
 
        {"Left Line2L Mux", "single-ended", "LINE2L"},
        {"Left Line2L Mux", "differential", "LINE2L"},
@@ -690,6 +692,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        /* Right Input */
        {"Right Line1R Mux", "single-ended", "LINE1R"},
        {"Right Line1R Mux", "differential", "LINE1R"},
+       {"Right Line1L Mux", "single-ended", "LINE1L"},
+       {"Right Line1L Mux", "differential", "LINE1L"},
 
        {"Right Line2R Mux", "single-ended", "LINE2R"},
        {"Right Line2R Mux", "differential", "LINE2R"},
index 8b50e59..01daf65 100644 (file)
@@ -530,6 +530,7 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
                                hubs->hp_startup_mode);
                        break;
                }
+               break;
 
        case SND_SOC_DAPM_PRE_PMD:
                snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
index c6b7439..6b81d0c 100644 (file)
@@ -936,7 +936,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        ssi_private->ssi_phys = res.start;
 
        ssi_private->irq = irq_of_parse_and_map(np, 0);
-       if (ssi_private->irq == NO_IRQ) {
+       if (ssi_private->irq == 0) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
                return -ENXIO;
        }
index a3d60d4..a2fd732 100644 (file)
@@ -112,7 +112,7 @@ static int imx_mc13783_probe(struct platform_device *pdev)
                return ret;
        }
 
-       if (machine_is_mx31_3ds()) {
+       if (machine_is_mx31_3ds() || machine_is_mx31moboard()) {
                imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
                        IMX_AUDMUX_V2_PTCR_SYN,
                        IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
index 46c5b4f..ca1be1d 100644 (file)
@@ -62,7 +62,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
        struct device_node *ssi_np, *codec_np;
        struct platform_device *ssi_pdev;
        struct i2c_client *codec_dev;
-       struct imx_sgtl5000_data *data;
+       struct imx_sgtl5000_data *data = NULL;
        int int_port, ext_port;
        int ret;
 
@@ -128,7 +128,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
+       data->codec_clk = clk_get(&codec_dev->dev, NULL);
        if (IS_ERR(data->codec_clk)) {
                ret = PTR_ERR(data->codec_clk);
                goto fail;
@@ -172,6 +172,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
        return 0;
 
 fail:
+       if (data && !IS_ERR(data->codec_clk))
+               clk_put(data->codec_clk);
        if (ssi_np)
                of_node_put(ssi_np);
        if (codec_np)
@@ -185,6 +187,7 @@ static int imx_sgtl5000_remove(struct platform_device *pdev)
        struct imx_sgtl5000_data *data = platform_get_drvdata(pdev);
 
        snd_soc_unregister_card(&data->card);
+       clk_put(data->codec_clk);
 
        return 0;
 }
index f58bcd8..57d6941 100644 (file)
@@ -600,19 +600,17 @@ static int imx_ssi_probe(struct platform_device *pdev)
        ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
        ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
 
-       ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
-       if (ret)
-               goto failed_pcm_fiq;
+       ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
+       ssi->dma_init = imx_pcm_dma_init(pdev);
 
-       ret = imx_pcm_dma_init(pdev);
-       if (ret)
-               goto failed_pcm_dma;
+       if (ssi->fiq_init && ssi->dma_init) {
+               ret = ssi->fiq_init;
+               goto failed_pcm;
+       }
 
        return 0;
 
-failed_pcm_dma:
-       imx_pcm_fiq_exit(pdev);
-failed_pcm_fiq:
+failed_pcm:
        snd_soc_unregister_component(&pdev->dev);
 failed_register:
        release_mem_region(res->start, resource_size(res));
@@ -628,8 +626,11 @@ static int imx_ssi_remove(struct platform_device *pdev)
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        struct imx_ssi *ssi = platform_get_drvdata(pdev);
 
-       imx_pcm_dma_exit(pdev);
-       imx_pcm_fiq_exit(pdev);
+       if (!ssi->dma_init)
+               imx_pcm_dma_exit(pdev);
+
+       if (!ssi->fiq_init)
+               imx_pcm_fiq_exit(pdev);
 
        snd_soc_unregister_component(&pdev->dev);
 
index fb1616b..560c40f 100644 (file)
@@ -211,6 +211,8 @@ struct imx_ssi {
        struct imx_dma_data filter_data_rx;
        struct imx_pcm_fiq_params fiq_params;
 
+       int fiq_init;
+       int dma_init;
        int enabled;
 };
 
index daa78a0..4a07f71 100644 (file)
@@ -1,6 +1,6 @@
 config SND_OMAP_SOC
        tristate "SoC Audio for the Texas Instruments OMAP chips"
-       depends on (ARCH_OMAP && DMA_OMAP) || (ARCH_ARM && COMPILE_TEST)
+       depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST)
        select SND_DMAENGINE_PCM
 
 config SND_OMAP_SOC_DMIC
@@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810
 
 config SND_OMAP_SOC_RX51
        tristate "SoC Audio support for Nokia RX-51"
-       depends on SND_OMAP_SOC && ARCH_ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
+       depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
        select SND_SOC_TPA6130A2
index 9cc6986..5dd87f4 100644 (file)
@@ -220,8 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv,
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
                               struct rsnd_mod *mod,
                               enum rsnd_reg reg);
-#define rsnd_is_gen1(s)                ((s)->info->flags & RSND_GEN1)
-#define rsnd_is_gen2(s)                ((s)->info->flags & RSND_GEN2)
+#define rsnd_is_gen1(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
+#define rsnd_is_gen2(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
 
 /*
  *     R-Car ADG
index 4d05613..1a38be0 100644 (file)
@@ -1380,7 +1380,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                                return -ENODEV;
 
                        list_add(&cpu_dai->dapm.list, &card->dapm_list);
-                       snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
                }
 
                if (cpu_dai->driver->probe) {
index c17c14c..b2949ae 100644 (file)
@@ -1949,7 +1949,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                w->active ? "active" : "inactive");
 
        list_for_each_entry(p, &w->sources, list_sink) {
-               if (p->connected && !p->connected(w, p->sink))
+               if (p->connected && !p->connected(w, p->source))
                        continue;
 
                if (p->connect)
@@ -3495,6 +3495,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                if (!w) {
                        dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
                                dai->driver->playback.stream_name);
+                       return -ENOMEM;
                }
 
                w->priv = dai;
@@ -3513,6 +3514,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                if (!w) {
                        dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
                                dai->driver->capture.stream_name);
+                       return -ENOMEM;
                }
 
                w->priv = dai;
index d0323a6..999550b 100644 (file)
@@ -262,7 +262,9 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
        }
 
        area->vm_ops = &usb_stream_hwdep_vm_ops;
-       area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+       area->vm_flags |= VM_DONTDUMP;
+       if (!read)
+               area->vm_flags |= VM_DONTEXPAND;
        area->vm_private_data = us122l;
        atomic_inc(&us122l->mmap_count);
 out:
index 63fb521..6234a51 100644 (file)
@@ -299,19 +299,6 @@ static void usX2Y_error_urb_status(struct usX2Ydev *usX2Y,
        usX2Y_clients_stop(usX2Y);
 }
 
-static void usX2Y_error_sequence(struct usX2Ydev *usX2Y,
-                                struct snd_usX2Y_substream *subs, struct urb *urb)
-{
-       snd_printk(KERN_ERR
-"Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
-"Most probably some urb of usb-frame %i is still missing.\n"
-"Cause could be too long delays in usb-hcd interrupt handling.\n",
-                  usb_get_current_frame_number(usX2Y->dev),
-                  subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
-                  usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame);
-       usX2Y_clients_stop(usX2Y);
-}
-
 static void i_usX2Y_urb_complete(struct urb *urb)
 {
        struct snd_usX2Y_substream *subs = urb->context;
@@ -328,12 +315,9 @@ static void i_usX2Y_urb_complete(struct urb *urb)
                usX2Y_error_urb_status(usX2Y, subs, urb);
                return;
        }
-       if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
-               subs->completed_urb = urb;
-       else {
-               usX2Y_error_sequence(usX2Y, subs, urb);
-               return;
-       }
+
+       subs->completed_urb = urb;
+
        {
                struct snd_usX2Y_substream *capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE],
                        *playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
index f2a1acd..814d0e8 100644 (file)
@@ -244,13 +244,8 @@ static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
                usX2Y_error_urb_status(usX2Y, subs, urb);
                return;
        }
-       if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
-               subs->completed_urb = urb;
-       else {
-               usX2Y_error_sequence(usX2Y, subs, urb);
-               return;
-       }
 
+       subs->completed_urb = urb;
        capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
        capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
        playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
index 099e7cd..7c43479 100644 (file)
@@ -5,7 +5,6 @@
 #include <stdbool.h>
 #include <sys/vfs.h>
 #include <sys/mount.h>
-#include <linux/magic.h>
 #include <linux/kernel.h>
 
 #include "debugfs.h"
index e297b74..ca0d3d9 100644 (file)
@@ -90,8 +90,20 @@ OPTIONS
        Number of mmap data pages. Must be a power of two.
 
 -g::
+       Enables call-graph (stack chain/backtrace) recording.
+
 --call-graph::
-       Do call-graph (stack chain/backtrace) recording.
+       Setup and enable call-graph (stack chain/backtrace) recording,
+       implies -g.
+
+       Allows specifying "fp" (frame pointer) or "dwarf"
+       (DWARF's CFI - Call Frame Information) as the method to collect
+       the information used to show the call graphs.
+
+       In some systems, where binaries are build with gcc
+       --fomit-frame-pointer, using the "fp" method will produce bogus
+       call graphs, using "dwarf", if available (perf tools linked to
+       the libunwind library) should be used instead.
 
 -q::
 --quiet::
index 58d6598..6a118e7 100644 (file)
@@ -140,20 +140,12 @@ Default is to monitor all CPUS.
 --asm-raw::
        Show raw instruction encoding of assembly instructions.
 
--G [type,min,order]::
+-G::
+       Enables call-graph (stack chain/backtrace) recording.
+
 --call-graph::
-        Display call chains using type, min percent threshold and order.
-       type can be either:
-       - flat: single column, linear exposure of call chains.
-       - graph: use a graph tree, displaying absolute overhead rates.
-       - fractal: like graph, but displays relative rates. Each branch of
-                the tree is considered as a new profiled object.
-
-       order can be either:
-       - callee: callee based call graph.
-       - caller: inverted caller based call graph.
-
-       Default: fractal,0.5,callee.
+       Setup and enable call-graph (stack chain/backtrace) recording,
+       implies -G.
 
 --ignore-callees=<regex>::
         Ignore callees of the function(s) matching the given regex.
index 3a0ff7f..64c043b 100644 (file)
@@ -770,6 +770,7 @@ check: $(OUTPUT)common-cmds.h
 install-bin: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
        $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
 ifndef NO_LIBPERL
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
index 9570c2b..b2519e4 100644 (file)
@@ -32,7 +32,7 @@ u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
 int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
                             struct perf_tsc_conversion *tc)
 {
-       bool cap_usr_time_zero;
+       bool cap_user_time_zero;
        u32 seq;
        int i = 0;
 
@@ -42,7 +42,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
                tc->time_mult = pc->time_mult;
                tc->time_shift = pc->time_shift;
                tc->time_zero = pc->time_zero;
-               cap_usr_time_zero = pc->cap_usr_time_zero;
+               cap_user_time_zero = pc->cap_user_time_zero;
                rmb();
                if (pc->lock == seq && !(seq & 1))
                        break;
@@ -52,7 +52,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
                }
        }
 
-       if (!cap_usr_time_zero)
+       if (!cap_user_time_zero)
                return -EOPNOTSUPP;
 
        return 0;
index 423875c..afe377b 100644 (file)
@@ -321,8 +321,6 @@ found:
        return perf_event__repipe(tool, event_sw, &sample_sw, machine);
 }
 
-extern volatile int session_done;
-
 static void sig_handler(int sig __maybe_unused)
 {
        session_done = 1;
index c2dff9c..9b5f077 100644 (file)
@@ -101,7 +101,7 @@ static int setup_cpunode_map(void)
 
        dir1 = opendir(PATH_SYS_NODE);
        if (!dir1)
-               return -1;
+               return 0;
 
        while ((dent1 = readdir(dir1)) != NULL) {
                if (dent1->d_type != DT_DIR ||
index 935d522..fbc2888 100644 (file)
@@ -888,11 +888,18 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
        while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) {
                err = perf_evlist__parse_sample(kvm->evlist, event, &sample);
                if (err) {
+                       perf_evlist__mmap_consume(kvm->evlist, idx);
                        pr_err("Failed to parse sample\n");
                        return -1;
                }
 
                err = perf_session_queue_event(kvm->session, event, &sample, 0);
+               /*
+                * FIXME: Here we can't consume the event, as perf_session_queue_event will
+                *        point to it, and it'll get possibly overwritten by the kernel.
+                */
+               perf_evlist__mmap_consume(kvm->evlist, idx);
+
                if (err) {
                        pr_err("Failed to enqueue sample: %d\n", err);
                        return -1;
index a41ac41..d046514 100644 (file)
@@ -712,21 +712,12 @@ static int get_stack_size(char *str, unsigned long *_size)
 }
 #endif /* LIBUNWIND_SUPPORT */
 
-int record_parse_callchain_opt(const struct option *opt,
-                              const char *arg, int unset)
+int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
 {
-       struct perf_record_opts *opts = opt->value;
        char *tok, *name, *saveptr = NULL;
        char *buf;
        int ret = -1;
 
-       /* --no-call-graph */
-       if (unset)
-               return 0;
-
-       /* We specified default option if none is provided. */
-       BUG_ON(!arg);
-
        /* We need buffer that we know we can write to. */
        buf = malloc(strlen(arg) + 1);
        if (!buf)
@@ -764,13 +755,9 @@ int record_parse_callchain_opt(const struct option *opt,
                                ret = get_stack_size(tok, &size);
                                opts->stack_dump_size = size;
                        }
-
-                       if (!ret)
-                               pr_debug("callchain: stack dump size %d\n",
-                                        opts->stack_dump_size);
 #endif /* LIBUNWIND_SUPPORT */
                } else {
-                       pr_err("callchain: Unknown -g option "
+                       pr_err("callchain: Unknown --call-graph option "
                               "value: %s\n", arg);
                        break;
                }
@@ -778,13 +765,52 @@ int record_parse_callchain_opt(const struct option *opt,
        } while (0);
 
        free(buf);
+       return ret;
+}
+
+static void callchain_debug(struct perf_record_opts *opts)
+{
+       pr_debug("callchain: type %d\n", opts->call_graph);
 
+       if (opts->call_graph == CALLCHAIN_DWARF)
+               pr_debug("callchain: stack dump size %d\n",
+                        opts->stack_dump_size);
+}
+
+int record_parse_callchain_opt(const struct option *opt,
+                              const char *arg,
+                              int unset)
+{
+       struct perf_record_opts *opts = opt->value;
+       int ret;
+
+       /* --no-call-graph */
+       if (unset) {
+               opts->call_graph = CALLCHAIN_NONE;
+               pr_debug("callchain: disabled\n");
+               return 0;
+       }
+
+       ret = record_parse_callchain(arg, opts);
        if (!ret)
-               pr_debug("callchain: type %d\n", opts->call_graph);
+               callchain_debug(opts);
 
        return ret;
 }
 
+int record_callchain_opt(const struct option *opt,
+                        const char *arg __maybe_unused,
+                        int unset __maybe_unused)
+{
+       struct perf_record_opts *opts = opt->value;
+
+       if (opts->call_graph == CALLCHAIN_NONE)
+               opts->call_graph = CALLCHAIN_FP;
+
+       callchain_debug(opts);
+       return 0;
+}
+
 static const char * const record_usage[] = {
        "perf record [<options>] [<command>]",
        "perf record [<options>] -- <command> [<options>]",
@@ -813,12 +839,12 @@ static struct perf_record record = {
        },
 };
 
-#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
+#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
 
 #ifdef LIBUNWIND_SUPPORT
-const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
+const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";
 #else
-const char record_callchain_help[] = CALLCHAIN_HELP "[fp]";
+const char record_callchain_help[] = CALLCHAIN_HELP "fp";
 #endif
 
 /*
@@ -858,9 +884,12 @@ const struct option record_options[] = {
                     "number of mmap data pages"),
        OPT_BOOLEAN(0, "group", &record.opts.group,
                    "put the counters into a counter group"),
-       OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts,
-                            "mode[,dump_size]", record_callchain_help,
-                            &record_parse_callchain_opt, "fp"),
+       OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
+                          NULL, "enables call-graph recording" ,
+                          &record_callchain_opt),
+       OPT_CALLBACK(0, "call-graph", &record.opts,
+                    "mode[,dump_size]", record_callchain_help,
+                    &record_parse_callchain_opt),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
        OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
index 8e50d8d..72eae74 100644 (file)
@@ -401,8 +401,6 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
        return 0;
 }
 
-extern volatile int session_done;
-
 static void sig_handler(int sig __maybe_unused)
 {
        session_done = 1;
@@ -568,6 +566,9 @@ static int __cmd_report(struct perf_report *rep)
                }
        }
 
+       if (session_done())
+               return 0;
+
        if (nr_samples == 0) {
                ui__error("The %s file has no samples!\n", session->filename);
                return 0;
index 7f31a3d..9c333ff 100644 (file)
@@ -553,8 +553,6 @@ static struct perf_tool perf_script = {
        .ordering_requires_timestamps = true,
 };
 
-extern volatile int session_done;
-
 static void sig_handler(int sig __maybe_unused)
 {
        session_done = 1;
index f686d5f..5098f14 100644 (file)
@@ -457,6 +457,7 @@ static int __run_perf_stat(int argc, const char **argv)
                        perror("failed to prepare workload");
                        return -1;
                }
+               child_pid = evsel_list->workload.pid;
        }
 
        if (group)
index 2122141..5a11f13 100644 (file)
@@ -810,7 +810,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                ret = perf_evlist__parse_sample(top->evlist, event, &sample);
                if (ret) {
                        pr_err("Can't parse sample, err = %d\n", ret);
-                       continue;
+                       goto next_event;
                }
 
                evsel = perf_evlist__id2evsel(session->evlist, sample.id);
@@ -825,13 +825,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                case PERF_RECORD_MISC_USER:
                        ++top->us_samples;
                        if (top->hide_user_symbols)
-                               continue;
+                               goto next_event;
                        machine = &session->machines.host;
                        break;
                case PERF_RECORD_MISC_KERNEL:
                        ++top->kernel_samples;
                        if (top->hide_kernel_symbols)
-                               continue;
+                               goto next_event;
                        machine = &session->machines.host;
                        break;
                case PERF_RECORD_MISC_GUEST_KERNEL:
@@ -847,7 +847,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                         */
                        /* Fall thru */
                default:
-                       continue;
+                       goto next_event;
                }
 
 
@@ -859,6 +859,8 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
                        machine__process_event(machine, event);
                } else
                        ++session->stats.nr_unknown_events;
+next_event:
+               perf_evlist__mmap_consume(top->evlist, idx);
        }
 }
 
@@ -1016,16 +1018,16 @@ out_delete:
 }
 
 static int
-parse_callchain_opt(const struct option *opt, const char *arg, int unset)
+callchain_opt(const struct option *opt, const char *arg, int unset)
 {
-       /*
-        * --no-call-graph
-        */
-       if (unset)
-               return 0;
-
        symbol_conf.use_callchain = true;
+       return record_callchain_opt(opt, arg, unset);
+}
 
+static int
+parse_callchain_opt(const struct option *opt, const char *arg, int unset)
+{
+       symbol_conf.use_callchain = true;
        return record_parse_callchain_opt(opt, arg, unset);
 }
 
@@ -1106,9 +1108,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                   "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"),
        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
                    "Show a column with the number of samples"),
-       OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,
-                            "mode[,dump_size]", record_callchain_help,
-                            &parse_callchain_opt, "fp"),
+       OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts,
+                          NULL, "enables call-graph recording",
+                          &callchain_opt),
+       OPT_CALLBACK(0, "call-graph", &top.record_opts,
+                    "mode[,dump_size]", record_callchain_help,
+                    &parse_callchain_opt),
        OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
                   "ignore callees of these functions in call graphs",
                   report_parse_ignore_callees_opt),
index f5aa637..99c8d9a 100644 (file)
 #include <sys/mman.h>
 #include <linux/futex.h>
 
+/* For older distros: */
+#ifndef MAP_STACK
+# define MAP_STACK             0x20000
+#endif
+
+#ifndef MADV_HWPOISON
+# define MADV_HWPOISON         100
+#endif
+
+#ifndef MADV_MERGEABLE
+# define MADV_MERGEABLE                12
+#endif
+
+#ifndef MADV_UNMERGEABLE
+# define MADV_UNMERGEABLE      13
+#endif
+
 static size_t syscall_arg__scnprintf_hex(char *bf, size_t size,
                                         unsigned long arg,
                                         u8 arg_idx __maybe_unused,
@@ -970,7 +987,7 @@ again:
                        err = perf_evlist__parse_sample(evlist, event, &sample);
                        if (err) {
                                fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err);
-                               continue;
+                               goto next_event;
                        }
 
                        if (trace->base_time == 0)
@@ -984,18 +1001,20 @@ again:
                        evsel = perf_evlist__id2evsel(evlist, sample.id);
                        if (evsel == NULL) {
                                fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
-                               continue;
+                               goto next_event;
                        }
 
                        if (sample.raw_data == NULL) {
                                fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
                                       perf_evsel__name(evsel), sample.tid,
                                       sample.cpu, sample.raw_size);
-                               continue;
+                               goto next_event;
                        }
 
                        handler = evsel->handler.func;
                        handler(trace, evsel, &sample);
+next_event:
+                       perf_evlist__mmap_consume(evlist, i);
 
                        if (done)
                                goto out_unmap_evlist;
@@ -1038,6 +1057,7 @@ static int trace__replay(struct trace *trace)
 
        trace->tool.sample        = trace__process_sample;
        trace->tool.mmap          = perf_event__process_mmap;
+       trace->tool.mmap2         = perf_event__process_mmap2;
        trace->tool.comm          = perf_event__process_comm;
        trace->tool.exit          = perf_event__process_exit;
        trace->tool.fork          = perf_event__process_fork;
index 214e17e..5f6f9b3 100644 (file)
@@ -87,7 +87,7 @@ CFLAGS += -Wall
 CFLAGS += -Wextra
 CFLAGS += -std=gnu99
 
-EXTLIBS = -lelf -lpthread -lrt -lm
+EXTLIBS = -lelf -lpthread -lrt -lm -ldl
 
 ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y)
   CFLAGS += -fstack-protector-all
@@ -180,6 +180,9 @@ FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS)
 ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y)
   CFLAGS += -DLIBELF_MMAP
 endif
+ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM),y)
+  CFLAGS += -DHAVE_ELF_GETPHDRNUM
+endif
 
 # include ARCH specific config
 -include $(src-perf)/arch/$(ARCH)/Makefile
index 708fb8e..f793057 100644 (file)
@@ -61,6 +61,15 @@ int main(void)
 }
 endef
 
+define SOURCE_ELF_GETPHDRNUM
+#include <libelf.h>
+int main(void)
+{
+       size_t dst;
+       return elf_getphdrnum(0, &dst);
+}
+endef
+
 ifndef NO_SLANG
 define SOURCE_SLANG
 #include <slang.h>
@@ -210,6 +219,7 @@ define SOURCE_LIBAUDIT
 
 int main(void)
 {
+       printf(\"error message: %s\", audit_errno_to_name(0));
        return audit_open();
 }
 endef
index 6fb781d..e3fedfa 100644 (file)
@@ -290,6 +290,7 @@ static int process_events(struct machine *machine, struct perf_evlist *evlist,
        for (i = 0; i < evlist->nr_mmaps; i++) {
                while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
                        ret = process_event(machine, evlist, event, state);
+                       perf_evlist__mmap_consume(evlist, i);
                        if (ret < 0)
                                return ret;
                }
index d444ea2..376c356 100644 (file)
@@ -36,6 +36,7 @@ static int find_comm(struct perf_evlist *evlist, const char *comm)
                            (pid_t)event->comm.tid == getpid() &&
                            strcmp(event->comm.comm, comm) == 0)
                                found += 1;
+                       perf_evlist__mmap_consume(evlist, i);
                }
        }
        return found;
index c4185b9..a7232c2 100644 (file)
@@ -122,6 +122,7 @@ int test__basic_mmap(void)
                        goto out_munmap;
                }
                nr_events[evsel->idx]++;
+               perf_evlist__mmap_consume(evlist, 0);
        }
 
        err = 0;
index fc5b9fc..524b221 100644 (file)
@@ -77,8 +77,10 @@ int test__syscall_open_tp_fields(void)
 
                                ++nr_events;
 
-                               if (type != PERF_RECORD_SAMPLE)
+                               if (type != PERF_RECORD_SAMPLE) {
+                                       perf_evlist__mmap_consume(evlist, i);
                                        continue;
+                               }
 
                                err = perf_evsel__parse_sample(evsel, event, &sample);
                                if (err) {
index b8a7056..7923b06 100644 (file)
@@ -263,6 +263,8 @@ int test__PERF_RECORD(void)
                                                 type);
                                        ++errs;
                                }
+
+                               perf_evlist__mmap_consume(evlist, i);
                        }
                }
 
index 0ab61b1..4ca1b93 100644 (file)
@@ -122,7 +122,7 @@ int test__perf_time_to_tsc(void)
                        if (event->header.type != PERF_RECORD_COMM ||
                            (pid_t)event->comm.pid != getpid() ||
                            (pid_t)event->comm.tid != getpid())
-                               continue;
+                               goto next_event;
 
                        if (strcmp(event->comm.comm, comm1) == 0) {
                                CHECK__(perf_evsel__parse_sample(evsel, event,
@@ -134,6 +134,8 @@ int test__perf_time_to_tsc(void)
                                                                 &sample));
                                comm2_time = sample.time;
                        }
+next_event:
+                       perf_evlist__mmap_consume(evlist, i);
                }
        }
 
index 2e41e2d..6e2b44e 100644 (file)
@@ -78,7 +78,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
                struct perf_sample sample;
 
                if (event->header.type != PERF_RECORD_SAMPLE)
-                       continue;
+                       goto next_event;
 
                err = perf_evlist__parse_sample(evlist, event, &sample);
                if (err < 0) {
@@ -88,6 +88,8 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
 
                total_periods += sample.period;
                nr_samples++;
+next_event:
+               perf_evlist__mmap_consume(evlist, 0);
        }
 
        if ((u64) nr_samples == total_periods) {
index 28fe589..a3e6487 100644 (file)
@@ -96,10 +96,10 @@ int test__task_exit(void)
 
 retry:
        while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
-               if (event->header.type != PERF_RECORD_EXIT)
-                       continue;
+               if (event->header.type == PERF_RECORD_EXIT)
+                       nr_exit++;
 
-               nr_exit++;
+               perf_evlist__mmap_consume(evlist, 0);
        }
 
        if (!exited || !nr_exit) {
index 194e2f4..6c15268 100644 (file)
@@ -315,8 +315,7 @@ static inline void advance_hpp(struct perf_hpp *hpp, int inc)
 }
 
 static int hist_entry__period_snprintf(struct perf_hpp *hpp,
-                                      struct hist_entry *he,
-                                      bool color)
+                                      struct hist_entry *he)
 {
        const char *sep = symbol_conf.field_sep;
        struct perf_hpp_fmt *fmt;
@@ -338,7 +337,7 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,
                } else
                        first = false;
 
-               if (color && fmt->color)
+               if (perf_hpp__use_color() && fmt->color)
                        ret = fmt->color(fmt, hpp, he);
                else
                        ret = fmt->entry(fmt, hpp, he);
@@ -358,12 +357,11 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
                .buf            = bf,
                .size           = size,
        };
-       bool color = !symbol_conf.field_sep;
 
        if (size == 0 || size > bfsz)
                size = hpp.size = bfsz;
 
-       ret = hist_entry__period_snprintf(&hpp, he, color);
+       ret = hist_entry__period_snprintf(&hpp, he);
        hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
 
        ret = fprintf(fp, "%s\n", bf);
@@ -482,6 +480,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
 
 print_entries:
        linesz = hists__sort_list_width(hists) + 3 + 1;
+       linesz += perf_hpp__color_overhead();
        line = malloc(linesz);
        if (line == NULL) {
                ret = -1;
index bfc5a27..7eae548 100644 (file)
@@ -809,7 +809,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
                    end = map__rip_2objdump(map, sym->end);
 
                offset = line_ip - start;
-               if (offset < 0 || (u64)line_ip > end)
+               if ((u64)line_ip < start || (u64)line_ip > end)
                        offset = -1;
                else
                        parsed_line = tmp2 + 1;
index 2b585bc..9e99060 100644 (file)
@@ -147,6 +147,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
 
 struct option;
 
+int record_parse_callchain(const char *arg, struct perf_record_opts *opts);
 int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
+int record_callchain_opt(const struct option *opt, const char *arg, int unset);
+
 extern const char record_callchain_help[];
 #endif /* __PERF_CALLCHAIN_H */
index 3e5f543..7defd77 100644 (file)
@@ -262,6 +262,21 @@ bool die_is_signed_type(Dwarf_Die *tp_die)
                ret == DW_ATE_signed_fixed);
 }
 
+/**
+ * die_is_func_def - Ensure that this DIE is a subprogram and definition
+ * @dw_die: a DIE
+ *
+ * Ensure that this DIE is a subprogram and NOT a declaration. This
+ * returns true if @dw_die is a function definition.
+ **/
+bool die_is_func_def(Dwarf_Die *dw_die)
+{
+       Dwarf_Attribute attr;
+
+       return (dwarf_tag(dw_die) == DW_TAG_subprogram &&
+               dwarf_attr(dw_die, DW_AT_declaration, &attr) == NULL);
+}
+
 /**
  * die_get_data_member_location - Get the data-member offset
  * @mb_die: a DIE of a member of a data structure
@@ -392,6 +407,10 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
 {
        struct __addr_die_search_param *ad = data;
 
+       /*
+        * Since a declaration entry doesn't has given pc, this always returns
+        * function definition entry.
+        */
        if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
            dwarf_haspc(fn_die, ad->addr)) {
                memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
@@ -407,7 +426,7 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
  * @die_mem: a buffer for result DIE
  *
  * Search a non-inlined function DIE which includes @addr. Stores the
- * DIE to @die_mem and returns it if found. Returns NULl if failed.
+ * DIE to @die_mem and returns it if found. Returns NULL if failed.
  */
 Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
                                    Dwarf_Die *die_mem)
@@ -434,16 +453,33 @@ static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
        return DIE_FIND_CB_CONTINUE;
 }
 
+/**
+ * die_find_top_inlinefunc - Search the top inlined function at given address
+ * @sp_die: a subprogram DIE which including @addr
+ * @addr: target address
+ * @die_mem: a buffer for result DIE
+ *
+ * Search an inlined function DIE which includes @addr. Stores the
+ * DIE to @die_mem and returns it if found. Returns NULL if failed.
+ * Even if several inlined functions are expanded recursively, this
+ * doesn't trace it down, and returns the topmost one.
+ */
+Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
+                                  Dwarf_Die *die_mem)
+{
+       return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
+}
+
 /**
  * die_find_inlinefunc - Search an inlined function at given address
- * @cu_die: a CU DIE which including @addr
+ * @sp_die: a subprogram DIE which including @addr
  * @addr: target address
  * @die_mem: a buffer for result DIE
  *
  * Search an inlined function DIE which includes @addr. Stores the
- * DIE to @die_mem and returns it if found. Returns NULl if failed.
+ * DIE to @die_mem and returns it if found. Returns NULL if failed.
  * If several inlined functions are expanded recursively, this trace
- * it and returns deepest one.
+ * it down and returns deepest one.
  */
 Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
                               Dwarf_Die *die_mem)
index 6ce1717..b4fe90c 100644 (file)
@@ -38,6 +38,9 @@ extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
 extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
                        int (*callback)(Dwarf_Die *, void *), void *data);
 
+/* Ensure that this DIE is a subprogram and definition (not declaration) */
+extern bool die_is_func_def(Dwarf_Die *dw_die);
+
 /* Compare diename and tname */
 extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
 
@@ -76,7 +79,11 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
 extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
                                    Dwarf_Die *die_mem);
 
-/* Search an inlined function including given address */
+/* Search the top inlined function including given address */
+extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
+                                         Dwarf_Die *die_mem);
+
+/* Search the deepest inlined function including given address */
 extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
                                      Dwarf_Die *die_mem);
 
index 9b393e7..49096ea 100644 (file)
@@ -187,7 +187,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                return -1;
        }
 
-       event->header.type = PERF_RECORD_MMAP2;
+       event->header.type = PERF_RECORD_MMAP;
        /*
         * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
         */
@@ -198,7 +198,6 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                char prot[5];
                char execname[PATH_MAX];
                char anonstr[] = "//anon";
-               unsigned int ino;
                size_t size;
                ssize_t n;
 
@@ -209,15 +208,12 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                strcpy(execname, "");
 
                /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
-               n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n",
-                      &event->mmap2.start, &event->mmap2.len, prot,
-                      &event->mmap2.pgoff, &event->mmap2.maj,
-                      &event->mmap2.min,
-                      &ino, execname);
-
-               event->mmap2.ino = (u64)ino;
+               n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n",
+                      &event->mmap.start, &event->mmap.len, prot,
+                      &event->mmap.pgoff,
+                      execname);
 
-               if (n != 8)
+               if (n != 5)
                        continue;
 
                if (prot[2] != 'x')
@@ -227,15 +223,15 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                        strcpy(execname, anonstr);
 
                size = strlen(execname) + 1;
-               memcpy(event->mmap2.filename, execname, size);
+               memcpy(event->mmap.filename, execname, size);
                size = PERF_ALIGN(size, sizeof(u64));
-               event->mmap2.len -= event->mmap.start;
-               event->mmap2.header.size = (sizeof(event->mmap2) -
-                                       (sizeof(event->mmap2.filename) - size));
-               memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
-               event->mmap2.header.size += machine->id_hdr_size;
-               event->mmap2.pid = tgid;
-               event->mmap2.tid = pid;
+               event->mmap.len -= event->mmap.start;
+               event->mmap.header.size = (sizeof(event->mmap) -
+                                       (sizeof(event->mmap.filename) - size));
+               memset(event->mmap.filename + size, 0, machine->id_hdr_size);
+               event->mmap.header.size += machine->id_hdr_size;
+               event->mmap.pid = tgid;
+               event->mmap.tid = pid;
 
                if (process(tool, event, &synth_sample, machine) != 0) {
                        rc = -1;
index f9f77be..e584cd3 100644 (file)
@@ -545,12 +545,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
 
        md->prev = old;
 
-       if (!evlist->overwrite)
-               perf_mmap__write_tail(md, old);
-
        return event;
 }
 
+void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
+{
+       if (!evlist->overwrite) {
+               struct perf_mmap *md = &evlist->mmap[idx];
+               unsigned int old = md->prev;
+
+               perf_mmap__write_tail(md, old);
+       }
+}
+
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
 {
        if (evlist->mmap[idx].base != NULL) {
index 880d713..206d093 100644 (file)
@@ -89,6 +89,8 @@ struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);
 
 union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
 
+void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);
+
 int perf_evlist__open(struct perf_evlist *evlist);
 void perf_evlist__close(struct perf_evlist *evlist);
 
index 0ce9feb..9f1ef9b 100644 (file)
@@ -678,7 +678,6 @@ void perf_evsel__config(struct perf_evsel *evsel,
                attr->sample_type       |= PERF_SAMPLE_WEIGHT;
 
        attr->mmap  = track;
-       attr->mmap2 = track && !perf_missing_features.mmap2;
        attr->comm  = track;
 
        /*
index 26441d0..c3e5a3b 100644 (file)
@@ -199,9 +199,11 @@ static int write_buildid(char *name, size_t name_len, u8 *build_id,
        return write_padded(fd, name, name_len + 1, len);
 }
 
-static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
-                               u16 misc, int fd)
+static int __dsos__write_buildid_table(struct list_head *head,
+                                      struct machine *machine,
+                                      pid_t pid, u16 misc, int fd)
 {
+       char nm[PATH_MAX];
        struct dso *pos;
 
        dsos__for_each_with_build_id(pos, head) {
@@ -215,6 +217,10 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
                if (is_vdso_map(pos->short_name)) {
                        name = (char *) VDSO__MAP_NAME;
                        name_len = sizeof(VDSO__MAP_NAME) + 1;
+               } else if (dso__is_kcore(pos)) {
+                       machine__mmap_name(machine, nm, sizeof(nm));
+                       name = nm;
+                       name_len = strlen(nm) + 1;
                } else {
                        name = pos->long_name;
                        name_len = pos->long_name_len + 1;
@@ -240,10 +246,10 @@ static int machine__write_buildid_table(struct machine *machine, int fd)
                umisc = PERF_RECORD_MISC_GUEST_USER;
        }
 
-       err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid,
-                                         kmisc, fd);
+       err = __dsos__write_buildid_table(&machine->kernel_dsos, machine,
+                                         machine->pid, kmisc, fd);
        if (err == 0)
-               err = __dsos__write_buildid_table(&machine->user_dsos,
+               err = __dsos__write_buildid_table(&machine->user_dsos, machine,
                                                  machine->pid, umisc, fd);
        return err;
 }
@@ -375,23 +381,31 @@ out_free:
        return err;
 }
 
-static int dso__cache_build_id(struct dso *dso, const char *debugdir)
+static int dso__cache_build_id(struct dso *dso, struct machine *machine,
+                              const char *debugdir)
 {
        bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
        bool is_vdso = is_vdso_map(dso->short_name);
+       char *name = dso->long_name;
+       char nm[PATH_MAX];
 
-       return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id),
-                                    dso->long_name, debugdir,
-                                    is_kallsyms, is_vdso);
+       if (dso__is_kcore(dso)) {
+               is_kallsyms = true;
+               machine__mmap_name(machine, nm, sizeof(nm));
+               name = nm;
+       }
+       return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
+                                    debugdir, is_kallsyms, is_vdso);
 }
 
-static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
+static int __dsos__cache_build_ids(struct list_head *head,
+                                  struct machine *machine, const char *debugdir)
 {
        struct dso *pos;
        int err = 0;
 
        dsos__for_each_with_build_id(pos, head)
-               if (dso__cache_build_id(pos, debugdir))
+               if (dso__cache_build_id(pos, machine, debugdir))
                        err = -1;
 
        return err;
@@ -399,8 +413,9 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
 
 static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
 {
-       int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir);
-       ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir);
+       int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine,
+                                         debugdir);
+       ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir);
        return ret;
 }
 
@@ -2753,6 +2768,18 @@ int perf_session__read_header(struct perf_session *session)
        if (perf_file_header__read(&f_header, header, fd) < 0)
                return -EINVAL;
 
+       /*
+        * Sanity check that perf.data was written cleanly; data size is
+        * initialized to 0 and updated only if the on_exit function is run.
+        * If data size is still 0 then the file contains only partial
+        * information.  Just warn user and process it as much as it can.
+        */
+       if (f_header.data.size == 0) {
+               pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n"
+                          "Was the 'perf record' command properly terminated?\n",
+                          session->filename);
+       }
+
        nr_attrs = f_header.attrs.size / f_header.attr_size;
        lseek(fd, f_header.attrs.offset, SEEK_SET);
 
index 46a0d35..9ff6cf3 100644 (file)
@@ -611,6 +611,8 @@ void hists__collapse_resort(struct hists *hists)
        next = rb_first(root);
 
        while (next) {
+               if (session_done())
+                       break;
                n = rb_entry(next, struct hist_entry, rb_node_in);
                next = rb_next(&n->rb_node_in);
 
index 1329b6b..ce8dc61 100644 (file)
@@ -5,6 +5,7 @@
 #include <pthread.h>
 #include "callchain.h"
 #include "header.h"
+#include "color.h"
 
 extern struct callchain_param callchain_param;
 
@@ -175,6 +176,18 @@ void perf_hpp__init(void);
 void perf_hpp__column_register(struct perf_hpp_fmt *format);
 void perf_hpp__column_enable(unsigned col);
 
+static inline size_t perf_hpp__use_color(void)
+{
+       return !symbol_conf.field_sep;
+}
+
+static inline size_t perf_hpp__color_overhead(void)
+{
+       return perf_hpp__use_color() ?
+              (COLOR_MAXLEN + sizeof(PERF_COLOR_RESET)) * PERF_HPP__MAX_INDEX
+              : 0;
+}
+
 struct perf_evlist;
 
 struct hist_browser_timer {
index 933d14f..6188d28 100644 (file)
@@ -792,7 +792,7 @@ static int machine__create_modules(struct machine *machine)
                modules = path;
        }
 
-       if (symbol__restricted_filename(path, "/proc/modules"))
+       if (symbol__restricted_filename(modules, "/proc/modules"))
                return -1;
 
        file = fopen(modules, "r");
index be03293..f069273 100644 (file)
@@ -118,7 +118,6 @@ static const Dwfl_Callbacks offline_callbacks = {
 static int debuginfo__init_offline_dwarf(struct debuginfo *self,
                                         const char *path)
 {
-       Dwfl_Module *mod;
        int fd;
 
        fd = open(path, O_RDONLY);
@@ -129,11 +128,11 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self,
        if (!self->dwfl)
                goto error;
 
-       mod = dwfl_report_offline(self->dwfl, "", "", fd);
-       if (!mod)
+       self->mod = dwfl_report_offline(self->dwfl, "", "", fd);
+       if (!self->mod)
                goto error;
 
-       self->dbg = dwfl_module_getdwarf(mod, &self->bias);
+       self->dbg = dwfl_module_getdwarf(self->mod, &self->bias);
        if (!self->dbg)
                goto error;
 
@@ -676,37 +675,42 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
 }
 
 /* Convert subprogram DIE to trace point */
-static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
-                                 bool retprobe, struct probe_trace_point *tp)
+static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
+                                 Dwarf_Addr paddr, bool retprobe,
+                                 struct probe_trace_point *tp)
 {
        Dwarf_Addr eaddr, highaddr;
-       const char *name;
-
-       /* Copy the name of probe point */
-       name = dwarf_diename(sp_die);
-       if (name) {
-               if (dwarf_entrypc(sp_die, &eaddr) != 0) {
-                       pr_warning("Failed to get entry address of %s\n",
-                                  dwarf_diename(sp_die));
-                       return -ENOENT;
-               }
-               if (dwarf_highpc(sp_die, &highaddr) != 0) {
-                       pr_warning("Failed to get end address of %s\n",
-                                  dwarf_diename(sp_die));
-                       return -ENOENT;
-               }
-               if (paddr > highaddr) {
-                       pr_warning("Offset specified is greater than size of %s\n",
-                                  dwarf_diename(sp_die));
-                       return -EINVAL;
-               }
-               tp->symbol = strdup(name);
-               if (tp->symbol == NULL)
-                       return -ENOMEM;
-               tp->offset = (unsigned long)(paddr - eaddr);
-       } else
-               /* This function has no name. */
-               tp->offset = (unsigned long)paddr;
+       GElf_Sym sym;
+       const char *symbol;
+
+       /* Verify the address is correct */
+       if (dwarf_entrypc(sp_die, &eaddr) != 0) {
+               pr_warning("Failed to get entry address of %s\n",
+                          dwarf_diename(sp_die));
+               return -ENOENT;
+       }
+       if (dwarf_highpc(sp_die, &highaddr) != 0) {
+               pr_warning("Failed to get end address of %s\n",
+                          dwarf_diename(sp_die));
+               return -ENOENT;
+       }
+       if (paddr > highaddr) {
+               pr_warning("Offset specified is greater than size of %s\n",
+                          dwarf_diename(sp_die));
+               return -EINVAL;
+       }
+
+       /* Get an appropriate symbol from symtab */
+       symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL);
+       if (!symbol) {
+               pr_warning("Failed to find symbol at 0x%lx\n",
+                          (unsigned long)paddr);
+               return -ENOENT;
+       }
+       tp->offset = (unsigned long)(paddr - sym.st_value);
+       tp->symbol = strdup(symbol);
+       if (!tp->symbol)
+               return -ENOMEM;
 
        /* Return probe must be on the head of a subprogram */
        if (retprobe) {
@@ -734,7 +738,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
        }
 
        /* If not a real subprogram, find a real one */
-       if (dwarf_tag(sc_die) != DW_TAG_subprogram) {
+       if (!die_is_func_def(sc_die)) {
                if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
                        pr_warning("Failed to find probe point in any "
                                   "functions.\n");
@@ -980,12 +984,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
        struct dwarf_callback_param *param = data;
        struct probe_finder *pf = param->data;
        struct perf_probe_point *pp = &pf->pev->point;
-       Dwarf_Attribute attr;
 
        /* Check tag and diename */
-       if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
-           !die_compare_name(sp_die, pp->function) ||
-           dwarf_attr(sp_die, DW_AT_declaration, &attr))
+       if (!die_is_func_def(sp_die) ||
+           !die_compare_name(sp_die, pp->function))
                return DWARF_CB_OK;
 
        /* Check declared file */
@@ -1151,7 +1153,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
        tev = &tf->tevs[tf->ntevs++];
 
        /* Trace point should be converted from subprogram DIE */
-       ret = convert_to_trace_point(&pf->sp_die, pf->addr,
+       ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr,
                                     pf->pev->point.retprobe, &tev->point);
        if (ret < 0)
                return ret;
@@ -1183,7 +1185,7 @@ int debuginfo__find_trace_events(struct debuginfo *self,
 {
        struct trace_event_finder tf = {
                        .pf = {.pev = pev, .callback = add_probe_trace_event},
-                       .max_tevs = max_tevs};
+                       .mod = self->mod, .max_tevs = max_tevs};
        int ret;
 
        /* Allocate result tevs array */
@@ -1252,7 +1254,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
        vl = &af->vls[af->nvls++];
 
        /* Trace point should be converted from subprogram DIE */
-       ret = convert_to_trace_point(&pf->sp_die, pf->addr,
+       ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr,
                                     pf->pev->point.retprobe, &vl->point);
        if (ret < 0)
                return ret;
@@ -1291,6 +1293,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self,
 {
        struct available_var_finder af = {
                        .pf = {.pev = pev, .callback = add_available_vars},
+                       .mod = self->mod,
                        .max_vls = max_vls, .externs = externs};
        int ret;
 
@@ -1324,8 +1327,8 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
                                struct perf_probe_point *ppt)
 {
        Dwarf_Die cudie, spdie, indie;
-       Dwarf_Addr _addr, baseaddr;
-       const char *fname = NULL, *func = NULL, *tmp;
+       Dwarf_Addr _addr = 0, baseaddr = 0;
+       const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp;
        int baseline = 0, lineno = 0, ret = 0;
 
        /* Adjust address with bias */
@@ -1346,27 +1349,36 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
        /* Find a corresponding function (name, baseline and baseaddr) */
        if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
                /* Get function entry information */
-               tmp = dwarf_diename(&spdie);
-               if (!tmp ||
+               func = basefunc = dwarf_diename(&spdie);
+               if (!func ||
                    dwarf_entrypc(&spdie, &baseaddr) != 0 ||
-                   dwarf_decl_line(&spdie, &baseline) != 0)
+                   dwarf_decl_line(&spdie, &baseline) != 0) {
+                       lineno = 0;
                        goto post;
-               func = tmp;
+               }
 
-               if (addr == (unsigned long)baseaddr)
+               fname = dwarf_decl_file(&spdie);
+               if (addr == (unsigned long)baseaddr) {
                        /* Function entry - Relative line number is 0 */
                        lineno = baseline;
-               else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
-                                            &indie)) {
+                       goto post;
+               }
+
+               /* Track down the inline functions step by step */
+               while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr,
+                                               &indie)) {
+                       /* There is an inline function */
                        if (dwarf_entrypc(&indie, &_addr) == 0 &&
-                           _addr == addr)
+                           _addr == addr) {
                                /*
                                 * addr is at an inline function entry.
                                 * In this case, lineno should be the call-site
-                                * line number.
+                                * line number. (overwrite lineinfo)
                                 */
                                lineno = die_get_call_lineno(&indie);
-                       else {
+                               fname = die_get_call_file(&indie);
+                               break;
+                       } else {
                                /*
                                 * addr is in an inline function body.
                                 * Since lineno points one of the lines
@@ -1374,19 +1386,27 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
                                 * be the entry line of the inline function.
                                 */
                                tmp = dwarf_diename(&indie);
-                               if (tmp &&
-                                   dwarf_decl_line(&spdie, &baseline) == 0)
-                                       func = tmp;
+                               if (!tmp ||
+                                   dwarf_decl_line(&indie, &baseline) != 0)
+                                       break;
+                               func = tmp;
+                               spdie = indie;
                        }
                }
+               /* Verify the lineno and baseline are in a same file */
+               tmp = dwarf_decl_file(&spdie);
+               if (!tmp || strcmp(tmp, fname) != 0)
+                       lineno = 0;
        }
 
 post:
        /* Make a relative line number or an offset */
        if (lineno)
                ppt->line = lineno - baseline;
-       else if (func)
+       else if (basefunc) {
                ppt->offset = addr - (unsigned long)baseaddr;
+               func = basefunc;
+       }
 
        /* Duplicate strings */
        if (func) {
@@ -1474,7 +1494,7 @@ static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
        return 0;
 }
 
-/* Search function from function name */
+/* Search function definition from function name */
 static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
 {
        struct dwarf_callback_param *param = data;
@@ -1485,7 +1505,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
        if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die)))
                return DWARF_CB_OK;
 
-       if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
+       if (die_is_func_def(sp_die) &&
            die_compare_name(sp_die, lr->function)) {
                lf->fname = dwarf_decl_file(sp_die);
                dwarf_decl_line(sp_die, &lr->offset);
index 17e94d0..3b7d630 100644 (file)
@@ -23,6 +23,7 @@ static inline int is_c_varname(const char *name)
 /* debug information structure */
 struct debuginfo {
        Dwarf           *dbg;
+       Dwfl_Module     *mod;
        Dwfl            *dwfl;
        Dwarf_Addr      bias;
 };
@@ -77,6 +78,7 @@ struct probe_finder {
 
 struct trace_event_finder {
        struct probe_finder     pf;
+       Dwfl_Module             *mod;           /* For solving symbols */
        struct probe_trace_event *tevs;         /* Found trace events */
        int                     ntevs;          /* Number of trace events */
        int                     max_tevs;       /* Max number of trace events */
@@ -84,6 +86,7 @@ struct trace_event_finder {
 
 struct available_var_finder {
        struct probe_finder     pf;
+       Dwfl_Module             *mod;           /* For solving symbols */
        struct variable_list    *vls;           /* Found variable lists */
        int                     nvls;           /* Number of variable lists */
        int                     max_vls;        /* Max no. of variable lists */
index 71b5412..2ac4bc9 100644 (file)
@@ -822,6 +822,8 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
                PyObject *pyevent = pyrf_event__new(event);
                struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
 
+               perf_evlist__mmap_consume(evlist, cpu);
+
                if (pyevent == NULL)
                        return PyErr_NoMemory();
 
index a85e4ae..c0c9795 100644 (file)
@@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
 
        event = find_cache_event(evsel);
        if (!event)
-               die("ug! no event found for type %" PRIu64, evsel->attr.config);
+               die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
 
        pid = raw_field_value(event, "common_pid", data);
 
index cc75a3c..95d91a0 100644 (file)
@@ -56,6 +56,17 @@ static void handler_call_die(const char *handler_name)
        Py_FatalError("problem in Python trace event handler");
 }
 
+/*
+ * Insert val into into the dictionary and decrement the reference counter.
+ * This is necessary for dictionaries since PyDict_SetItemString() does not 
+ * steal a reference, as opposed to PyTuple_SetItem().
+ */
+static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val)
+{
+       PyDict_SetItemString(dict, key, val);
+       Py_DECREF(val);
+}
+
 static void define_value(enum print_arg_type field_type,
                         const char *ev_name,
                         const char *field_name,
@@ -279,11 +290,11 @@ static void python_process_tracepoint(union perf_event *perf_event
                PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
                PyTuple_SetItem(t, n++, PyString_FromString(comm));
        } else {
-               PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu));
-               PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s));
-               PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns));
-               PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid));
-               PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm));
+               pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu));
+               pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s));
+               pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns));
+               pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid));
+               pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm));
        }
        for (field = event->format.fields; field; field = field->next) {
                if (field->flags & FIELD_IS_STRING) {
@@ -313,7 +324,7 @@ static void python_process_tracepoint(union perf_event *perf_event
                if (handler)
                        PyTuple_SetItem(t, n++, obj);
                else
-                       PyDict_SetItemString(dict, field->name, obj);
+                       pydict_set_item_string_decref(dict, field->name, obj);
 
        }
        if (!handler)
@@ -370,21 +381,21 @@ static void python_process_general_event(union perf_event *perf_event
        if (!handler || !PyCallable_Check(handler))
                goto exit;
 
-       PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel)));
-       PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize(
+       pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel)));
+       pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize(
                        (const char *)&evsel->attr, sizeof(evsel->attr)));
-       PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize(
+       pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize(
                        (const char *)sample, sizeof(*sample)));
-       PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize(
+       pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize(
                        (const char *)sample->raw_data, sample->raw_size));
-       PyDict_SetItemString(dict, "comm",
+       pydict_set_item_string_decref(dict, "comm",
                        PyString_FromString(thread->comm));
        if (al->map) {
-               PyDict_SetItemString(dict, "dso",
+               pydict_set_item_string_decref(dict, "dso",
                        PyString_FromString(al->map->dso->name));
        }
        if (al->sym) {
-               PyDict_SetItemString(dict, "symbol",
+               pydict_set_item_string_decref(dict, "symbol",
                        PyString_FromString(al->sym->name));
        }
 
index 51f5edf..568b750 100644 (file)
@@ -256,6 +256,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
                tool->sample = process_event_sample_stub;
        if (tool->mmap == NULL)
                tool->mmap = process_event_stub;
+       if (tool->mmap2 == NULL)
+               tool->mmap2 = process_event_stub;
        if (tool->comm == NULL)
                tool->comm = process_event_stub;
        if (tool->fork == NULL)
@@ -531,6 +533,9 @@ static int flush_sample_queue(struct perf_session *s,
                return 0;
 
        list_for_each_entry_safe(iter, tmp, head, list) {
+               if (session_done())
+                       return 0;
+
                if (iter->timestamp > limit)
                        break;
 
@@ -1160,7 +1165,6 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
        }
 }
 
-#define session_done() (*(volatile int *)(&session_done))
 volatile int session_done;
 
 static int __perf_session__process_pipe_events(struct perf_session *self,
@@ -1308,7 +1312,7 @@ int __perf_session__process_events(struct perf_session *session,
        file_offset = page_offset;
        head = data_offset - page_offset;
 
-       if (data_offset + data_size < file_size)
+       if (data_size && (data_offset + data_size < file_size))
                file_size = data_offset + data_size;
 
        progress_next = file_size / 16;
@@ -1372,10 +1376,13 @@ more:
                                    "Processing events...");
        }
 
+       err = 0;
+       if (session_done())
+               goto out_err;
+
        if (file_pos < file_size)
                goto more;
 
-       err = 0;
        /* do the final flush for ordered samples */
        session->ordered_samples.next_flush = ULLONG_MAX;
        err = flush_sample_queue(session, tool);
index 3aa75fb..04bf737 100644 (file)
@@ -124,4 +124,8 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
 
 #define perf_session__set_tracepoints_handlers(session, array) \
        __perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array))
+
+extern volatile int session_done;
+
+#define session_done() (*(volatile int *)(&session_done))
 #endif /* __PERF_SESSION_H */
index a7b9ab5..a9c829b 100644 (file)
@@ -8,6 +8,22 @@
 #include "symbol.h"
 #include "debug.h"
 
+#ifndef HAVE_ELF_GETPHDRNUM
+static int elf_getphdrnum(Elf *elf, size_t *dst)
+{
+       GElf_Ehdr gehdr;
+       GElf_Ehdr *ehdr;
+
+       ehdr = gelf_getehdr(elf, &gehdr);
+       if (!ehdr)
+               return -1;
+
+       *dst = ehdr->e_phnum;
+
+       return 0;
+}
+#endif
+
 #ifndef NT_GNU_BUILD_ID
 #define NT_GNU_BUILD_ID 3
 #endif
index fe7a27d..e9e1c03 100644 (file)
@@ -186,7 +186,7 @@ void parse_proc_kallsyms(struct pevent *pevent,
        char *next = NULL;
        char *addr_str;
        char *mod;
-       char *fmt;
+       char *fmt = NULL;
 
        line = strtok_r(file, "\n", &next);
        while (line) {
index 4fa655d..41bd855 100644 (file)
@@ -151,7 +151,7 @@ static int check_timer_create(int which)
        fflush(stdout);
 
        done = 0;
-       timer_create(which, NULL, &id);
+       err = timer_create(which, NULL, &id);
        if (err < 0) {
                perror("Can't create timer\n");
                return -1;
index 979bff4..1cf9ccb 100644 (file)
@@ -1064,10 +1064,12 @@ EXPORT_SYMBOL_GPL(gfn_to_hva);
 unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable)
 {
        struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
-       if (writable)
+       unsigned long hva = __gfn_to_hva_many(slot, gfn, NULL, false);
+
+       if (!kvm_is_error_hva(hva) && writable)
                *writable = !memslot_is_readonly(slot);
 
-       return __gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL, false);
+       return hva;
 }
 
 static int kvm_read_hva(void *data, void __user *hva, int len)
@@ -3089,7 +3091,7 @@ static const struct file_operations *stat_fops[] = {
 
 static int kvm_init_debug(void)
 {
-       int r = -EFAULT;
+       int r = -EEXIST;
        struct kvm_stats_debugfs_item *p;
 
        kvm_debugfs_dir = debugfs_create_dir("kvm", NULL);